diff --git a/composer.json b/composer.json index 9dd56c87b4..fe72f26221 100644 --- a/composer.json +++ b/composer.json @@ -422,7 +422,8 @@ }, "drupal/entity_browser": { "2856138 - Entity browser cardinality validation": "patches/2856138-entity-browser-cardinality-validation.patch", - "3191302 - Make modal iframe tab accessible": "patches/3191302-make-modal-iframe-tab-accessible.patch" + "3191302 - Make modal iframe tab accessible": "patches/3191302-make-modal-iframe-tab-accessible.patch", + "3483265 - Make it possible to show latest revision in form widget": "patches/3483265-make-it-possible-to-show-latest-revision-in-fomr-widget.patch" }, "drupal/entity_browser_table": { "3194622 - Custom field validation should not be applied to remove button": "patches/3794622-limit-remove-button-validators.patch", diff --git a/composer.lock b/composer.lock index 92f1a8e408..c0ce6adf41 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": "bfb384d0a25ecbb4faa65c784488e2e6", + "content-hash": "8776ac0f72db18e6d1023bf162ed6d46", "packages": [ { "name": "asm89/stack-cors", @@ -274,7 +274,7 @@ "version": "v4.1.0", "source": { "type": "git", - "url": "git@github.com:fengyuanchen/cropper.git", + "url": "https://github.com/fengyuanchen/cropper.git", "reference": "617d9bdb8688cc4edb3b03bc49a04b83c7facbe7" }, "dist": { @@ -15505,16 +15505,16 @@ }, { "name": "gettext/gettext", - "version": "v5.7.3", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/php-gettext/Gettext.git", - "reference": "95820f020e4f2f05e0bbaa5603e4c6ec3edc50f1" + "reference": "8657e580747bb3baacccdcebe69cac094661e404" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/95820f020e4f2f05e0bbaa5603e4c6ec3edc50f1", - "reference": "95820f020e4f2f05e0bbaa5603e4c6ec3edc50f1", + "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/8657e580747bb3baacccdcebe69cac094661e404", + "reference": "8657e580747bb3baacccdcebe69cac094661e404", "shasum": "" }, "require": { @@ -15559,7 +15559,7 @@ "support": { "email": "oom@oscarotero.com", "issues": "https://github.com/php-gettext/Gettext/issues", - "source": "https://github.com/php-gettext/Gettext/tree/v5.7.3" + "source": "https://github.com/php-gettext/Gettext/tree/v5.7.0" }, "funding": [ { @@ -15575,7 +15575,7 @@ "type": "patreon" } ], - "time": "2024-12-01T10:18:08+00:00" + "time": "2022-07-27T19:54:55+00:00" }, { "name": "gettext/languages", @@ -17281,16 +17281,16 @@ }, { "name": "league/uri-interfaces", - "version": "7.4.1", + "version": "7.4.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "8d43ef5c841032c87e2de015972c06f3865ef718" + "reference": "bd8c487ec236930f7bbc42b8d374fa882fbba0f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/8d43ef5c841032c87e2de015972c06f3865ef718", - "reference": "8d43ef5c841032c87e2de015972c06f3865ef718", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/bd8c487ec236930f7bbc42b8d374fa882fbba0f3", + "reference": "bd8c487ec236930f7bbc42b8d374fa882fbba0f3", "shasum": "" }, "require": { @@ -17353,7 +17353,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.1" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.0" }, "funding": [ { @@ -17361,7 +17361,7 @@ "type": "github" } ], - "time": "2024-03-23T07:42:40+00:00" + "time": "2023-11-24T15:40:42+00:00" }, { "name": "masterminds/html5", @@ -19503,16 +19503,16 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.9.3", + "version": "v6.9.1", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e" + "reference": "039de174cd9c17a8389754d3b877a2ed22743e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e", - "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/039de174cd9c17a8389754d3b877a2ed22743e18", + "reference": "039de174cd9c17a8389754d3b877a2ed22743e18", "shasum": "" }, "require": { @@ -19572,7 +19572,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.1" }, "funding": [ { @@ -19580,7 +19580,7 @@ "type": "github" } ], - "time": "2024-11-24T18:04:13+00:00" + "time": "2023-11-25T22:23:28+00:00" }, { "name": "phpoption/phpoption", @@ -21072,16 +21072,16 @@ }, { "name": "robrichards/xmlseclibs", - "version": "3.1.3", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/robrichards/xmlseclibs.git", - "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07" + "reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/2bdfd742624d739dfadbd415f00181b4a77aaf07", - "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07", + "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/f8f19e58f26cdb42c54b214ff8a820760292f8df", + "reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df", "shasum": "" }, "require": { @@ -21108,9 +21108,9 @@ ], "support": { "issues": "https://github.com/robrichards/xmlseclibs/issues", - "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.3" + "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.1" }, - "time": "2024-11-20T21:13:56+00:00" + "time": "2020-09-05T13:00:25+00:00" }, { "name": "rogervila/array-diff-multidimensional", @@ -22209,16 +22209,16 @@ }, { "name": "simplesamlphp/assert", - "version": "v1.5.0", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/simplesamlphp/assert.git", - "reference": "f6872f002d34b8e20c19d0823b107d2c74ddfd9d" + "reference": "8598fb2005f4eed689e30ebffd4e8e85ed7ce9aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/assert/zipball/f6872f002d34b8e20c19d0823b107d2c74ddfd9d", - "reference": "f6872f002d34b8e20c19d0823b107d2c74ddfd9d", + "url": "https://api.github.com/repos/simplesamlphp/assert/zipball/8598fb2005f4eed689e30ebffd4e8e85ed7ce9aa", + "reference": "8598fb2005f4eed689e30ebffd4e8e85ed7ce9aa", "shasum": "" }, "require": { @@ -22226,13 +22226,11 @@ "ext-filter": "*", "ext-pcre": "*", "ext-spl": "*", - "league/uri-interfaces": "^7.4", "php": "^8.1", "webmozart/assert": "^1.11" }, "require-dev": { - "ext-intl": "*", - "simplesamlphp/simplesamlphp-test-framework": "^1.7" + "simplesamlphp/simplesamlphp-test-framework": "^1.5.5" }, "type": "library", "extra": { @@ -22262,22 +22260,22 @@ "description": "A wrapper around webmozart/assert to make it useful beyond checking method arguments", "support": { "issues": "https://github.com/simplesamlphp/assert/issues", - "source": "https://github.com/simplesamlphp/assert/tree/v1.5.0" + "source": "https://github.com/simplesamlphp/assert/tree/v1.1.1" }, - "time": "2024-11-19T18:52:10+00:00" + "time": "2024-03-19T21:09:48+00:00" }, { "name": "simplesamlphp/composer-module-installer", - "version": "v1.3.5", + "version": "v1.3.4", "source": { "type": "git", "url": "https://github.com/simplesamlphp/composer-module-installer.git", - "reference": "7bf413c2d28e48dff6755d74a7e45087cf144604" + "reference": "36508ed9580a30c4d5ab0bb3c25c00d0b5d42946" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/composer-module-installer/zipball/7bf413c2d28e48dff6755d74a7e45087cf144604", - "reference": "7bf413c2d28e48dff6755d74a7e45087cf144604", + "url": "https://api.github.com/repos/simplesamlphp/composer-module-installer/zipball/36508ed9580a30c4d5ab0bb3c25c00d0b5d42946", + "reference": "36508ed9580a30c4d5ab0bb3c25c00d0b5d42946", "shasum": "" }, "require": { @@ -22305,64 +22303,22 @@ "description": "A Composer plugin that allows installing SimpleSAMLphp modules through Composer.", "support": { "issues": "https://github.com/simplesamlphp/composer-module-installer/issues", - "source": "https://github.com/simplesamlphp/composer-module-installer/tree/v1.3.5" + "source": "https://github.com/simplesamlphp/composer-module-installer/tree/v1.3.4" }, - "time": "2024-11-16T09:42:27+00:00" - }, - { - "name": "simplesamlphp/composer-xmlprovider-installer", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/simplesamlphp/composer-xmlprovider-installer.git", - "reference": "ce09a877a1de9469f1a872f04703d75d6bafcdc6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/composer-xmlprovider-installer/zipball/ce09a877a1de9469f1a872f04703d75d6bafcdc6", - "reference": "ce09a877a1de9469f1a872f04703d75d6bafcdc6", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^2.0", - "php": "^8.1" - }, - "require-dev": { - "composer/composer": "^2.4", - "simplesamlphp/simplesamlphp-test-framework": "^1.5.4" - }, - "type": "composer-plugin", - "extra": { - "class": "SimpleSAML\\Composer\\XMLProvider\\XMLProviderInstallerPlugin" - }, - "autoload": { - "psr-4": { - "SimpleSAML\\Composer\\XMLProvider\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "description": "A composer plugin that will auto-generate a classmap with all classes that implement SerializableElementInterface.", - "support": { - "issues": "https://github.com/simplesamlphp/composer-xmlprovider-installer/issues", - "source": "https://github.com/simplesamlphp/composer-xmlprovider-installer/tree/v1.0.1" - }, - "time": "2024-09-15T22:34:50+00:00" + "time": "2023-03-08T20:58:22+00:00" }, { "name": "simplesamlphp/saml2", - "version": "v4.16.14", + "version": "v4.6.11", "source": { "type": "git", "url": "https://github.com/simplesamlphp/saml2.git", - "reference": "fe6c7bdda5e166e326d19d78f230d959ab51d01d" + "reference": "1b5d48753c78d02e88667068e633531c233141fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/saml2/zipball/fe6c7bdda5e166e326d19d78f230d959ab51d01d", - "reference": "fe6c7bdda5e166e326d19d78f230d959ab51d01d", + "url": "https://api.github.com/repos/simplesamlphp/saml2/zipball/1b5d48753c78d02e88667068e633531c233141fb", + "reference": "1b5d48753c78d02e88667068e633531c233141fb", "shasum": "" }, "require": { @@ -22374,9 +22330,6 @@ "robrichards/xmlseclibs": "^3.1.1", "webmozart/assert": "^1.9" }, - "conflict": { - "robrichards/xmlseclibs": "3.1.2" - }, "require-dev": { "mockery/mockery": "^1.3", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", @@ -22408,22 +22361,22 @@ "description": "SAML2 PHP library from SimpleSAMLphp", "support": { "issues": "https://github.com/simplesamlphp/saml2/issues", - "source": "https://github.com/simplesamlphp/saml2/tree/v4.16.14" + "source": "https://github.com/simplesamlphp/saml2/tree/v4.6.11" }, - "time": "2024-12-01T22:26:30+00:00" + "time": "2024-01-25T19:39:46+00:00" }, { "name": "simplesamlphp/simplesamlphp", - "version": "v2.3.5", + "version": "v2.2.1", "source": { "type": "git", "url": "https://github.com/simplesamlphp/simplesamlphp.git", - "reference": "406cc3a0be3b154b2b0c08a48f30705332c729fb" + "reference": "0819ecbe66a11380c64bed0afb71c8d6dd4b714e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp/zipball/406cc3a0be3b154b2b0c08a48f30705332c729fb", - "reference": "406cc3a0be3b154b2b0c08a48f30705332c729fb", + "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp/zipball/0819ecbe66a11380c64bed0afb71c8d6dd4b714e", + "reference": "0819ecbe66a11380c64bed0afb71c8d6dd4b714e", "shasum": "" }, "require": { @@ -22443,11 +22396,10 @@ "php": "^8.1", "phpmailer/phpmailer": "^6.8", "psr/log": "^3.0", - "simplesamlphp/assert": "^1.1", + "simplesamlphp/assert": "^1.0.0", "simplesamlphp/composer-module-installer": "^1.3", - "simplesamlphp/saml2": "^4.16", - "simplesamlphp/simplesamlphp-assets-base": "~2.3.0", - "simplesamlphp/xml-security": "^1.7", + "simplesamlphp/saml2": "^4.6", + "simplesamlphp/simplesamlphp-assets-base": "~2.1.5", "symfony/cache": "^6.4", "symfony/config": "^6.4", "symfony/console": "^6.4", @@ -22458,7 +22410,6 @@ "symfony/http-foundation": "^6.4", "symfony/http-kernel": "^6.4", "symfony/intl": "^6.4", - "symfony/password-hasher": "^6.4", "symfony/polyfill-intl-icu": "^1.28", "symfony/routing": "^6.4", "symfony/translation-contracts": "^3.0", @@ -22466,7 +22417,7 @@ "symfony/var-exporter": "^6.4", "symfony/yaml": "^6.4", "twig/intl-extra": "^3.7", - "twig/twig": "^3.14.0" + "twig/twig": "^3.5" }, "require-dev": { "ext-curl": "*", @@ -22476,6 +22427,7 @@ "predis/predis": "^2.2", "simplesamlphp/simplesamlphp-module-adfs": "^2.1", "simplesamlphp/simplesamlphp-test-framework": "^1.5.4", + "simplesamlphp/xml-security": "^1.6.0", "symfony/translation": "^6.4" }, "suggest": { @@ -22491,7 +22443,7 @@ "type": "project", "extra": { "branch-alias": { - "dev-master": "2.4.0.x-dev" + "dev-master": "2.2.x-dev" } }, "autoload": { @@ -22540,25 +22492,25 @@ "issues": "https://github.com/simplesamlphp/simplesamlphp/issues", "source": "https://github.com/simplesamlphp/simplesamlphp" }, - "time": "2024-12-02T11:51:07+00:00" + "time": "2024-03-16T23:03:28+00:00" }, { "name": "simplesamlphp/simplesamlphp-assets-base", - "version": "v2.3.2", + "version": "v2.1.12", "source": { "type": "git", "url": "https://github.com/simplesamlphp/simplesamlphp-assets-base.git", - "reference": "3f83d1afb16a2a807ac26f6a6a8e2f03bad75abe" + "reference": "bde7ad38a534776a9fb2943d7d1a9880c738c925" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp-assets-base/zipball/3f83d1afb16a2a807ac26f6a6a8e2f03bad75abe", - "reference": "3f83d1afb16a2a807ac26f6a6a8e2f03bad75abe", + "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp-assets-base/zipball/bde7ad38a534776a9fb2943d7d1a9880c738c925", + "reference": "bde7ad38a534776a9fb2943d7d1a9880c738c925", "shasum": "" }, "require": { - "php": "^8.1", - "simplesamlphp/composer-module-installer": "^1.3.4" + "php": ">=7.4 || ^8.0", + "simplesamlphp/composer-module-installer": "^1.3.2" }, "type": "simplesamlphp-module", "notification-url": "https://packagist.org/downloads/", @@ -22574,134 +22526,9 @@ "description": "Assets for the SimpleSAMLphp main repository", "support": { "issues": "https://github.com/simplesamlphp/simplesamlphp-assets-base/issues", - "source": "https://github.com/simplesamlphp/simplesamlphp-assets-base/tree/v2.3.2" - }, - "time": "2024-12-01T01:26:55+00:00" - }, - { - "name": "simplesamlphp/xml-common", - "version": "v1.20.1", - "source": { - "type": "git", - "url": "https://github.com/simplesamlphp/xml-common.git", - "reference": "525095b5c09072996217d7f4acb9052af9bc9578" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/xml-common/zipball/525095b5c09072996217d7f4acb9052af9bc9578", - "reference": "525095b5c09072996217d7f4acb9052af9bc9578", - "shasum": "" - }, - "require": { - "ext-date": "*", - "ext-dom": "*", - "ext-libxml": "*", - "ext-pcre": "*", - "ext-spl": "*", - "ext-xmlreader": "*", - "php": "^8.1", - "simplesamlphp/assert": "^1.2", - "simplesamlphp/composer-xmlprovider-installer": "~1.0.0", - "symfony/finder": "^6.4" - }, - "require-dev": { - "simplesamlphp/simplesamlphp-test-framework": "^1.7" + "source": "https://github.com/simplesamlphp/simplesamlphp-assets-base/tree/v2.1.12" }, - "type": "simplesamlphp-xmlprovider", - "autoload": { - "psr-4": { - "SimpleSAML\\XML\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-or-later" - ], - "authors": [ - { - "name": "Jaime Perez", - "email": "jaime.perez@uninett.no" - }, - { - "name": "Tim van Dijen", - "email": "tvdijen@gmail.com" - } - ], - "description": "A library with classes and utilities for handling XML structures.", - "homepage": "http://simplesamlphp.org", - "keywords": [ - "saml", - "xml" - ], - "support": { - "issues": "https://github.com/simplesamlphp/xml-common/issues", - "source": "https://github.com/simplesamlphp/xml-common" - }, - "time": "2024-12-03T07:51:40+00:00" - }, - { - "name": "simplesamlphp/xml-security", - "version": "v1.10.0", - "source": { - "type": "git", - "url": "https://github.com/simplesamlphp/xml-security.git", - "reference": "2f478b2308b06c10542488ce9690a98baaf2fdfa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/xml-security/zipball/2f478b2308b06c10542488ce9690a98baaf2fdfa", - "reference": "2f478b2308b06c10542488ce9690a98baaf2fdfa", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-hash": "*", - "ext-mbstring": "*", - "ext-openssl": "*", - "ext-pcre": "*", - "ext-spl": "*", - "php": "^8.1", - "simplesamlphp/assert": "^1.5", - "simplesamlphp/xml-common": "^1.20.0" - }, - "require-dev": { - "simplesamlphp/simplesamlphp-test-framework": "^1.7" - }, - "type": "simplesamlphp-xmlprovider", - "autoload": { - "psr-4": { - "SimpleSAML\\XMLSecurity\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-or-later" - ], - "authors": [ - { - "name": "Jaime Perez Crespo", - "email": "jaime.perez@uninett.no", - "role": "Maintainer" - }, - { - "name": "Tim van Dijen", - "email": "tvdijen@gmail.com", - "role": "Maintainer" - } - ], - "description": "SimpleSAMLphp library for XML Security", - "homepage": "https://github.com/simplesamlphp/xml-security", - "keywords": [ - "security", - "signature", - "xml", - "xmldsig" - ], - "support": { - "issues": "https://github.com/simplesamlphp/xml-security/issues", - "source": "https://github.com/simplesamlphp/xml-security/tree/v1.10.0" - }, - "time": "2024-12-01T23:34:04+00:00" + "time": "2024-02-19T15:32:24+00:00" }, { "name": "simshaun/recurr", @@ -23141,16 +22968,16 @@ }, { "name": "symfony/cache", - "version": "v6.4.16", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "70d60e9a3603108563010f8592dff15a6f15dfae" + "reference": "0ef36534694c572ff526d91c7181f3edede176e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/70d60e9a3603108563010f8592dff15a6f15dfae", - "reference": "70d60e9a3603108563010f8592dff15a6f15dfae", + "url": "https://api.github.com/repos/symfony/cache/zipball/0ef36534694c572ff526d91c7181f3edede176e7", + "reference": "0ef36534694c572ff526d91c7181f3edede176e7", "shasum": "" }, "require": { @@ -23217,7 +23044,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.4.16" + "source": "https://github.com/symfony/cache/tree/v6.4.4" }, "funding": [ { @@ -23233,20 +23060,20 @@ "type": "tidelift" } ], - "time": "2024-11-20T10:10:54+00:00" + "time": "2024-02-22T20:27:10+00:00" }, { "name": "symfony/cache-contracts", - "version": "v3.5.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b" + "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b", - "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/df6a1a44c890faded49a5fca33c2d5c5fd3c2197", + "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197", "shasum": "" }, "require": { @@ -23293,7 +23120,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/cache-contracts/tree/v3.5.0" }, "funding": [ { @@ -23309,20 +23136,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/config", - "version": "v6.4.14", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef" + "reference": "6ea4affc27f2086c9d16b92ab5429ce1e3c38047" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/4e55e7e4ffddd343671ea972216d4509f46c22ef", - "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef", + "url": "https://api.github.com/repos/symfony/config/zipball/6ea4affc27f2086c9d16b92ab5429ce1e3c38047", + "reference": "6ea4affc27f2086c9d16b92ab5429ce1e3c38047", "shasum": "" }, "require": { @@ -23368,7 +23195,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v6.4.14" + "source": "https://github.com/symfony/config/tree/v6.4.4" }, "funding": [ { @@ -23384,7 +23211,7 @@ "type": "tidelift" } ], - "time": "2024-11-04T11:33:53+00:00" + "time": "2024-02-26T07:52:26+00:00" }, { "name": "symfony/console", @@ -23482,16 +23309,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v6.4.16", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "7a379d8871f6a36f01559c14e11141cc02eb8dc8" + "reference": "70ab1f65a4516ef741e519ea938e6aa465e6aa36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/7a379d8871f6a36f01559c14e11141cc02eb8dc8", - "reference": "7a379d8871f6a36f01559c14e11141cc02eb8dc8", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/70ab1f65a4516ef741e519ea938e6aa465e6aa36", + "reference": "70ab1f65a4516ef741e519ea938e6aa465e6aa36", "shasum": "" }, "require": { @@ -23543,7 +23370,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.4.16" + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.15" }, "funding": [ { @@ -23559,20 +23386,20 @@ "type": "tidelift" } ], - "time": "2024-11-25T14:52:46+00:00" + "time": "2024-11-09T06:56:25+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { @@ -23610,7 +23437,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -23626,7 +23453,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/dom-crawler", @@ -23852,16 +23679,16 @@ }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.5.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", + "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", "shasum": "" }, "require": { @@ -23908,7 +23735,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" }, "funding": [ { @@ -23924,7 +23751,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/filesystem", @@ -24058,16 +23885,16 @@ }, { "name": "symfony/framework-bundle", - "version": "v6.4.13", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "e8b0bd921f9bd35ea4d1508067c3f3f6e2036418" + "reference": "c76d3881596860ead95f5444a5ce4414447f0067" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/e8b0bd921f9bd35ea4d1508067c3f3f6e2036418", - "reference": "e8b0bd921f9bd35ea4d1508067c3f3f6e2036418", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/c76d3881596860ead95f5444a5ce4414447f0067", + "reference": "c76d3881596860ead95f5444a5ce4414447f0067", "shasum": "" }, "require": { @@ -24076,7 +23903,7 @@ "php": ">=8.1", "symfony/cache": "^5.4|^6.0|^7.0", "symfony/config": "^6.1|^7.0", - "symfony/dependency-injection": "^6.4.12|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.1|^7.0", "symfony/event-dispatcher": "^5.4|^6.0|^7.0", @@ -24106,7 +23933,6 @@ "symfony/mime": "<6.4", "symfony/property-access": "<5.4", "symfony/property-info": "<5.4", - "symfony/runtime": "<5.4.45|>=6.0,<6.4.13|>=7.0,<7.1.6", "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", "symfony/security-core": "<5.4", "symfony/security-csrf": "<5.4", @@ -24187,7 +24013,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v6.4.13" + "source": "https://github.com/symfony/framework-bundle/tree/v6.4.4" }, "funding": [ { @@ -24203,20 +24029,20 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2024-02-22T22:50:59+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.4.16", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "431771b7a6f662f1575b3cfc8fd7617aa9864d57" + "reference": "ba020a321a95519303a3f09ec2824d34d601c388" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/431771b7a6f662f1575b3cfc8fd7617aa9864d57", - "reference": "431771b7a6f662f1575b3cfc8fd7617aa9864d57", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ba020a321a95519303a3f09ec2824d34d601c388", + "reference": "ba020a321a95519303a3f09ec2824d34d601c388", "shasum": "" }, "require": { @@ -24226,12 +24052,12 @@ "symfony/polyfill-php83": "^1.27" }, "conflict": { - "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + "symfony/cache": "<6.3" }, "require-dev": { "doctrine/dbal": "^2.13.1|^3|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", + "symfony/cache": "^6.3|^7.0", "symfony/dependency-injection": "^5.4|^6.0|^7.0", "symfony/expression-language": "^5.4|^6.0|^7.0", "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", @@ -24264,7 +24090,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.16" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.14" }, "funding": [ { @@ -24280,20 +24106,20 @@ "type": "tidelift" } ], - "time": "2024-11-13T18:58:10+00:00" + "time": "2024-11-05T16:39:55+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.16", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "8838b5b21d807923b893ccbfc2cbeda0f1bc00f0" + "reference": "b002a5b3947653c5aee3adac2a024ea615fd3ff5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8838b5b21d807923b893ccbfc2cbeda0f1bc00f0", - "reference": "8838b5b21d807923b893ccbfc2cbeda0f1bc00f0", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b002a5b3947653c5aee3adac2a024ea615fd3ff5", + "reference": "b002a5b3947653c5aee3adac2a024ea615fd3ff5", "shasum": "" }, "require": { @@ -24378,7 +24204,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.16" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.15" }, "funding": [ { @@ -24394,20 +24220,20 @@ "type": "tidelift" } ], - "time": "2024-11-27T12:49:36+00:00" + "time": "2024-11-13T13:57:37+00:00" }, { "name": "symfony/intl", - "version": "v6.4.15", + "version": "v6.4.3", "source": { "type": "git", "url": "https://github.com/symfony/intl.git", - "reference": "b1d5e8d82615b60f229216edfee0b59e2ef66da6" + "reference": "2628ded562ca132ed7cdea72f5ec6aaf65d94414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/b1d5e8d82615b60f229216edfee0b59e2ef66da6", - "reference": "b1d5e8d82615b60f229216edfee0b59e2ef66da6", + "url": "https://api.github.com/repos/symfony/intl/zipball/2628ded562ca132ed7cdea72f5ec6aaf65d94414", + "reference": "2628ded562ca132ed7cdea72f5ec6aaf65d94414", "shasum": "" }, "require": { @@ -24424,8 +24250,7 @@ "Symfony\\Component\\Intl\\": "" }, "exclude-from-classmap": [ - "/Tests/", - "/Resources/data/" + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -24461,7 +24286,7 @@ "localization" ], "support": { - "source": "https://github.com/symfony/intl/tree/v6.4.15" + "source": "https://github.com/symfony/intl/tree/v6.4.3" }, "funding": [ { @@ -24477,7 +24302,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T15:28:48+00:00" + "time": "2024-01-23T14:51:35+00:00" }, { "name": "symfony/mailer", @@ -24711,78 +24536,6 @@ ], "time": "2024-05-31T14:49:08+00:00" }, - { - "name": "symfony/password-hasher", - "version": "v6.4.13", - "source": { - "type": "git", - "url": "https://github.com/symfony/password-hasher.git", - "reference": "e97a1b31f60b8bdfc1fdedab4398538da9441d47" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/e97a1b31f60b8bdfc1fdedab4398538da9441d47", - "reference": "e97a1b31f60b8bdfc1fdedab4398538da9441d47", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "conflict": { - "symfony/security-core": "<5.4" - }, - "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/security-core": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\PasswordHasher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Robin Chalas", - "email": "robin.chalas@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides password hashing utilities", - "homepage": "https://symfony.com", - "keywords": [ - "hashing", - "password" - ], - "support": { - "source": "https://github.com/symfony/password-hasher/tree/v6.4.13" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-25T14:18:03+00:00" - }, { "name": "symfony/phpunit-bridge", "version": "v5.4.41", @@ -25106,20 +24859,20 @@ }, { "name": "symfony/polyfill-intl-icu", - "version": "v1.31.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-icu.git", - "reference": "d80a05e9904d2c2b9b95929f3e4b5d3a8f418d78" + "reference": "07094a28851a49107f3ab4f9120ca2975a64b6e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/d80a05e9904d2c2b9b95929f3e4b5d3a8f418d78", - "reference": "d80a05e9904d2c2b9b95929f3e4b5d3a8f418d78", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/07094a28851a49107f3ab4f9120ca2975a64b6e1", + "reference": "07094a28851a49107f3ab4f9120ca2975a64b6e1", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance and support of other locales than \"en\"" @@ -25170,7 +24923,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.29.0" }, "funding": [ { @@ -25186,7 +24939,7 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-01-29T20:12:16+00:00" }, { "name": "symfony/polyfill-intl-idn", @@ -26037,16 +25790,16 @@ }, { "name": "symfony/routing", - "version": "v6.4.16", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "91e02e606b4b705c2f4fb42f7e7708b7923a3220" + "reference": "640a74250d13f9c30d5ca045b6aaaabcc8215278" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/91e02e606b4b705c2f4fb42f7e7708b7923a3220", - "reference": "91e02e606b4b705c2f4fb42f7e7708b7923a3220", + "url": "https://api.github.com/repos/symfony/routing/zipball/640a74250d13f9c30d5ca045b6aaaabcc8215278", + "reference": "640a74250d13f9c30d5ca045b6aaaabcc8215278", "shasum": "" }, "require": { @@ -26100,7 +25853,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.4.16" + "source": "https://github.com/symfony/routing/tree/v6.4.13" }, "funding": [ { @@ -26116,7 +25869,7 @@ "type": "tidelift" } ], - "time": "2024-11-13T15:31:34+00:00" + "time": "2024-10-01T08:30:56+00:00" }, { "name": "symfony/serializer", @@ -26218,16 +25971,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.5.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", "shasum": "" }, "require": { @@ -26281,7 +26034,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" }, "funding": [ { @@ -26297,7 +26050,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/string", @@ -26387,16 +26140,16 @@ }, { "name": "symfony/translation-contracts", - "version": "v3.5.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", "shasum": "" }, "require": { @@ -26445,7 +26198,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0" }, "funding": [ { @@ -26461,20 +26214,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/twig-bridge", - "version": "v6.4.16", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "32ec012ed4f6426441a66014471bdb26674744be" + "reference": "256f330026d1c97187b61aa5c29e529499877f13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/32ec012ed4f6426441a66014471bdb26674744be", - "reference": "32ec012ed4f6426441a66014471bdb26674744be", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/256f330026d1c97187b61aa5c29e529499877f13", + "reference": "256f330026d1c97187b61aa5c29e529499877f13", "shasum": "" }, "require": { @@ -26554,7 +26307,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v6.4.16" + "source": "https://github.com/symfony/twig-bridge/tree/v6.4.4" }, "funding": [ { @@ -26570,7 +26323,7 @@ "type": "tidelift" } ], - "time": "2024-11-25T11:59:11+00:00" + "time": "2024-02-15T11:26:02+00:00" }, { "name": "symfony/validator", @@ -26955,22 +26708,22 @@ }, { "name": "twig/intl-extra", - "version": "v3.16.0", + "version": "v3.8.0", "source": { "type": "git", "url": "https://github.com/twigphp/intl-extra.git", - "reference": "4eeab2a3f8d04d1838be7251ab2d183f817aea7b" + "reference": "7b3db67c700735f473a265a97e1adaeba3e6ca0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/4eeab2a3f8d04d1838be7251ab2d183f817aea7b", - "reference": "4eeab2a3f8d04d1838be7251ab2d183f817aea7b", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/7b3db67c700735f473a265a97e1adaeba3e6ca0c", + "reference": "7b3db67c700735f473a265a97e1adaeba3e6ca0c", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/intl": "^5.4|^6.4|^7.0", - "twig/twig": "^3.13|^4.0" + "php": ">=7.2.5", + "symfony/intl": "^5.4|^6.0|^7.0", + "twig/twig": "^3.0" }, "require-dev": { "symfony/phpunit-bridge": "^6.4|^7.0" @@ -27003,7 +26756,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/intl-extra/tree/v3.16.0" + "source": "https://github.com/twigphp/intl-extra/tree/v3.8.0" }, "funding": [ { @@ -27015,7 +26768,7 @@ "type": "tidelift" } ], - "time": "2024-11-20T13:19:52+00:00" + "time": "2023-11-21T17:27:48+00:00" }, { "name": "twig/twig", @@ -28143,6 +27896,7 @@ "drupal/fieldhelptext": 10, "drupal/flag": 10, "drupal/graphql_menu": 15, + "drupal/html_tag_usage": 10, "drupal/image_style_warmer": 5, "drupal/jsonapi_resources": 10, "drupal/limited_field_widgets": 15, @@ -28162,13 +27916,12 @@ "drupal/simplesamlphp_auth": 5, "drupal/styleguide": 10, "drupal/user_history": 15, - "drupal/viewfield": 10, - "drupal/html_tag_usage": 10 + "drupal/viewfield": 10 }, "prefer-stable": true, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "platform-overrides": { "php": "8.1" }, diff --git a/config/sync/core.entity_form_display.node.campaign_landing_page.default.yml b/config/sync/core.entity_form_display.node.campaign_landing_page.default.yml index 8fa1f33df9..f81c6960b7 100644 --- a/config/sync/core.entity_form_display.node.campaign_landing_page.default.yml +++ b/config/sync/core.entity_form_display.node.campaign_landing_page.default.yml @@ -4,7 +4,7 @@ status: true dependencies: config: - entity_browser.browser.audiences_checkboxes - - entity_browser.browser.events_browser + - entity_browser.browser.promo_blocks_browser - field.field.node.campaign_landing_page.field_administration - field.field.node.campaign_landing_page.field_benefit_categories - field.field.node.campaign_landing_page.field_clp_audience @@ -48,6 +48,7 @@ dependencies: - node.type.campaign_landing_page - workflows.workflow.editorial module: + - change_labels - content_moderation - entity_browser - entity_browser_entity_form @@ -320,6 +321,7 @@ content: field_widget_display_settings: { } additional_fields: options: null + show_latest_revision: false third_party_settings: limited_field_widgets: limit_values: 2 @@ -336,6 +338,7 @@ content: open: false field_widget_display_settings: { } selection_mode: selection_append + show_latest_revision: false third_party_settings: { } field_clp_events_header: type: string_textfield_with_counter @@ -375,6 +378,7 @@ content: field_widget_replace: 0 additional_fields: options: null + show_latest_revision: false third_party_settings: { } field_clp_faq_cta: type: paragraphs @@ -729,26 +733,26 @@ content: textcount_status_message: '@remaining_count characters remaining' third_party_settings: { } field_clp_what_you_can_do_promos: - type: inline_entity_form_complex_table_view_mode + type: entity_reference_browser_table_widget weight: 7 region: content settings: - form_mode: default - override_labels: true - label_singular: Promo - label_plural: Promos - allow_new: true - allow_existing: true - match_operator: CONTAINS - allow_duplicate: false - collapsible: false - collapsed: false - revision: true - removed_reference: optional - allow_system_delete: false + entity_browser: promo_blocks_browser + field_widget_display: linked_title + field_widget_display_settings: + target_blank: '1' + field_widget_edit: '1' + field_widget_remove: '1' + show_latest_revision: '1' + selection_mode: selection_append + additional_fields: + options: + status: status + field_widget_replace: 0 + open: 0 third_party_settings: - entity_browser_entity_form: - entity_browser_id: promo_blocks_browser + change_labels: + add_another: '' field_clp_why_this_matters: type: string_textarea_with_counter weight: 2 diff --git a/config/sync/entity_browser.browser.promo_blocks_browser.yml b/config/sync/entity_browser.browser.promo_blocks_browser.yml index e4b04135d7..848688ca5d 100644 --- a/config/sync/entity_browser.browser.promo_blocks_browser.yml +++ b/config/sync/entity_browser.browser.promo_blocks_browser.yml @@ -5,6 +5,7 @@ dependencies: config: - views.view.custom_block_entity_browsers module: + - entity_browser_entity_form - views name: promo_blocks_browser label: 'Promo blocks browser' @@ -16,16 +17,26 @@ display_configuration: auto_open: false selection_display: no_display selection_display_configuration: { } -widget_selector: single +widget_selector: tabs widget_selector_configuration: { } widgets: 03832723-7601-4ed8-82e3-09617277e3bb: id: view uuid: 03832723-7601-4ed8-82e3-09617277e3bb - label: '' + label: 'Add existing promo' weight: 1 settings: submit_text: 'Select promo' auto_select: false view: custom_block_entity_browsers view_display: entity_browser_2 + 0fd08d3a-6f12-4949-8672-19dd8d928877: + id: entity_form + uuid: 0fd08d3a-6f12-4949-8672-19dd8d928877 + label: 'Add new promo' + weight: 2 + settings: + submit_text: 'Save promo' + entity_type: block_content + bundle: promo + form_mode: default diff --git a/config/sync/field.field.node.campaign_landing_page.field_clp_what_you_can_do_promos.yml b/config/sync/field.field.node.campaign_landing_page.field_clp_what_you_can_do_promos.yml index d969e49805..dc47d17608 100644 --- a/config/sync/field.field.node.campaign_landing_page.field_clp_what_you_can_do_promos.yml +++ b/config/sync/field.field.node.campaign_landing_page.field_clp_what_you_can_do_promos.yml @@ -8,10 +8,14 @@ dependencies: - node.type.campaign_landing_page module: - entity_reference_validators + - tmgmt_content third_party_settings: entity_reference_validators: circular_reference: false + circular_reference_deep: false duplicate_reference: true + tmgmt_content: + excluded: false id: node.campaign_landing_page.field_clp_what_you_can_do_promos field_name: field_clp_what_you_can_do_promos entity_type: node @@ -29,6 +33,7 @@ settings: promo: promo sort: field: _none - auto_create: false + direction: ASC + auto_create: true auto_create_bundle: '' field_type: entity_reference diff --git a/docroot/modules/custom/va_gov_workflow/src/EventSubscriber/EntityEventSubscriber.php b/docroot/modules/custom/va_gov_workflow/src/EventSubscriber/EntityEventSubscriber.php index 685e54ea27..4fd21f5a52 100644 --- a/docroot/modules/custom/va_gov_workflow/src/EventSubscriber/EntityEventSubscriber.php +++ b/docroot/modules/custom/va_gov_workflow/src/EventSubscriber/EntityEventSubscriber.php @@ -95,7 +95,7 @@ public function __construct( UserPermsService $user_perms_service, WorkflowContentControl $workflow_content_control, Flagger $flagger, - NotificationsManager $notifications_manager + NotificationsManager $notifications_manager, ) { $this->entityTypeManager = $entity_type_manager; $this->userPermsService = $user_perms_service; @@ -351,7 +351,7 @@ protected function getIefTypeFields(NodeInterface $node): array { $field_displays = $form_display->toArray(); foreach ($field_displays['content'] as $field_name => $field_display) { - if ($this->isNodeIef($node, $field_name)) { + if ($this->isNodeOrBlockIef($node, $field_name)) { $operations = [ 'field_widget_edit' => !empty($field_display['settings']['field_widget_edit']), 'field_widget_remove' => !empty($field_display['settings']['field_widget_remove']), @@ -366,7 +366,7 @@ protected function getIefTypeFields(NodeInterface $node): array { } /** - * Checks to see if a field is an entity reference that targets a node. + * Determines whether an entity reference targets a node or content block. * * @param \Drupal\node\NodeInterface $node * The node object. @@ -376,7 +376,7 @@ protected function getIefTypeFields(NodeInterface $node): array { * @return bool * TRUE if it is an ief field targeting a node, FALSE otherwise. */ - protected function isNodeIef(NodeInterface $node, $field_name): bool { + protected function isNodeOrBlockIef(NodeInterface $node, $field_name): bool { $field_definition = $node->getFieldDefinition($field_name); if (empty($field_definition)) { return FALSE; @@ -388,7 +388,7 @@ protected function isNodeIef(NodeInterface $node, $field_name): bool { ]; $target_type = $field_definition->getItemDefinition()->getSettings()['target_type'] ?? ''; - return (in_array($fieldType, $field_types_for_ief)) && ($target_type === "node"); + return (in_array($fieldType, $field_types_for_ief)) && ($target_type === "node" || $target_type === "block_content"); } /** diff --git a/patches/2851580-fix-remove-button-entity-browser.patch b/patches/2851580-fix-remove-button-entity-browser.patch new file mode 100644 index 0000000000..0db684ded0 --- /dev/null +++ b/patches/2851580-fix-remove-button-entity-browser.patch @@ -0,0 +1,1120 @@ +diff --git a/js/entity_browser.entity_reference.js b/js/entity_browser.entity_reference.js +index 93ee783e11756f2a0318e8030f46e6c73bbc3b89..b1ae719cb75c4f76ce32ca83e43e00acf68ef58d 100644 +--- a/js/entity_browser.entity_reference.js ++++ b/js/entity_browser.entity_reference.js +@@ -23,6 +23,15 @@ + } + }); + }); ++ ++ $(context).find('[data-entity-browser-entities-list] .remove-button').each(function () { ++ $(once('entity-browser-remove', $(this))).on('mousedown', function(e) { ++ var $currentItems = $(this).parents('[data-entity-browser-entities-list]:first'); ++ $(this).parents('.item-container:first').remove(); ++ Drupal.entityBrowserEntityReference.updateTargetId($currentItems); ++ }) ++ }); ++ + // The AJAX callback will give us a flag when we need to re-open the + // browser, most likely due to a "Replace" button being clicked. + if (typeof drupalSettings.entity_browser_reopen_browser !== 'undefined' && drupalSettings.entity_browser_reopen_browser) { +@@ -50,13 +59,31 @@ + * Object with the sortable area. + */ + Drupal.entityBrowserEntityReference.entitiesReordered = function (widget) { +- var items = $(widget).find('.item-container'); ++ Drupal.entityBrowserEntityReference.updateTargetId($(widget)); ++ }; ++ ++ /** ++ * Updates the 'target_id' element. ++ * ++ * @param {object} $currentItems ++ * Object with '.entities-list.sortable' element. ++ */ ++ Drupal.entityBrowserEntityReference.updateTargetId = function ($currentItems) { ++ var items = $currentItems.find('.item-container'); + var ids = []; + for (var i = 0; i < items.length; i++) { + ids[i] = $(items[i]).attr('data-entity-id'); ++ // If using weight field, update it. ++ $(items[i]).find('input[name*="[_weight]"]').val(i); + } ++ var $target_id_element = $currentItems.parent().find('input[type*=hidden][name*="[target_id]"]'); ++ $target_id_element.val(ids.join(' ')); + +- $(widget).parent().find('input[type*=hidden][name*="[target_id]"]').val(ids.join(' ')); +- }; ++ // Trigger ajax submission to restore entity browser form element. ++ var cardinality = parseInt($target_id_element.attr('data-cardinality')); ++ if (ids.length < cardinality && $target_id_element.attr('data-entity-browser-visible') === "0") { ++ $target_id_element.trigger('entity_browser_value_updated'); ++ } ++ } + + }(jQuery, Drupal, Sortable)); +diff --git a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php +index 13df19e50ae44393a651c46a0524065d0c29c921..a1e4150dcf179bf233edd3ff276b94045fbb9e85 100644 +--- a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php ++++ b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php +@@ -15,7 +15,6 @@ use Drupal\entity_browser\Element\EntityBrowserElement; + use Drupal\entity_browser\Entity\EntityBrowser; + use Symfony\Component\DependencyInjection\ContainerInterface; + use Symfony\Component\Validator\ConstraintViolation; +-use Symfony\Component\Validator\ConstraintViolationInterface; + use Symfony\Component\Validator\ConstraintViolationListInterface; + + /** +@@ -77,6 +76,13 @@ class EntityReferenceBrowserWidget extends WidgetBase { + */ + protected $entityDisplayRepository; + ++ /** ++ * If triggering element was hidden target_id element. ++ * ++ * @var bool ++ */ ++ protected $entityBrowserValueUpdated; ++ + /** + * {@inheritdoc} + */ +@@ -294,6 +300,12 @@ class EntityReferenceBrowserWidget extends WidgetBase { + if ($violations->count() > 0) { + /** @var \Symfony\Component\Validator\ConstraintViolation $violation */ + foreach ($violations as $offset => $violation) { ++ $trigger = $form_state->getTriggeringElement(); ++ if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') { ++ // Skip validation if it is triggered by hidden target_id. ++ $violations->remove($offset); ++ continue; ++ } + // The value of the required field is checked through the "not null" + // constraint, whose message is not very useful. We override it here for + // better UX. +@@ -336,30 +348,21 @@ class EntityReferenceBrowserWidget extends WidgetBase { + public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { + + $entities = $this->formElementEntities($items, $element, $form_state); +- +- // Get correct ordered list of entity IDs. +- $ids = array_map( +- function (EntityInterface $entity) { +- return $entity->id(); +- }, +- $entities +- ); +- +- // We store current entity IDs as we might need them in future requests. If +- // some other part of the form triggers an AJAX request with +- // #limit_validation_errors we won't have access to the value of the +- // target_id element and won't be able to build the form as a result of +- // that. This will cause missing submit (Remove, Edit, ...) elements, which +- // might result in unpredictable results. +- $form_state->set(['entity_browser_widget', $this->getFormStateKey($items)], $ids); ++ $items->setValue($entities); + + $hidden_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName() . '-target-id'); + $details_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName()); + ++ // Get configuration required to check entity browser availability. ++ $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(); ++ $selection_mode = $this->getSetting('selection_mode'); ++ ++ $display_entity_browser = EntityBrowserElement::isEntityBrowserAvailable($selection_mode, $cardinality, count($entities)); ++ + $element += [ + '#id' => $details_id, + '#type' => 'details', +- '#open' => !empty($entities) || $this->getSetting('open'), ++ '#open' => !empty($entities) || $this->getSetting('open') || $this->entityBrowserValueUpdated, + '#required' => $this->fieldDefinition->isRequired(), + // We are not using Entity browser's hidden element since we maintain + // selected entities in it during entire process. +@@ -367,7 +370,11 @@ class EntityReferenceBrowserWidget extends WidgetBase { + '#type' => 'hidden', + '#id' => $hidden_id, + // We need to repeat ID here as it is otherwise skipped when rendering. +- '#attributes' => ['id' => $hidden_id], ++ '#attributes' => [ ++ 'id' => $hidden_id, ++ 'data-cardinality' => $cardinality, ++ 'data-entity-browser-visible' => $display_entity_browser, ++ ], + '#default_value' => implode(' ', array_map( + function (EntityInterface $item) { + return $item->getEntityTypeId() . ':' . $item->id(); +@@ -381,15 +388,14 @@ class EntityReferenceBrowserWidget extends WidgetBase { + 'wrapper' => $details_id, + 'event' => 'entity_browser_value_updated', + ], ++ '#submit' => [[get_class($this), 'entityBrowserValueUpdated']], ++ '#limit_validation_errors' => [array_merge($element['#field_parents'], [$this->fieldDefinition->getName()])], ++ '#executes_submit_callback' => TRUE, + ], + ]; + +- // Get configuration required to check entity browser availability. +- $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(); +- $selection_mode = $this->getSetting('selection_mode'); +- + // Enable entity browser if requirements for that are fulfilled. +- if (EntityBrowserElement::isEntityBrowserAvailable($selection_mode, $cardinality, count($ids))) { ++ if ($display_entity_browser) { + $persistentData = $this->getPersistentData(); + + $element['entity_browser'] = [ +@@ -406,6 +412,13 @@ class EntityReferenceBrowserWidget extends WidgetBase { + [get_called_class(), 'processEntityBrowser'], + ], + ]; ++ ++ $element['target_id']['#attributes']['data-entity-browser-available'] = 1; ++ } ++ else { ++ // Allow non-ajax remove button to trigger ajax refresh when ++ // cardinality. ++ $element['target_id']['#attributes']['data-entity-browser-visible'] = 0; + } + + $element['#attached']['library'][] = 'entity_browser/entity_reference'; +@@ -453,9 +466,6 @@ class EntityReferenceBrowserWidget extends WidgetBase { + if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') { + $parents = array_slice($trigger['#array_parents'], 0, -1); + } +- elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], '_remove_')) { +- $parents = array_slice($trigger['#array_parents'], 0, -static::$deleteDepth); +- } + elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], '_replace_')) { + $parents = array_slice($trigger['#array_parents'], 0, -static::$deleteDepth); + // We need to re-open the browser. Instead of just passing "TRUE", send +@@ -469,21 +479,10 @@ class EntityReferenceBrowserWidget extends WidgetBase { + return $parents; + } + +- /** +- * {@inheritdoc} +- */ +- public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) { +- if (($trigger = $form_state->getTriggeringElement())) { +- // Can be triggered by "Remove" button. +- if (end($trigger['#parents']) === 'remove_button') { +- return FALSE; +- } +- } +- return parent::errorElement($element, $violation, $form, $form_state); +- } +- + /** + * Submit callback for remove buttons. ++ * ++ * @deprecated Handled in javascript. See entity_browser.entity_reference.js. + */ + public static function removeItemSubmit(&$form, FormStateInterface $form_state) { + $triggering_element = $form_state->getTriggeringElement(); +@@ -499,7 +498,6 @@ class EntityReferenceBrowserWidget extends WidgetBase { + // @todo add weight field. + if ($item == $id) { + array_splice($values, $index, 1); +- + break; + } + } +@@ -515,6 +513,30 @@ class EntityReferenceBrowserWidget extends WidgetBase { + } + } + ++ /** ++ * Submit callback. ++ */ ++ public static function entityBrowserValueUpdated(&$form, FormStateInterface $form_state) { ++ $triggering_element = $form_state->getTriggeringElement(); ++ $parents = array_slice($triggering_element['#parents'], 0, -1); ++ $array_parents = array_slice($triggering_element['#array_parents'], 0, -1); ++ ++ $values = $form_state->getValue($parents); ++ ++ $entities = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id'])); ++ $values = []; ++ foreach ($entities as $entity) { ++ $values[]['target_id'] = explode(':', $entity)[1]; ++ } ++ ++ // Set new value for this widget in the form_state. ++ $element = &NestedArray::getValue($form, $array_parents); ++ $form_state->setValueForElement($element, $values); ++ ++ // Rebuild form. ++ $form_state->setRebuild(); ++ } ++ + /** + * Builds the render array for displaying the current results. + * +@@ -551,7 +573,10 @@ class EntityReferenceBrowserWidget extends WidgetBase { + + return [ + '#theme_wrappers' => ['container'], +- '#attributes' => ['class' => $classes], ++ '#attributes' => [ ++ 'class' => $classes, ++ 'data-entity-browser-entities-list' => 1, ++ ], + '#prefix' => '
' . $this->getCardinalityMessage($entities) . '
', + 'items' => array_map( + function (ContentEntityInterface $entity, $row_id) use ($field_widget_display, $details_id, $field_parents, $replace_button_access) { +@@ -577,13 +602,7 @@ class EntityReferenceBrowserWidget extends WidgetBase { + 'remove_button' => [ + '#type' => 'submit', + '#value' => $this->t('Remove'), +- '#ajax' => [ +- 'callback' => [get_class($this), 'updateWidgetCallback'], +- 'wrapper' => $details_id, +- ], +- '#submit' => [[get_class($this), 'removeItemSubmit']], + '#name' => $this->fieldDefinition->getName() . '_remove_' . $entity->id() . '_' . $row_id . '_' . md5(json_encode($field_parents)), +- '#limit_validation_errors' => [array_merge($field_parents, [$this->fieldDefinition->getName()])], + '#attributes' => [ + 'data-entity-id' => $entity->getEntityTypeId() . ':' . $entity->id(), + 'data-row-id' => $row_id, +@@ -755,13 +774,22 @@ class EntityReferenceBrowserWidget extends WidgetBase { + */ + protected function formElementEntities(FieldItemListInterface $items, array $element, FormStateInterface $form_state) { + $entities = []; +- $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type'); +- $entity_storage = $this->entityTypeManager->getStorage($entity_type); + + // Find IDs from target_id element (it stores selected entities in form). + // This was added to help solve a really edge casey bug in IEF. +- if (($target_id_entities = $this->getEntitiesByTargetId($element, $form_state)) !== FALSE) { +- return $target_id_entities; ++ $element_path = array_merge($element['#field_parents'], [$this->fieldDefinition->getName()]); ++ $input_exists = NULL; ++ $input_value = NestedArray::getValue($form_state->getUserInput(), $element_path, $input_exists); ++ $value_exists = NestedArray::keyExists($form_state->getValues(), $element_path); ++ ++ if (!$value_exists && $input_exists && isset($input_value['target_id']) && !is_array($input_value['target_id'])) { ++ $data = empty($input_value['target_id']) ? [] : explode(' ', trim($input_value['target_id'])); ++ $values = []; ++ foreach ($data as $data_item) { ++ $values[]['target_id'] = explode(':', $data_item)[1]; ++ } ++ $items->setValue($values); ++ return $items->referencedEntities(); + } + + // Determine if we're submitting and if submit came from this widget. +@@ -780,62 +808,27 @@ class EntityReferenceBrowserWidget extends WidgetBase { + (array_slice($trigger['#parents'], 0, count($element['#field_parents'])) == $element['#field_parents']) && + (array_slice($trigger['#parents'], 0, $field_name_key) == $element['#field_parents']); + } +- }; ++ } + + if ($is_relevant_submit) { + // Submit was triggered by hidden "target_id" element when entities were + // added via entity browser. + if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') { +- $parents = $trigger['#parents']; +- } +- // Submit was triggered by one of the "Remove" buttons. We need to walk +- // few levels up to read value of "target_id" element. +- elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], $this->fieldDefinition->getName() . '_remove_') === 0) { +- $parents = array_merge(array_slice($trigger['#parents'], 0, -static::$deleteDepth), ['target_id']); ++ $parents = array_slice($trigger['#parents'], 0, -1); ++ $this->entityBrowserValueUpdated = TRUE; + } + + if (isset($parents) && $value = $form_state->getValue($parents)) { +- $entities = EntityBrowserElement::processEntityIds($value); +- return $entities; +- } +- return $entities; +- } +- // IDs from a previous request might be saved in the form state. +- elseif ($form_state->has([ +- 'entity_browser_widget', +- $this->getFormStateKey($items), +- ]) +- ) { +- $stored_ids = $form_state->get([ +- 'entity_browser_widget', +- $this->getFormStateKey($items), +- ]); +- $indexed_entities = $entity_storage->loadMultiple($stored_ids); +- +- // Selection can contain same entity multiple times. Since loadMultiple() +- // returns unique list of entities, it's necessary to recreate list of +- // entities in order to preserve selection of duplicated entities. +- foreach ($stored_ids as $entity_id) { +- if (isset($indexed_entities[$entity_id])) { +- $entities[] = $indexed_entities[$entity_id]; +- } ++ $items->setValue($value); ++ $entities = $items->referencedEntities(); + } + return $entities; + } ++ + // We are loading for for the first time so we need to load any existing + // values that might already exist on the entity. Also, remove any leftover + // data from removed entity references. +- else { +- foreach ($items as $item) { +- if (isset($item->target_id)) { +- $entity = $entity_storage->load($item->target_id); +- if (!empty($entity)) { +- $entities[] = $entity; +- } +- } +- } +- return $entities; +- } ++ return $items->referencedEntities(); + } + + /** +@@ -853,39 +846,4 @@ class EntityReferenceBrowserWidget extends WidgetBase { + return $dependencies; + } + +- /** +- * Get selected elements from target_id element on form. +- * +- * @param array $element +- * The form element. +- * @param \Drupal\Core\Form\FormStateInterface $form_state +- * The form state. +- * +- * @return \Drupal\Core\Entity\EntityInterface[]|false +- * Return list of entities if they are available or false. +- */ +- protected function getEntitiesByTargetId(array $element, FormStateInterface $form_state) { +- $target_id_element_path = array_merge( +- $element['#field_parents'], +- [$this->fieldDefinition->getName(), 'target_id'] +- ); +- +- $user_input = $form_state->getUserInput(); +- +- $ief_submit = (!empty($user_input['_triggering_element_name']) && strpos($user_input['_triggering_element_name'], 'ief-edit-submit') === 0); +- +- if (!$ief_submit || !NestedArray::keyExists($form_state->getUserInput(), $target_id_element_path)) { +- return FALSE; +- } +- +- // TODO Figure out how to avoid using raw user input. +- $current_user_input = NestedArray::getValue($form_state->getUserInput(), $target_id_element_path); +- if (!is_array($current_user_input)) { +- $entities = EntityBrowserElement::processEntityIds($current_user_input); +- return $entities; +- } +- +- return FALSE; +- } +- + } +diff --git a/src/Plugin/Field/FieldWidget/FileBrowserWidget.php b/src/Plugin/Field/FieldWidget/FileBrowserWidget.php +index 3ead83437b6b7a981b6cde4ef95f8dc66c801676..d9b353abcad87488e3acc599331d6202fc6988c9 100644 +--- a/src/Plugin/Field/FieldWidget/FileBrowserWidget.php ++++ b/src/Plugin/Field/FieldWidget/FileBrowserWidget.php +@@ -164,7 +164,10 @@ class FileBrowserWidget extends EntityReferenceBrowserWidget { + $current = [ + '#type' => 'table', + '#empty' => $this->t('No files yet'), +- '#attributes' => ['class' => ['entities-list']], ++ '#attributes' => [ ++ 'class' => ['entities-list'], ++ 'data-entity-browser-entities-list' => 1, ++ ], + '#tabledrag' => [ + [ + 'action' => 'order', +@@ -230,9 +233,9 @@ class FileBrowserWidget extends EntityReferenceBrowserWidget { + } + } + +- $current[$entity_id] = [ ++ $current[$delta] = [ + '#attributes' => [ +- 'class' => ['draggable'], ++ 'class' => ['item-container', 'draggable'], + 'data-entity-id' => $entity->getEntityTypeId() . ':' . $entity_id, + 'data-row-id' => $delta, + ], +@@ -240,12 +243,12 @@ class FileBrowserWidget extends EntityReferenceBrowserWidget { + + // Provide a rendered entity if a view builder is available. + if ($has_file_entity) { +- $current[$entity_id]['display'] = $this->entityTypeManager->getViewBuilder('file')->view($entity, $view_mode); ++ $current[$delta]['display'] = $this->entityTypeManager->getViewBuilder('file')->view($entity, $view_mode); + } + // For images, support a preview image style as an alternative. + elseif ($field_type == 'image' && !empty($widget_settings['preview_image_style'])) { + $uri = $entity->getFileUri(); +- $current[$entity_id]['display'] = [ ++ $current[$delta]['display'] = [ + '#weight' => -10, + '#theme' => 'image_style', + '#width' => $width, +@@ -257,9 +260,9 @@ class FileBrowserWidget extends EntityReferenceBrowserWidget { + // Assume that the file name is part of the preview output if + // file entity is installed, do not show this column in that case. + if (!$has_file_entity) { +- $current[$entity_id]['filename'] = ['#markup' => $entity->label()]; ++ $current[$delta]['filename'] = ['#markup' => $entity->label()]; + } +- $current[$entity_id] += [ ++ $current[$delta] += [ + 'meta' => [ + 'display_field' => [ + '#type' => 'checkbox', +@@ -331,11 +334,6 @@ class FileBrowserWidget extends EntityReferenceBrowserWidget { + 'remove_button' => [ + '#type' => 'submit', + '#value' => $this->t('Remove'), +- '#ajax' => [ +- 'callback' => [get_class($this), 'updateWidgetCallback'], +- 'wrapper' => $details_id, +- ], +- '#submit' => [[get_class($this), 'removeItemSubmit']], + '#name' => $field_machine_name . '_remove_' . $entity_id . '_' . md5(json_encode($field_parents)), + '#limit_validation_errors' => [array_merge($field_parents, [$field_machine_name, 'target_id'])], + '#attributes' => [ +@@ -370,27 +368,27 @@ class FileBrowserWidget extends EntityReferenceBrowserWidget { + public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { + $ids = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id'])); + $return = []; +- foreach ($ids as $id) { ++ foreach ($ids as $delta => $id) { + $id = explode(':', $id)[1]; +- if (is_array($values['current']) && isset($values['current'][$id])) { ++ if (is_array($values['current']) && isset($values['current'][$delta])) { + $item_values = [ + 'target_id' => $id, +- '_weight' => $values['current'][$id]['_weight'], ++ '_weight' => $values['current'][$delta]['_weight'], + ]; + if ($this->fieldDefinition->getType() == 'file') { +- if (isset($values['current'][$id]['meta']['description'])) { +- $item_values['description'] = $values['current'][$id]['meta']['description']; ++ if (isset($values['current'][$delta]['meta']['description'])) { ++ $item_values['description'] = $values['current'][$delta]['meta']['description']; + } +- if ($this->fieldDefinition->getSetting('display_field') && isset($values['current'][$id]['meta']['display_field'])) { +- $item_values['display'] = $values['current'][$id]['meta']['display_field']; ++ if ($this->fieldDefinition->getSetting('display_field') && isset($values['current'][$delta]['meta']['display_field'])) { ++ $item_values['display'] = $values['current'][$delta]['meta']['display_field']; + } + } + if ($this->fieldDefinition->getType() == 'image') { +- if (isset($values['current'][$id]['meta']['alt'])) { +- $item_values['alt'] = $values['current'][$id]['meta']['alt']; ++ if (isset($values['current'][$delta]['meta']['alt'])) { ++ $item_values['alt'] = $values['current'][$delta]['meta']['alt']; + } +- if (isset($values['current'][$id]['meta']['title'])) { +- $item_values['title'] = $values['current'][$id]['meta']['title']; ++ if (isset($values['current'][$delta]['meta']['title'])) { ++ $item_values['title'] = $values['current'][$delta]['meta']['title']; + } + } + $return[] = $item_values; +diff --git a/tests/src/FunctionalJavascript/EntityBrowserViewsWidgetTest.php b/tests/src/FunctionalJavascript/EntityBrowserViewsWidgetTest.php +index ae1dab6ade41f4f5f3729ebf10621b47360bf907..3bd84855b54af5433d701fe29dcea4a420d2c1cc 100644 +--- a/tests/src/FunctionalJavascript/EntityBrowserViewsWidgetTest.php ++++ b/tests/src/FunctionalJavascript/EntityBrowserViewsWidgetTest.php +@@ -45,11 +45,14 @@ class EntityBrowserViewsWidgetTest extends EntityBrowserWebDriverTestBase { + * Tests Entity Browser views widget. + */ + public function testViewsWidget() { ++ $current_user = \Drupal::currentUser(); ++ + // Create a file so that our test View isn't empty. + \Drupal::service('file_system')->copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example.jpg'); + /** @var \Drupal\file\FileInterface $file */ + $file = File::create([ + 'uri' => 'public://example.jpg', ++ 'uid' => $current_user->id(), + ]); + $file->save(); + +diff --git a/tests/src/FunctionalJavascript/EntityBrowserWebDriverTestBase.php b/tests/src/FunctionalJavascript/EntityBrowserWebDriverTestBase.php +index 53e9a1b41ed0c90f2c9f0815325c68bf67ba4d00..346a4c8d0a7cac140ad636e33b5dad9b9b5f3249 100644 +--- a/tests/src/FunctionalJavascript/EntityBrowserWebDriverTestBase.php ++++ b/tests/src/FunctionalJavascript/EntityBrowserWebDriverTestBase.php +@@ -314,4 +314,31 @@ abstract class EntityBrowserWebDriverTestBase extends WebDriverTestBase { + return version_compare(\Drupal::VERSION, $version, '>='); + } + ++ /** ++ * Click the remove button for an .item-container at a given position. ++ * ++ * @param int $position ++ * The xpath position of the item to remove. ++ */ ++ protected function removeItemAtPosition($position): void { ++ $this->assertSession() ++ ->elementExists('xpath', '(//input[contains(@class, "remove-button")])[' . $position . ']') ++ ->press(); ++ } ++ ++ /** ++ * Check the order of .item-container elements. ++ * ++ * @param array $expected ++ * The expected order of .item-container elements. Each key ++ * represents the order using xpath. Each value some text contained ++ * within the item. ++ */ ++ protected function assertItemOrder(array $expected): void { ++ foreach ($expected as $key => $value) { ++ $this->assertSession() ++ ->elementContains('xpath', "(//*[contains(@class, 'item-container')])[" . $key . "]", $value); ++ } ++ } ++ + } +diff --git a/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php b/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php +index c885a2ba018655ab7824597683d12edf5d8b14cc..80eb99631ac70809bbc2c7fe00e4e1f362c6f703 100644 +--- a/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php ++++ b/tests/src/FunctionalJavascript/EntityReferenceWidgetTest.php +@@ -6,6 +6,7 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface; + use Drupal\entity_browser\Element\EntityBrowserElement; + use Drupal\field\Entity\FieldConfig; + use Drupal\field\Entity\FieldStorageConfig; ++use Drupal\file\Entity\File; + use Drupal\FunctionalJavascriptTests\SortableTestTrait; + use Drupal\node\Entity\Node; + use Drupal\user\Entity\Role; +@@ -127,6 +128,7 @@ class EntityReferenceWidgetTest extends EntityBrowserWebDriverTestBase { + $title = $title_field->getValue(); + $this->assertEquals('Walrus', $title); + $title_field->setValue('Alpaca'); ++ $this->getSession()->switchToIFrame(); + $this->assertSession() + ->elementExists('css', '.ui-dialog-buttonset.form-actions .form-submit') + ->press(); +@@ -550,18 +552,14 @@ class EntityReferenceWidgetTest extends EntityBrowserWebDriverTestBase { + + $this->drupalGet('node/7/edit'); + +- $correct_order = [ ++ $this->assertItemOrder([ + 1 => 'Daisy', + 2 => 'Gatsby', + 3 => 'Nick', + 4 => 'Santa Claus', + 5 => 'Easter Bunny', + 6 => 'Pumpkin King', +- ]; +- foreach ($correct_order as $key => $value) { +- $this->assertSession() +- ->elementContains('xpath', "(//div[contains(@class, 'item-container')])[" . $key . "]", $value); +- } ++ ]); + + // In the second set of selections, drag the first item into the second + // position. +@@ -570,32 +568,271 @@ class EntityReferenceWidgetTest extends EntityBrowserWebDriverTestBase { + $assert_session->elementsCount('css', $item_selector, 3); + $this->sortableAfter("$item_selector:first-child", "$item_selector:nth-child(2)", $list_selector); + +- $correct_order = [ ++ $this->assertItemOrder([ + 4 => 'Easter Bunny', + 5 => 'Santa Claus', + 6 => 'Pumpkin King', +- ]; +- foreach ($correct_order as $key => $value) { +- $this->assertSession() +- ->elementContains('xpath', "(//div[contains(@class, 'item-container')])[" . $key . "]", $value); +- } ++ ]); + + // Test that order is preserved after removing item. +- $this->assertSession() +- ->elementExists('xpath', '(//input[contains(@class, "remove-button")])[5]') +- ->press(); ++ $this->removeItemAtPosition(5); + + $this->waitForAjaxToFinish(); + +- $correct_order = [ ++ $this->assertItemOrder([ + 4 => 'Easter Bunny', + 5 => 'Pumpkin King', +- ]; ++ ]); + +- foreach ($correct_order as $key => $value) { +- $this->assertSession() +- ->elementContains('xpath', "(//div[contains(@class, 'item-container')])[" . $key . "]", $value); ++ } ++ ++ /** ++ * Tests that reorder plus remove functions properly. ++ */ ++ public function testDragAndDropAndRemove() { ++ ++ // Test reorder plus remove. ++ $current_user = \Drupal::currentUser(); ++ ++ $file_system = \Drupal::service('file_system'); ++ ++ $files = [ ++ 1 => 'file1', ++ 2 => 'file2', ++ 3 => 'file3', ++ 4 => 'file4', ++ 5 => 'file5', ++ 6 => 'file6', ++ 7 => 'file7', ++ 8 => 'file8', ++ ]; ++ $values = []; ++ foreach ($files as $key => $filename) { ++ $file_system->copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://' . $filename . '.jpg'); ++ /** @var \Drupal\file\FileInterface $file */ ++ $file = File::create([ ++ 'uri' => 'public://' . $filename . '.jpg', ++ 'uid' => $current_user->id(), ++ ]); ++ $file->save(); ++ $values[] = ['target_id' => $file->id()]; + } ++ ++ $node = Node::create( ++ [ ++ 'title' => 'Testing file sort and remove', ++ 'type' => 'article', ++ 'field_reference' => $values, ++ ] ++ ); ++ ++ $node->save(); ++ $edit_link = $node->toUrl('edit-form')->toString(); ++ $this->drupalGet($edit_link); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ 2 => 'file2.jpg', ++ 3 => 'file3.jpg', ++ 4 => 'file4.jpg', ++ 5 => 'file5.jpg', ++ 6 => 'file6.jpg', ++ 7 => 'file7.jpg', ++ 8 => 'file8.jpg', ++ ]); ++ ++ $list_selector = '[data-drupal-selector="edit-field-reference-current"]'; ++ $item_selector = "$list_selector .item-container"; ++ $this->sortableAfter("$item_selector:first-child", "$item_selector:nth-child(2)", $list_selector); ++ ++ $this->assertItemOrder([ ++ 1 => 'file2.jpg', ++ 2 => 'file1.jpg', ++ ]); ++ ++ // Test that order is preserved after removing item. ++ $this->removeItemAtPosition(2); ++ ++ $this->assertItemOrder([ ++ 1 => 'file2.jpg', ++ 2 => 'file3.jpg', ++ ]); ++ ++ $this->sortableAfter("$item_selector:nth-child(2)", "$item_selector:nth-child(3)", $list_selector); ++ ++ $this->assertItemOrder([ ++ 2 => 'file4.jpg', ++ 3 => 'file3.jpg', ++ ]); ++ ++ // Test that order is preserved after removing item. ++ $this->removeItemAtPosition(3); ++ ++ $this->assertItemOrder([ ++ 2 => 'file4.jpg', ++ 3 => 'file5.jpg', ++ ]); ++ ++ $this->sortableAfter("$item_selector:nth-child(3)", "$item_selector:nth-child(4)", $list_selector); ++ ++ $this->assertItemOrder([ ++ 3 => 'file6.jpg', ++ 4 => 'file5.jpg', ++ ]); ++ ++ // Test that order is preserved after removing item. ++ $this->removeItemAtPosition(4); ++ ++ $this->assertItemOrder([ ++ 3 => 'file6.jpg', ++ 4 => 'file7.jpg', ++ ]); ++ ++ $this->sortableAfter("$item_selector:first-child", "$item_selector:nth-child(4)", $list_selector); ++ ++ $this->assertItemOrder([ ++ 1 => 'file4.jpg', ++ 2 => 'file6.jpg', ++ 3 => 'file7.jpg', ++ 4 => 'file2.jpg', ++ 5 => 'file8.jpg', ++ ]); ++ ++ // Test that order is preserved after removing two items. ++ $this->removeItemAtPosition(3); ++ $this->removeItemAtPosition(3); ++ ++ $this->assertItemOrder([ ++ 1 => 'file4.jpg', ++ 2 => 'file6.jpg', ++ 3 => 'file8.jpg', ++ ]); ++ ++ // Test that remove with duplicate items removes the one at the correct ++ // delta. If you remove file 1 at position 3, it should remove that one, ++ // not the same entity at position 1. ++ $values = [ ++ ['target_id' => 1], ++ ['target_id' => 2], ++ ['target_id' => 1], ++ ['target_id' => 3], ++ ['target_id' => 4], ++ ['target_id' => 5], ++ ]; ++ $node->field_reference->setValue($values); ++ $node->save(); ++ ++ $edit_link = $node->toUrl('edit-form')->toString(); ++ $this->drupalGet($edit_link); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ 2 => 'file2.jpg', ++ 3 => 'file1.jpg', ++ 4 => 'file3.jpg', ++ 5 => 'file4.jpg', ++ 6 => 'file5.jpg', ++ ]); ++ ++ $this->removeItemAtPosition(3); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ 2 => 'file2.jpg', ++ 3 => 'file3.jpg', ++ 4 => 'file4.jpg', ++ 5 => 'file5.jpg', ++ ]); ++ ++ $this->drupalGet($edit_link); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ 2 => 'file2.jpg', ++ 3 => 'file1.jpg', ++ 4 => 'file3.jpg', ++ 5 => 'file4.jpg', ++ 6 => 'file5.jpg', ++ ]); ++ ++ $this->removeItemAtPosition(1); ++ ++ $this->assertItemOrder([ ++ 1 => 'file2.jpg', ++ 2 => 'file1.jpg', ++ 3 => 'file3.jpg', ++ 4 => 'file4.jpg', ++ 5 => 'file5.jpg', ++ ]); ++ ++ // Test that removing item that reduces selection count to less than ++ // cardinality number restores entity browser element. ++ FieldStorageConfig::load('node.field_reference') ++ ->setCardinality(1) ++ ->save(); ++ ++ $values = [ ++ ['target_id' => 1], ++ ]; ++ $node->field_reference->setValue($values); ++ $node->save(); ++ ++ $this->assertSession()->buttonExists('Save')->press(); ++ ++ // Reopen the form. ++ $edit_link = $node->toUrl('edit-form')->toString(); ++ $this->drupalGet($edit_link); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ ]); ++ // The entity browser element should not be visible with cardinality 1 and ++ // 1 currently selected item. ++ $this->assertSession()->linkNotExists('Select entities'); ++ $this->assertSession()->buttonExists('Remove')->press(); ++ $this->waitForAjaxToFinish(); ++ // There should be no current selection. ++ $this->assertSession() ++ ->elementNotExists('xpath', "//*[contains(@class, 'item-container')]"); ++ // The entity browser element should be visible with cardinality 1 and ++ // no current selection. ++ $this->assertSession()->linkExists('Select entities'); ++ ++ FieldStorageConfig::load('node.field_reference') ++ ->setCardinality(2) ++ ->save(); ++ ++ $values = [ ++ ['target_id' => 1], ++ ['target_id' => 2], ++ ]; ++ $node->field_reference->setValue($values); ++ $node->save(); ++ ++ // Reopen the form. ++ $edit_link = $node->toUrl('edit-form')->toString(); ++ $this->drupalGet($edit_link); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ 2 => 'file2.jpg', ++ ]); ++ // The entity browser element should not be visible with ++ // cardinality 2 and 2 selections. ++ $this->assertSession()->linkNotExists('Select entities'); ++ $this->assertSession()->elementExists('xpath', "(//*[contains(@class, 'item-container')])[2]") ++ ->findButton('Remove') ++ ->press(); ++ $this->waitForAjaxToFinish(); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ ]); ++ ++ // The entity browser element should be visible with cardinality 2 and ++ // and a selection count of 1. ++ $this->assertSession()->linkExists('Select entities'); ++ + } + + /** +diff --git a/tests/src/FunctionalJavascript/ImageFieldTest.php b/tests/src/FunctionalJavascript/ImageFieldTest.php +index a9ed2b967175cfdca9c4875b0b1272ad12edd5e8..05b1cc650be23183134036694e3749d21a8076ec 100644 +--- a/tests/src/FunctionalJavascript/ImageFieldTest.php ++++ b/tests/src/FunctionalJavascript/ImageFieldTest.php +@@ -140,14 +140,14 @@ class ImageFieldTest extends EntityBrowserWebDriverTestBase { + $this->getSession()->switchToIFrame(); + // Check if the image thumbnail exists. + $this->assertSession() +- ->waitForElementVisible('xpath', '//tr[@data-drupal-selector="edit-field-image-current-1"]'); ++ ->waitForElementVisible('xpath', '//tr[@data-drupal-selector="edit-field-image-current-0"]'); + // Test if the image filename is present. + $this->assertSession()->pageTextContains('example.jpg'); + // Test specifying Alt and Title texts and saving the node. + $alt_text = 'Test alt text.'; + $title_text = 'Test title text.'; +- $this->getSession()->getPage()->fillField('field_image[current][1][meta][alt]', $alt_text); +- $this->getSession()->getPage()->fillField('field_image[current][1][meta][title]', $title_text); ++ $this->getSession()->getPage()->fillField('field_image[current][0][meta][alt]', $alt_text); ++ $this->getSession()->getPage()->fillField('field_image[current][0][meta][title]', $title_text); + $this->getSession()->getPage()->fillField('title[0][value]', 'Node 1'); + $this->getSession()->getPage()->pressButton('Save'); + $this->assertSession()->pageTextContains('Article Node 1 has been created.'); +@@ -256,4 +256,193 @@ class ImageFieldTest extends EntityBrowserWebDriverTestBase { + $this->assertStringContainsString('entity-browser-test', $file->getFileUri()); + } + ++ /** ++ * Tests that reorder plus remove functions properly. ++ */ ++ public function testDragAndDropAndRemove() { ++ $current_user = \Drupal::currentUser(); ++ $file_system = \Drupal::service('file_system'); ++ ++ $files = [ ++ 1 => 'file1', ++ 2 => 'file2', ++ 3 => 'file3', ++ 4 => 'file4', ++ 5 => 'file5', ++ 6 => 'file6', ++ 7 => 'file7', ++ 8 => 'file8', ++ ]; ++ $values = []; ++ foreach ($files as $filename) { ++ $file_system->copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://' . $filename . '.jpg'); ++ /** @var \Drupal\file\FileInterface $file */ ++ $file = File::create([ ++ 'uri' => 'public://' . $filename . '.jpg', ++ 'uid' => $current_user->id(), ++ ]); ++ $file->save(); ++ $values[] = ['target_id' => $file->id()]; ++ } ++ ++ $node = Node::create( ++ [ ++ 'title' => 'Testing file sort and remove', ++ 'type' => 'article', ++ 'field_image' => $values, ++ ] ++ ); ++ $node->save(); ++ ++ $edit_link = $node->toUrl('edit-form')->toString(); ++ $this->drupalGet($edit_link); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ 2 => 'file2.jpg', ++ 3 => 'file3.jpg', ++ 4 => 'file4.jpg', ++ 5 => 'file5.jpg', ++ 6 => 'file6.jpg', ++ 7 => 'file7.jpg', ++ 8 => 'file8.jpg', ++ ]); ++ ++ $file1_handle = $this->assertSession()->elementExists('xpath', "(//a[contains(@class, 'tabledrag-handle')])[1]"); ++ $file2 = $this->assertSession()->elementExists('xpath', "(//tr[contains(@class, 'item-container')])[2]"); ++ $file1_handle->dragTo($file2); ++ ++ $this->assertItemOrder([ ++ 1 => 'file2.jpg', ++ 2 => 'file1.jpg', ++ ]); ++ ++ // Test that order is preserved after removing item. ++ $this->removeItemAtPosition(2); ++ ++ $this->assertItemOrder([ ++ 1 => 'file2.jpg', ++ 2 => 'file3.jpg', ++ ]); ++ ++ $file3 = $this->assertSession()->elementExists('xpath', "(//tr[contains(@class, 'item-container')])[2]"); ++ $this->dragDropElement($file3, 160, 0); ++ ++ $file3_handle = $this->assertSession()->elementExists('xpath', "(//a[contains(@class, 'tabledrag-handle')])[2]"); ++ $file4 = $this->assertSession()->elementExists('xpath', "(//tr[contains(@class, 'item-container')])[3]"); ++ $file3_handle->dragTo($file4); ++ ++ $this->assertItemOrder([ ++ 2 => 'file4.jpg', ++ 3 => 'file3.jpg', ++ ]); ++ ++ // Test that order is preserved after removing item. ++ $this->removeItemAtPosition(3); ++ ++ $this->assertItemOrder([ ++ 2 => 'file4.jpg', ++ 3 => 'file5.jpg', ++ ]); ++ ++ $file5 = $this->assertSession()->elementExists('xpath', "(//tr[contains(@class, 'item-container')])[3]"); ++ $this->dragDropElement($file5, 160, 0); ++ ++ $file5_handle = $this->assertSession()->elementExists('xpath', "(//a[contains(@class, 'tabledrag-handle')])[3]"); ++ $file6 = $this->assertSession()->elementExists('xpath', "(//tr[contains(@class, 'item-container')])[4]"); ++ $file5_handle->dragTo($file6); ++ ++ $this->assertItemOrder([ ++ 3 => 'file6.jpg', ++ 4 => 'file5.jpg', ++ ]); ++ ++ // Test that order is preserved after removing item. ++ $this->removeItemAtPosition(4); ++ ++ $this->assertItemOrder([ ++ 3 => 'file6.jpg', ++ 4 => 'file7.jpg', ++ ]); ++ ++ $file2_handle = $this->assertSession()->elementExists('xpath', "(//a[contains(@class, 'tabledrag-handle')])[1]"); ++ $file8 = $this->assertSession()->elementExists('xpath', "(//tr[contains(@class, 'item-container')])[5]"); ++ $file2_handle->dragTo($file8); ++ ++ $this->assertItemOrder([ ++ 1 => 'file4.jpg', ++ 2 => 'file6.jpg', ++ 3 => 'file7.jpg', ++ 4 => 'file8.jpg', ++ 5 => 'file2.jpg', ++ ]); ++ ++ // Test that order is preserved after removing two items. ++ $this->removeItemAtPosition(3); ++ $this->removeItemAtPosition(4); ++ ++ $this->assertItemOrder([ ++ 1 => 'file4.jpg', ++ 2 => 'file6.jpg', ++ 3 => 'file8.jpg', ++ ]); ++ ++ // Test that remove with duplicate items removes the one at the ++ // correct delta. If you remove file 1 at position 3, it should ++ // remove that one, not the same entity at position 1. ++ $values = [ ++ ['target_id' => 2], ++ ['target_id' => 3], ++ ['target_id' => 2], ++ ['target_id' => 4], ++ ['target_id' => 5], ++ ['target_id' => 6], ++ ]; ++ $node->field_image->setValue($values); ++ $node->save(); ++ ++ $edit_link = $node->toUrl('edit-form')->toString(); ++ $this->drupalGet($edit_link); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ 2 => 'file2.jpg', ++ 3 => 'file1.jpg', ++ 4 => 'file3.jpg', ++ 5 => 'file4.jpg', ++ 6 => 'file5.jpg', ++ ]); ++ ++ $this->removeItemAtPosition(3); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ 2 => 'file2.jpg', ++ 3 => 'file3.jpg', ++ 4 => 'file4.jpg', ++ 5 => 'file5.jpg', ++ ]); ++ ++ $this->drupalGet($edit_link); ++ ++ $this->assertItemOrder([ ++ 1 => 'file1.jpg', ++ 2 => 'file2.jpg', ++ 3 => 'file1.jpg', ++ 4 => 'file3.jpg', ++ 5 => 'file4.jpg', ++ 6 => 'file5.jpg', ++ ]); ++ ++ $this->removeItemAtPosition(1); ++ ++ $this->assertItemOrder([ ++ 1 => 'file2.jpg', ++ 2 => 'file1.jpg', ++ 3 => 'file3.jpg', ++ 4 => 'file4.jpg', ++ 5 => 'file5.jpg', ++ ]); ++ } ++ + } diff --git a/patches/3395042-fix-remove-button-entity-browser-table.patch b/patches/3395042-fix-remove-button-entity-browser-table.patch new file mode 100644 index 0000000000..026474ccdd --- /dev/null +++ b/patches/3395042-fix-remove-button-entity-browser-table.patch @@ -0,0 +1,89 @@ +diff --git a/js/entity_browser.table.js b/js/entity_browser.table.js +index 9bc5479b270ea1f79f54b7a38e2bc81cbc3c2305..c2603a3d8709f2fcd7c65fc9dbfcf1c545cb6f44 100644 +--- a/js/entity_browser.table.js ++++ b/js/entity_browser.table.js +@@ -34,6 +34,15 @@ + } + }); + }); ++ ++ $(context).find('[data-entity-browser-entities-list] .remove-button').each(function () { ++ $(once('entity-browser-remove', $(this))).on('mousedown', function(e) { ++ var $currentItems = $(this).parents('[data-entity-browser-entities-list]:first'); ++ $(this).parents('.item-container:first').remove(); ++ Drupal.entityBrowserEntityReferenceTable.updateTargetId($currentItems); ++ }) ++ }); ++ + // The AJAX callback will give us a flag when we need to re-open the + // browser, most likely due to a "Replace" button being clicked. + if (typeof drupalSettings.entity_browser_reopen_browser !== 'undefined' && drupalSettings.entity_browser_reopen_browser) { +@@ -76,15 +85,25 @@ + * Object with the sortable area. + */ + Drupal.entityBrowserEntityReferenceTable.entitiesReordered = function (widget) { +- var items = $(widget).find('.item-container'); ++ Drupal.entityBrowserEntityReferenceTable.updateTargetId($(widget)); ++ }; ++ ++ /** ++ * Updates the 'target_id' element. ++ * ++ * @param {object} $currentItems ++ * Object with '.entities-list.sortable' element. ++ */ ++ Drupal.entityBrowserEntityReferenceTable.updateTargetId = function (widget) { ++ var items = widget.find('.item-container'); + var ids = []; + for (var i = 0; i < items.length; i++) { + ids[i] = $(items[i]).attr('data-entity-id'); + } +- let currenItems = $(widget).parent().parent(); +- if ($(widget).parents('.field--widget-entity_reference_browser_table_widget').length > 0) { +- currenItems = $(widget).parents('.field--widget-entity_reference_browser_table_widget'); ++ let currenItems = widget.parent().parent(); ++ if (widget.parents('.field--widget-entity_reference_browser_table_widget').length > 0) { ++ currenItems = widget.parents('.field--widget-entity_reference_browser_table_widget'); + } + currenItems.find('input[type*=hidden][name*="[target_id]"]').val(ids.join(' ')); +- }; ++ } + }(jQuery, Drupal, Sortable)); +diff --git a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserTableWidget.php b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserTableWidget.php +index 177fda745f27e21d6377025e50b2caee9f28894a..8d3a64e230518cde15b87a866551841e71e374dc 100644 +--- a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserTableWidget.php ++++ b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserTableWidget.php +@@ -131,7 +131,8 @@ class EntityReferenceBrowserTableWidget extends EntityReferenceBrowserWidget { + $element['#attributes']['class'] = [ + 'field--widget-entity_reference_browser_table_widget', + ]; +- $element['#attached']['library'][] = 'entity_browser_table/entity_browser_table'; ++ // Override the entity_browser/entity_reference library. ++ $element['#attached']['library'] = ['entity_browser_table/entity_browser_table']; + + return $element; + } +@@ -154,7 +155,10 @@ class EntityReferenceBrowserTableWidget extends EntityReferenceBrowserWidget { + $table = [ + '#type' => 'table', + '#header' => $this->buildTableHeaders(), +- '#attributes' => ['class' => ['table--widget-' . $this->getPluginId()]], ++ '#attributes' => [ ++ 'class' => ['table--widget-' . $this->getPluginId()], ++ 'data-entity-browser-entities-list' => 1, ++ ], + '#empty' => $this->t('Use the buttons above to add content to this area.'), + ]; + return array_merge($table, $this->buildTableRows($entities, $details_id, $field_parents)); +@@ -277,11 +281,6 @@ class EntityReferenceBrowserTableWidget extends EntityReferenceBrowserWidget { + return [ + '#type' => 'submit', + '#value' => $this->t('Remove'), +- '#ajax' => [ +- 'callback' => [get_class($this), 'updateWidgetCallback'], +- 'wrapper' => $details_id, +- ], +- '#submit' => [[get_class($this), 'removeItemSubmit']], + '#name' => $this->fieldDefinition->getName() . '_remove_' . $entity->id() . '_' . $row_id . '_' . md5(json_encode($field_parents)), + '#limit_validation_errors' => [array_merge($field_parents, [$this->fieldDefinition->getName()])], + '#attributes' => [ diff --git a/patches/3483265-make-it-possible-to-show-latest-revision-in-fomr-widget.patch b/patches/3483265-make-it-possible-to-show-latest-revision-in-fomr-widget.patch new file mode 100644 index 0000000000..ae291f92ba --- /dev/null +++ b/patches/3483265-make-it-possible-to-show-latest-revision-in-fomr-widget.patch @@ -0,0 +1,184 @@ +diff --git a/config/schema/entity_browser.schema.yml b/config/schema/entity_browser.schema.yml +index 67a1d79cc7c9a07dd5db72908cfefb46584d9a9a..ccb446d739903d5ff5dde6dce8357aa67bc1c195 100644 +--- a/config/schema/entity_browser.schema.yml ++++ b/config/schema/entity_browser.schema.yml +@@ -190,6 +190,9 @@ field.widget.settings.entity_browser_entity_reference: + selection_mode: + type: string + label: 'Selection mode' ++ show_latest_revision: ++ type: boolean ++ label: 'Show latest revision' + + entity_browser.field_widget_display.label: + type: mapping +diff --git a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php +index 13df19e50ae44393a651c46a0524065d0c29c921..89d6776e3e287c0338622e467fd06e4985b0aea6 100644 +--- a/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php ++++ b/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget.php +@@ -96,21 +96,23 @@ class EntityReferenceBrowserWidget extends WidgetBase { + */ + public static function defaultSettings() { + return [ +- 'entity_browser' => NULL, +- 'open' => FALSE, +- 'field_widget_display' => 'label', +- 'field_widget_edit' => TRUE, +- 'field_widget_remove' => TRUE, +- 'field_widget_replace' => FALSE, +- 'field_widget_display_settings' => [], +- 'selection_mode' => EntityBrowserElement::SELECTION_MODE_APPEND, +- ] + parent::defaultSettings(); ++ 'entity_browser' => NULL, ++ 'open' => FALSE, ++ 'field_widget_display' => 'label', ++ 'field_widget_edit' => TRUE, ++ 'field_widget_remove' => TRUE, ++ 'field_widget_replace' => FALSE, ++ 'field_widget_display_settings' => [], ++ 'selection_mode' => EntityBrowserElement::SELECTION_MODE_APPEND, ++ 'show_latest_revision' => FALSE, ++ ] + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ +- public function settingsForm(array $form, FormStateInterface $form_state) { ++ public function settingsForm(array $form, FormStateInterface $form_state) ++ { + $element = parent::settingsForm($form, $form_state); + + $browsers = []; +@@ -220,6 +222,13 @@ class EntityReferenceBrowserWidget extends WidgetBase { + '#default_value' => $this->getSetting('open'), + ]; + ++ $element['show_latest_revision'] = [ ++ '#title' => $this->t('Show the latest revision of the entity.'), ++ '#description' => $this->t('If marked, latest revision will be shown in the edit form.'), ++ '#type' => 'checkbox', ++ '#default_value' => $this->getSetting('show_latest_revision'), ++ ]; ++ + $element['selection_mode'] = [ + '#title' => $this->t('Selection mode'), + '#description' => $this->t('Determines how selection in entity browser will be handled. Will selection be appended/prepended or it will be replaced in case of editing.'), +@@ -270,6 +279,7 @@ class EntityReferenceBrowserWidget extends WidgetBase { + public function settingsSummary() { + $summary = $this->summaryBase(); + $field_widget_display = $this->getSetting('field_widget_display'); ++ $show_latest_revision = $this->getSetting('show_latest_revision'); + + if (!empty($field_widget_display)) { + $pluginDefinition = $this->fieldDisplayManager->getDefinition($field_widget_display); +@@ -283,6 +293,8 @@ class EntityReferenceBrowserWidget extends WidgetBase { + $view_mode_label = $plugin->getViewModeLabel(); + $summary[] = $this->t('View Mode: @view_mode', ['@view_mode' => $view_mode_label]); + } ++ $latest_revision_label = $show_latest_revision ? $this->t('Showing latest revisions.') : $this->t('Showing default revisions.'); ++ $summary[] = $latest_revision_label; + } + return $summary; + } +@@ -750,13 +762,14 @@ class EntityReferenceBrowserWidget extends WidgetBase { + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + * +- * @return \Drupal\Core\Entity\EntityInterface[] ++ * @return \Drupal\Core\Entity\EntityInterface[]|\Drupal\Core\Entity\RevisionableStorageInterface[] + * The list of entities for the form element. + */ + protected function formElementEntities(FieldItemListInterface $items, array $element, FormStateInterface $form_state) { + $entities = []; + $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type'); + $entity_storage = $this->entityTypeManager->getStorage($entity_type); ++ $show_latest_revision = $this->getSetting('show_latest_revision'); + + // Find IDs from target_id element (it stores selected entities in form). + // This was added to help solve a really edge casey bug in IEF. +@@ -780,7 +793,7 @@ class EntityReferenceBrowserWidget extends WidgetBase { + (array_slice($trigger['#parents'], 0, count($element['#field_parents'])) == $element['#field_parents']) && + (array_slice($trigger['#parents'], 0, $field_name_key) == $element['#field_parents']); + } +- }; ++ } + + if ($is_relevant_submit) { + // Submit was triggered by hidden "target_id" element when entities were +@@ -794,9 +807,23 @@ class EntityReferenceBrowserWidget extends WidgetBase { + $parents = array_merge(array_slice($trigger['#parents'], 0, -static::$deleteDepth), ['target_id']); + } + +- if (isset($parents) && $value = $form_state->getValue($parents)) { +- $entities = EntityBrowserElement::processEntityIds($value); +- return $entities; ++ if (isset($parents) && $referenced_entities = $form_state->getValue($parents)) { ++ ++ // Turn that string into an array of entity IDs. ++ $referenced_entities = explode(' ', $referenced_entities); ++ $values = []; ++ foreach ($referenced_entities as $referenced_entity) { ++ $values[]['target_id'] = explode(':', $referenced_entity)[1]; ++ } ++ $items->setValue($values); ++ // If we intend to show the latest revisions, we need to get them. ++ if ($show_latest_revision) { ++ $entities = $this->getEntitiesLatestRevision($items); ++ } ++ else { ++ $entities = $items->referencedEntities(); ++ } ++ // return $entities; + } + return $entities; + } +@@ -822,7 +849,11 @@ class EntityReferenceBrowserWidget extends WidgetBase { + } + return $entities; + } +- // We are loading for for the first time so we need to load any existing ++ // If we intend to show the latest revisions, we need to get them. ++ elseif ($show_latest_revision) { ++ return $this->getEntitiesLatestRevision($items); ++ } ++ // We are loading for the first time so we need to load any existing + // values that might already exist on the entity. Also, remove any leftover + // data from removed entity references. + else { +@@ -838,6 +869,35 @@ class EntityReferenceBrowserWidget extends WidgetBase { + } + } + ++ /** ++ * Get the latest revision of the referenced entities. ++ * ++ * @param \Drupal\Core\Field\FieldItemListInterface $items ++ * The field items. ++ * ++ * @return \Drupal\Core\Entity\RevisionableStorageInterface[] ++ * The list of referenced entities with the latest revision. ++ */ ++ protected function getEntitiesLatestRevision(FieldItemListInterface $items) { ++ $entities = []; ++ $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type'); ++ /** @var \Drupal\Core\Entity\RevisionableStorageInterface $entity_storage */ ++ $entity_storage = $this->entityTypeManager->getStorage($entity_type); ++ ++ foreach ($items as $item) { ++ if (isset($item->target_id)) { ++ $entity = $entity_storage->load($item->target_id); ++ $latest_revision_id = $entity_storage->getLatestRevisionId($item->target_id); ++ $entity = $entity_storage->loadRevision($latest_revision_id); ++ if (!empty($entity)) { ++ $entities[] = $entity; ++ } ++ } ++ } ++ ++ return $entities; ++ } ++ + /** + * {@inheritdoc} + */ diff --git a/tests/cypress/integration/features/content_type/campaign_landing_page/clp_what_you_can_do.feature b/tests/cypress/integration/features/content_type/campaign_landing_page/clp_what_you_can_do.feature deleted file mode 100644 index 764fde03f7..0000000000 --- a/tests/cypress/integration/features/content_type/campaign_landing_page/clp_what_you_can_do.feature +++ /dev/null @@ -1,17 +0,0 @@ -@content_type__campaign_landing_page -Feature: Content Type: Campaign Landing Page - - Scenario: Test that expected form elements are present in What You Can Do segment - Given I am logged in as a user with the "content_admin" role - When I am at "node/add/campaign_landing_page" - And I click to collapse "Hero banner" - And I click to expand "What you can do" - Then I can fill in field with selector "#edit-field-clp-what-you-can-do-header-0-value" with fake text - And I can fill in field with selector "#edit-field-clp-what-you-can-do-intro-0-value" with fake text - And I should see "What you can do promos" - When I click the "Add new Promo" button -# TODO: Test if Add media button is there - And I can fill in "URL" field with fake link - And I can fill in "Link summary" field with fake text - When I click the "Cancel" button -# TODO: Test Add existing promo feature and modal diff --git a/tests/cypress/integration/step_definitions/common/i_create_a_node.js b/tests/cypress/integration/step_definitions/common/i_create_a_node.js index 3aa2dd7bf7..b3efaabb7b 100644 --- a/tests/cypress/integration/step_definitions/common/i_create_a_node.js +++ b/tests/cypress/integration/step_definitions/common/i_create_a_node.js @@ -103,43 +103,96 @@ const creators = { cy.findAllByLabelText("Introduction").type(faker.lorem.sentence(), { force: true, }); - cy.get( - "#edit-field-clp-what-you-can-do-promos-actions-ief-add" - ).scrollIntoView(); - cy.get("#edit-field-clp-what-you-can-do-promos-actions-ief-add").click({ - force: true, + }); + cy.get( + "#edit-field-clp-what-you-can-do-promos-entity-browser-entity-browser-open-modal" + ).should("exist"); + cy.get( + "#edit-field-clp-what-you-can-do-promos-entity-browser-entity-browser-open-modal" + ).click({ + force: true, + }); + cy.wait(3000); + cy.get("iframe.entity-browser-modal-iframe").should("exist"); + cy.wait(3000); + + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { + cy.contains("Add new promo").click({ force: true }); + cy.wait(5000); + }); + cy.get("iframe.entity-browser-modal-iframe").should("exist"); + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { + cy.findByDisplayValue("Add media").click({ force: true }); + }); + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { + cy.get('div[role="dialog"]').within(() => { + cy.get(".dropzone", { + timeout: 60000, + }); }); - cy.contains("Add media").click({ force: true }); }); - cy.get('div[role="dialog"]').within(() => { - cy.get(".dropzone", { - timeout: 60000, + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { + cy.get(".dropzone").attachFile("images/polygon_image.png", { + subjectType: "drag-n-drop", + }); + cy.wait(10000); + }); + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { + cy.findAllByLabelText("Alternative text").type(faker.lorem.sentence(), { + force: true, + }); }); - cy.get(".dropzone").attachFile("images/polygon_image.png", { - subjectType: "drag-n-drop", + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { + cy.get( + '[data-drupal-selector="edit-media-0-fields-field-owner"]' + ).select("VACO", { force: true }); }); - cy.wait(1000); - cy.findAllByLabelText("Alternative text").type(faker.lorem.sentence(), { - force: true, + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { + cy.get("button").contains("Save and insert").click({ force: true }); + cy.wait(5000); }); - cy.get('[data-drupal-selector="edit-media-0-fields-field-owner"]').select( - "VACO", - { force: true } - ); - cy.get("button").contains("Save and insert").click({ force: true }); - }); - cy.contains("What you can do") - .parent() + cy.get("iframe.entity-browser-modal-iframe") + .iframe() .within(() => { - cy.findAllByLabelText("URL").focus(); cy.findAllByLabelText("URL").type(faker.internet.url(), { force: true, }); + }); + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { cy.findAllByLabelText("Link text").type(faker.lorem.sentence(), { force: true, }); - cy.findAllByLabelText("Section").select("VACO", { force: true }); }); + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { + cy.get( + '[data-drupal-selector="edit-inline-entity-form-field-owner"]' + ).select("VACO", { force: true }); + }); + cy.get("iframe.entity-browser-modal-iframe") + .iframe() + .within(() => { + cy.get("#edit-submit").click({ force: true }); + }); + cy.get("iframe.entity-browser-modal-iframe").should("not.exist"); + cy.contains("What you can do").click(); // VA Benefits