From 521ea89513482821a63fb5e50c6a7f54a33e5e6f Mon Sep 17 00:00:00 2001 From: Brian Canini <canini.16@osu.edu> Date: Fri, 22 Jan 2021 12:42:32 -0500 Subject: [PATCH] core security update (8.9.10 => 8.9.13) - book module patch update --- composer.json | 4 +- composer.lock | 56 +-- vendor/composer/installed.json | 60 +-- vendor/pear/archive_tar/.github/FUNDING.yml | 2 + .../pear/archive_tar/.github/dependabot.yml | 11 + .../archive_tar/.github/workflows/build.yml | 41 ++ vendor/pear/archive_tar/Archive/Tar.php | 22 +- vendor/pear/archive_tar/package.xml | 26 +- .../archive_tar/tests/out_of_path_fnames.phpt | 18 + web/core/MAINTAINERS.txt | 1 - web/core/PATCHES.txt | 2 +- web/core/composer.json | 4 +- web/core/core.api.php | 15 +- web/core/core.libraries.yml | 2 +- web/core/drupalci.yml | 14 +- web/core/install.php | 4 + web/core/lib/Drupal.php | 2 +- .../Core/Annotation/ContextDefinition.php | 3 +- .../Entity/ThirdPartySettingsInterface.php | 6 +- .../Drupal/Core/Entity/EntityViewBuilder.php | 2 +- .../Core/Entity/Query/Sql/pgsql/Condition.php | 11 +- .../Sql/SqlContentEntityStorageSchema.php | 6 +- web/core/lib/Drupal/Core/File/FileSystem.php | 16 +- web/core/lib/Drupal/Core/Form/form.api.php | 6 +- .../Core/Language/LanguageInterface.php | 7 + .../lib/Drupal/Core/Mail/MailInterface.php | 20 +- .../Translator/FileTranslation.php | 3 +- .../Plugin/Block/TestContextAwareBlock.php | 2 +- web/core/modules/book/src/BookManager.php | 2 +- .../book/src/Form/BookSettingsForm.php | 4 +- .../book/tests/src/Functional/BookTest.php | 8 + .../BookJavascriptTest.php | 22 ++ .../contact/src/MessageViewBuilder.php | 4 - .../src/Functional/ContactPersonalTest.php | 21 +- web/core/modules/editor/editor.module | 4 +- .../tests/src/Functional/EditorAdminTest.php | 17 + .../Plugin/Field/FieldWidget/ImageWidget.php | 13 +- .../ImageFieldWidgetMultipleTest.php | 75 ++++ .../language/src/Form/LanguageFormBase.php | 2 +- .../src/Functional/NodeLoadMultipleTest.php | 68 ++++ .../Plugin/Field/FieldWidget/PathWidget.php | 2 +- .../tests/src/Functional/PathAliasTest.php | 6 + .../migrations/d6_term_node_revision.yml | 2 +- .../Migrate/d6/MigrateTermNodeComplete.php | 12 + .../Plugin/views/query/PostgresqlDateSql.php | 2 +- .../views/query/PostgresqlDateSqlTest.php | 2 +- web/core/scripts/dev/commit-code-check.sh | 352 ++++++++++++++++++ .../Core/Field/MapBaseFieldTest.php | 65 ++++ .../Core/Installer/InstallerLanguageTest.php | 3 +- .../Annotation/ContextDefinitionTest.php | 1 + .../Listeners/DeprecationListenerTrait.php | 2 +- .../translations/drupal-8.0.0.fr____CA.po | 4 + .../files/translations/drupal-8.0.x.fr-CA.po | 0 53 files changed, 923 insertions(+), 136 deletions(-) create mode 100644 vendor/pear/archive_tar/.github/FUNDING.yml create mode 100644 vendor/pear/archive_tar/.github/dependabot.yml create mode 100644 vendor/pear/archive_tar/.github/workflows/build.yml create mode 100644 vendor/pear/archive_tar/tests/out_of_path_fnames.phpt create mode 100644 web/core/modules/image/tests/src/FunctionalJavascript/ImageFieldWidgetMultipleTest.php create mode 100755 web/core/scripts/dev/commit-code-check.sh create mode 100644 web/core/tests/Drupal/KernelTests/Core/Field/MapBaseFieldTest.php create mode 100644 web/core/tests/fixtures/files/translations/drupal-8.0.0.fr____CA.po create mode 100644 web/core/tests/fixtures/files/translations/drupal-8.0.x.fr-CA.po diff --git a/composer.json b/composer.json index f1e6d27d65..b52660f43f 100644 --- a/composer.json +++ b/composer.json @@ -106,7 +106,7 @@ "drupal/console": "1.9.7", "drupal/content_access": "1.0-alpha1", "drupal/core-composer-scaffold": "^9.0", - "drupal/core-recommended": "8.9.10", + "drupal/core-recommended": "8.9.13", "drupal/crop": "2.1", "drupal/ctools": "3.4", "drupal/devel": "2.0", @@ -271,7 +271,7 @@ "patches": { "drupal/core": { "2799049": "patches/role_based_email_access-2799049-d87.patch", - "2862291": "https://www.drupal.org/files/issues/2019-07-02/2862291-21.patch", + "2862291": "https://www.drupal.org/files/issues/2020-12-08/2862291-29.patch", "2949017": "https://www.drupal.org/files/issues/2019-12-12/2949017-59.patch" }, "drupal/addtocalendar": { diff --git a/composer.lock b/composer.lock index 62e3c42761..61ba46e334 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5d5f9a310b1ba9a78bdeea9d9f10d611", + "content-hash": "026a0ebd2567a6fed694c15d5f98fe44", "packages": [ { "name": "alchemy/zippy", @@ -3361,16 +3361,16 @@ }, { "name": "drupal/core", - "version": "8.9.10", + "version": "8.9.13", "source": { "type": "git", "url": "https://github.com/drupal/core.git", - "reference": "e725c01cdf6fb6d8b330a27fa75caab91034805a" + "reference": "a53db77b55a035453d7229e0c3069f8591cb4cb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core/zipball/e725c01cdf6fb6d8b330a27fa75caab91034805a", - "reference": "e725c01cdf6fb6d8b330a27fa75caab91034805a", + "url": "https://api.github.com/repos/drupal/core/zipball/a53db77b55a035453d7229e0c3069f8591cb4cb6", + "reference": "a53db77b55a035453d7229e0c3069f8591cb4cb6", "shasum": "" }, "require": { @@ -3397,8 +3397,8 @@ "laminas/laminas-diactoros": "^1.8", "laminas/laminas-feed": "^2.12", "masterminds/html5": "^2.1", - "pear/archive_tar": "^1.4.11", - "php": ">=7.0.8", + "pear/archive_tar": "^1.4.12", + "php": "^7.0.8", "psr/log": "^1.0", "stack/builder": "^1.0", "symfony-cmf/routing": "^1.4", @@ -3568,7 +3568,7 @@ }, "patches_applied": { "2799049": "patches/role_based_email_access-2799049-d87.patch", - "2862291": "https://www.drupal.org/files/issues/2019-07-02/2862291-21.patch", + "2862291": "https://www.drupal.org/files/issues/2020-12-08/2862291-29.patch", "2949017": "https://www.drupal.org/files/issues/2019-12-12/2949017-59.patch" } }, @@ -3593,7 +3593,7 @@ "GPL-2.0-or-later" ], "description": "Drupal is an open source content management platform powering millions of websites and applications.", - "time": "2020-11-26T01:49:15+00:00" + "time": "2021-01-19T23:11:00+00:00" }, { "name": "drupal/core-composer-scaffold", @@ -3644,16 +3644,16 @@ }, { "name": "drupal/core-recommended", - "version": "8.9.10", + "version": "8.9.13", "source": { "type": "git", "url": "https://github.com/drupal/core-recommended.git", - "reference": "106e2a3e6f00f8867d1867e9d7b1376961a264f7" + "reference": "7a940fd5b64d2b22366680e2a60d96bf2c10089d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core-recommended/zipball/106e2a3e6f00f8867d1867e9d7b1376961a264f7", - "reference": "106e2a3e6f00f8867d1867e9d7b1376961a264f7", + "url": "https://api.github.com/repos/drupal/core-recommended/zipball/7a940fd5b64d2b22366680e2a60d96bf2c10089d", + "reference": "7a940fd5b64d2b22366680e2a60d96bf2c10089d", "shasum": "" }, "require": { @@ -3665,7 +3665,7 @@ "doctrine/common": "v2.7.3", "doctrine/inflector": "v1.2.0", "doctrine/lexer": "1.0.2", - "drupal/core": "8.9.10", + "drupal/core": "8.9.13", "easyrdf/easyrdf": "0.9.1", "egulias/email-validator": "2.1.17", "guzzlehttp/guzzle": "6.5.4", @@ -3678,7 +3678,7 @@ "laminas/laminas-zendframework-bridge": "1.0.4", "masterminds/html5": "2.3.0", "paragonie/random_compat": "v9.99.99", - "pear/archive_tar": "1.4.11", + "pear/archive_tar": "1.4.12", "pear/console_getopt": "v1.4.3", "pear/pear-core-minimal": "v1.10.10", "pear/pear_exception": "v1.0.1", @@ -3722,7 +3722,7 @@ "GPL-2.0-or-later" ], "description": "Locked core dependencies; require this project INSTEAD OF drupal/core.", - "time": "2020-11-26T01:49:15+00:00" + "time": "2021-01-19T23:11:00+00:00" }, { "name": "drupal/crop", @@ -10419,16 +10419,16 @@ }, { "name": "pear/archive_tar", - "version": "1.4.11", + "version": "1.4.12", "source": { "type": "git", "url": "https://github.com/pear/Archive_Tar.git", - "reference": "17d355cb7d3c4ff08e5729f29cd7660145208d9d" + "reference": "19bb8e95490d3e3ad92fcac95500ca80bdcc7495" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/17d355cb7d3c4ff08e5729f29cd7660145208d9d", - "reference": "17d355cb7d3c4ff08e5729f29cd7660145208d9d", + "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/19bb8e95490d3e3ad92fcac95500ca80bdcc7495", + "reference": "19bb8e95490d3e3ad92fcac95500ca80bdcc7495", "shasum": "" }, "require": { @@ -10481,7 +10481,17 @@ "archive", "tar" ], - "time": "2020-11-19T22:10:24+00:00" + "funding": [ + { + "url": "https://github.com/mrook", + "type": "github" + }, + { + "url": "https://www.patreon.com/michielrook", + "type": "patreon" + } + ], + "time": "2021-01-18T19:32:54+00:00" }, { "name": "pear/console_getopt", @@ -13352,12 +13362,12 @@ "version": "1.9.1", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", + "url": "https://github.com/webmozarts/assert.git", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 9bd9a9a89e..abcc40928a 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -3468,17 +3468,17 @@ }, { "name": "drupal/core", - "version": "8.9.10", - "version_normalized": "8.9.10.0", + "version": "8.9.13", + "version_normalized": "8.9.13.0", "source": { "type": "git", "url": "https://github.com/drupal/core.git", - "reference": "e725c01cdf6fb6d8b330a27fa75caab91034805a" + "reference": "a53db77b55a035453d7229e0c3069f8591cb4cb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core/zipball/e725c01cdf6fb6d8b330a27fa75caab91034805a", - "reference": "e725c01cdf6fb6d8b330a27fa75caab91034805a", + "url": "https://api.github.com/repos/drupal/core/zipball/a53db77b55a035453d7229e0c3069f8591cb4cb6", + "reference": "a53db77b55a035453d7229e0c3069f8591cb4cb6", "shasum": "" }, "require": { @@ -3505,8 +3505,8 @@ "laminas/laminas-diactoros": "^1.8", "laminas/laminas-feed": "^2.12", "masterminds/html5": "^2.1", - "pear/archive_tar": "^1.4.11", - "php": ">=7.0.8", + "pear/archive_tar": "^1.4.12", + "php": "^7.0.8", "psr/log": "^1.0", "stack/builder": "^1.0", "symfony-cmf/routing": "^1.4", @@ -3645,7 +3645,7 @@ "drupal/workflows": "self.version", "drupal/workspaces": "self.version" }, - "time": "2020-11-26T01:49:15+00:00", + "time": "2021-01-19T23:11:00+00:00", "type": "drupal-core", "extra": { "drupal-scaffold": { @@ -3677,7 +3677,7 @@ }, "patches_applied": { "2799049": "patches/role_based_email_access-2799049-d87.patch", - "2862291": "https://www.drupal.org/files/issues/2019-07-02/2862291-21.patch", + "2862291": "https://www.drupal.org/files/issues/2020-12-08/2862291-29.patch", "2949017": "https://www.drupal.org/files/issues/2019-12-12/2949017-59.patch" } }, @@ -3755,17 +3755,17 @@ }, { "name": "drupal/core-recommended", - "version": "8.9.10", - "version_normalized": "8.9.10.0", + "version": "8.9.13", + "version_normalized": "8.9.13.0", "source": { "type": "git", "url": "https://github.com/drupal/core-recommended.git", - "reference": "106e2a3e6f00f8867d1867e9d7b1376961a264f7" + "reference": "7a940fd5b64d2b22366680e2a60d96bf2c10089d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core-recommended/zipball/106e2a3e6f00f8867d1867e9d7b1376961a264f7", - "reference": "106e2a3e6f00f8867d1867e9d7b1376961a264f7", + "url": "https://api.github.com/repos/drupal/core-recommended/zipball/7a940fd5b64d2b22366680e2a60d96bf2c10089d", + "reference": "7a940fd5b64d2b22366680e2a60d96bf2c10089d", "shasum": "" }, "require": { @@ -3777,7 +3777,7 @@ "doctrine/common": "v2.7.3", "doctrine/inflector": "v1.2.0", "doctrine/lexer": "1.0.2", - "drupal/core": "8.9.10", + "drupal/core": "8.9.13", "easyrdf/easyrdf": "0.9.1", "egulias/email-validator": "2.1.17", "guzzlehttp/guzzle": "6.5.4", @@ -3790,7 +3790,7 @@ "laminas/laminas-zendframework-bridge": "1.0.4", "masterminds/html5": "2.3.0", "paragonie/random_compat": "v9.99.99", - "pear/archive_tar": "1.4.11", + "pear/archive_tar": "1.4.12", "pear/console_getopt": "v1.4.3", "pear/pear-core-minimal": "v1.10.10", "pear/pear_exception": "v1.0.1", @@ -3828,7 +3828,7 @@ "conflict": { "webflo/drupal-core-strict": "*" }, - "time": "2020-11-26T01:49:15+00:00", + "time": "2021-01-19T23:11:00+00:00", "type": "metapackage", "notification-url": "https://packagist.org/downloads/", "license": [ @@ -10752,17 +10752,17 @@ }, { "name": "pear/archive_tar", - "version": "1.4.11", - "version_normalized": "1.4.11.0", + "version": "1.4.12", + "version_normalized": "1.4.12.0", "source": { "type": "git", "url": "https://github.com/pear/Archive_Tar.git", - "reference": "17d355cb7d3c4ff08e5729f29cd7660145208d9d" + "reference": "19bb8e95490d3e3ad92fcac95500ca80bdcc7495" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/17d355cb7d3c4ff08e5729f29cd7660145208d9d", - "reference": "17d355cb7d3c4ff08e5729f29cd7660145208d9d", + "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/19bb8e95490d3e3ad92fcac95500ca80bdcc7495", + "reference": "19bb8e95490d3e3ad92fcac95500ca80bdcc7495", "shasum": "" }, "require": { @@ -10777,7 +10777,7 @@ "ext-xz": "Lzma2 compression support.", "ext-zlib": "Gzip compression support." }, - "time": "2020-11-19T22:10:24+00:00", + "time": "2021-01-18T19:32:54+00:00", "type": "library", "extra": { "branch-alias": { @@ -10816,6 +10816,16 @@ "keywords": [ "archive", "tar" + ], + "funding": [ + { + "url": "https://github.com/mrook", + "type": "github" + }, + { + "url": "https://www.patreon.com/michielrook", + "type": "patreon" + } ] }, { @@ -13784,12 +13794,12 @@ "version_normalized": "1.9.1.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", + "url": "https://github.com/webmozarts/assert.git", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, diff --git a/vendor/pear/archive_tar/.github/FUNDING.yml b/vendor/pear/archive_tar/.github/FUNDING.yml new file mode 100644 index 0000000000..4a0f72b641 --- /dev/null +++ b/vendor/pear/archive_tar/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [mrook] +patreon: michielrook diff --git a/vendor/pear/archive_tar/.github/dependabot.yml b/vendor/pear/archive_tar/.github/dependabot.yml new file mode 100644 index 0000000000..a51bb0bd4a --- /dev/null +++ b/vendor/pear/archive_tar/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "composer" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" diff --git a/vendor/pear/archive_tar/.github/workflows/build.yml b/vendor/pear/archive_tar/.github/workflows/build.yml new file mode 100644 index 0000000000..b972caea6b --- /dev/null +++ b/vendor/pear/archive_tar/.github/workflows/build.yml @@ -0,0 +1,41 @@ +on: + push: + branches: + - master + pull_request: + +jobs: + test: + runs-on: ${{ matrix.operating-system }} + strategy: + fail-fast: true + matrix: + operating-system: [ ubuntu-latest ] + php: [ '5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0' ] + dependencies: [ 'locked' ] + + name: PHP ${{ matrix.php }} on ${{ matrix.operating-system }} with ${{ matrix.dependencies }} dependencies + + steps: + - uses: actions/checkout@v2 + name: Checkout repository + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + + - uses: ramsey/composer-install@v1 + with: + dependency-versions: ${{ matrix.dependencies }} + + - name: Install PEAR + run: | + sudo apt-get install php-pear + + - name: Run tests + run: | + sudo pear install -f package.xml + pear version + pear run-tests -qr tests/ || cat run-tests.log + for i in `find tests/ -name '*.out'`; do echo "$i"; cat "$i"; done diff --git a/vendor/pear/archive_tar/Archive/Tar.php b/vendor/pear/archive_tar/Archive/Tar.php index 92710741c5..76771d5b54 100644 --- a/vendor/pear/archive_tar/Archive/Tar.php +++ b/vendor/pear/archive_tar/Archive/Tar.php @@ -1397,16 +1397,20 @@ public function _writeHeader($p_filename, $p_stored_filename) $v_magic = 'ustar '; $v_version = ' '; + $v_uname = ''; + $v_gname = ''; if (function_exists('posix_getpwuid')) { $userinfo = posix_getpwuid($v_info[4]); $groupinfo = posix_getgrgid($v_info[5]); - $v_uname = $userinfo['name']; - $v_gname = $groupinfo['name']; - } else { - $v_uname = ''; - $v_gname = ''; + if (isset($userinfo['name'])) { + $v_uname = $userinfo['name']; + } + + if (isset($groupinfo['name'])) { + $v_gname = $groupinfo['name']; + } } $v_devmajor = ''; @@ -2120,6 +2124,14 @@ public function _extractList( } } } elseif ($v_header['typeflag'] == "2") { + if (strpos(realpath(dirname($v_header['link'])), realpath($p_path)) !== 0) { + $this->_error( + 'Out-of-path file extraction {' + . $v_header['filename'] . ' --> ' . + $v_header['link'] . '}' + ); + return false; + } if (!$p_symlinks) { $this->_warning('Symbolic links are not allowed. ' . 'Unable to extract {' diff --git a/vendor/pear/archive_tar/package.xml b/vendor/pear/archive_tar/package.xml index 6edf4fd103..5da8ee884a 100644 --- a/vendor/pear/archive_tar/package.xml +++ b/vendor/pear/archive_tar/package.xml @@ -32,10 +32,10 @@ Also Lzma2 compressed archives are supported with xz extension.</description> <email>stig@php.net</email> <active>no</active> </helper> - <date>2020-11-19</date> - <time>22:06:48</time> + <date>2021-01-18</date> + <time>19:29:56</time> <version> - <release>1.4.11</release> + <release>1.4.12</release> <api>1.4.0</api> </version> <stability> @@ -44,8 +44,7 @@ Also Lzma2 compressed archives are supported with xz extension.</description> </stability> <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license> <notes> -* Fix Bug #27002: Filename manipulation vulnerabilities (CVE-2020-28948 / - CVE-2020-28949) [mrook] +* Fix Bug #27008: Symlink out-of-path write vulnerability (CVE-2020-36193) [mrook] </notes> <contents> <dir name="/"> @@ -75,7 +74,22 @@ Also Lzma2 compressed archives are supported with xz extension.</description> </dependencies> <phprelease /> <changelog> - <release> + <release> + <version> + <release>1.4.11</release> + <api>1.4.0</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <date>2020-11-19</date> + <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license> + <notes> +* Fix Bug #27002: Filename manipulation vulnerabilities (CVE-2020-28948 / CVE-2020-28949) [mrook] + </notes> + </release> + <release> <version> <release>1.4.10</release> <api>1.4.0</api> diff --git a/vendor/pear/archive_tar/tests/out_of_path_fnames.phpt b/vendor/pear/archive_tar/tests/out_of_path_fnames.phpt new file mode 100644 index 0000000000..a26100c812 --- /dev/null +++ b/vendor/pear/archive_tar/tests/out_of_path_fnames.phpt @@ -0,0 +1,18 @@ +--TEST-- +tests writes to out-of-path filenames +--SKIPIF-- +--FILE-- +<?php +require_once dirname(__FILE__) . '/setup.php.inc'; +$tar = new Archive_Tar(dirname(__FILE__) . '/out_of_path_symlink.tar'); +$tar->extract(); +$phpunit->assertErrors(array(array('package' => 'PEAR_Error', 'message' => "Out-of-path file extraction {symlink --> /tmp/}")), 'after 1'); +$phpunit->assertFileNotExists('symlink/whatever-filename', 'Out-of-path filename should not have succeeded'); +echo 'tests done'; +?> +--CLEAN-- +<?php +@unlink("symlink"); +?> +--EXPECT-- +tests done diff --git a/web/core/MAINTAINERS.txt b/web/core/MAINTAINERS.txt index 9383214f56..153666a745 100644 --- a/web/core/MAINTAINERS.txt +++ b/web/core/MAINTAINERS.txt @@ -541,7 +541,6 @@ participate in mentoring. - Lucas Hedding 'heddn' https://www.drupal.org/u/heddn - Tara King 'sparklingrobots' https://www.drupal.org/u/sparklingrobots - Rachel Lawson 'rachel_norfolk' https://www.drupal.org/u/rachel_norfolk -- Valery Lourie 'valthebald' https://www.drupal.org/u/valthebald - Elli Ludwigson 'ekl1773' https://www.drupal.org/u/ekl1773 - Jess Myrbo 'xjm' https://www.drupal.org/u/xjm - Matthew Radcliffe 'mradcliffe' https://www.drupal.org/u/mradcliffe diff --git a/web/core/PATCHES.txt b/web/core/PATCHES.txt index 4edba0f65b..19c8c45be3 100644 --- a/web/core/PATCHES.txt +++ b/web/core/PATCHES.txt @@ -6,7 +6,7 @@ Source: patches/role_based_email_access-2799049-d87.patch 2862291 -Source: https://www.drupal.org/files/issues/2019-07-02/2862291-21.patch +Source: https://www.drupal.org/files/issues/2020-12-08/2862291-29.patch 2949017 diff --git a/web/core/composer.json b/web/core/composer.json index fb70c051cf..f7ba3f2403 100644 --- a/web/core/composer.json +++ b/web/core/composer.json @@ -17,7 +17,7 @@ "ext-SPL": "*", "ext-tokenizer": "*", "ext-xml": "*", - "php": ">=7.0.8", + "php": "^7.0.8", "symfony/class-loader": "~3.4.0", "symfony/console": "~3.4.0", "symfony/dependency-injection": "~3.4.26", @@ -46,7 +46,7 @@ "laminas/laminas-diactoros": "^1.8", "composer/semver": "^1.0", "asm89/stack-cors": "^1.1", - "pear/archive_tar": "^1.4.11", + "pear/archive_tar": "^1.4.12", "psr/log": "^1.0" }, "conflict": { diff --git a/web/core/core.api.php b/web/core/core.api.php index 23f153f2c0..5c0c86fb20 100644 --- a/web/core/core.api.php +++ b/web/core/core.api.php @@ -64,10 +64,9 @@ * * @section more_info Further information * - * - @link https://api.drupal.org/api/drupal/groups/8 All topics @endlink * - @link https://www.drupal.org/project/examples Examples project (sample modules) @endlink * - @link https://www.drupal.org/list-changes API change notices @endlink - * - @link https://www.drupal.org/developing/api/8 Drupal 8 API longer references @endlink + * - @link https://www.drupal.org/docs/drupal-apis Drupal API longer references @endlink */ /** @@ -208,7 +207,7 @@ * information. See the @link info_types Information types topic @endlink for * an overview of the different types of information. The sections below have * more information about the configuration API; see - * https://www.drupal.org/developing/api/8/configuration for more details. + * https://www.drupal.org/docs/drupal-apis/configuration-api for more details. * * @section sec_storage Configuration storage * In Drupal, there is a concept of the "active" configuration, which is the @@ -576,9 +575,9 @@ * * Cache contexts are services tagged with 'cache.context', whose classes * implement \Drupal\Core\Cache\Context\CacheContextInterface. See - * https://www.drupal.org/developing/api/8/cache/contexts for more information - * on cache contexts, including a list of the contexts that exist in Drupal - * core, and information on how to define your own contexts. See the + * https://www.drupal.org/docs/drupal-apis/cache-api/cache-contexts for more + * information on cache contexts, including a list of the contexts that exist in + * Drupal core, and information on how to define your own contexts. See the * @link container Services and the Dependency Injection Container @endlink * topic for more information about services. * @@ -1242,10 +1241,10 @@ * site; CSS files, which alter the styling applied to the HTML; and * JavaScript, Flash, images, and other files. For more information, see the * @link theme_render Theme system and render API topic @endlink and - * https://www.drupal.org/docs/8/theming + * https://www.drupal.org/docs/theming-drupal * - Modules: Modules add to or alter the behavior and functionality of Drupal, * by using one or more of the methods listed below. For more information - * about creating modules, see https://www.drupal.org/developing/modules/8 + * about creating modules, see https://www.drupal.org/docs/creating-custom-modules * - Installation profiles: Installation profiles can be used to * create distributions, which are complete specific-purpose packages of * Drupal including additional modules, themes, and data. For more diff --git a/web/core/core.libraries.yml b/web/core/core.libraries.yml index a17ce7c0e0..e1384470a8 100644 --- a/web/core/core.libraries.yml +++ b/web/core/core.libraries.yml @@ -21,7 +21,7 @@ classList: gpl-compatible: true js: assets/vendor/classList/classList.min.js: { weight: -21, browsers: { IE: 'lte IE 9', '!IE': false }, minified: true } - deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the the native browser implementation instead. See https://www.drupal.org/node/3089511 + deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the native browser implementation instead. See https://www.drupal.org/node/3089511 ckeditor: remote: https://github.com/ckeditor/ckeditor-dev diff --git a/web/core/drupalci.yml b/web/core/drupalci.yml index 1cec41cef3..00baeca187 100644 --- a/web/core/drupalci.yml +++ b/web/core/drupalci.yml @@ -4,15 +4,13 @@ build: assessment: validate_codebase: - phplint: - eslint: - # A test must pass eslinting standards check in order to continue processing. - halt-on-fail: false - phpcs: - # phpcs will use core's specified version of Coder. - sniff-all-files: false - halt-on-fail: false + # Core's code quality is checked by container_command.commit_checks. testing: + # Run code quality checks. + container_command.commit-checks: + commands: + - "core/scripts/dev/commit-code-check.sh --drupalci" + halt-on-fail: true # run_tests task is executed several times in order of performance speeds. # halt-on-fail can be set on the run_tests tasks in order to fail fast. # suppress-deprecations is false in order to be alerted to usages of diff --git a/web/core/install.php b/web/core/install.php index 8cc3880c81..f3d04220d8 100644 --- a/web/core/install.php +++ b/web/core/install.php @@ -29,6 +29,10 @@ print 'Your PHP installation is too old. Drupal requires at least PHP 7.0.8. See the <a href="https://www.drupal.org/requirements">system requirements</a> page for more information.'; exit; } +elseif (version_compare(PHP_VERSION, '8.0', '>=')) { + print 'Update to the latest release of Drupal 9 for improved PHP 8 support, or use PHP 7.4. See the <a href="https://www.drupal.org/requirements">system requirements</a> page for more information.'; + exit; +} // Initialize the autoloader. $class_loader = require_once $root_path . '/autoload.php'; diff --git a/web/core/lib/Drupal.php b/web/core/lib/Drupal.php index ee860ae0ac..f156199060 100644 --- a/web/core/lib/Drupal.php +++ b/web/core/lib/Drupal.php @@ -82,7 +82,7 @@ class Drupal { /** * The current system version. */ - const VERSION = '8.9.10'; + const VERSION = '8.9.13'; /** * Core API compatibility. diff --git a/web/core/lib/Drupal/Core/Annotation/ContextDefinition.php b/web/core/lib/Drupal/Core/Annotation/ContextDefinition.php index 0284e9230e..695bf1169a 100644 --- a/web/core/lib/Drupal/Core/Annotation/ContextDefinition.php +++ b/web/core/lib/Drupal/Core/Annotation/ContextDefinition.php @@ -3,7 +3,6 @@ namespace Drupal\Core\Annotation; use Drupal\Component\Annotation\Plugin; -use Drupal\Core\StringTranslation\TranslatableMarkup; /** * @defgroup plugin_context Annotation for context definition @@ -106,7 +105,7 @@ public function __construct(array $values) { // used in the classes they pass to. foreach (['label', 'description'] as $key) { // @todo Remove this workaround in https://www.drupal.org/node/2362727. - if (isset($values[$key]) && $values[$key] instanceof TranslatableMarkup) { + if (isset($values[$key]) && $values[$key] instanceof Translation) { $values[$key] = (string) $values[$key]->get(); } else { diff --git a/web/core/lib/Drupal/Core/Config/Entity/ThirdPartySettingsInterface.php b/web/core/lib/Drupal/Core/Config/Entity/ThirdPartySettingsInterface.php index 990a5b4fe1..3223a41bad 100644 --- a/web/core/lib/Drupal/Core/Config/Entity/ThirdPartySettingsInterface.php +++ b/web/core/lib/Drupal/Core/Config/Entity/ThirdPartySettingsInterface.php @@ -6,9 +6,9 @@ * Interface for configuration entities to store third party information. * * A third party is a module that needs to store tightly coupled information to - * the configuration entity. For example, a module alters the node type form - * can use this to store its configuration so that it will be deployed with the - * node type. + * the configuration entity. For example, a module that alters the node type + * form can use this to store its configuration so that it will be deployed + * with the node type. */ interface ThirdPartySettingsInterface { diff --git a/web/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/web/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index bb052164da..60918bab47 100644 --- a/web/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/web/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -108,7 +108,7 @@ public function __construct(EntityTypeInterface $entity_type, EntityRepositoryIn $this->languageManager = $language_manager; $this->themeRegistry = $theme_registry ?: \Drupal::service('theme.registry'); if (!$entity_display_repository) { - @trigger_error('Calling EntityViewBuilder::__construct() with the $entity_repository argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); + @trigger_error('Calling EntityViewBuilder::__construct() with the $entity_display_repository argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); $entity_display_repository = \Drupal::service('entity_display.repository'); } $this->entityDisplayRepository = $entity_display_repository; diff --git a/web/core/lib/Drupal/Core/Entity/Query/Sql/pgsql/Condition.php b/web/core/lib/Drupal/Core/Entity/Query/Sql/pgsql/Condition.php index 1b561ed48e..f885a0e823 100644 --- a/web/core/lib/Drupal/Core/Entity/Query/Sql/pgsql/Condition.php +++ b/web/core/lib/Drupal/Core/Entity/Query/Sql/pgsql/Condition.php @@ -18,18 +18,17 @@ public static function translateCondition(&$condition, SelectInterface $sql_quer $condition['where'] = 'LOWER(' . $sql_query->escapeField($condition['real_field']) . ') ' . $condition['operator'] . ' ('; $condition['where_args'] = []; - $n = 1; // Only use the array values in case an associative array is passed as an // argument following similar pattern in // \Drupal\Core\Database\Connection::expandArguments(). - foreach ($condition['value'] as $value) { - $condition['where'] .= 'LOWER(:value' . $n . '),'; - $condition['where_args'][':value' . $n] = $value; - $n++; + $where_prefix = str_replace('.', '_', $condition['real_field']); + foreach ($condition['value'] as $key => $value) { + $where_id = $where_prefix . $key; + $condition['where'] .= 'LOWER(:' . $where_id . '),'; + $condition['where_args'][':' . $where_id] = $value; } $condition['where'] = trim($condition['where'], ','); $condition['where'] .= ')'; - return; } parent::translateCondition($condition, $sql_query, $case_sensitive); } diff --git a/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php index b3b085ca68..017c76f588 100644 --- a/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php +++ b/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php @@ -2378,7 +2378,11 @@ protected function getDedicatedTableSchema(FieldStorageDefinitionInterface $stor // A dedicated table only contain rows for actual field values, and no // rows for entities where the field is empty. Thus, we can safely // enforce 'not null' on the columns for the field's required properties. - $data_schema['fields'][$real_name]['not null'] = $properties[$column_name]->isRequired(); + // Fields can have dynamic properties, so we need to make sure that the + // property is statically defined. + if (isset($properties[$column_name])) { + $data_schema['fields'][$real_name]['not null'] = $properties[$column_name]->isRequired(); + } } // Add indexes. diff --git a/web/core/lib/Drupal/Core/File/FileSystem.php b/web/core/lib/Drupal/Core/File/FileSystem.php index 0c5e9a19db..8732645248 100644 --- a/web/core/lib/Drupal/Core/File/FileSystem.php +++ b/web/core/lib/Drupal/Core/File/FileSystem.php @@ -535,12 +535,22 @@ public function prepareDirectory(&$directory, $options = self::MODIFY_PERMISSION } if (!is_dir($directory)) { + if (!($options & static::CREATE_DIRECTORY)) { + return FALSE; + } + // Let mkdir() recursively create directories and use the default // directory permissions. - if ($options & static::CREATE_DIRECTORY) { - return @$this->mkdir($directory, NULL, TRUE); + $success = @$this->mkdir($directory, NULL, TRUE); + if ($success) { + return TRUE; + } + // If the operation failed, check again if the directory was created + // by another process/server, only report a failure if not. In this case + // we still need to ensure the directory is writable. + if (!is_dir($directory)) { + return FALSE; } - return FALSE; } $writable = is_writable($directory); diff --git a/web/core/lib/Drupal/Core/Form/form.api.php b/web/core/lib/Drupal/Core/Form/form.api.php index 976c3fdebc..12adb32965 100644 --- a/web/core/lib/Drupal/Core/Form/form.api.php +++ b/web/core/lib/Drupal/Core/Form/form.api.php @@ -165,7 +165,7 @@ function hook_ajax_render_alter(array &$data) { * $form_state->getFormObject()->getEntity(). * * Implementations are responsible for adding cache contexts/tags/max-age as - * needed. See https://www.drupal.org/developing/api/8/cache. + * needed. See https://www.drupal.org/docs/8/api/cache-api/cache-api. * * In addition to hook_form_alter(), which is called for all forms, there are * two more specific form hooks available. The first, @@ -216,7 +216,7 @@ function hook_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_stat * Provide a form-specific alteration instead of the global hook_form_alter(). * * Implementations are responsible for adding cache contexts/tags/max-age as - * needed. See https://www.drupal.org/developing/api/8/cache. + * needed. See https://www.drupal.org/docs/8/api/cache-api/cache-api. * * Modules can implement hook_form_FORM_ID_alter() to modify a specific form, * rather than implementing hook_form_alter() and checking the form ID, or @@ -259,7 +259,7 @@ function hook_form_FORM_ID_alter(&$form, \Drupal\Core\Form\FormStateInterface $f * Provide a form-specific alteration for shared ('base') forms. * * Implementations are responsible for adding cache contexts/tags/max-age as - * needed. See https://www.drupal.org/developing/api/8/cache. + * needed. See https://www.drupal.org/docs/8/api/cache-api/cache-api. * * By default, when \Drupal::formBuilder()->getForm() is called, Drupal looks * for a function with the same name as the form ID, and uses that function to diff --git a/web/core/lib/Drupal/Core/Language/LanguageInterface.php b/web/core/lib/Drupal/Core/Language/LanguageInterface.php index 2568cb33f3..9e9907a0cc 100644 --- a/web/core/lib/Drupal/Core/Language/LanguageInterface.php +++ b/web/core/lib/Drupal/Core/Language/LanguageInterface.php @@ -51,6 +51,13 @@ interface LanguageInterface { */ const LANGCODE_SITE_DEFAULT = 'site_default'; + /** + * A regex for validating language codes according to W3C specifications. + * + * @see https://www.w3.org/International/articles/language-tags/ + */ + const VALID_LANGCODE_REGEX = '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*'; + /** * The language state when referring to configurable languages. */ diff --git a/web/core/lib/Drupal/Core/Mail/MailInterface.php b/web/core/lib/Drupal/Core/Mail/MailInterface.php index fbde1b86dc..910cfa9133 100644 --- a/web/core/lib/Drupal/Core/Mail/MailInterface.php +++ b/web/core/lib/Drupal/Core/Mail/MailInterface.php @@ -15,12 +15,20 @@ interface MailInterface { * Formats a message prior to sending. * * Allows to preprocess, format, and postprocess a mail message before it is - * passed to the sending system. By default, all messages may contain HTML and - * are converted to plain-text by the Drupal\Core\Mail\Plugin\Mail\PhpMail - * implementation. For example, an alternative implementation could override - * the default implementation and also sanitize the HTML for usage in a MIME- - * encoded email, but still invoking the Drupal\Core\Mail\Plugin\Mail\PhpMail - * implementation to generate an alternate plain-text version for sending. + * passed to the sending system. The message body is received as an array of + * lines that are either strings or objects implementing + * \Drupal\Component\Render\MarkupInterface. It must be converted to the + * format expected by mail() which is a single string that can be either + * plain text or HTML. In the HTML case an alternate plain-text version can + * be returned in $message['plain']. + * + * The conversion process consists of the following steps: + * - If the output is HTML then convert any input line that is a string using + * \Drupal\Component\Utility\Html\Html::Escape(). + * - If the output is plain text then convert any input line that is markup + * using \Drupal\Core\Mail\MailFormatHelper::htmlToText(). + * - Join the input lines into a single string. + * - Wrap long lines using \Drupal\Core\Mail\MailFormatHelper::wrapMail(). * * @param array $message * A message array, as described in hook_mail_alter(). diff --git a/web/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php b/web/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php index b096ba390c..91f70d2f1d 100644 --- a/web/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php +++ b/web/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php @@ -5,6 +5,7 @@ use Drupal\Component\Gettext\PoStreamReader; use Drupal\Component\Gettext\PoMemoryWriter; use Drupal\Core\File\FileSystemInterface; +use Drupal\Core\Language\LanguageInterface; /** * File based string translation. @@ -106,7 +107,7 @@ protected function getTranslationFilesPattern($langcode = NULL) { // The file name matches: drupal-[release version].[language code].po // When provided the $langcode is use as language code. If not provided all // language codes will match. - return '!drupal-[0-9a-z\.-]+\.' . (!empty($langcode) ? preg_quote($langcode, '!') : '[^\.]+') . '\.po$!'; + return '!drupal-[0-9]+\.[0-9]+\.([0-9]+|x)(-[a-z]+[0-9]*)?\.' . (!empty($langcode) ? preg_quote($langcode, '!') : LanguageInterface::VALID_LANGCODE_REGEX) . '\.po$!'; } /** diff --git a/web/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareBlock.php b/web/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareBlock.php index edc66168d3..e73167b423 100644 --- a/web/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareBlock.php +++ b/web/core/modules/block/tests/modules/block_test/src/Plugin/Block/TestContextAwareBlock.php @@ -14,7 +14,7 @@ * admin_label = @Translation("Test context-aware block"), * context_definitions = { * "user" = @ContextDefinition("entity:user", required = FALSE, - * constraints = { "NotNull" = {} } + * label = @Translation("User Context"), constraints = { "NotNull" = {} } * ), * } * ) diff --git a/web/core/modules/book/src/BookManager.php b/web/core/modules/book/src/BookManager.php index c11809a4ec..4245b977fd 100644 --- a/web/core/modules/book/src/BookManager.php +++ b/web/core/modules/book/src/BookManager.php @@ -233,7 +233,7 @@ public function addFormElements(array $form, FormStateInterface $form_state, Nod // The node can become a new book, if it is not one already. $options = [$nid => $this->t('- Create a new book -')] + $options; } - if (!$node->book['bid']) { + if (!$node->book['bid'] || $nid === 'new' || $node->book['original_bid'] === 0) { // The node is not currently in the hierarchy. $options = [0 => $this->t('- None -')] + $options; } diff --git a/web/core/modules/book/src/Form/BookSettingsForm.php b/web/core/modules/book/src/Form/BookSettingsForm.php index 19edbad09a..3a92ba9a82 100644 --- a/web/core/modules/book/src/Form/BookSettingsForm.php +++ b/web/core/modules/book/src/Form/BookSettingsForm.php @@ -55,7 +55,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { - $child_type = array_filter($form_state->getValue('book_child_type')); + $child_type = $form_state->getValue('book_child_type'); if ($form_state->isValueEmpty(['book_allowed_types', $child_type])) { $form_state->setErrorByName('book_child_type', $this->t('The content type for the %add-child link must be one of those selected as an allowed book outline type.', ['%add-child' => $this->t('Add child page')])); } @@ -75,7 +75,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $this->config('book.settings') // Remove unchecked types. ->set('allowed_types', $allowed_types) - ->set('child_type', array_filter($form_state->getValue('book_child_type'))) + ->set('child_type', $form_state->getValue('book_child_type')) ->save(); parent::submitForm($form, $form_state); diff --git a/web/core/modules/book/tests/src/Functional/BookTest.php b/web/core/modules/book/tests/src/Functional/BookTest.php index 24e35e7551..3733699975 100644 --- a/web/core/modules/book/tests/src/Functional/BookTest.php +++ b/web/core/modules/book/tests/src/Functional/BookTest.php @@ -644,4 +644,12 @@ public function testBookNavigationBlockOnUnpublishedBook() { $this->assertText($this->book->label(), 'Unpublished book with "Show block only on book pages" book navigation settings.'); } + /** + * Tests that the book settings form can be saved without error. + */ + public function testSettingsForm() { + $this->drupalLogin($this->adminUser); + $this->drupalPostForm('admin/structure/book/settings', [], 'Save configuration'); + } + } diff --git a/web/core/modules/book/tests/src/FunctionalJavascript/BookJavascriptTest.php b/web/core/modules/book/tests/src/FunctionalJavascript/BookJavascriptTest.php index 7f42954a4e..13537a1695 100644 --- a/web/core/modules/book/tests/src/FunctionalJavascript/BookJavascriptTest.php +++ b/web/core/modules/book/tests/src/FunctionalJavascript/BookJavascriptTest.php @@ -162,4 +162,26 @@ protected function assertOrderInPage(array $items) { $this->assertSame($items, array_values($strings), "Found strings, ordered as: $ordered."); } + /** + * Tests book outline AJAX request. + */ + public function testBookAddOutline() { + $this->drupalLogin($this->drupalCreateUser(['create book content', 'create new books', 'add content to books'])); + $this->drupalGet('node/add/book'); + $assert_session = $this->assertSession(); + $session = $this->getSession(); + $page = $session->getPage(); + + $page->find('css', '#edit-book')->click(); + $book_select = $page->findField("book[bid]"); + $book_select->setValue('new'); + $assert_session->waitForText('This will be the top-level page in this book.'); + $assert_session->pageTextContains('This will be the top-level page in this book.'); + $assert_session->pageTextNotContains('No book selected.'); + $book_select->setValue(0); + $assert_session->waitForText('No book selected.'); + $assert_session->pageTextContains('No book selected.'); + $assert_session->pageTextNotContains('This will be the top-level page in this book.'); + } + } diff --git a/web/core/modules/contact/src/MessageViewBuilder.php b/web/core/modules/contact/src/MessageViewBuilder.php index 5155341d80..4aeb249994 100644 --- a/web/core/modules/contact/src/MessageViewBuilder.php +++ b/web/core/modules/contact/src/MessageViewBuilder.php @@ -4,7 +4,6 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityViewBuilder; -use Drupal\Core\Mail\MailFormatHelper; use Drupal\Core\Render\Element; /** @@ -40,9 +39,6 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N $build[$key]['#label_display'] = 'hidden'; } } - $build['#post_render'][] = function ($html, array $elements) { - return MailFormatHelper::htmlToText($html); - }; } return $build; } diff --git a/web/core/modules/contact/tests/src/Functional/ContactPersonalTest.php b/web/core/modules/contact/tests/src/Functional/ContactPersonalTest.php index 0029b2eb39..26f7a1080d 100644 --- a/web/core/modules/contact/tests/src/Functional/ContactPersonalTest.php +++ b/web/core/modules/contact/tests/src/Functional/ContactPersonalTest.php @@ -4,6 +4,7 @@ use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Render\PlainTextOutput; +use Drupal\Component\Utility\Html; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Test\AssertMailTrait; use Drupal\Tests\BrowserTestBase; @@ -25,7 +26,7 @@ class ContactPersonalTest extends BrowserTestBase { * * @var array */ - public static $modules = ['contact', 'dblog']; + public static $modules = ['contact', 'dblog', 'mail_html_test']; /** * {@inheritdoc} @@ -116,6 +117,20 @@ public function testSendPersonalContactMessage() { $this->assertRaw(new FormattableMarkup('@sender_name (@sender_email) sent @recipient_name an email.', $placeholders)); // Ensure an unescaped version of the email does not exist anywhere. $this->assertNoRaw($this->webUser->getEmail()); + + // Test HTML mails. + $mail_config = $this->config('system.mail'); + $mail_config->set('interface.default', 'test_html_mail_collector'); + $mail_config->save(); + + $this->drupalLogin($this->webUser); + $message['message[0][value]'] = 'This <i>is</i> a more <b>specific</b> <sup>test</sup>, the emails are formatted now.'; + $message = $this->submitPersonalContact($this->contactUser, $message); + + // Assert mail content. + $this->assertMailString('body', 'Hello ' . $variables['@recipient-name'], 1); + $this->assertMailString('body', $this->webUser->getDisplayName(), 1); + $this->assertMailString('body', Html::Escape($message['message[0][value]']), 1); } /** @@ -326,8 +341,8 @@ protected function checkContactAccess($response, $contact_value = NULL) { */ protected function submitPersonalContact(AccountInterface $account, array $message = []) { $message += [ - 'subject[0][value]' => $this->randomMachineName(16), - 'message[0][value]' => $this->randomMachineName(64), + 'subject[0][value]' => $this->randomMachineName(16) . '< " =+ >', + 'message[0][value]' => $this->randomMachineName(64) . '< " =+ >', ]; $this->drupalPostForm('user/' . $account->id() . '/contact', $message, t('Send message')); return $message; diff --git a/web/core/modules/editor/editor.module b/web/core/modules/editor/editor.module index 4ccea91890..0fb4267e77 100644 --- a/web/core/modules/editor/editor.module +++ b/web/core/modules/editor/editor.module @@ -247,7 +247,9 @@ function editor_form_filter_admin_format_submit($form, FormStateInterface $form_ $original_editor->delete(); } - if ($editor_plugin = $form_state->get('editor_plugin')) { + $editor_set = $form_state->getValue(['editor', 'editor']) !== ""; + $subform_array_exists = (!empty($form['editor']['settings']['subform']) && is_array($form['editor']['settings']['subform'])); + if (($editor_plugin = $form_state->get('editor_plugin')) && $editor_set && $subform_array_exists) { $subform_state = SubformState::createForSubform($form['editor']['settings']['subform'], $form, $form_state); $editor_plugin->submitConfigurationForm($form['editor']['settings']['subform'], $subform_state); } diff --git a/web/core/modules/editor/tests/src/Functional/EditorAdminTest.php b/web/core/modules/editor/tests/src/Functional/EditorAdminTest.php index 3065c3fa60..431d77c6c9 100644 --- a/web/core/modules/editor/tests/src/Functional/EditorAdminTest.php +++ b/web/core/modules/editor/tests/src/Functional/EditorAdminTest.php @@ -155,6 +155,23 @@ public function testDisableFormatWithEditor() { $this->assertRaw($text); } + /** + * Tests switching text editor to none does not throw a TypeError. + */ + public function testSwitchEditorToNone() { + $this->enableUnicornEditor(); + $this->drupalLogin($this->adminUser); + $this->drupalGet('admin/config/content/formats/manage/filtered_html'); + $edit = $this->selectUnicornEditor(); + + // Switch editor to 'None'. + $edit = [ + 'editor[editor]' => '', + ]; + $this->submitForm($edit, 'Configure'); + $this->submitForm($edit, 'Save configuration'); + } + /** * Adds an editor to a new format using the UI. * diff --git a/web/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php b/web/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php index 47aa51fa15..c8141874ce 100644 --- a/web/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php +++ b/web/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php @@ -206,11 +206,15 @@ public static function process($element, FormStateInterface $form_state, $form) 'uri' => $file->getFileUri(), ]; + $dimension_key = $variables['uri'] . '.image_preview_dimensions'; // Determine image dimensions. if (isset($element['#value']['width']) && isset($element['#value']['height'])) { $variables['width'] = $element['#value']['width']; $variables['height'] = $element['#value']['height']; } + elseif ($form_state->has($dimension_key)) { + $variables += $form_state->get($dimension_key); + } else { $image = \Drupal::service('image.factory')->get($file->getFileUri()); if ($image->isValid()) { @@ -233,14 +237,7 @@ public static function process($element, FormStateInterface $form_state, $form) // Store the dimensions in the form so the file doesn't have to be // accessed again. This is important for remote files. - $element['width'] = [ - '#type' => 'hidden', - '#value' => $variables['width'], - ]; - $element['height'] = [ - '#type' => 'hidden', - '#value' => $variables['height'], - ]; + $form_state->set($dimension_key, ['width' => $variables['width'], 'height' => $variables['height']]); } elseif (!empty($element['#default_image'])) { $default_image = $element['#default_image']; diff --git a/web/core/modules/image/tests/src/FunctionalJavascript/ImageFieldWidgetMultipleTest.php b/web/core/modules/image/tests/src/FunctionalJavascript/ImageFieldWidgetMultipleTest.php new file mode 100644 index 0000000000..0638c03b5a --- /dev/null +++ b/web/core/modules/image/tests/src/FunctionalJavascript/ImageFieldWidgetMultipleTest.php @@ -0,0 +1,75 @@ +<?php + +namespace Drupal\Tests\image\FunctionalJavascript; + +use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; +use Drupal\node\Entity\Node; +use Drupal\Tests\image\Kernel\ImageFieldCreationTrait; +use Drupal\Tests\TestFileCreationTrait; + +/** + * Tests the image field widget support multiple upload correctly. + * + * @group image + */ +class ImageFieldWidgetMultipleTest extends WebDriverTestBase { + + use ImageFieldCreationTrait; + use TestFileCreationTrait; + + /** + * {@inheritdoc} + */ + protected static $modules = ['node', 'field_ui', 'image']; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * Tests image widget element support multiple upload correctly. + */ + public function testWidgetElementMultipleUploads(): void { + $image_factory = \Drupal::service('image.factory'); + $file_system = \Drupal::service('file_system'); + $web_driver = $this->getSession()->getDriver(); + + $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + $field_name = 'images'; + $storage_settings = ['cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED]; + $field_settings = ['alt_field_required' => 0]; + $this->createImageField($field_name, 'article', $storage_settings, $field_settings); + $this->drupalLogin($this->drupalCreateUser(['access content', 'create article content'])); + $this->drupalGet('node/add/article'); + $this->xpath('//input[@name="title[0][value]"]')[0]->setValue('Test'); + + $images = $this->getTestFiles('image'); + $images = array_slice($images, 0, 5); + + $paths = []; + foreach ($images as $image) { + $paths[] = $file_system->realpath($image->uri); + } + + $remote_paths = []; + foreach ($paths as $path) { + $remote_paths[] = $web_driver->uploadFileAndGetRemoteFilePath($path); + } + + $multiple_field = $this->xpath('//input[@multiple]')[0]; + $multiple_field->setValue(implode("\n", $remote_paths)); + $this->assertSession()->waitForElementVisible('css', '[data-drupal-selector="edit-images-4-preview"]'); + $this->getSession()->getPage()->findButton('Save')->click(); + + $node = Node::load(1); + foreach ($paths as $delta => $path) { + $node_image = $node->{$field_name}[$delta]; + $original_image = $image_factory->get($path); + $this->assertEquals($original_image->getWidth(), $node_image->width, "Correct width of image #$delta"); + $this->assertEquals($original_image->getHeight(), $node_image->height, "Correct height of image #$delta"); + } + } + +} diff --git a/web/core/modules/language/src/Form/LanguageFormBase.php b/web/core/modules/language/src/Form/LanguageFormBase.php index d30c4b6922..2c96635747 100644 --- a/web/core/modules/language/src/Form/LanguageFormBase.php +++ b/web/core/modules/language/src/Form/LanguageFormBase.php @@ -95,7 +95,7 @@ public function commonForm(array &$form) { */ public function validateCommon(array $form, FormStateInterface $form_state) { // Ensure sane field values for langcode and name. - if (!isset($form['langcode_view']) && !preg_match('@^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$@', $form_state->getValue('langcode'))) { + if (!isset($form['langcode_view']) && !preg_match('@^' . LanguageInterface::VALID_LANGCODE_REGEX . '$@', $form_state->getValue('langcode'))) { $form_state->setErrorByName('langcode', $this->t('%field must be a valid language tag as <a href=":url">defined by the W3C</a>.', [ '%field' => $form['langcode']['#title'], ':url' => 'http://www.w3.org/International/articles/language-tags/', diff --git a/web/core/modules/node/tests/src/Functional/NodeLoadMultipleTest.php b/web/core/modules/node/tests/src/Functional/NodeLoadMultipleTest.php index effa4f52d0..42455c45c9 100644 --- a/web/core/modules/node/tests/src/Functional/NodeLoadMultipleTest.php +++ b/web/core/modules/node/tests/src/Functional/NodeLoadMultipleTest.php @@ -2,6 +2,8 @@ namespace Drupal\Tests\node\Functional; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; use Drupal\node\Entity\Node; /** @@ -65,4 +67,70 @@ public function testNodeMultipleLoad() { } } + /** + * Creates four nodes with not case sensitive fields and load them. + */ + public function testNodeMultipleLoadCaseSensitiveFalse() { + $field_first_storage = FieldStorageConfig::create([ + 'field_name' => 'field_first', + 'entity_type' => 'node', + 'type' => 'string', + 'settings' => [ + 'case_sensitive' => FALSE, + ], + ]); + $field_first_storage->save(); + + FieldConfig::create([ + 'field_storage' => $field_first_storage, + 'bundle' => 'page', + ])->save(); + + $field_second_storage = FieldStorageConfig::create([ + 'field_name' => 'field_second', + 'entity_type' => 'node', + 'type' => 'string', + 'settings' => [ + 'case_sensitive' => FALSE, + ], + ]); + $field_second_storage->save(); + + FieldConfig::create([ + 'field_storage' => $field_second_storage, + 'bundle' => 'page', + ])->save(); + + // Test create nodes with values for field_first and field_second. + $node1 = $this->drupalCreateNode([ + 'type' => 'page', + 'field_first' => '1234', + 'field_second' => 'test_value_1', + ]); + $node2 = $this->drupalCreateNode([ + 'type' => 'page', + 'field_first' => '1234', + 'field_second' => 'test_value_2', + ]); + $node3 = $this->drupalCreateNode([ + 'type' => 'page', + 'field_first' => '5678', + 'field_second' => 'test_value_1', + ]); + $node4 = $this->drupalCreateNode([ + 'type' => 'page', + 'field_first' => '5678', + 'field_second' => 'test_value_2', + ]); + + // Load nodes by two properties (field_first and field_second). + $nodes = $this->container->get('entity_type.manager')->getStorage('node') + ->loadByProperties(['field_first' => ['1234', '5678'], 'field_second' => 'test_value_1']); + $this->assertCount(2, $nodes); + $this->assertEqual($node1->field_first->value, $nodes[$node1->id()]->field_first->value); + $this->assertEqual($node1->field_second->value, $nodes[$node1->id()]->field_second->value); + $this->assertEqual($node3->field_first->value, $nodes[$node3->id()]->field_first->value); + $this->assertEqual($node3->field_second->value, $nodes[$node3->id()]->field_second->value); + } + } diff --git a/web/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php b/web/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php index 5ddc00902d..159cca68af 100644 --- a/web/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php +++ b/web/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php @@ -84,7 +84,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen public static function validateFormElement(array &$element, FormStateInterface $form_state) { // Trim the submitted value of whitespace and slashes. $alias = rtrim(trim($element['alias']['#value']), " \\/"); - if (!empty($alias)) { + if ($alias !== '') { $form_state->setValueForElement($element['alias'], $alias); /** @var \Drupal\path_alias\PathAliasInterface $path_alias */ diff --git a/web/core/modules/path/tests/src/Functional/PathAliasTest.php b/web/core/modules/path/tests/src/Functional/PathAliasTest.php index 9216535779..4bea17dcd1 100644 --- a/web/core/modules/path/tests/src/Functional/PathAliasTest.php +++ b/web/core/modules/path/tests/src/Functional/PathAliasTest.php @@ -343,6 +343,12 @@ public function testNodeAlias() { // Create sixth test node. $node6 = $this->drupalCreateNode(); + // Test the special case where the alias is '0'. + $edit = ['path[0][alias]' => '0']; + $this->drupalGet($node6->toUrl('edit-form')); + $this->submitForm($edit, 'Save'); + $this->assertSession()->pageTextContains('The alias path has to start with a slash.'); + // Create an invalid alias with two leading slashes and verify that the // extra slash is removed when the link is generated. This ensures that URL // aliases cannot be used to inject external URLs. diff --git a/web/core/modules/taxonomy/migrations/d6_term_node_revision.yml b/web/core/modules/taxonomy/migrations/d6_term_node_revision.yml index 4b196ac2df..b202994bf3 100644 --- a/web/core/modules/taxonomy/migrations/d6_term_node_revision.yml +++ b/web/core/modules/taxonomy/migrations/d6_term_node_revision.yml @@ -12,7 +12,7 @@ process: - plugin: migration_lookup migration: - - d6_node_copmplete + - d6_node_complete - d6_node_revision source: vid - diff --git a/web/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeComplete.php b/web/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeComplete.php index f7ce2a008a..56c2067fb3 100644 --- a/web/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeComplete.php +++ b/web/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeComplete.php @@ -64,4 +64,16 @@ public function testTermNode() { $this->assertSame('3', $node->field_vocabulary_2_i_1_[1]->target_id); } + /** + * Tests the Drupal 6 term-node association to Drupal 8 node revisions. + */ + public function testTermNodeRevision() { + $this->executeMigrations(['d6_term_node_revision']); + + $node = \Drupal::entityTypeManager()->getStorage('node')->loadRevision(2001); + $this->assertCount(2, $node->field_vocabulary_3_i_2_); + $this->assertSame('4', $node->field_vocabulary_3_i_2_[0]->target_id); + $this->assertSame('5', $node->field_vocabulary_3_i_2_[1]->target_id); + } + } diff --git a/web/core/modules/views/src/Plugin/views/query/PostgresqlDateSql.php b/web/core/modules/views/src/Plugin/views/query/PostgresqlDateSql.php index c919fadb67..60b1f298bd 100644 --- a/web/core/modules/views/src/Plugin/views/query/PostgresqlDateSql.php +++ b/web/core/modules/views/src/Plugin/views/query/PostgresqlDateSql.php @@ -66,7 +66,7 @@ public function __construct(Connection $database) { public function getDateField($field, $string_date) { if ($string_date) { // Ensures compatibility with field offset operation below. - return "TO_TIMESTAMP($field, 'YYYY-MM-DD HH24:MI:SS')"; + return "TO_TIMESTAMP($field, 'YYYY-MM-DD\"T\"HH24:MI:SS')"; } return "TO_TIMESTAMP($field)"; } diff --git a/web/core/modules/views/tests/src/Unit/Plugin/views/query/PostgresqlDateSqlTest.php b/web/core/modules/views/tests/src/Unit/Plugin/views/query/PostgresqlDateSqlTest.php index 14f367d261..acb53f35b0 100644 --- a/web/core/modules/views/tests/src/Unit/Plugin/views/query/PostgresqlDateSqlTest.php +++ b/web/core/modules/views/tests/src/Unit/Plugin/views/query/PostgresqlDateSqlTest.php @@ -38,7 +38,7 @@ public function setUp() { public function testGetDateField() { $date_sql = new PostgresqlDateSql($this->database); - $expected = "TO_TIMESTAMP(foo.field, 'YYYY-MM-DD HH24:MI:SS')"; + $expected = "TO_TIMESTAMP(foo.field, 'YYYY-MM-DD\"T\"HH24:MI:SS')"; $this->assertEquals($expected, $date_sql->getDateField('foo.field', TRUE)); $expected = 'TO_TIMESTAMP(foo.field)'; diff --git a/web/core/scripts/dev/commit-code-check.sh b/web/core/scripts/dev/commit-code-check.sh new file mode 100755 index 0000000000..3ad565b132 --- /dev/null +++ b/web/core/scripts/dev/commit-code-check.sh @@ -0,0 +1,352 @@ +#!/bin/bash +# +# This script performs code quality checks. +# +# @internal +# This script is not covered by Drupal core's backwards compatibility promise. +# It exists only for core development purposes. +# +# The script makes the following checks: +# - File modes. +# - No changes to core/node_modules directory. +# - PHPCS checks PHP and YAML files. +# - Eslint checks JavaScript files. +# - Checks .es6.js and .js files are equivalent. +# - Stylelint checks CSS files. +# - Checks .pcss.css and .css files are equivalent. + +# Searches an array. +contains_element() { + local e + for e in ${@:2}; do [[ "$e" == "$1" ]] && return 0; done + return 1 +} + +CACHED=0 +DRUPALCI=0 +BRANCH="" +while test $# -gt 0; do + case "$1" in + -h|--help) + echo "Drupal code quality checks" + echo " " + echo "options:" + echo "-h, --help show brief help" + echo "--branch BRANCH creates list of files to check by comparing against a branch" + echo "--cached checks staged files" + echo "--drupalci a special mode for DrupalCI" + echo " " + echo "Example usage: sh ./core/scripts/dev/commit-code-check.sh --branch 9.2.x" + exit 0 + ;; + --branch) + BRANCH="$2" + if [[ "$BRANCH" == "" ]]; then + printf "The --branch option requires a value. For example: --branch 9.2.x\n" + exit; + fi + shift 2 + ;; + --cached) + CACHED=1 + shift + ;; + --drupalci) + DRUPALCI=1 + shift + ;; + *) + break + ;; + esac +done + +# Set up variables to make colored output simple. Color output is disabled on +# DrupalCI because it is breaks reporting. +# @todo https://www.drupal.org/project/drupalci_testbot/issues/3181869 +if [[ "$DRUPALCI" == "1" ]]; then + red="" + green="" + reset="" + DRUPAL_VERSION=$(php -r "include 'vendor/autoload.php'; print preg_replace('#\.[0-9]+-dev#', '.x', \Drupal::VERSION);") +else + red=$(tput setaf 1 && tput bold) + green=$(tput setaf 2) + reset=$(tput sgr0) +fi + +# Gets list of files to check. +if [[ "$BRANCH" != "" ]]; then + FILES=$(git diff --name-only $BRANCH HEAD); +elif [[ "$CACHED" == "0" ]]; then + # For DrupalCI patch testing or when running without --cached or --branch, + # list of all changes in the working directory. + FILES=$(git ls-files --other --modified --exclude-standard --exclude=vendor) +else + # Check staged files only. + if git rev-parse --verify HEAD >/dev/null 2>&1 + then + AGAINST=HEAD + else + # Initial commit: diff against an empty tree object + AGAINST=4b825dc642cb6eb9a060e54bf8d69288fbee4904 + fi + FILES=$(git diff --cached --name-only $AGAINST); +fi + +if [[ "$FILES" == "" ]] && [[ "$DRUPALCI" == "1" ]]; then + # If the FILES is empty we might be testing a merge request on DrupalCI. We + # need to diff against the Drupal branch or tag related to the Drupal version. + printf "Creating list of files to check by comparing branch to %s\n" "$DRUPAL_VERSION" + # On DrupalCI there's a merge commit so we can compare to HEAD~1. + FILES=$(git diff --name-only HEAD~1 HEAD); +fi + +TOP_LEVEL=$(git rev-parse --show-toplevel) + +# Build up a list of absolute file names. +ABS_FILES= +for FILE in $FILES; do + ABS_FILES="$ABS_FILES $TOP_LEVEL/$FILE" +done + +# Exit early if there are no files. +if [[ "$ABS_FILES" == "" ]]; then + printf "There are no files to check. If you have staged a commit use the --cached option.\n" + exit; +fi; + +# This script assumes that composer install and yarn install have already been +# run and all dependencies are updated. +FINAL_STATUS=0 + +cd "$TOP_LEVEL" + +# Add a separator line to make the output easier to read. +printf "\n" +printf -- '-%.0s' {1..100} +printf "\n" + +for FILE in $FILES; do + STATUS=0; + # Print a line to separate output. + printf "Checking %s\n" "$FILE" + printf "\n" + + # Ensure the file still exists (i.e. is not being deleted). + if [ -a $FILE ]; then + if [ ${FILE: -3} != ".sh" ]; then + # Ensure the file has the correct mode. + STAT="$(stat -f "%A" $FILE 2>/dev/null)" + if [ $? -ne 0 ]; then + STAT="$(stat -c "%a" $FILE 2>/dev/null)" + fi + if [ "$STAT" -ne "644" ]; then + printf "${red}check failed:${reset} file $FILE should be 644 not $STAT\n" + STATUS=1 + fi + fi + fi + + # Don't commit changes to vendor. + if [[ "$FILE" =~ ^vendor/ ]]; then + printf "${red}check failed:${reset} file in vendor directory being committed ($FILE)\n" + STATUS=1 + fi + + # Don't commit changes to core/node_modules. + if [[ "$FILE" =~ ^core/node_modules/ ]]; then + printf "${red}check failed:${reset} file in core/node_modules directory being committed ($FILE)\n" + STATUS=1 + fi + + ############################################################################ + ### PHP AND YAML FILES + ############################################################################ + if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.(inc|install|module|php|profile|test|theme|yml)$ ]]; then + # Test files with phpcs rules. + vendor/bin/phpcs "$TOP_LEVEL/$FILE" --runtime-set installed_paths "$TOP_LEVEL/vendor/drupal/coder/coder_sniffer" --standard="$TOP_LEVEL/core/phpcs.xml.dist" + PHPCS=$? + if [ "$PHPCS" -ne "0" ]; then + # If there are failures set the status to a number other than 0. + STATUS=1 + else + printf "PHPCS: $FILE ${green}passed${reset}\n" + fi + fi + + ############################################################################ + ### JAVASCRIPT FILES + ############################################################################ + if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]] && [[ ! $FILE =~ ^core/tests/Drupal/Nightwatch ]] && [[ ! $FILE =~ ^core/assets/vendor/jquery.ui/ui ]]; then + # Work out the root name of the JavaScript so we can ensure that the ES6 + # version has been compiled correctly. + if [[ $FILE =~ \.es6\.js$ ]]; then + BASENAME=${FILE%.es6.js} + COMPILE_CHECK=1 + else + BASENAME=${FILE%.js} + # We only need to compile check if the .es6.js file is not also + # changing. This is because the compile check will occur for the + # .es6.js file. This might occur if the compile scripts have changed. + contains_element "$BASENAME.es6.js" "${FILES[@]}" + HASES6=$? + if [ "$HASES6" -ne "0" ]; then + COMPILE_CHECK=1 + else + COMPILE_CHECK=0 + fi + fi + if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.es6.js" ]]; then + cd "$TOP_LEVEL/core" + yarn run build:js --check --file "$TOP_LEVEL/$BASENAME.es6.js" + CORRECTJS=$? + if [ "$CORRECTJS" -ne "0" ]; then + # No need to write any output the yarn run command will do this for + # us. + STATUS=1 + fi + # Check the coding standards. + if [[ -f ".eslintrc.passing.json" ]]; then + node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json "$TOP_LEVEL/$BASENAME.es6.js" + CORRECTJS=$? + if [ "$CORRECTJS" -ne "0" ]; then + # No need to write any output the node command will do this for us. + STATUS=1 + fi + fi + cd $TOP_LEVEL + else + # If there is no .es6.js file then there should be unless the .js is + # not really Drupal's. + if ! [[ "$FILE" =~ ^core/assets/vendor ]] && ! [[ "$FILE" =~ ^core/scripts/js ]] && ! [[ "$FILE" =~ ^core/scripts/css ]] && ! [[ "$FILE" =~ core/postcss.config.js ]] && ! [[ -f "$TOP_LEVEL/$BASENAME.es6.js" ]]; then + printf "${red}FAILURE${reset} $FILE does not have a corresponding $BASENAME.es6.js\n" + STATUS=1 + fi + fi + elif [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]] && [[ $FILE =~ ^core/assets/vendor/jquery.ui/ui ]]; then + ## Check for minified file changes. + if [[ $FILE =~ -min\.js$ ]]; then + BASENAME=${FILE%-min.js} + contains_element "$BASENAME.js" "${FILES[@]}" + HASSRC=$? + if [ "$HASSRC" -ne "0" ]; then + COMPILE_CHECK=1 + else + ## Source was also changed and will be checked. + COMPILE_CHECK=0 + fi + else + ## Check for source changes. + BASENAME=${FILE%.js} + COMPILE_CHECK=1 + fi + if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.js" ]]; then + cd "$TOP_LEVEL/core" + yarn run build:jqueryui --check --file "$TOP_LEVEL/$BASENAME.js" + CORRECTJS=$? + if [ "$CORRECTJS" -ne "0" ]; then + # The yarn run command will write any error output. + STATUS=1 + fi + cd $TOP_LEVEL + else + # If there is no .js source file + if ! [[ -f "$TOP_LEVEL/$BASENAME.js" ]]; then + printf "${red}FAILURE${reset} $FILE does not have a corresponding $BASENAME.js\n" + STATUS=1 + fi + fi + else + # Check coding standards of Nightwatch files. + if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]]; then + cd "$TOP_LEVEL/core" + # Check the coding standards. + if [[ -f ".eslintrc.passing.json" ]]; then + node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json "$TOP_LEVEL/$FILE" + CORRECTJS=$? + if [ "$CORRECTJS" -ne "0" ]; then + # No need to write any output the node command will do this for us. + STATUS=1 + fi + fi + cd $TOP_LEVEL + fi + fi + + ############################################################################ + ### CSS FILES + ############################################################################ + if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.css$ ]]; then + # Work out the root name of the CSS so we can ensure that the PostCSS + # version has been compiled correctly. + if [[ $FILE =~ \.pcss\.css$ ]]; then + BASENAME=${FILE%.pcss.css} + COMPILE_CHECK=1 + else + BASENAME=${FILE%.css} + # We only need to compile check if the .pcss.css file is not also + # changing. This is because the compile check will occur for the + # .pcss.css file. This might occur if the compiled stylesheets have + # changed. + contains_element "$BASENAME.pcss.css" "${FILES[@]}" + HASPOSTCSS=$? + if [ "$HASPOSTCSS" -ne "0" ]; then + COMPILE_CHECK=1 + else + COMPILE_CHECK=0 + fi + fi + # PostCSS + if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.pcss.css" ]]; then + cd "$TOP_LEVEL/core" + yarn run build:css --check --file "$TOP_LEVEL/$BASENAME.pcss.css" + CORRECTCSS=$? + if [ "$CORRECTCSS" -ne "0" ]; then + # No need to write any output the yarn run command will do this for + # us. + STATUS=1 + fi + cd $TOP_LEVEL + fi + fi + if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.css$ ]] && [[ -f "core/node_modules/.bin/stylelint" ]]; then + BASENAME=${FILE%.css} + # We only need to use stylelint on the .pcss.css file. So if this CSS file + # has a corresponding .pcss don't do stylelint. + if [[ $FILE =~ \.pcss\.css$ ]] || [[ ! -f "$TOP_LEVEL/$BASENAME.pcss.css" ]]; then + cd "$TOP_LEVEL/core" + node_modules/.bin/stylelint "$TOP_LEVEL/$FILE" + if [ "$?" -ne "0" ]; then + STATUS=1 + else + printf "STYLELINT: $FILE ${green}passed${reset}\n" + fi + cd $TOP_LEVEL + fi + fi + + if [[ "$STATUS" == "1" ]]; then + FINAL_STATUS=1 + # There is no need to print a failure message. The fail will be described + # already. + else + printf "%s ${green}passed${reset}\n" "$FILE" + fi + + # Print a line to separate each file's checks. + printf "\n" + printf -- '-%.0s' {1..100} + printf "\n" +done + +if [[ "$FINAL_STATUS" == "1" ]] && [[ "$DRUPALCI" == "1" ]]; then + printf "${red}Drupal code quality checks failed.${reset}\n" + printf "To reproduce this output locally:\n" + printf "* Apply the change as a patch\n" + printf "* Run this command locally: sh ./core/scripts/dev/commit-code-check.sh\n" + printf "OR:\n" + printf "* From the merge request branch\n" + printf "* Run this command locally: sh ./core/scripts/dev/commit-code-check.sh --branch %s\n" "$DRUPAL_VERSION" +fi +exit $FINAL_STATUS diff --git a/web/core/tests/Drupal/KernelTests/Core/Field/MapBaseFieldTest.php b/web/core/tests/Drupal/KernelTests/Core/Field/MapBaseFieldTest.php new file mode 100644 index 0000000000..1ff63f89ac --- /dev/null +++ b/web/core/tests/Drupal/KernelTests/Core/Field/MapBaseFieldTest.php @@ -0,0 +1,65 @@ +<?php + +namespace Drupal\KernelTests\Core\Field; + +use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\entity_test_update\Entity\EntityTestUpdate; +use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; + +/** + * Tests map base fields. + * + * @group Field + */ +class MapBaseFieldTest extends EntityKernelTestBase { + + /** + * The entity definition update manager. + * + * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface + */ + protected $entityDefinitionUpdateManager; + + /** + * {@inheritdoc} + */ + public static $modules = ['entity_test_update']; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager'); + + // Install every entity type's schema that wasn't installed in the parent + // method. + foreach (array_diff_key($this->entityTypeManager->getDefinitions(), array_flip(['user', 'entity_test'])) as $entity_type_id => $entity_type) { + $this->installEntitySchema($entity_type_id); + } + } + + /** + * Tests uninstalling map item base field. + */ + public function testUninstallMapItemBaseField() { + $definitions['data_map'] = BaseFieldDefinition::create('map') + ->setLabel(t('Data')) + ->setRequired(TRUE); + + $this->state->set('entity_test_update.additional_base_field_definitions', $definitions); + + $this->entityDefinitionUpdateManager->installFieldStorageDefinition('data_map', 'entity_test_update', 'entity_test', $definitions['data_map']); + + $entity = EntityTestUpdate::create([ + 'data_map' => [ + 'key' => 'value', + ], + ]); + $entity->save(); + + $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($definitions['data_map']); + } + +} diff --git a/web/core/tests/Drupal/KernelTests/Core/Installer/InstallerLanguageTest.php b/web/core/tests/Drupal/KernelTests/Core/Installer/InstallerLanguageTest.php index 0381e2e9ff..2846749f4f 100644 --- a/web/core/tests/Drupal/KernelTests/Core/Installer/InstallerLanguageTest.php +++ b/web/core/tests/Drupal/KernelTests/Core/Installer/InstallerLanguageTest.php @@ -20,8 +20,9 @@ public function testInstallerTranslationFiles() { // Different translation files would be found depending on which language // we are looking for. $expected_translation_files = [ - NULL => ['drupal-8.0.0-beta2.hu.po', 'drupal-8.0.0.de.po'], + NULL => ['drupal-8.0.0-beta2.hu.po', 'drupal-8.0.0.de.po', 'drupal-8.0.x.fr-CA.po'], 'de' => ['drupal-8.0.0.de.po'], + 'fr-CA' => ['drupal-8.0.x.fr-CA.po'], 'hu' => ['drupal-8.0.0-beta2.hu.po'], 'it' => [], ]; diff --git a/web/core/tests/Drupal/KernelTests/Core/Plugin/Annotation/ContextDefinitionTest.php b/web/core/tests/Drupal/KernelTests/Core/Plugin/Annotation/ContextDefinitionTest.php index ecb3c1fb82..3e0e1be7db 100644 --- a/web/core/tests/Drupal/KernelTests/Core/Plugin/Annotation/ContextDefinitionTest.php +++ b/web/core/tests/Drupal/KernelTests/Core/Plugin/Annotation/ContextDefinitionTest.php @@ -25,6 +25,7 @@ public function testConstraints() { $this->assertArrayHasKey('user', $definition['context_definitions']); $this->assertInstanceOf(ContextDefinition::class, $definition['context_definitions']['user']); $this->assertEquals(['NotNull' => []], $definition['context_definitions']['user']->getConstraints()); + $this->assertEquals("User Context", $definition['context_definitions']['user']->getLabel()); } } diff --git a/web/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/web/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php index 2d95a8dc6a..f6d4d1960d 100644 --- a/web/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php +++ b/web/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php @@ -142,7 +142,7 @@ public static function getSkippedDeprecations() { 'The "core/html5shiv" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3086383', 'The "core/matchmedia" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3086653', 'The "core/matchmedia.addListener" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3086653', - 'The "core/classList" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the the native browser implementation instead. See https://www.drupal.org/node/3089511', + 'The "core/classList" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the native browser implementation instead. See https://www.drupal.org/node/3089511', 'The "core/jquery.ui.datepicker" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3081864', 'The "locale/drupal.locale.datepicker" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3081864', 'The "https://www.drupal.org/link-relations/create" string as a RestResource plugin annotation URI path key is deprecated in Drupal 8.4.0, now a valid link relation type name must be specified, so "create" must be specified instead before Drupal 9.0.0. See https://www.drupal.org/node/2737401.', diff --git a/web/core/tests/fixtures/files/translations/drupal-8.0.0.fr____CA.po b/web/core/tests/fixtures/files/translations/drupal-8.0.0.fr____CA.po new file mode 100644 index 0000000000..e2106954b8 --- /dev/null +++ b/web/core/tests/fixtures/files/translations/drupal-8.0.0.fr____CA.po @@ -0,0 +1,4 @@ +# This file exists to prove that +# \Drupal\Core\StringTranslation\Translator\FileTranslation::findTranslationFiles() +# does not find it. See +# \Drupal\KernelTests\Core\Installer\InstallerLanguageTest::testInstallerTranslationFiles() diff --git a/web/core/tests/fixtures/files/translations/drupal-8.0.x.fr-CA.po b/web/core/tests/fixtures/files/translations/drupal-8.0.x.fr-CA.po new file mode 100644 index 0000000000..e69de29bb2 -- GitLab