diff --git a/defaults/config.json b/defaults/config.json deleted file mode 100644 index 33232728549ab08fde3cbc88946ce0140ccbc5ca..0000000000000000000000000000000000000000 --- a/defaults/config.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "preferred-install": { - "*": "dist" - }, - "sort-packages": true, - "discard-changes": true, - "gitlab-domains": [ - "gitlab.lakedrops.com" - ], - "allow-plugins": { - "composer/installers": true, - "cweagans/composer-patches": true, - "dealerdirect/phpcodesniffer-composer-installer": true, - "drupal/core-composer-scaffold": true, - "drupal/core-vendor-hardening": true, - "endroid/installer": true, - "lakedrops/*": true, - "mxr576/ddqg-composer-audit": true, - "oomphinc/composer-installers-extender": true, - "php-http/discovery": true, - "phpstan/extension-installer": true, - "zodiacmedia/drupal-libraries-installer": true - } -} diff --git a/defaults/curated.json b/defaults/curated.json deleted file mode 100644 index a0975c66d1ba466f4b8b501d55c3d551866e041e..0000000000000000000000000000000000000000 --- a/defaults/curated.json +++ /dev/null @@ -1,64 +0,0 @@ -[ - "DDQG-deprecated-drupal-variationcache-1.5.0.0", - "DDQG-unsupported-drupal-activitypub-1.0.0.0-alpha19", - "DDQG-unsupported-drupal-advanced_cors-1.5.0.0", - "DDQG-unsupported-drupal-analog_digital_clock-10.1.2.0", - "DDQG-unsupported-drupal-apitools-2.0.0.0-alpha3", - "DDQG-unsupported-drupal-blazy-2.16.0.0", - "DDQG-unsupported-drupal-blazy-2.27.0.0", - "DDQG-unsupported-drupal-block_field-1.0.0.0-RC5", - "DDQG-unsupported-drupal-book-2.0.0.0-alpha5", - "DDQG-unsupported-drupal-commerce_xquantity-2.0.0.0-beta1", - "DDQG-unsupported-drupal-conditional_fields-4.0.0.0-alpha6", - "DDQG-unsupported-drupal-config_update-2.0.0.0-alpha4", - "DDQG-unsupported-drupal-content_access-2.0.0.0", - "DDQG-unsupported-drupal-core-10.4.9999999.9999999-dev", - "DDQG-unsupported-drupal-default_content-2.0.0.0-alpha2", - "DDQG-unsupported-drupal-eca-2.1.0.0-RC1", - "DDQG-unsupported-drupal-elasticsearch_connector-8.0.0.0-alpha2", - "DDQG-unsupported-drupal-entitygroupfield-1.0.0.0-beta1", - "DDQG-unsupported-drupal-entity_import-0.0.0.0-dev", - "DDQG-unsupported-drupal-facets-3.0.0.0-beta4", - "DDQG-unsupported-drupal-feeds-3.0.0.0-beta5", - "DDQG-unsupported-drupal-flag-4.0.0.0-beta4", - "DDQG-unsupported-drupal-hacked-2.0.0.0-beta5", - "DDQG-unsupported-drupal-hide_submit_d8-0.0.0.0-dev", - "DDQG-unsupported-drupal-ief_popup-2.2.2.0", - "DDQG-unsupported-drupal-inline_entity_form-3.0.0.0-RC20", - "DDQG-unsupported-drupal-invoice_ninja-1.0.0.0-beta7", - "DDQG-unsupported-drupal-layout_library-1.0.0.0-beta4", - "DDQG-unsupported-drupal-matomo-2.0.0.0-alpha1", - "DDQG-unsupported-drupal-media_entity_link-2.0.1.0", - "DDQG-unsupported-drupal-menu_breadcrumb-2.0.0.0-alpha0", - "DDQG-unsupported-drupal-name-1.0.0.0-RC6", - "DDQG-unsupported-drupal-navigation_extra_tools-1.0.0.0-beta4", - "DDQG-unsupported-drupal-office_hours-0.0.0.0-dev", - "DDQG-unsupported-drupal-openai-1.0.0.0-beta6", - "DDQG-unsupported-drupal-openid_connect-3.0.0.0-alpha4", - "DDQG-unsupported-drupal-peertube-2.0.0.0-beta2", - "DDQG-unsupported-drupal-phone_number-2.0.0.0-alpha7", - "DDQG-unsupported-drupal-project_browser-2.0.0.0-alpha5", - "DDQG-unsupported-drupal-reader-1.0.0.0-beta2", - "DDQG-unsupported-drupal-readmehelp-2.0.0.0-beta1", - "DDQG-unsupported-drupal-schema_diff-1.0.0.0-alpha2", - "DDQG-unsupported-drupal-services-5.0.0.0-beta10", - "DDQG-unsupported-drupal-shs-2.0.0.0-RC12", - "DDQG-unsupported-drupal-simple_oauth-6.0.0.0-beta8", - "DDQG-unsupported-drupal-slick-2.9.0.0", - "DDQG-unsupported-drupal-slick-2.11.0.0", - "DDQG-unsupported-drupal-slick_views-2.7.0.0", - "DDQG-unsupported-drupal-slick_views-2.8.0.0", - "DDQG-unsupported-drupal-tamper-1.0.0.0-alpha5", - "DDQG-unsupported-drupal-term_merge-2.0.0.0-beta6", - "DDQG-unsupported-drupal-term_reference_change-2.0.0.0-beta5", - "DDQG-unsupported-drupal-toolbar_language_switcher-2.0.0.0-alpha2", - "DDQG-unsupported-drupal-viewfield-3.0.0.0-beta11", - "DDQG-unsupported-drupal-viewsreference-2.0.0.0-beta10", - "DDQG-unsupported-drupal-views_base_url-2.1.0.0-beta1", - "DDQG-unsupported-drupal-views_field_view-1.0.0.0-beta5", - "DDQG-unsupported-drupal-views_menu_children_filter-3.0.0.0-RC4", - "DDQG-unsupported-drupal-views_tree-2.0.0.0-RC1", - "DDQG-unsupported-drupal-xls_serialization-0.0.0.0-dev", - "DDQG-unsupported-drupal-xnumber-2.0.0.0-beta2", - "GHSA-mg8j-w93w-xjgc" -] diff --git a/defaults/drupal-libraries.json b/defaults/drupal-libraries.json deleted file mode 100644 index 976f00bce42d3b6d0c8ec68401b296ba059ed028..0000000000000000000000000000000000000000 --- a/defaults/drupal-libraries.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "blazy": "https://github.com/dinbror/blazy/archive/1.8.2.zip", - "codemirror": "https://github.com/components/codemirror/archive/refs/tags/5.65.12.zip", - "colorbox": "https://github.com/jackmoore/colorbox/archive/refs/tags/1.6.4.zip", - "dompurify": "https://github.com/cure53/DOMPurify/archive/refs/tags/3.0.9.zip", - "dropzone": "https://github.com/dropzone/dropzone/releases/download/v5.9.3/dist.zip", - "imagesloaded": "https://api.github.com/repos/desandro/imagesloaded/zipball/67c4e57453120935180c45c6820e7d3fbd2ea1f9", - "fontawesome": "https://use.fontawesome.com/releases/v6.5.1/fontawesome-free-6.5.1-web.zip", - "friendly-challenge": "https://registry.npmjs.org/friendly-challenge/-/friendly-challenge-0.9.16.tgz", - "fullcalendar": "https://github.com/fullcalendar/fullcalendar-workspace/releases/download/v6.1.11/fullcalendar-scheduler-6.1.11.zip", - "jquery.chosen": "https://github.com/harvesthq/chosen/releases/download/v1.8.7/chosen_v1.8.7.zip", - "jquery.geocomplete": "https://github.com/ubilabs/geocomplete/archive/refs/tags/1.7.0.zip", - "jquery.hotkeys": "https://github.com/jeresig/jquery.hotkeys/archive/refs/tags/0.2.0.zip", - "jquery.icheck": "https://github.com/drgullin/icheck/archive/refs/heads/1.0.3.zip", - "jquery.image-picker": "https://github.com/rvera/image-picker/archive/refs/tags/0.3.1.zip", - "jquery.inputmask": "https://github.com/RobinHerbots/jquery.inputmask/archive/refs/tags/5.0.8.zip", - "jquery.intl-tel-input": "https://github.com/jackocnr/intl-tel-input/archive/refs/tags/v17.0.19.zip", - "jquery.rateit": "https://github.com/gjunge/rateit.js/archive/refs/tags/1.1.5.zip", - "jquery.select2": "https://github.com/select2/select2/archive/refs/tags/4.0.13.zip", - "jquery-simple-color": "https://github.com/recurser/jquery-simple-color/archive/refs/tags/v1.2.3.tar.gz", - "jquery.textcounter": "https://github.com/ractoon/jQuery-Text-Counter/archive/refs/tags/0.9.1.zip", - "jquery.timepicker": "https://github.com/jonthornton/jquery-timepicker/archive/refs/tags/1.14.0.zip", - "jquery.toggles": "https://github.com/simontabor/jquery-toggles/archive/refs/tags/v4.0.0.zip", - "masonry": "https://api.github.com/repos/desandro/masonry/zipball/3b0883cf4a4a046896719b9cf282ea74be7ffecd", - "popperjs": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "progress-tracker": "https://github.com/NigelOToole/progress-tracker/archive/refs/tags/2.0.7.zip", - "select2": "https://github.com/select2/select2/archive/4.0.13.zip", - "signature_pad": "https://github.com/szimek/signature_pad/archive/refs/tags/v2.3.0.zip", - "slick": "https://github.com/kenwheeler/slick/archive/1.8.0.zip", - "slide-element": "https://github.com/alexmacarthur/slide-element/archive/refs/tags/v2.3.1.zip", - "spectrum": "https://github.com/bgrins/spectrum/archive/refs/tags/1.8.0.tar.gz", - "svg-pan-zoom": "https://github.com/ariutta/svg-pan-zoom/archive/refs/tags/3.6.1.zip", - "tabby": "https://github.com/cferdinandi/tabby/archive/refs/tags/v12.0.3.zip", - "tippyjs": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz" -} diff --git a/defaults/drupal-scaffold.json b/defaults/drupal-scaffold.json deleted file mode 100644 index f9b73b366d1cfbc9e1e7381225bb8c324c1808aa..0000000000000000000000000000000000000000 --- a/defaults/drupal-scaffold.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "locations": { - "web-root": "./web" - }, - "file-mapping": { - "[project-root]/.editorconfig": false, - "[project-root]/.gitattributes": false, - "[web-root]/example.gitignore": false, - "[web-root]/INSTALL.txt": false, - "[web-root]/README.txt": false, - "[web-root]/sites/README.txt": false, - "[web-root]/sites/development.services.yml": false, - "[web-root]/sites/example.settings.local.php": false, - "[web-root]/sites/example.sites.php": false, - "[web-root]/sites/default/default.services.yml": false, - "[web-root]/sites/default/default.settings.php": false, - "[web-root]/modules/README.txt": false, - "[web-root]/profiles/README.txt": false, - "[web-root]/themes/README.txt": false - } -} diff --git a/defaults/installer-paths.json b/defaults/installer-paths.json deleted file mode 100644 index a3dd98f85a65f85632fe7603e3880381baee9086..0000000000000000000000000000000000000000 --- a/defaults/installer-paths.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "tests/{$name}": [ - "type:lakedrops-tests" - ] -} diff --git a/defaults/installer-types.json b/defaults/installer-types.json deleted file mode 100644 index 6ceaaa22002215fb37b83df8b0ab08c29d7d950f..0000000000000000000000000000000000000000 --- a/defaults/installer-types.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "lakedrops-tests" -] diff --git a/patches/architect-d10.patch b/patches/architect-d10.patch deleted file mode 100644 index 12864147a7e85bab06762e78f376df8f69881b6b..0000000000000000000000000000000000000000 --- a/patches/architect-d10.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/architect.info.yml b/architect.info.yml -index 58e1fc9..0c0b8ce 100644 ---- a/architect.info.yml -+++ b/architect.info.yml -@@ -1,8 +1,7 @@ - name: Architect - type: theme - description: the ARCHITECT Theme - A DESIGN SAVVY THEME USING UIKITTY. --# core: 8.x --core_version_requirement: ^8.8 || ^9 -+core_version_requirement: ^10 || ^11 - base theme: uikitty - libraries: - - architect/default diff --git a/patches/bootstrap_barrio.patch b/patches/bootstrap_barrio.patch deleted file mode 100644 index b6be3b7cc81aeb8c80fa9f9ac19f1532caef9d36..0000000000000000000000000000000000000000 --- a/patches/bootstrap_barrio.patch +++ /dev/null @@ -1,186 +0,0 @@ -diff --git a/scss/components/sidebar.scss b/scss/components/sidebar.scss -index c5ab44c..61121c4 100644 ---- a/scss/components/sidebar.scss -+++ b/scss/components/sidebar.scss -@@ -28,6 +28,6 @@ - border-bottom: $border-width solid $border-color; - } - nav ul.nav { -- margin: 0 -($spacer); -+ margin: 0 (-($spacer)); - } - } -\ No newline at end of file -diff --git a/css/components/form.css b/css/components/form.css -index 199631a57515d8781757760a508f8a7eb2044b1c..01c4b99980d6d7ebf414136099b79a8e366100b8 100644 ---- a/css/components/form.css -+++ b/css/components/form.css -@@ -15,8 +15,8 @@ - margin: 0 0.3em; - } - --.form-row>fieldset, --.form-row>div { -+form .form-row>fieldset, -+form .form-row>div { - padding-right: 5px; - padding-left: 5px; - flex: 0 1 auto; -diff --git a/scss/components/form.scss b/scss/components/form.scss -index 61031d9dbf043267d631ed5c3afe2e02994dfbc7..a0be78538cd56f489d79ceadf520b6b147542131 100644 ---- a/scss/components/form.scss -+++ b/scss/components/form.scss -@@ -7,7 +7,7 @@ - content: ''; - vertical-align: super; - display: inline-block; -- background-image: url(../images/required.svg); -+ background-image: url(#{$barrio_path_images}required.svg); - background-repeat: no-repeat; - background-size: ($input-height / 2) ($input-height / 2); - width: ($input-height / 2); -@@ -15,13 +15,15 @@ - margin: 0 0.3em; - } - --.form-row>fieldset, --.form-row>div { -- padding-right: 5px; -- padding-left: 5px; -- flex: 0 1 auto; -- width: auto; -- max-width: none; -+form { -+ .form-row>fieldset, -+ .form-row>div { -+ padding-right: 5px; -+ padding-left: 5px; -+ flex: 0 1 auto; -+ width: auto; -+ max-width: none; -+ } - } - - .row .form-actions { -@@ -37,4 +39,4 @@ form .filter-wrapper { - - form .form-type-textarea { - margin-bottom: 0; --} -\ No newline at end of file -+} -diff --git a/scss/barrio.scss b/scss/barrio.scss -index 1d2ea1ac16e7d255c3095df33961ae249de77d64..3d02566aeceb4ca8555de674d039a8cc0ba86cef 100644 ---- a/scss/barrio.scss -+++ b/scss/barrio.scss -@@ -1,3 +1,5 @@ -+$barrio_path_images: "../../images/" !default; -+ - @import "./components/mixins"; - @import "./components/affix"; - @import "./components/alerts"; -@@ -43,4 +45,4 @@ - @import "./components/vertical-tabs.component"; - @import "./components/vertical-tabs"; - @import "./components/views"; --@import "./components/webform"; -\ No newline at end of file -+@import "./components/webform"; -diff --git a/scss/components/file.scss b/scss/components/file.scss -index fe3500d53450aa0d7576901b12e8c2c346e8208b..ae14d9613953706d719cd865d181a892f35af11b 100644 ---- a/scss/components/file.scss -+++ b/scss/components/file.scss -@@ -24,57 +24,57 @@ - - .file--general, - .file--application-octet-stream { -- background-image: url(../images/icons/application-octet-stream.png); -+ background-image: url(#{$barrio_path_images}icons/application-octet-stream.png); - } - - .file--package-x-generic { -- background-image: url(../images/icons/package-x-generic.png); -+ background-image: url(#{$barrio_path_images}icons/package-x-generic.png); - } - - .file--x-office-spreadsheet { -- background-image: url(../images/icons/x-office-spreadsheet.png); -+ background-image: url(#{$barrio_path_images}icons/x-office-spreadsheet.png); - } - - .file--x-office-document { -- background-image: url(../images/icons/x-office-document.png); -+ background-image: url(#{$barrio_path_images}icons/x-office-document.png); - } - - .file--x-office-presentation { -- background-image: url(../images/icons/x-office-presentation.png); -+ background-image: url(#{$barrio_path_images}icons/x-office-presentation.png); - } - - .file--text-x-script { -- background-image: url(../images/icons/text-x-script.png); -+ background-image: url(#{$barrio_path_images}icons/text-x-script.png); - } - - .file--text-html { -- background-image: url(../images/icons/text-html.png); -+ background-image: url(#{$barrio_path_images}icons/text-html.png); - } - - .file--text-plain { -- background-image: url(../images/icons/text-plain.png); -+ background-image: url(#{$barrio_path_images}icons/text-plain.png); - } - - .file--application-pdf { -- background-image: url(../images/icons/application-pdf.png); -+ background-image: url(#{$barrio_path_images}icons/application-pdf.png); - } - - .file--application-x-executable { -- background-image: url(../images/icons/application-x-executable.png); -+ background-image: url(#{$barrio_path_images}icons/application-x-executable.png); - } - - .file--audio { -- background-image: url(../images/icons/audio-x-generic.png); -+ background-image: url(#{$barrio_path_images}icons/audio-x-generic.png); - } - - .file--video { -- background-image: url(../images/icons/video-x-generic.png); -+ background-image: url(#{$barrio_path_images}icons/video-x-generic.png); - } - - .file--text { -- background-image: url(../images/icons/text-x-generic.png); -+ background-image: url(#{$barrio_path_images}icons/text-x-generic.png); - } - - .file--image { -- background-image: url(../images/icons/image-x-generic.png); --} -\ No newline at end of file -+ background-image: url(#{$barrio_path_images}icons/image-x-generic.png); -+} -diff --git a/scss/components/webform.scss b/scss/components/webform.scss -index 8d5c308e43fba1d0771d6ccc91da836fb3b3a9c5..905c5f2458cb761a0334bfdea5d1068ae181b645 100644 ---- a/scss/components/webform.scss -+++ b/scss/components/webform.scss -@@ -9,7 +9,7 @@ - content: ''; - vertical-align: super; - display: inline-block; -- background-image: url(../../../contrib/bootstrap_barrio/images/required.svg); -+ background-image: url(#{$barrio_path_images}required.svg); - background-repeat: no-repeat; - background-size: 7px 7px; - width: 7px; -@@ -50,4 +50,4 @@ - padding: 0; - } - } --} -\ No newline at end of file -+} diff --git a/patches/column-d.patch b/patches/column-d.patch deleted file mode 100644 index c37335f7a53500e3d76a85859e156269377752ac..0000000000000000000000000000000000000000 --- a/patches/column-d.patch +++ /dev/null @@ -1,16 +0,0 @@ -Index: src/PhpSpreadsheet/Writer/Csv.php -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== ---- src/PhpSpreadsheet/Writer/Csv.php (date 1563359827000) -+++ src/PhpSpreadsheet/Writer/Csv.php (date 1563359827000) -@@ -120,7 +120,7 @@ - // Write rows to file - for ($row = 1; $row <= $maxRow; ++$row) { - // Convert the row to an array... -- $cellsArray = $sheet->rangeToArray('A' . $row . ':' . $maxCol . $row, '', $this->preCalculateFormulas); -+ $cellsArray = $sheet->rangeToArray('D' . $row . ':' . $maxCol . $row, '', $this->preCalculateFormulas); - // ... and write to the file - $this->writeLine($fileHandle, $cellsArray[0]); - } diff --git a/patches/d10-3.json b/patches/d10-3.json deleted file mode 100644 index f37823c02148b9001c8eff75ba6210496fc0f69a..0000000000000000000000000000000000000000 --- a/patches/d10-3.json +++ /dev/null @@ -1,262 +0,0 @@ -{ - "patches": { - "drupal/architect": { - "#3137889 Drupal 9": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3137889.diff", - "#local Drupal 10": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/architect-d10.patch" - }, - "drupal/background_image": { - "#3161262 Pre render fix": "https://www.drupal.org/files/issues/2020-07-24/implement_trustedcallbackinterface.patch", - "#3170843 Dom Ready": "https://www.drupal.org/files/issues/2020-09-14/3170843-2.background_image.Library-drupaldomready-does-not-exist-in-Drupal-9.patch", - "#3173283 Cache": "https://www.drupal.org/files/issues/2020-09-26/3173283-2.background_image.Cache-context-also-requires-URL.patch", - "#3173340 Exception": "https://www.drupal.org/files/issues/2020-09-26/3173340-2.background_image.Error-Call-to-a-member-function-hasEntityToken-on-bool-in-DrupalbackgroundimageCacheContextBackgroundImageSettingsTextCacheContextgetContext.patch", - "#3128542 FileScan": "https://www.drupal.org/files/issues/2020-09-11/background_image-missing_folder-3128542-8.patch" - }, - "drupal/bootstrap_clean_blog": { - "#3146287 Drupal 9": "https://www.drupal.org/files/issues/2021-10-23/3146287_3.patch" - }, - "drupal/ckeditor_find": { - "#3252081 Library location": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3252081.diff" - }, - "drupal/codesnippet": { - "#3021431 Add Yaml as code format": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3021431.diff" - }, - "drupal/comment_notify": { - "#2926228 Always notify admin": "https://www.drupal.org/files/issues/2019-09-11/2926228-11.comment_notify.Optionally-notify-site-admin-of-all-comments.patch" - }, - "drupal/commerce_xquantity": { - "#3404050 Exceptions in actions of xquantity_stock": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3404050-2.diff" - }, - "drupal/config_devel": { - "#3219083 Export raw data": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3219083.diff", - "#3227881 Support config_rewrite": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227881.diff?v=2" - }, - "drupal/config_sync": { - "#3176955 Drupal 9 test compatibility": "https://www.drupal.org/files/issues/2021-03-19/config_sync-config-sync-test-core-3176955-4.patch" - }, - "drupal/config_update": { - "#3436790 Module weight": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3436790.diff?v=2", - "#3056249 Core hash for localisation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3056249.diff?v=2" - }, - "drupal/content_lock": { - "#2951652 No content lock für CLI commands": "https://www.drupal.org/files/issues/2022-04-05/2951652-13.patch", - "#3160781 Release lock when browsing away from locked form": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3160781-2.diff" - }, - "drupal/core_for_review": { - "#3227732 Book hierarchy migration": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227732.diff" - }, - "drupal/core": { - "#local Hot fix timezoone": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/timezone.patch", - "#2647292 Date/time Views filter tries strotime() relative to Unix epoch": "https://www.drupal.org/files/issues/2024-03-17/with-dependency-injection-2647292-75.patch", - "#2884879 Ignore empty fields for REST export": "https://www.drupal.org/files/issues/2884879-11.patch", - "#2966735 Workaround for DateTime exposed filter": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2966735.diff", - "#3043879 Layout Builder Config": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3043879-new.diff", - "#3195171 Fix view header group rendering": "https://www.drupal.org/files/issues/2022-01-05/3195171-7-fix-view-header-group-rendering.patch", - "#3180227 Missing options for entity refernces in views": "https://www.drupal.org/files/issues/2020-11-02/3180227-2.patch", - "#local Json API and entity reference warnings": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/jsonapi-entityref-warning-10.3.patch", - "#local Issue 3168966 Insert Media inline in CKEditor widget": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/5758.diff", - "#3228298 Empty path alias exception": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3228298.diff?v=2", - "#3227816 Hide password reset link": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227816.diff", - "#3249628 Comments and paragraphs": "https://www.drupal.org/files/issues/2022-12-11/1415-11.diff", - "#2840283 Views action path with EVA": "https://www.drupal.org/files/issues/problem_with_action-2840283-25.patch", - "#2816447 RSS feed titles wrong language": "https://www.drupal.org/files/issues/2023-08-03/2816447-58.patch", - "#2915705 TypedData any": "https://www.drupal.org/files/issues/2022-12-11/2915705-82.patch", - "#3302450 Views field default type": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3302450.diff", - "#3311849 Views exposed filter, empty sections": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311849.diff", - "#2797583 Actions for content moderation": "https://www.drupal.org/files/issues/2022-09-20/core-provide-moderation-states-as-actions-2797583-207.patch", - "#2918537 Save unpublished versions of published content without manage book privileges": "https://www.drupal.org/files/issues/2022-12-14/2918537-92.patch", - "#3194462 Big pipe explode issue": "https://www.drupal.org/files/issues/2023-02-28/3194462-30.patch", - "#3090234 Broken node theming for book export": "https://www.drupal.org/files/issues/2020-10-06/3090234-9.patch", - "#3346430 DoPreSave load revision": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3346430.diff", - "#3340973 Media Lib type error on array_filter": "https://www.drupal.org/files/issues/2023-02-10/fix_media_library_php_error-3340973-2.patch", - "#3073822 Error: Call to a member function access() on null in Drupal\\comment\\CommentAccessControlHandler->checkAccess()": "https://www.drupal.org/files/issues/2023-01-12/3073822-12.patch", - "#2761273 Exposed filter values as token": "https://www.drupal.org/files/issues/2022-10-30/2761273-50.patch", - "#2955321 Non-translatable fields": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2955321.diff", - "#3420862 Empty comment body": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3420862.diff", - "#3053757 Filter for allowed moderation transitions": "https://www.drupal.org/files/issues/2024-03-17/content-moderation-user-filter-3053757-25.patch", - "#3298701 Local patch to prevent BC issue in 10.3": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3298701.diff", - "#3159113 Local patch to prevent BC issue in 10.3": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3159113.diff", - "#3392903 Local patch to work around %formatter in custom_field": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3392903-follow-up.diff", - "#3438893 Views entity reverse": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3438893.diff", - "#3456176 10.3 missing status-message theme suggestions": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3456176.patch", - "#944582 File System Permissions": "https://www.drupal.org/files/issues/2024-01-16/944582-10.2.x-192.patch", - "#2849279 Inconsistent changed timestamps when moderated entity": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2849279.patch", - "#3043725 Provide a Entity Handler for user cancellation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3043725-104.diff", - "#3473763 locale_string_is_safe should accept \"<front>\" as being safe": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3473763.diff", - "#3492453 Memory leak in DrupalKernel when installing modules": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3492453.diff" - }, - "drupal/dashboards": { - "#3366586 Shortcut error": "https://git.drupalcode.org/project/dashboards/-/merge_requests/26.diff" - }, - "drupal/diff": { - "#3311234 Link to diff with prev rev": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311234.diff?v=3", - "#3311372 Configure date format": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311372.diff?v=2", - "#3360589 Form validation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3360589.diff?v=4" - }, - "drupal/dynamic_entity_reference": { - "#3099176 Errors when new entity types are added": "https://www.drupal.org/files/issues/2023-09-08/3099176-3.x-16.diff" - }, - "drupal/entity_import": { - "#3061935 Make it work without content_translation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3061935.diff" - }, - "drupal/entity_share": { - "#3306745 File Share overrides the name of media entity": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3306745.diff", - "#3347453 VBO Dependency": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3347453.diff" - }, - "drupal/entitygroupfield": { - "#3228312 Group load fails": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3228312.diff" - }, - "drupal/fakeobjects": { - "#3208959 Library path": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3208959.diff" - }, - "drupal/field_group": { - "#2858336 Token support": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2858336.diff" - }, - "drupal/flag": { - "#3238783 Fix flag action": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3238783.diff", - "#2500091 Activate token support": "https://www.drupal.org/files/issues/2022-12-30/flag_hook-tokens_2500091-43.patch" - }, - "drupal/fullcalendar_view": { - "#3392567 Fix href": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3392567.diff", - "#3084395 Daterange basefield": "https://www.drupal.org/files/issues/2022-03-25/3084395-11.patch" - }, - "drupal/gin_toolbar": { - "#3319445 Permission check": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3319445.diff" - }, - "drupal/glossify": { - "#3360633 HTML entities": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3360633.diff" - }, - "drupal/group": { - "#3416324 Related entity tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3416324.diff" - }, - "drupal/http_client_manager": { - "#3385117 Code generator": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3385117.diff" - }, - "drupal/imagemagick": { - "#local debug": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/imagick-debug.diff" - }, - "drupal/inline_entity_form": { - "#3204051 Check access": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3204051.diff?v=4" - }, - "drupal/layout_library": { - "#3041543 Layout per display mode": "https://www.drupal.org/files/issues/2020-08-17/3041543-4.layout_library.Allow-created-layouts-to-be-limited-to-and-set-for-specific-view-modes.patch" - }, - "drupal/ldap": { - "#3227813 Hide password field": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227813.diff" - }, - "drupal/linkchecker": { - "#3376854 Base path": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3376854.diff", - "#3313343 Disable cron": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3313343-3.diff" - }, - "drupal/linkit": { - "#3442127 Deprecate Drupal.EditorFeature": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3442127.diff" - }, - "drupal/menu_block": { - "#3082445 Add option to always render parent menu item": "https://www.drupal.org/files/issues/2020-10-12/menu_block-render-parent-3082445-26.patch" - }, - "drupal/pdfpreview": { - "#local debug": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/pdfpreview-debug.diff" - }, - "drupal/pluginreference": { - "#2979414 Allow no selection": "https://www.drupal.org/files/issues/2018-07-18/pluginreference-allow_no_plugin_to_be_selected-2979414-2.patch" - }, - "drupal/profile": { - "#2899744 Multi-lingual": "https://www.drupal.org/files/issues/2022-04-17/2899744-54.patch" - }, - "drupal/project_browser": { - "#3490176 Extend source plugins": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3490176.diff" - }, - "drupal/prompt": { - "#3449733 ECA 2": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3449733.diff" - }, - "drupal/recaptcha": { - "#3113837 Downgrade unknown error": "https://www.drupal.org/files/issues/2020-02-15/3113837-2.recaptcha.Downgrade-unknown-error-info.patch" - }, - "drupal/role_theme_switcher": { - "#3414975 Missing keys": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3414975.diff" - }, - "drupal/select2": { - "#3211796 Z-Index in modal dialog": "https://www.drupal.org/files/issues/2023-04-18/select2-modal-zindex-mult-form-elem-fix-3211796-17.patch" - }, - "drupal/simple_menu_icons": { - "#3379128 Do not delete all": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3379128.diff", - "#3404501 Set version and license": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3404501.diff" - }, - "drupal/smart_date": { - "#3478471 Filter contains partly": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3478471.diff" - }, - "drupal/smart_login": { - "#2831386 Error 403 redirect": "https://www.drupal.org/files/issues/this_is_silently-2831386-2.patch", - "#2709529 Redirect after login": "https://www.drupal.org/files/issues/redirect_to_page_of-2709529-2.patch", - "#2901154 Properly handle destination": "https://www.drupal.org/files/issues/setting_form_redirect-2901154-2.patch" - }, - "drupal/smtp": { - "#3252427 Add tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3252427.diff" - }, - "drupal/social_link_field": { - "#3241310 Add more channels": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3241310.diff" - }, - "drupal/social_profile_field": { - "#3242595 Drupal 9": "https://www.drupal.org/files/issues/2021-10-11/3242595-3.patch", - "#2724653 Langparam": "https://www.drupal.org/files/issues/social_profile_field-fix-warnings-in-formatter-1.patch", - "#2799907 Alter icon path": "https://www.drupal.org/files/issues/allow_for_different-2799907-2.patch" - }, - "drupal/subgroup": { - "#3170683 Provide parent as token": "https://www.drupal.org/files/issues/2022-06-02/subgroup-parent_token-3170683-3.patch" - }, - "drupal/svg_image_field": { - "#3090673 URL field formatter": "https://www.drupal.org/files/issues/2019-10-29/3090673-2.svg_image_field.Add-support-for-a-URL-image-formatter.patch" - }, - "drupal/tamper": { - "#3268276 Timezone plugin": "https://www.drupal.org/files/issues/2023-04-06/0001-Issue-3268276-Reworking-timezones.patch", - "#3480140 Yaml encoding": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3480140.diff" - }, - "drupal/taxonomy_machine_name": { - "#3193233 Filter doesn't work": "https://www.drupal.org/files/issues/2021-01-17/taxonomy_machine_name-view-save-error-3193233.patch", - "#3128397 View can not be saved": "https://www.drupal.org/files/issues/2020-04-16/config_schema-3128397-3.patch", - "#3352586 Parameter to disable machine name in terms overview ": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/33525862-2.diff" - }, - "drupal/token": { - "#3437013 Referrer tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3437013.diff" - }, - "drupal/uikitty": { - "#3149175 Drupal 9": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3149175.diff", - "#local Drupal 10": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/uikitty-d10.patch" - }, - "drupal/views_data_export": { - "#3046184 Optional CSV Header (fix missing row on batch run)": "https://www.drupal.org/files/issues/2023-02-09/views_data_export-optional_csv_header-3046184-9.patch" - }, - "drupal/video_embed_field": { - "#3238136 Vimeo hash parameter on private video url": "https://www.drupal.org/files/issues/2022-08-23/video_embed_field-vimeo_hash_parameters_private_video-3238136-8.patch" - }, - "drupal/views_entity_embed": { - "#3307775 Fix for CKe5": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3307775.diff" - }, - "drupal/views_fieldsets": { - "#3395642 Fieldsets with only images don't display": "https://www.drupal.org/files/issues/2023-10-20/views_fieldset-allow_img.patch" - }, - "drupal/views_tree": { - "#3344199 Add event just once": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3344199.diff" - }, - "drupal/webform": { - "#3487530 Overview permissions": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3487530.diff" - }, - "drupal/webform_content_creator": { - "#3484892 Permission": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3484892.diff" - }, - "drupal/workflow": { - "#2948377 Do not hide widget when having only one option": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/show-widget-2948377-2.patch" - }, - "drupal/wysiwyg_template": { - "#3354588 Drupal 10 compatible": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3354588.diff" - }, - "grahl/ldap": { - "#Local Remove deprecation warnings": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/grahl-ldap.patch" - }, - "henrywhitaker3/healthchecks-io": { - "#76 Double slashes in URL": "https://patch-diff.githubusercontent.com/raw/henrywhitaker3/PHP-healthchecks.io/pull/77.diff" - }, - "phpcollection/phpcollection": { - "#31 PHP 8.1 deprecation": "https://patch-diff.githubusercontent.com/raw/schmittjoh/php-collection/pull/32.diff" - } - } -} diff --git a/patches/d10-4.json b/patches/d10-4.json deleted file mode 100644 index 11c2bebf728be6b0b645cb812b096127e325c6f8..0000000000000000000000000000000000000000 --- a/patches/d10-4.json +++ /dev/null @@ -1,262 +0,0 @@ -{ - "patches": { - "drupal/architect": { - "#3137889 Drupal 9": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3137889.diff", - "#local Drupal 10": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/architect-d10.patch" - }, - "drupal/background_image": { - "#3161262 Pre render fix": "https://www.drupal.org/files/issues/2020-07-24/implement_trustedcallbackinterface.patch", - "#3170843 Dom Ready": "https://www.drupal.org/files/issues/2020-09-14/3170843-2.background_image.Library-drupaldomready-does-not-exist-in-Drupal-9.patch", - "#3173283 Cache": "https://www.drupal.org/files/issues/2020-09-26/3173283-2.background_image.Cache-context-also-requires-URL.patch", - "#3173340 Exception": "https://www.drupal.org/files/issues/2020-09-26/3173340-2.background_image.Error-Call-to-a-member-function-hasEntityToken-on-bool-in-DrupalbackgroundimageCacheContextBackgroundImageSettingsTextCacheContextgetContext.patch", - "#3128542 FileScan": "https://www.drupal.org/files/issues/2020-09-11/background_image-missing_folder-3128542-8.patch" - }, - "drupal/bootstrap_clean_blog": { - "#3146287 Drupal 9": "https://www.drupal.org/files/issues/2021-10-23/3146287_3.patch" - }, - "drupal/ckeditor_find": { - "#3252081 Library location": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3252081.diff" - }, - "drupal/codesnippet": { - "#3021431 Add Yaml as code format": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3021431.diff" - }, - "drupal/comment_notify": { - "#2926228 Always notify admin": "https://www.drupal.org/files/issues/2019-09-11/2926228-11.comment_notify.Optionally-notify-site-admin-of-all-comments.patch" - }, - "drupal/commerce_xquantity": { - "#3404050 Exceptions in actions of xquantity_stock": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3404050-2.diff" - }, - "drupal/config_devel": { - "#3219083 Export raw data": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3219083.diff", - "#3227881 Support config_rewrite": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227881.diff?v=2" - }, - "drupal/config_sync": { - "#3176955 Drupal 9 test compatibility": "https://www.drupal.org/files/issues/2021-03-19/config_sync-config-sync-test-core-3176955-4.patch" - }, - "drupal/config_update": { - "#3436790 Module weight": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3436790.diff?v=2", - "#3056249 Core hash for localisation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3056249.diff?v=2" - }, - "drupal/content_lock": { - "#2951652 No content lock für CLI commands": "https://www.drupal.org/files/issues/2022-04-05/2951652-13.patch", - "#3160781 Release lock when browsing away from locked form": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3160781-2.diff" - }, - "drupal/core_for_review": { - "#3227732 Book hierarchy migration": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227732.diff" - }, - "drupal/core": { - "#local Hot fix timezoone": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/timezone.patch", - "#2647292 Date/time Views filter tries strotime() relative to Unix epoch": "https://www.drupal.org/files/issues/2024-03-17/with-dependency-injection-2647292-75.patch", - "#2884879 Ignore empty fields for REST export": "https://www.drupal.org/files/issues/2884879-11.patch", - "#2966735 Workaround for DateTime exposed filter": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2966735.diff", - "#3043879 Layout Builder Config": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3043879-new.diff", - "#3195171 Fix view header group rendering": "https://www.drupal.org/files/issues/2022-01-05/3195171-7-fix-view-header-group-rendering.patch", - "#3180227 Missing options for entity refernces in views": "https://www.drupal.org/files/issues/2020-11-02/3180227-2.patch", - "#local Json API and entity reference warnings": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/jsonapi-entityref-warning-10.3.patch", - "#local Issue 3168966 Insert Media inline in CKEditor widget": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/5758-1.diff", - "#3228298 Empty path alias exception": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3228298.diff?v=2", - "#3227816 Hide password reset link": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227816.diff", - "#3249628 Comments and paragraphs": "https://www.drupal.org/files/issues/2022-12-11/1415-11.diff", - "#2840283 Views action path with EVA": "https://www.drupal.org/files/issues/problem_with_action-2840283-25.patch", - "#2816447 RSS feed titles wrong language": "https://www.drupal.org/files/issues/2023-08-03/2816447-58.patch", - "#2915705 TypedData any": "https://www.drupal.org/files/issues/2022-12-11/2915705-82.patch", - "#3302450 Views field default type": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3302450.diff", - "#3311849 Views exposed filter, empty sections": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311849.diff", - "#2797583 Actions for content moderation": "https://www.drupal.org/files/issues/2022-09-20/core-provide-moderation-states-as-actions-2797583-207.patch", - "#2918537 Save unpublished versions of published content without manage book privileges": "https://www.drupal.org/files/issues/2022-12-14/2918537-92.patch", - "#3194462 Big pipe explode issue": "https://www.drupal.org/files/issues/2023-02-28/3194462-30.patch", - "#3090234 Broken node theming for book export": "https://www.drupal.org/files/issues/2020-10-06/3090234-9.patch", - "#3346430 DoPreSave load revision": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3346430.diff", - "#3340973 Media Lib type error on array_filter": "https://www.drupal.org/files/issues/2023-02-10/fix_media_library_php_error-3340973-2.patch", - "#3073822 Error: Call to a member function access() on null in Drupal\\comment\\CommentAccessControlHandler->checkAccess()": "https://www.drupal.org/files/issues/2023-01-12/3073822-12.patch", - "#2761273 Exposed filter values as token": "https://www.drupal.org/files/issues/2022-10-30/2761273-50.patch", - "#2955321 Non-translatable fields": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2955321.diff", - "#3420862 Empty comment body": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3420862.diff", - "#3053757 Filter for allowed moderation transitions": "https://www.drupal.org/files/issues/2024-03-17/content-moderation-user-filter-3053757-25.patch", - "#3298701 Local patch to prevent BC issue in 10.3": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3298701.diff", - "#3159113 Local patch to prevent BC issue in 10.3": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3159113.diff", - "#3392903 Local patch to work around %formatter in custom_field": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3392903-follow-up.diff", - "#3438893 Views entity reverse": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3438893.diff", - "#3456176 10.3 missing status-message theme suggestions": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3456176.patch", - "#944582 File System Permissions": "https://www.drupal.org/files/issues/2024-01-16/944582-10.2.x-192.patch", - "#2849279 Inconsistent changed timestamps when moderated entity": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2849279.patch", - "#3043725 Provide a Entity Handler for user cancellation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3043725-104.diff", - "#3473763 locale_string_is_safe should accept \"<front>\" as being safe": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3473763.diff", - "#3492453 Memory leak in DrupalKernel when installing modules": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3492453.diff?v=2" - }, - "drupal/dashboards": { - "#3366586 Shortcut error": "https://git.drupalcode.org/project/dashboards/-/merge_requests/26.diff" - }, - "drupal/diff": { - "#3311234 Link to diff with prev rev": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311234.diff?v=3", - "#3311372 Configure date format": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311372.diff?v=2", - "#3360589 Form validation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3360589.diff?v=4" - }, - "drupal/dynamic_entity_reference": { - "#3099176 Errors when new entity types are added": "https://www.drupal.org/files/issues/2023-09-08/3099176-3.x-16.diff" - }, - "drupal/entity_import": { - "#3061935 Make it work without content_translation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3061935.diff" - }, - "drupal/entity_share": { - "#3306745 File Share overrides the name of media entity": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3306745.diff", - "#3347453 VBO Dependency": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3347453.diff" - }, - "drupal/entitygroupfield": { - "#3228312 Group load fails": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3228312.diff" - }, - "drupal/fakeobjects": { - "#3208959 Library path": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3208959.diff" - }, - "drupal/field_group": { - "#2858336 Token support": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2858336.diff" - }, - "drupal/flag": { - "#3238783 Fix flag action": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3238783.diff", - "#2500091 Activate token support": "https://www.drupal.org/files/issues/2022-12-30/flag_hook-tokens_2500091-43.patch" - }, - "drupal/fullcalendar_view": { - "#3392567 Fix href": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3392567.diff", - "#3084395 Daterange basefield": "https://www.drupal.org/files/issues/2022-03-25/3084395-11.patch" - }, - "drupal/gin_toolbar": { - "#3319445 Permission check": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3319445.diff" - }, - "drupal/glossify": { - "#3360633 HTML entities": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3360633.diff" - }, - "drupal/group": { - "#3416324 Related entity tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3416324.diff" - }, - "drupal/http_client_manager": { - "#3385117 Code generator": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3385117.diff" - }, - "drupal/imagemagick": { - "#local debug": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/imagick-debug.diff" - }, - "drupal/inline_entity_form": { - "#3204051 Check access": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3204051.diff?v=4" - }, - "drupal/layout_library": { - "#3041543 Layout per display mode": "https://www.drupal.org/files/issues/2020-08-17/3041543-4.layout_library.Allow-created-layouts-to-be-limited-to-and-set-for-specific-view-modes.patch" - }, - "drupal/ldap": { - "#3227813 Hide password field": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227813.diff" - }, - "drupal/linkchecker": { - "#3376854 Base path": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3376854.diff", - "#3313343 Disable cron": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3313343-3.diff" - }, - "drupal/linkit": { - "#3442127 Deprecate Drupal.EditorFeature": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3442127.diff" - }, - "drupal/menu_block": { - "#3082445 Add option to always render parent menu item": "https://www.drupal.org/files/issues/2020-10-12/menu_block-render-parent-3082445-26.patch" - }, - "drupal/pdfpreview": { - "#local debug": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/pdfpreview-debug.diff" - }, - "drupal/pluginreference": { - "#2979414 Allow no selection": "https://www.drupal.org/files/issues/2018-07-18/pluginreference-allow_no_plugin_to_be_selected-2979414-2.patch" - }, - "drupal/profile": { - "#2899744 Multi-lingual": "https://www.drupal.org/files/issues/2022-04-17/2899744-54.patch" - }, - "drupal/project_browser": { - "#3490176 Extend source plugins": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3490176.diff" - }, - "drupal/prompt": { - "#3449733 ECA 2": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3449733.diff" - }, - "drupal/recaptcha": { - "#3113837 Downgrade unknown error": "https://www.drupal.org/files/issues/2020-02-15/3113837-2.recaptcha.Downgrade-unknown-error-info.patch" - }, - "drupal/role_theme_switcher": { - "#3414975 Missing keys": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3414975.diff" - }, - "drupal/select2": { - "#3211796 Z-Index in modal dialog": "https://www.drupal.org/files/issues/2023-04-18/select2-modal-zindex-mult-form-elem-fix-3211796-17.patch" - }, - "drupal/simple_menu_icons": { - "#3379128 Do not delete all": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3379128.diff", - "#3404501 Set version and license": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3404501.diff" - }, - "drupal/smart_date": { - "#3478471 Filter contains partly": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3478471.diff" - }, - "drupal/smart_login": { - "#2831386 Error 403 redirect": "https://www.drupal.org/files/issues/this_is_silently-2831386-2.patch", - "#2709529 Redirect after login": "https://www.drupal.org/files/issues/redirect_to_page_of-2709529-2.patch", - "#2901154 Properly handle destination": "https://www.drupal.org/files/issues/setting_form_redirect-2901154-2.patch" - }, - "drupal/smtp": { - "#3252427 Add tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3252427.diff" - }, - "drupal/social_link_field": { - "#3241310 Add more channels": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3241310.diff" - }, - "drupal/social_profile_field": { - "#3242595 Drupal 9": "https://www.drupal.org/files/issues/2021-10-11/3242595-3.patch", - "#2724653 Langparam": "https://www.drupal.org/files/issues/social_profile_field-fix-warnings-in-formatter-1.patch", - "#2799907 Alter icon path": "https://www.drupal.org/files/issues/allow_for_different-2799907-2.patch" - }, - "drupal/subgroup": { - "#3170683 Provide parent as token": "https://www.drupal.org/files/issues/2022-06-02/subgroup-parent_token-3170683-3.patch" - }, - "drupal/svg_image_field": { - "#3090673 URL field formatter": "https://www.drupal.org/files/issues/2019-10-29/3090673-2.svg_image_field.Add-support-for-a-URL-image-formatter.patch" - }, - "drupal/tamper": { - "#3268276 Timezone plugin": "https://www.drupal.org/files/issues/2023-04-06/0001-Issue-3268276-Reworking-timezones.patch", - "#3480140 Yaml encoding": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3480140.diff" - }, - "drupal/taxonomy_machine_name": { - "#3193233 Filter doesn't work": "https://www.drupal.org/files/issues/2021-01-17/taxonomy_machine_name-view-save-error-3193233.patch", - "#3128397 View can not be saved": "https://www.drupal.org/files/issues/2020-04-16/config_schema-3128397-3.patch", - "#3352586 Parameter to disable machine name in terms overview ": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/33525862-2.diff" - }, - "drupal/token": { - "#3437013 Referrer tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3437013.diff" - }, - "drupal/uikitty": { - "#3149175 Drupal 9": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3149175.diff", - "#local Drupal 10": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/uikitty-d10.patch" - }, - "drupal/views_data_export": { - "#3046184 Optional CSV Header (fix missing row on batch run)": "https://www.drupal.org/files/issues/2023-02-09/views_data_export-optional_csv_header-3046184-9.patch" - }, - "drupal/video_embed_field": { - "#3238136 Vimeo hash parameter on private video url": "https://www.drupal.org/files/issues/2022-08-23/video_embed_field-vimeo_hash_parameters_private_video-3238136-8.patch" - }, - "drupal/views_entity_embed": { - "#3307775 Fix for CKe5": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3307775.diff" - }, - "drupal/views_fieldsets": { - "#3395642 Fieldsets with only images don't display": "https://www.drupal.org/files/issues/2023-10-20/views_fieldset-allow_img.patch" - }, - "drupal/views_tree": { - "#3344199 Add event just once": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3344199.diff" - }, - "drupal/webform": { - "#3487530 Overview permissions": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3487530.diff" - }, - "drupal/webform_content_creator": { - "#3484892 Permission": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3484892.diff" - }, - "drupal/workflow": { - "#2948377 Do not hide widget when having only one option": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/show-widget-2948377-2.patch" - }, - "drupal/wysiwyg_template": { - "#3354588 Drupal 10 compatible": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3354588.diff" - }, - "grahl/ldap": { - "#Local Remove deprecation warnings": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/grahl-ldap.patch" - }, - "henrywhitaker3/healthchecks-io": { - "#76 Double slashes in URL": "https://patch-diff.githubusercontent.com/raw/henrywhitaker3/PHP-healthchecks.io/pull/77.diff" - }, - "phpcollection/phpcollection": { - "#31 PHP 8.1 deprecation": "https://patch-diff.githubusercontent.com/raw/schmittjoh/php-collection/pull/32.diff" - } - } -} diff --git a/patches/d10.json b/patches/d10.json deleted file mode 100644 index 5396e502e41b3db8f906a3b784219e81a46f94dd..0000000000000000000000000000000000000000 --- a/patches/d10.json +++ /dev/null @@ -1,238 +0,0 @@ -{ - "patches": { - "drupal/architect": { - "#3137889 Drupal 9": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3137889.diff", - "#local Drupal 10": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/architect-d10.patch" - }, - "drupal/background_image": { - "#3161262 Pre render fix": "https://www.drupal.org/files/issues/2020-07-24/implement_trustedcallbackinterface.patch", - "#3170843 Dom Ready": "https://www.drupal.org/files/issues/2020-09-14/3170843-2.background_image.Library-drupaldomready-does-not-exist-in-Drupal-9.patch", - "#3173283 Cache": "https://www.drupal.org/files/issues/2020-09-26/3173283-2.background_image.Cache-context-also-requires-URL.patch", - "#3173340 Exception": "https://www.drupal.org/files/issues/2020-09-26/3173340-2.background_image.Error-Call-to-a-member-function-hasEntityToken-on-bool-in-DrupalbackgroundimageCacheContextBackgroundImageSettingsTextCacheContextgetContext.patch", - "#3128542 FileScan": "https://www.drupal.org/files/issues/2020-09-11/background_image-missing_folder-3128542-8.patch" - }, - "drupal/bootstrap_clean_blog": { - "#3146287 Drupal 9": "https://www.drupal.org/files/issues/2021-10-23/3146287_3.patch" - }, - "drupal/book": { - "#2918537 Save unpublished versions of published content without manage book privileges": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2918537.diff", - "#3090234 Broken node theming for book export": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3090234.patch" - }, - "drupal/ckeditor_find": { - "#3252081 Library location": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3252081.diff" - }, - "drupal/codesnippet": { - "#3021431 Add Yaml as code format": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3021431.diff" - }, - "drupal/comment_notify": { - "#2926228 Always notify admin": "https://www.drupal.org/files/issues/2019-09-11/2926228-11.comment_notify.Optionally-notify-site-admin-of-all-comments.patch" - }, - "drupal/commerce_xquantity": { - "#3404050 Exceptions in actions of xquantity_stock": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3404050-2.diff" - }, - "drupal/config_devel": { - "#3219083 Export raw data": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3219083.diff", - "#3227881 Support config_rewrite": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227881.diff?v=2" - }, - "drupal/config_sync": { - "#3176955 Drupal 9 test compatibility": "https://www.drupal.org/files/issues/2021-03-19/config_sync-config-sync-test-core-3176955-4.patch" - }, - "drupal/config_update": { - "#3436790 Module weight": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3436790.diff?v=2", - "#3056249 Core hash for localisation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3056249.diff" - }, - "drupal/content_lock": { - "#2951652 No content lock für CLI commands": "https://www.drupal.org/files/issues/2022-04-05/2951652-13.patch", - "#3160781 Release lock when browsing away from locked form": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3160781-2.diff" - }, - "drupal/core_for_review": { - "#3227732 Book hierarchy migration": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227732.diff" - }, - "drupal/core": { - "#local Hot fix timezoone": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/timezone.patch", - "#2647292 Date/time Views filter tries strotime() relative to Unix epoch": "https://www.drupal.org/files/issues/2022-02-10/with-dependency-injection-2647292-67.patch", - "#2884879 Ignore empty fields for REST export": "https://www.drupal.org/files/issues/2884879-11.patch", - "#2966735 Workaround for DateTime exposed filter": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2966735.diff", - "#3043879 Layout Builder Config": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3043879.diff", - "#3195171 Fix view header group rendering": "https://www.drupal.org/files/issues/2022-01-05/3195171-7-fix-view-header-group-rendering.patch", - "#3180227 Missing options for entity refernces in views": "https://www.drupal.org/files/issues/2020-11-02/3180227-2.patch", - "#local Json API and entity reference warnings": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/jsonapi-entityref-warning.patch?v=3", - "#3228298 Empty path alias exception": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3228298.diff?v=2", - "#3227816 Hide password reset link": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227816.diff", - "#3249628 Comments and paragraphs": "https://www.drupal.org/files/issues/2022-12-11/1415-11.diff", - "#2840283 Views action path with EVA": "https://www.drupal.org/files/issues/problem_with_action-2840283-25.patch", - "#2816447 RSS feed titles wrong language": "https://www.drupal.org/files/issues/2023-08-03/2816447-58.patch", - "#2915705 TypedData any": "https://www.drupal.org/files/issues/2022-12-11/2915705-82.patch", - "#3302450 Views field default type": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3302450.diff", - "#3311849 Views exposed filter, empty sections": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311849.diff", - "#2797583 Actions for content moderation": "https://www.drupal.org/files/issues/2022-09-20/core-provide-moderation-states-as-actions-2797583-207.patch", - "#2918537 Save unpublished versions of published content without manage book privileges": "https://www.drupal.org/files/issues/2022-12-14/2918537-92.patch", - "#3047110 Taxonomy moderation": "https://www.drupal.org/files/issues/2021-07-23/3047110-32.patch", - "#3194462 Big pipe explode issue": "https://www.drupal.org/files/issues/2023-02-28/3194462-30.patch", - "#3090234 Broken node theming for book export": "https://www.drupal.org/files/issues/2020-10-06/3090234-9.patch", - "#3346430 DoPreSave load revision": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3346430.diff", - "#3340973 Media Lib type error on array_filter": "https://www.drupal.org/files/issues/2023-02-10/fix_media_library_php_error-3340973-2.patch", - "#3073822 Error: Call to a member function access() on null in Drupal\\comment\\CommentAccessControlHandler->checkAccess()": "https://www.drupal.org/files/issues/2023-01-12/3073822-12.patch", - "#2761273 Exposed filter values as token": "https://www.drupal.org/files/issues/2022-10-30/2761273-50.patch", - "#2955321 Non-translatable fields": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2955321.diff", - "#3420862 Empty comment body": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3420862.diff", - "#3413508 Admin page access": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3413508.diff", - "#3053757 Filter for allowed moderation transitions": "https://www.drupal.org/files/issues/2024-03-12/content-moderation-user-filter-3053757-24.patch", - "#944582 File System Permissions": "https://www.drupal.org/files/issues/2024-01-16/944582-10.2.x-192.patch" - }, - "drupal/dashboards": { - "#3366586 Shortcut error": "https://git.drupalcode.org/project/dashboards/-/merge_requests/26.diff" - }, - "drupal/diff": { - "#3311234 Link to diff with prev rev": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311234.diff?v=3", - "#3311372 Configure date format": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311372.diff?v=2", - "#3360589 Form validation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3360589.diff?v=4" - }, - "drupal/dynamic_entity_reference": { - "#3099176 Errors when new entity types are added": "https://www.drupal.org/files/issues/2023-09-08/3099176-3.x-16.diff" - }, - "drupal/eca": { - "#3352393 Condition list contains": "https://git.drupalcode.org/project/eca/-/merge_requests/349.diff", - "#3368530 eca_log tokens": "https://git.drupalcode.org/project/eca/-/merge_requests/370.diff", - "#3373537 Plain token replacement": "https://git.drupalcode.org/project/eca/-/merge_requests/375.diff", - "#3351738 Views argument default plugin": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/eca_views_argument.patch", - "#3396915 Improve #3368530 to cleanup context": "https://www.drupal.org/files/issues/2023-10-26/3396915-2.diff" - }, - "drupal/elasticsearch_connector": { - "#2952301 Flatten keys": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/elasticsearch_connector.patch" - }, - "drupal/entity_import": { - "#3061935 Make it work without content_translation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3061935.diff" - }, - "drupal/entity_share": { - "#3306745 File Share overrides the name of media entity": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3306745.diff", - "#3347453 VBO Dependency": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3347453.diff" - }, - "drupal/entitygroupfield": { - "#3228312 Group load fails": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3228312.diff" - }, - "drupal/fakeobjects": { - "#3208959 Library path": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3208959.diff" - }, - "drupal/field_group": { - "#2858336 Token support": "https://www.drupal.org/files/issues/2019-01-15/field_group-tokens_in_classes-2858336-16.patch" - }, - "drupal/flag": { - "#3238783 Fix flag action": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3238783.diff", - "#2500091 Activate token support": "https://www.drupal.org/files/issues/2022-12-30/flag_hook-tokens_2500091-43.patch" - }, - "drupal/fullcalendar_view": { - "#3392567 Fix href": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3392567.diff", - "#3084395 Daterange basefield": "https://www.drupal.org/files/issues/2022-03-25/3084395-11.patch" - }, - "drupal/gin_toolbar": { - "#3319445 Permission check": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3319445.diff" - }, - "drupal/glossify": { - "#3360633 HTML entities": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3360633.diff" - }, - "drupal/http_client_manager": { - "#3385117 Code generator": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3385117.diff" - }, - "drupal/inline_entity_form": { - "#3204051 Check access": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3204051.diff?v=4" - }, - "drupal/layout_library": { - "#3041543 Layout per display mode": "https://www.drupal.org/files/issues/2020-08-17/3041543-4.layout_library.Allow-created-layouts-to-be-limited-to-and-set-for-specific-view-modes.patch" - }, - "drupal/ldap": { - "#3227813 Hide password field": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227813.diff" - }, - "drupal/linkchecker": { - "#3376854 Base path": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3376854.diff", - "#3313343 Disable cron": "https://www.drupal.org/files/issues/2022-10-03/support_disabling_cron-3313343-2.patch" - }, - "drupal/menu_block": { - "#3082445 Add option to always render parent menu item": "https://www.drupal.org/files/issues/2020-10-12/menu_block-render-parent-3082445-26.patch" - }, - "drupal/pluginreference": { - "#2979414 Allow no selection": "https://www.drupal.org/files/issues/2018-07-18/pluginreference-allow_no_plugin_to_be_selected-2979414-2.patch" - }, - "drupal/profile": { - "#2899744 Multi-lingual": "https://www.drupal.org/files/issues/2022-04-17/2899744-54.patch" - }, - "drupal/recaptcha": { - "#3113837 Downgrade unknown error": "https://www.drupal.org/files/issues/2020-02-15/3113837-2.recaptcha.Downgrade-unknown-error-info.patch" - }, - "drupal/role_theme_switcher": { - "#3414975 Missing keys": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3414975.diff" - }, - "drupal/select2": { - "#3211796 Z-Index in modal dialog": "https://www.drupal.org/files/issues/2023-04-18/select2-modal-zindex-mult-form-elem-fix-3211796-17.patch" - }, - "drupal/simple_menu_icons": { - "#3379128 Do not delete all": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3379128.diff", - "#3404501 Set version and license": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3404501.diff" - }, - "drupal/smart_login": { - "#2831386 Error 403 redirect": "https://www.drupal.org/files/issues/this_is_silently-2831386-2.patch", - "#2709529 Redirect after login": "https://www.drupal.org/files/issues/redirect_to_page_of-2709529-2.patch", - "#2901154 Properly handle destination": "https://www.drupal.org/files/issues/setting_form_redirect-2901154-2.patch" - }, - "drupal/smtp": { - "#3252427 Add tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3252427.diff" - }, - "drupal/social_link_field": { - "#3241310 Add more channels": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3241310.diff" - }, - "drupal/social_profile_field": { - "#3242595 Drupal 9": "https://www.drupal.org/files/issues/2021-10-11/3242595-3.patch", - "#2724653 Langparam": "https://www.drupal.org/files/issues/social_profile_field-fix-warnings-in-formatter-1.patch", - "#2799907 Alter icon path": "https://www.drupal.org/files/issues/allow_for_different-2799907-2.patch" - }, - "drupal/svg_image_field": { - "#3090673 URL field formatter": "https://www.drupal.org/files/issues/2019-10-29/3090673-2.svg_image_field.Add-support-for-a-URL-image-formatter.patch" - }, - "drupal/tamper": { - "#3279973 WordCount plugin": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3279973.diff", - "#3268276 Timezone plugin": "https://www.drupal.org/files/issues/2023-04-06/0001-Issue-3268276-Reworking-timezones.patch" - }, - "drupal/taxonomy_machine_name": { - "#3193233 Filter doesn't work": "https://www.drupal.org/files/issues/2021-01-17/taxonomy_machine_name-view-save-error-3193233.patch", - "#3128397 View can not be saved": "https://www.drupal.org/files/issues/2020-04-16/config_schema-3128397-3.patch", - "#3352586 Parameter to disable machine name in terms overview ": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3352586-2.diff" - }, - "drupal/title": { - "#3172331 D9 compatibility": "https://www.drupal.org/files/issues/2021-08-24/D9-compatibility_3172331.patch" - }, - "drupal/token": { - "#3437013 Referrer tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3437013.diff" - }, - "drupal/uikitty": { - "#3149175 Drupal 9": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3149175.diff", - "#local Drupal 10": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/uikitty-d10.patch" - }, - "drupal/views_data_export": { - "#3046184 Optional CSV Header (fix missing row on batch run)": "https://www.drupal.org/files/issues/2023-02-09/views_data_export-optional_csv_header-3046184-9.patch" - }, - "drupal/video_embed_field": { - "#3238136 Vimeo hash parameter on private video url": "https://www.drupal.org/files/issues/2022-08-23/video_embed_field-vimeo_hash_parameters_private_video-3238136-8.patch" - }, - "drupal/views_fieldsets": { - "#3395642 Fieldsets with only images don't display": "https://www.drupal.org/files/issues/2023-10-20/views_fieldset-allow_img.patch" - }, - "drupal/views_tree": { - "#3344199 Add event just once": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3344199.diff" - }, - "drupal/workflow": { - "#2948377 Do not hide widget when having only one option": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/show-widget-2948377-2.patch" - }, - "drupal/wysiwyg_template": { - "#3354588 Drupal 10 compatible": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3354588.diff" - }, - "grahl/ldap": { - "#Local Remove deprecation warnings": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/grahl-ldap.patch" - }, - "henrywhitaker3/healthchecks-io": { - "#76 Double slashes in URL": "https://patch-diff.githubusercontent.com/raw/henrywhitaker3/PHP-healthchecks.io/pull/77.diff" - }, - "phpcollection/phpcollection": { - "#31 PHP 8.1 deprecation": "https://patch-diff.githubusercontent.com/raw/schmittjoh/php-collection/pull/32.diff" - } - } -} diff --git a/patches/d10/2849279.patch b/patches/d10/2849279.patch deleted file mode 100644 index 76fc59a2bfaa875a9107831798b93bd4976d29fa..0000000000000000000000000000000000000000 --- a/patches/d10/2849279.patch +++ /dev/null @@ -1,506 +0,0 @@ -diff --git a/core/modules/content_moderation/content_moderation.post_update.php b/core/modules/content_moderation/content_moderation.post_update.php -index 57a61b62..70cb827c 100644 ---- a/core/modules/content_moderation/content_moderation.post_update.php -+++ b/core/modules/content_moderation/content_moderation.post_update.php -@@ -17,3 +17,10 @@ function content_moderation_removed_post_updates() { - 'content_moderation_post_update_views_field_plugin_id' => '9.0.0', - ]; - } -+ -+/** -+ * Clear caches due to new entity form handlers. -+ */ -+function content_moderation_post_update_new_entity_form_handlers() { -+ // Empty post-update hook. -+} -diff --git a/core/modules/content_moderation/src/EntityOperations.php b/core/modules/content_moderation/src/EntityOperations.php -index 52d2ca39..4cf39210 100644 ---- a/core/modules/content_moderation/src/EntityOperations.php -+++ b/core/modules/content_moderation/src/EntityOperations.php -@@ -7,12 +7,11 @@ use Drupal\content_moderation\Entity\ContentModerationStateInterface; - use Drupal\Core\DependencyInjection\ContainerInjectionInterface; - use Drupal\Core\Entity\ContentEntityInterface; - use Drupal\Core\Entity\Display\EntityViewDisplayInterface; -+use Drupal\Core\Entity\EntityFormBuilderInterface; - use Drupal\Core\Entity\EntityInterface; - use Drupal\Core\Entity\EntityPublishedInterface; - use Drupal\Core\Entity\EntityTypeBundleInfoInterface; - use Drupal\Core\Entity\EntityTypeManagerInterface; --use Drupal\Core\Form\FormBuilderInterface; --use Drupal\content_moderation\Form\EntityModerationForm; - use Drupal\Core\Routing\RouteBuilderInterface; - use Drupal\workflows\Entity\Workflow; - use Symfony\Component\DependencyInjection\ContainerInterface; -@@ -39,11 +38,11 @@ class EntityOperations implements ContainerInjectionInterface { - protected $entityTypeManager; - - /** -- * The Form Builder service. -+ * The Entity Form Builder service. - * -- * @var \Drupal\Core\Form\FormBuilderInterface -+ * @var \Drupal\Core\Entity\EntityFormBuilderInterface - */ -- protected $formBuilder; -+ protected $entityFormBuilder; - - /** - * The entity bundle information service. -@@ -66,17 +65,17 @@ class EntityOperations implements ContainerInjectionInterface { - * Moderation information service. - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * Entity type manager service. -- * @param \Drupal\Core\Form\FormBuilderInterface $form_builder -+ * @param \Drupal\Core\Entity\EntityFormBuilderInterface $entity_form_builder - * The form builder. - * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info - * The entity bundle information service. - * @param \Drupal\Core\Routing\RouteBuilderInterface $router_builder - * The router builder service. - */ -- public function __construct(ModerationInformationInterface $moderation_info, EntityTypeManagerInterface $entity_type_manager, FormBuilderInterface $form_builder, EntityTypeBundleInfoInterface $bundle_info, RouteBuilderInterface $router_builder) { -+ public function __construct(ModerationInformationInterface $moderation_info, EntityTypeManagerInterface $entity_type_manager, EntityFormBuilderInterface $entity_form_builder, EntityTypeBundleInfoInterface $bundle_info, RouteBuilderInterface $router_builder) { - $this->moderationInfo = $moderation_info; - $this->entityTypeManager = $entity_type_manager; -- $this->formBuilder = $form_builder; -+ $this->entityFormBuilder = $entity_form_builder; - $this->bundleInfo = $bundle_info; - $this->routerBuilder = $router_builder; - } -@@ -88,7 +87,7 @@ class EntityOperations implements ContainerInjectionInterface { - return new static( - $container->get('content_moderation.moderation_information'), - $container->get('entity_type.manager'), -- $container->get('form_builder'), -+ $container->get('entity.form_builder'), - $container->get('entity_type.bundle.info'), - $container->get('router.builder') - ); -@@ -293,7 +292,7 @@ class EntityOperations implements ContainerInjectionInterface { - return; - } - -- $build['content_moderation_control'] = $this->formBuilder->getForm(EntityModerationForm::class, $entity); -+ $build['content_moderation_control'] = $this->entityFormBuilder->getForm($entity, 'moderate'); - } - - /** -diff --git a/core/modules/content_moderation/src/EntityTypeInfo.php b/core/modules/content_moderation/src/EntityTypeInfo.php -index 31aa4d36..c4d90a79 100644 ---- a/core/modules/content_moderation/src/EntityTypeInfo.php -+++ b/core/modules/content_moderation/src/EntityTypeInfo.php -@@ -2,6 +2,7 @@ - - namespace Drupal\content_moderation; - -+use Drupal\content_moderation\Form\EntityModerationForm; - use Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList; - use Drupal\Core\Entity\BundleEntityFormBase; - use Drupal\Core\Entity\ContentEntityFormInterface; -@@ -171,6 +172,10 @@ class EntityTypeInfo implements ContainerInjectionInterface { - $type->setHandlerClass('moderation', $handler_class); - } - -+ if (!$type->getFormClass('moderate')) { -+ $type->setFormClass('moderate', EntityModerationForm::class); -+ } -+ - if (!$type->hasLinkTemplate('latest-version') && $type->hasLinkTemplate('canonical')) { - $type->setLinkTemplate('latest-version', $type->getLinkTemplate('canonical') . '/latest'); - } -@@ -380,7 +385,7 @@ class EntityTypeInfo implements ContainerInjectionInterface { - */ - protected function isModeratedEntityEditForm(FormInterface $form_object) { - return $form_object instanceof ContentEntityFormInterface && -- in_array($form_object->getOperation(), ['edit', 'default', 'layout_builder'], TRUE) && -+ in_array($form_object->getOperation(), ['edit', 'default', 'layout_builder', 'moderate'], TRUE) && - $this->moderationInfo->isModeratedEntity($form_object->getEntity()); - } - -diff --git a/core/modules/content_moderation/src/Form/EntityModerationForm.php b/core/modules/content_moderation/src/Form/EntityModerationForm.php -index 751c6b35..fcc64d8b 100644 ---- a/core/modules/content_moderation/src/Form/EntityModerationForm.php -+++ b/core/modules/content_moderation/src/Form/EntityModerationForm.php -@@ -2,69 +2,46 @@ - - namespace Drupal\content_moderation\Form; - --use Drupal\Component\Datetime\TimeInterface; --use Drupal\Core\Entity\ContentEntityInterface; --use Drupal\Core\Entity\RevisionLogInterface; --use Drupal\Core\Form\FormBase; -+use Drupal\Core\Entity\ContentEntityForm; -+use Drupal\Core\Entity\Entity\EntityFormDisplay; - use Drupal\Core\Form\FormStateInterface; --use Drupal\content_moderation\ModerationInformationInterface; --use Drupal\content_moderation\StateTransitionValidationInterface; --use Drupal\workflows\Transition; --use Symfony\Component\DependencyInjection\ContainerInterface; - - /** - * The EntityModerationForm provides a simple UI for changing moderation state. - * - * @internal - */ --class EntityModerationForm extends FormBase { -+class EntityModerationForm extends ContentEntityForm { - - /** -- * The moderation information service. -- * -- * @var \Drupal\content_moderation\ModerationInformationInterface -- */ -- protected $moderationInfo; -- -- /** -- * The time service. -- * -- * @var \Drupal\Component\Datetime\TimeInterface -- */ -- protected $time; -- -- /** -- * The moderation state transition validation service. -- * -- * @var \Drupal\content_moderation\StateTransitionValidationInterface -- */ -- protected $validation; -- -- /** -- * EntityModerationForm constructor. -- * -- * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_info -- * The moderation information service. -- * @param \Drupal\content_moderation\StateTransitionValidationInterface $validation -- * The moderation state transition validation service. -- * @param \Drupal\Component\Datetime\TimeInterface $time -- * The time service. -+ * {@inheritdoc} - */ -- public function __construct(ModerationInformationInterface $moderation_info, StateTransitionValidationInterface $validation, TimeInterface $time) { -- $this->moderationInfo = $moderation_info; -- $this->validation = $validation; -- $this->time = $time; -+ public function getFormDisplay(FormStateInterface $form_state) { -+ // Create a transient display that is not persisted, but used only for -+ // building the components required for the moderation form. -+ $display = EntityFormDisplay::collectRenderDisplay($this->entity, $this->getOperation(), FALSE); -+ $display->setComponent('moderation_state', [ -+ 'type' => 'moderation_state_default', -+ 'weight' => 1, -+ 'settings' => [], -+ ]); -+ if ($log_message_field = $this->entity->getEntityType()->getRevisionMetadataKey('revision_log_message')) { -+ $display->setComponent($log_message_field, [ -+ 'weight' => 2, -+ 'type' => 'string_textarea', -+ 'settings' => [ -+ 'rows' => 1, -+ ], -+ ]); -+ } -+ return $display; - } - - /** - * {@inheritdoc} - */ -- public static function create(ContainerInterface $container) { -- return new static( -- $container->get('content_moderation.moderation_information'), -- $container->get('content_moderation.state_transition_validation'), -- $container->get('datetime.time') -- ); -+ public function getBaseFormId() { -+ return $this->getFormId(); - } - - /** -@@ -77,58 +54,20 @@ class EntityModerationForm extends FormBase { - /** - * {@inheritdoc} - */ -- public function buildForm(array $form, FormStateInterface $form_state, ?ContentEntityInterface $entity = NULL) { -- $current_state = $entity->moderation_state->value; -- $workflow = $this->moderationInfo->getWorkflowForEntity($entity); -- -- /** @var \Drupal\workflows\Transition[] $transitions */ -- $transitions = $this->validation->getValidTransitions($entity, $this->currentUser()); -- -- // Exclude self-transitions. -- $transitions = array_filter($transitions, function (Transition $transition) use ($current_state) { -- return $transition->to()->id() != $current_state; -- }); -- -- $target_states = []; -- -- foreach ($transitions as $transition) { -- $target_states[$transition->to()->id()] = $transition->to()->label(); -- } -- -- if (!count($target_states)) { -- return $form; -- } -- -- if ($current_state) { -- $form['current'] = [ -- '#type' => 'item', -- '#title' => $this->t('Moderation state'), -- '#markup' => $workflow->getTypePlugin()->getState($current_state)->label(), -- ]; -- } -- -- // Persist the entity so we can access it in the submit handler. -- $form_state->set('entity', $entity); -- -- $form['new_state'] = [ -- '#type' => 'select', -- '#title' => $this->t('Change to'), -- '#options' => $target_states, -- ]; -- -- $form['revision_log'] = [ -- '#type' => 'textfield', -- '#title' => $this->t('Log message'), -- '#size' => 30, -- ]; -- -- $form['submit'] = [ -- '#type' => 'submit', -- '#value' => $this->t('Apply'), -+ public function buildForm(array $form, FormStateInterface $form_state) { -+ $form = parent::buildForm($form, $form_state); -+ // Hide the "advanced" part of the entity form so things like menu links -+ // are not displayed. -+ $form['advanced'] = [ -+ '#type' => 'container', -+ '#access' => FALSE, - ]; - -+ // Use the entity_moderation_form template, which contains its own set of -+ // classes. - $form['#theme'] = ['entity_moderation_form']; - $form['#attached']['library'][] = 'content_moderation/content_moderation'; -+ $form['#attributes']['class'] = []; - - // Moderating an entity is allowed in a workspace. - $form_state->set('workspace_safe', TRUE); -@@ -139,33 +78,41 @@ class EntityModerationForm extends FormBase { - /** - * {@inheritdoc} - */ -- public function submitForm(array &$form, FormStateInterface $form_state) { -- /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ -- $entity = $form_state->get('entity'); -- /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */ -- $storage = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId()); -- $entity = $storage->createRevision($entity, $entity->isDefaultRevision()); -- -- $new_state = $form_state->getValue('new_state'); -- -- $entity->set('moderation_state', $new_state); -+ protected function addRevisionableFormFields(array &$form) { -+ parent::addRevisionableFormFields($form); -+ // Show the revision fields as a simple container and ensure the revision -+ // checkbox is not selectable. -+ $form['revision_information']['#type'] = 'container'; -+ $form['revision']['#type'] = 'value'; -+ unset($form['revision_information']['#group']); -+ } - -- if ($entity instanceof RevisionLogInterface) { -- $entity->setRevisionCreationTime($this->time->getRequestTime()); -- $entity->setRevisionLogMessage($form_state->getValue('revision_log')); -- $entity->setRevisionUserId($this->currentUser()->id()); -- } -- $entity->save(); -+ /** -+ * {@inheritdoc} -+ */ -+ protected function actions(array $form, FormStateInterface $form_state) { -+ return [ -+ 'submit' => [ -+ '#type' => 'submit', -+ '#value' => $this->t('Apply'), -+ '#submit' => ['::submitForm', '::save'], -+ ], -+ ]; -+ } - -+ /** -+ * {@inheritdoc} -+ */ -+ public function submitForm(array &$form, FormStateInterface $form_state) { -+ parent::submitForm($form, $form_state); - $this->messenger()->addStatus($this->t('The moderation state has been updated.')); - -- $new_state = $this->moderationInfo->getWorkflowForEntity($entity)->getTypePlugin()->getState($new_state); - // The page we're on likely won't be visible if we just set the entity to - // the default state, as we hide that latest-revision tab if there is no - // pending revision. Redirect to the canonical URL instead, since that will - // still exist. -- if ($new_state->isDefaultRevisionState()) { -- $form_state->setRedirectUrl($entity->toUrl('canonical')); -+ if ($this->entity->isDefaultRevision()) { -+ $form_state->setRedirectUrl($this->entity->toUrl('canonical')); - } - } - -diff --git a/core/modules/content_moderation/templates/entity-moderation-form.html.twig b/core/modules/content_moderation/templates/entity-moderation-form.html.twig -index bae2fc95..28813e5e 100644 ---- a/core/modules/content_moderation/templates/entity-moderation-form.html.twig -+++ b/core/modules/content_moderation/templates/entity-moderation-form.html.twig -@@ -1,7 +1,4 @@ - <ul class="entity-moderation-form"> -- <li class="entity-moderation-form__item">{{ form.current }}</li> -- <li class="entity-moderation-form__item">{{ form.new_state }}</li> -- <li class="entity-moderation-form__item">{{ form.revision_log }}</li> -- <li class="entity-moderation-form__item">{{ form.submit }}</li> -+ <li class="entity-moderation-form__item">{{ form.moderation_state }}</li> -+ <li class="entity-moderation-form__item">{{ form|without('moderation_state') }}</li> - </ul> --{{ form|without('current', 'new_state', 'revision_log', 'submit') }} -diff --git a/core/modules/content_moderation/tests/src/Functional/LayoutBuilderContentModerationIntegrationTest.php b/core/modules/content_moderation/tests/src/Functional/LayoutBuilderContentModerationIntegrationTest.php -index 8d523fa4..1ebf22d1 100644 ---- a/core/modules/content_moderation/tests/src/Functional/LayoutBuilderContentModerationIntegrationTest.php -+++ b/core/modules/content_moderation/tests/src/Functional/LayoutBuilderContentModerationIntegrationTest.php -@@ -119,7 +119,7 @@ class LayoutBuilderContentModerationIntegrationTest extends BrowserTestBase { - - $this->drupalGet($node->toUrl()); - // Publish the node. Revision count: 2. -- $page->fillField('new_state', 'published'); -+ $page->fillField('moderation_state[0][state]', 'published'); - $page->pressButton('Apply'); - - // Modify the layout. -@@ -148,7 +148,7 @@ class LayoutBuilderContentModerationIntegrationTest extends BrowserTestBase { - - // Publish the node. Revision count: 4. - $page->clickLink('Latest version'); -- $page->fillField('new_state', 'published'); -+ $page->fillField('moderation_state[0][state]', 'published'); - $page->pressButton('Apply'); - - // Block is visible on the live node page. -@@ -200,7 +200,7 @@ class LayoutBuilderContentModerationIntegrationTest extends BrowserTestBase { - // Publish the draft of the page ensure the draft inline block content - // appears on the published page. - $this->submitForm([ -- 'new_state' => 'published', -+ 'moderation_state[0][state]' => 'published', - ], 'Apply'); - $assert_session->pageTextContains('The moderation state has been updated.'); - $assert_session->pageTextContains('Example block body'); -diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationFormTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationFormTest.php -index a265feb7..feecc1fd 100644 ---- a/core/modules/content_moderation/tests/src/Functional/ModerationFormTest.php -+++ b/core/modules/content_moderation/tests/src/Functional/ModerationFormTest.php -@@ -93,7 +93,7 @@ class ModerationFormTest extends ModerationStateTestBase { - // live revision. - $this->drupalGet($canonical_path); - $this->assertSession()->statusCodeEquals(200); -- $this->assertSession()->fieldExists('edit-new-state'); -+ $this->assertSession()->fieldExists('moderation_state[0][state]'); - - // The latest version page should not show, because there is no pending - // revision. -@@ -111,7 +111,7 @@ class ModerationFormTest extends ModerationStateTestBase { - // live revision. - $this->drupalGet($canonical_path); - $this->assertSession()->statusCodeEquals(200); -- $this->assertSession()->fieldExists('edit-new-state'); -+ $this->assertSession()->fieldExists('moderation_state[0][state]'); - - // Preview the draft. - $this->drupalGet($edit_path); -@@ -127,7 +127,7 @@ class ModerationFormTest extends ModerationStateTestBase { - ]); - $this->assertSession()->statusCodeEquals(200); - $this->assertSession()->addressEquals($preview_url); -- $this->assertSession()->fieldNotExists('edit-new-state'); -+ $this->assertSession()->fieldNotExists('moderation_state[0][state]'); - - // The latest version page should not show, because there is still no - // pending revision. -@@ -158,7 +158,7 @@ class ModerationFormTest extends ModerationStateTestBase { - // live revision. - $this->drupalGet($canonical_path); - $this->assertSession()->statusCodeEquals(200); -- $this->assertSession()->fieldNotExists('edit-new-state'); -+ $this->assertSession()->fieldNotExists('moderation_state[0][state]'); - - // The latest version page should not show, because there is still no - // pending revision. -@@ -176,18 +176,18 @@ class ModerationFormTest extends ModerationStateTestBase { - // live revision. - $this->drupalGet($canonical_path); - $this->assertSession()->statusCodeEquals(200); -- $this->assertSession()->fieldNotExists('edit-new-state'); -+ $this->assertSession()->fieldNotExists('moderation_state[0][state]'); - - // The latest version page should show the moderation form and have "Draft" - // status, because the pending revision is in "Draft". - $this->drupalGet($latest_version_path); - $this->assertSession()->statusCodeEquals(200); -- $this->assertSession()->fieldExists('edit-new-state'); -+ $this->assertSession()->fieldExists('moderation_state[0][state]'); - $this->assertSession()->pageTextContains('Draft'); - - // Submit the moderation form to change status to published. - $this->drupalGet($latest_version_path); -- $this->submitForm(['new_state' => 'published'], 'Apply'); -+ $this->submitForm(['moderation_state[0][state]' => 'published'], 'Apply'); - - // The latest version page should not show, because there is no - // pending revision. -@@ -250,12 +250,12 @@ class ModerationFormTest extends ModerationStateTestBase { - // status, because the pending revision is in "Draft". - $this->drupalGet('entity_test_mulrevpub/manage/1/latest'); - $this->assertSession()->statusCodeEquals(200); -- $this->assertSession()->pageTextContains('Moderation state'); -- $this->assertSession()->pageTextContains('Draft'); -+ $this->assertSession()->elementContains('css', '.entity-moderation-form', 'Current state'); -+ $this->assertSession()->elementContains('css', '.entity-moderation-form', 'Draft'); - - // Submit the moderation form to change status to published. - $this->drupalGet('entity_test_mulrevpub/manage/1/latest'); -- $this->submitForm(['new_state' => 'published'], 'Apply'); -+ $this->submitForm(['moderation_state[0][state]' => 'published'], 'Apply'); - - // The latest version page should not show, because there is no - // pending revision. -@@ -279,7 +279,7 @@ class ModerationFormTest extends ModerationStateTestBase { - $this->grantUserPermissionToCreateContentOfType($another_user, 'moderated_content'); - $this->drupalLogin($another_user); - $this->drupalGet(sprintf('node/%d/latest', $node->id())); -- $this->submitForm(['new_state' => 'published'], 'Apply'); -+ $this->submitForm(['moderation_state[0][state]' => 'published'], 'Apply'); - - $this->drupalGet(sprintf('node/%d/revisions', $node->id())); - $this->assertSession()->pageTextContains('by ' . $another_user->getAccountName()); -diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationLocaleTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationLocaleTest.php -index 206433a9..89f1d405 100644 ---- a/core/modules/content_moderation/tests/src/Functional/ModerationLocaleTest.php -+++ b/core/modules/content_moderation/tests/src/Functional/ModerationLocaleTest.php -@@ -675,7 +675,7 @@ class ModerationLocaleTest extends ModerationStateTestBase { - * TRUE if the moderation form could be find in the page, FALSE otherwise. - */ - public function hasModerationForm() { -- return (bool) $this->xpath('//ul[@class="entity-moderation-form"]'); -+ return (bool) $this->getSession()->getPage()->find('css', '.entity-moderation-form'); - } - - } -diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationStateBlockTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationStateBlockTest.php -index 5880df23..45c8194d 100644 ---- a/core/modules/content_moderation/tests/src/Functional/ModerationStateBlockTest.php -+++ b/core/modules/content_moderation/tests/src/Functional/ModerationStateBlockTest.php -@@ -135,7 +135,7 @@ class ModerationStateBlockTest extends ModerationStateTestBase { - - // Open the latest tab and publish the new draft. - $edit = [ -- 'new_state' => 'published', -+ 'moderation_state[0][state]' => 'published', - ]; - $this->drupalGet('admin/content/block/' . $block->id() . '/latest'); - $this->submitForm($edit, 'Apply'); diff --git a/patches/d10/2858336.diff b/patches/d10/2858336.diff deleted file mode 100644 index e819ff26fe172a6561bbb46d5edfefed77d46548..0000000000000000000000000000000000000000 --- a/patches/d10/2858336.diff +++ /dev/null @@ -1,89 +0,0 @@ -diff --git a/src/FieldGroupFormatterBase.php b/src/FieldGroupFormatterBase.php -index 5dc4e213e4a8b74379afe871e8f5f03ef2a38104..1f54d0edf7d23bd9fae250540ec541651b16fee3 100644 ---- a/src/FieldGroupFormatterBase.php -+++ b/src/FieldGroupFormatterBase.php -@@ -126,6 +126,14 @@ abstract class FieldGroupFormatterBase extends PluginSettingsBase implements Fie - '#element_validate' => [[$class, 'validateCssClass']], - ]; - -+ if (\Drupal::moduleHandler()->moduleExists('token')) { -+ $form['token_help'] = [ -+ '#theme' => 'token_tree_link', -+ '#token_types' => [$this->group->entity_type], -+ '#weight' => 12 -+ ]; -+ $form['classes']['#element_validate'] = [[$class, 'validateCssClassToken']]; -+ } - return $form; - - } -@@ -222,6 +230,22 @@ abstract class FieldGroupFormatterBase extends PluginSettingsBase implements Fie - return $this->preRender($element, $processed_object); - } - -+ /** -+ * Validate the entered css class with token from the submitted format settings. -+ * -+ * @param array $element -+ * The validated element. -+ * @param \Drupal\Core\Form\FormStateInterface $form_state -+ * The state of the form. -+ */ -+ public static function validateCssClassToken(array $element, FormStateInterface $form_state) { -+ $form_state_values = $form_state->getValues(); -+ $plugin_name = $form_state->get('plugin_settings_edit'); -+ if (!empty($form_state_values['fields'][$plugin_name]['settings_edit_form']['settings']['classes']) && !preg_match('![A-Za-z0-9-_ ]*(\[[\w]+:([\w]+:)*[\w]+\])*!', $form_state_values['fields'][$plugin_name]['settings_edit_form']['settings']['classes'])) { -+ $form_state->setError($element, t('The css class must include only letters, numbers, underscores, dashes and token fields.')); -+ } -+ } -+ - /** - * Validate the entered css class from the submitted format settings. - * -diff --git a/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php b/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php -index 66a11048f5ee2f9fb55a377dabd52b3b9a71d2de..e5a163f82ee066175cd000ef7d16056960c694cc 100644 ---- a/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php -+++ b/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php -@@ -4,6 +4,7 @@ namespace Drupal\field_group\Plugin\field_group\FieldGroupFormatter; - - use Drupal\Component\Utility\Html; - use Drupal\Component\Utility\Xss; -+use Drupal\Core\Entity\EntityInterface; - use Drupal\Core\Form\FormState; - use Drupal\Core\Render\Markup; - use Drupal\Core\Template\Attribute; -@@ -55,6 +56,12 @@ class HtmlElement extends FieldGroupFormatterBase { - - // Add the classes to the attributes array. - $classes = $this->getClasses(); -+ if (\Drupal::moduleHandler()->moduleExists('token')) { -+ if (isset($processed_object["#{$this->group->entity_type}"])) { -+ $entity = $processed_object["#{$this->group->entity_type}"]; -+ $classes = $this->tokenizeClasses($entity, $classes); -+ } -+ } - if (!empty($classes)) { - if (!isset($element_attributes['class'])) { - $element_attributes['class'] = []; -@@ -259,5 +266,21 @@ class HtmlElement extends FieldGroupFormatterBase { - return $defaults; - - } -+ -+ /** -+ * @param \Drupal\Core\Entity\EntityInterface $entity -+ * @param array $classes -+ * @return array -+ */ -+ private function tokenizeClasses(EntityInterface $entity, array $classes) { -+ $token = \Drupal::token(); -+ $variables = [ -+ $entity->getEntityTypeId() => $entity -+ ]; -+ -+ $classString = implode(' ', $classes); -+ $tokenizedClasses = $token->replace($classString, $variables, ['clear' => TRUE]); -+ return explode(' ', $tokenizedClasses); -+ } - - } diff --git a/patches/d10/2909185-2.diff b/patches/d10/2909185-2.diff deleted file mode 100644 index 981ab688d3cb740804f7e2eb22de44de2e87bc10..0000000000000000000000000000000000000000 --- a/patches/d10/2909185-2.diff +++ /dev/null @@ -1,261 +0,0 @@ -diff --git a/core/core.services.yml b/core/core.services.yml -index da5011df77..106db6a337 100644 ---- a/core/core.services.yml -+++ b/core/core.services.yml -@@ -895,7 +895,8 @@ services: - arguments: ['@request_stack'] - Drupal\Core\Routing\RouteMatchInterface: '@current_route_match' - event_dispatcher: -- class: Symfony\Component\EventDispatcher\EventDispatcher -+ class: Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher -+ arguments: ['@service_container'] - Psr\EventDispatcher\EventDispatcherInterface: '@event_dispatcher' - Symfony\Contracts\EventDispatcher\EventDispatcherInterface: '@event_dispatcher' - controller_resolver: -diff --git a/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php -index fd99e0335e..41afec903e 100644 ---- a/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php -+++ b/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php -@@ -2,8 +2,6 @@ - - namespace Drupal\Component\EventDispatcher; - --@trigger_error('The ' . __NAMESPACE__ . '\ContainerAwareEventDispatcher is deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. Use Symfony\Component\EventDispatcher\EventDispatcher instead. See https://www.drupal.org/node/3376090', E_USER_DEPRECATED); -- - use Psr\EventDispatcher\StoppableEventInterface; - use Symfony\Component\DependencyInjection\ContainerInterface; - use Symfony\Component\EventDispatcher\EventDispatcherInterface; -@@ -32,11 +30,6 @@ - * runtime is not affected by this change though. - * </dd> - * </dl> -- * -- * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. Use -- * \Symfony\Component\EventDispatcher\EventDispatcher instead. -- * -- * @see https://www.drupal.org/node/3376090 - */ - class ContainerAwareEventDispatcher implements EventDispatcherInterface { - -diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php -index 20fd28594a..368692ba7b 100644 ---- a/core/lib/Drupal/Core/CoreServiceProvider.php -+++ b/core/lib/Drupal/Core/CoreServiceProvider.php -@@ -30,7 +30,6 @@ - use Psr\Log\LoggerAwareInterface; - use Symfony\Component\DependencyInjection\Compiler\PassConfig; - use Symfony\Component\DependencyInjection\Reference; --use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; - use Symfony\Component\EventDispatcher\EventSubscriberInterface; - - /** -@@ -86,7 +85,7 @@ public function register(ContainerBuilder $container) { - $container->addCompilerPass(new TwigExtensionPass()); - - // Add a compiler pass for registering event subscribers. -- $container->addCompilerPass(new RegisterEventSubscribersPass(new RegisterListenersPass()), PassConfig::TYPE_AFTER_REMOVING); -+ $container->addCompilerPass(new RegisterEventSubscribersPass(), PassConfig::TYPE_AFTER_REMOVING); - $container->addCompilerPass(new LoggerAwarePass(), PassConfig::TYPE_AFTER_REMOVING); - - $container->addCompilerPass(new RegisterAccessChecksPass()); -diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php -index 82d1f1614d..0cae61c39f 100644 ---- a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php -+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php -@@ -4,50 +4,59 @@ - - use Symfony\Component\DependencyInjection\ContainerBuilder; - use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; --use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; -+use Symfony\Component\EventDispatcher\EventSubscriberInterface; - - /** -- * Wraps the Symfony event subscriber pass to use different tag names. -+ * Registers all event subscribers to the event dispatcher. - */ - class RegisterEventSubscribersPass implements CompilerPassInterface { - -- /** -- * Constructs a RegisterEventSubscribersPass object. -- * -- * @param \Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass $pass -- * The Symfony compiler pass that registers event subscribers. -- */ -- public function __construct( -- protected RegisterListenersPass $pass, -- ) {} -- - /** - * {@inheritdoc} - */ - public function process(ContainerBuilder $container) { -- $this->renameTag($container, 'event_subscriber', 'kernel.event_subscriber'); -- $this->pass->process($container); -- $this->renameTag($container, 'kernel.event_subscriber', 'event_subscriber'); -- } -+ if (!$container->hasDefinition('event_dispatcher')) { -+ return; -+ } - -- /** -- * Renames tags in the container. -- * -- * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container -- * The container. -- * @param string $source_tag -- * The tag to be renamed. -- * @param string $target_tag -- * The tag to rename with. -- */ -- protected function renameTag(ContainerBuilder $container, string $source_tag, string $target_tag): void { -- foreach ($container->getDefinitions() as $definition) { -- if ($definition->hasTag($source_tag)) { -- $attributes = $definition->getTag($source_tag)[0]; -- $definition->addTag($target_tag, $attributes); -- $definition->clearTag($source_tag); -+ $definition = $container->getDefinition('event_dispatcher'); -+ -+ $event_subscriber_info = []; -+ foreach ($container->findTaggedServiceIds('event_subscriber') as $id => $attributes) { -+ -+ // We must assume that the class value has been correctly filled, even if -+ // the service is created by a factory. -+ $class = $container->getDefinition($id)->getClass(); -+ -+ $interface = EventSubscriberInterface::class; -+ if (!is_subclass_of($class, $interface)) { -+ throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); -+ } -+ -+ // Get all subscribed events. -+ foreach ($class::getSubscribedEvents() as $event_name => $params) { -+ if (is_string($params)) { -+ $priority = 0; -+ $event_subscriber_info[$event_name][$priority][] = ['service' => [$id, $params]]; -+ } -+ elseif (is_string($params[0])) { -+ $priority = $params[1] ?? 0; -+ $event_subscriber_info[$event_name][$priority][] = ['service' => [$id, $params[0]]]; -+ } -+ else { -+ foreach ($params as $listener) { -+ $priority = $listener[1] ?? 0; -+ $event_subscriber_info[$event_name][$priority][] = ['service' => [$id, $listener[0]]]; -+ } -+ } - } - } -+ -+ foreach (array_keys($event_subscriber_info) as $event_name) { -+ krsort($event_subscriber_info[$event_name]); -+ } -+ -+ $definition->addArgument($event_subscriber_info); - } - - } -diff --git a/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php b/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php -index 63c52f408c..de8ecb220f 100644 ---- a/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php -+++ b/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php -@@ -4,6 +4,7 @@ - - namespace Drupal\Tests\layout_builder\Unit; - -+use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher; - use Drupal\Component\Plugin\Exception\PluginException; - use Drupal\Core\Access\AccessResult; - use Drupal\Core\Block\BlockManagerInterface; -@@ -23,7 +24,6 @@ - use Drupal\layout_builder\SectionComponent; - use Drupal\Tests\UnitTestCase; - use Prophecy\Argument; --use Symfony\Component\EventDispatcher\EventDispatcher; - - /** - * @coversDefaultClass \Drupal\layout_builder\Section -@@ -62,7 +62,7 @@ class SectionRenderTest extends UnitTestCase { - /** - * The event dispatcher. - * -- * @var \Symfony\Component\EventDispatcher\EventDispatcher -+ * @var \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher - */ - protected $eventDispatcher; - -@@ -77,7 +77,7 @@ protected function setUp(): void { - $this->contextHandler = $this->prophesize(ContextHandlerInterface::class); - $this->contextRepository = $this->prophesize(ContextRepositoryInterface::class); - // @todo Refactor this into some better tests in https://www.drupal.org/node/2942605. -- $this->eventDispatcher = (new \ReflectionClass(EventDispatcher::class))->newInstanceWithoutConstructor(); -+ $this->eventDispatcher = (new \ReflectionClass(ContainerAwareEventDispatcher::class))->newInstanceWithoutConstructor(); - - $this->account = $this->prophesize(AccountInterface::class); - $subscriber = new BlockComponentRenderArray($this->account->reveal()); -diff --git a/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php b/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php -index 9ee42e3f70..a8fd9e87d6 100644 ---- a/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php -+++ b/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php -@@ -25,7 +25,6 @@ - * synchronizations. - * - * @group EventDispatcher -- * @group legacy - */ - class ContainerAwareEventDispatcherTest extends TestCase { - -diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php -index c567bc0398..94ced461bc 100644 ---- a/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php -+++ b/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php -@@ -4,13 +4,13 @@ - - namespace Drupal\Tests\Core\EventSubscriber; - -+use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher as EventDispatcher; - use Drupal\Core\EventSubscriber\RedirectResponseSubscriber; - use Drupal\Core\Routing\TrustedRedirectResponse; - use Drupal\Core\Utility\UnroutedUrlAssemblerInterface; - use Drupal\Tests\UnitTestCase; - use Psr\Log\LoggerInterface; - use Symfony\Component\DependencyInjection\Container; --use Symfony\Component\EventDispatcher\EventDispatcher; - use Symfony\Component\HttpFoundation\RedirectResponse; - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpKernel\Event\ResponseEvent; -@@ -87,7 +87,7 @@ protected function setUp(): void { - * @dataProvider providerTestDestinationRedirect - */ - public function testDestinationRedirect(Request $request, $expected) { -- $dispatcher = new EventDispatcher(); -+ $dispatcher = new EventDispatcher(\Drupal::getContainer()); - $kernel = $this->createMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $response = new RedirectResponse('http://example.com/drupal'); - $request->headers->set('HOST', 'example.com'); -@@ -128,7 +128,7 @@ public static function providerTestDestinationRedirect() { - * @dataProvider providerTestDestinationRedirectToExternalUrl - */ - public function testDestinationRedirectToExternalUrl($request, $expected) { -- $dispatcher = new EventDispatcher(); -+ $dispatcher = new EventDispatcher(\Drupal::getContainer()); - $kernel = $this->createMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $response = new RedirectResponse('http://other-example.com'); - -@@ -143,7 +143,7 @@ public function testDestinationRedirectToExternalUrl($request, $expected) { - * @covers ::checkRedirectUrl - */ - public function testRedirectWithOptInExternalUrl() { -- $dispatcher = new EventDispatcher(); -+ $dispatcher = new EventDispatcher(\Drupal::getContainer()); - $kernel = $this->createMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $response = new TrustedRedirectResponse('http://external-url.com'); - $request = Request::create(''); -@@ -176,7 +176,7 @@ public static function providerTestDestinationRedirectToExternalUrl() { - * @dataProvider providerTestDestinationRedirectWithInvalidUrl - */ - public function testDestinationRedirectWithInvalidUrl(Request $request) { -- $dispatcher = new EventDispatcher(); -+ $dispatcher = new EventDispatcher(\Drupal::getContainer()); - $kernel = $this->createMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $response = new RedirectResponse('http://example.com/drupal'); - diff --git a/patches/d10/2909185.diff b/patches/d10/2909185.diff deleted file mode 100644 index a81389bab60dcd591f0495f907416ee0d3c9fc33..0000000000000000000000000000000000000000 --- a/patches/d10/2909185.diff +++ /dev/null @@ -1,291 +0,0 @@ -Subject: [PATCH] Issue #2909185 by longwave, donquixote, andypost, andregp, pounard, jibran, martin107, kostyashupenko, catch, znerol: Replace ContainerAwareEventDispatcher with Symfony EventDispatcher ---- -Index: core/core.services.yml -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/core/core.services.yml b/core/core.services.yml ---- a/core/core.services.yml (revision 6e733866fe414d0521be3d781c631c9368b818b1) -+++ b/core/core.services.yml (revision ed13e942030535430a4a8861cbf49808a0934a72) -@@ -873,7 +873,8 @@ - arguments: ['@request_stack'] - Drupal\Core\Routing\RouteMatchInterface: '@current_route_match' - event_dispatcher: -- class: Symfony\Component\EventDispatcher\EventDispatcher -+ class: Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher -+ arguments: ['@service_container'] - Psr\EventDispatcher\EventDispatcherInterface: '@event_dispatcher' - Symfony\Contracts\EventDispatcher\EventDispatcherInterface: '@event_dispatcher' - controller_resolver: -Index: core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php ---- a/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php (revision 6e733866fe414d0521be3d781c631c9368b818b1) -+++ b/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php (revision ed13e942030535430a4a8861cbf49808a0934a72) -@@ -2,8 +2,6 @@ - - namespace Drupal\Component\EventDispatcher; - --@trigger_error('The ' . __NAMESPACE__ . '\ContainerAwareEventDispatcher is deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. Use Symfony\Component\EventDispatcher\EventDispatcher instead. See https://www.drupal.org/node/3376090', E_USER_DEPRECATED); -- - use Psr\EventDispatcher\StoppableEventInterface; - use Symfony\Component\DependencyInjection\ContainerInterface; - use Symfony\Component\EventDispatcher\EventDispatcherInterface; -@@ -32,11 +30,6 @@ - * runtime is not affected by this change though. - * </dd> - * </dl> -- * -- * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. Use -- * \Symfony\Component\EventDispatcher\EventDispatcher instead. -- * -- * @see https://www.drupal.org/node/3376090 - */ - class ContainerAwareEventDispatcher implements EventDispatcherInterface { - -Index: core/lib/Drupal/Core/CoreServiceProvider.php -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php ---- a/core/lib/Drupal/Core/CoreServiceProvider.php (revision 6e733866fe414d0521be3d781c631c9368b818b1) -+++ b/core/lib/Drupal/Core/CoreServiceProvider.php (revision ed13e942030535430a4a8861cbf49808a0934a72) -@@ -29,7 +29,6 @@ - use Psr\Log\LoggerAwareInterface; - use Symfony\Component\DependencyInjection\Compiler\PassConfig; - use Symfony\Component\DependencyInjection\Reference; --use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; - use Symfony\Component\EventDispatcher\EventSubscriberInterface; - - /** -@@ -83,7 +82,7 @@ - $container->addCompilerPass(new TwigExtensionPass()); - - // Add a compiler pass for registering event subscribers. -- $container->addCompilerPass(new RegisterEventSubscribersPass(new RegisterListenersPass()), PassConfig::TYPE_AFTER_REMOVING); -+ $container->addCompilerPass(new RegisterEventSubscribersPass(), PassConfig::TYPE_AFTER_REMOVING); - $container->addCompilerPass(new LoggerAwarePass(), PassConfig::TYPE_AFTER_REMOVING); - - $container->addCompilerPass(new RegisterAccessChecksPass()); -Index: core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php ---- a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php (revision 6e733866fe414d0521be3d781c631c9368b818b1) -+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php (revision ed13e942030535430a4a8861cbf49808a0934a72) -@@ -4,50 +4,59 @@ - - use Symfony\Component\DependencyInjection\ContainerBuilder; - use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; --use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; -+use Symfony\Component\EventDispatcher\EventSubscriberInterface; - - /** -- * Wraps the Symfony event subscriber pass to use different tag names. -+ * Registers all event subscribers to the event dispatcher. - */ - class RegisterEventSubscribersPass implements CompilerPassInterface { - -- /** -- * Constructs a RegisterEventSubscribersPass object. -- * -- * @param \Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass $pass -- * The Symfony compiler pass that registers event subscribers. -- */ -- public function __construct( -- protected RegisterListenersPass $pass -- ) {} -- - /** - * {@inheritdoc} - */ - public function process(ContainerBuilder $container) { -- $this->renameTag($container, 'event_subscriber', 'kernel.event_subscriber'); -- $this->pass->process($container); -- $this->renameTag($container, 'kernel.event_subscriber', 'event_subscriber'); -- } -+ if (!$container->hasDefinition('event_dispatcher')) { -+ return; -+ } -+ -+ $definition = $container->getDefinition('event_dispatcher'); -+ -+ $event_subscriber_info = []; -+ foreach ($container->findTaggedServiceIds('event_subscriber') as $id => $attributes) { -+ -+ // We must assume that the class value has been correctly filled, even if -+ // the service is created by a factory. -+ $class = $container->getDefinition($id)->getClass(); -+ -+ $interface = EventSubscriberInterface::class; -+ if (!is_subclass_of($class, $interface)) { -+ throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); -+ } - -- /** -- * Renames tags in the container. -- * -- * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container -- * The container. -- * @param string $source_tag -- * The tag to be renamed. -- * @param string $target_tag -- * The tag to rename with. -- */ -- protected function renameTag(ContainerBuilder $container, string $source_tag, string $target_tag): void { -- foreach ($container->getDefinitions() as $definition) { -- if ($definition->hasTag($source_tag)) { -- $attributes = $definition->getTag($source_tag)[0]; -- $definition->addTag($target_tag, $attributes); -- $definition->clearTag($source_tag); -+ // Get all subscribed events. -+ foreach ($class::getSubscribedEvents() as $event_name => $params) { -+ if (is_string($params)) { -+ $priority = 0; -+ $event_subscriber_info[$event_name][$priority][] = ['service' => [$id, $params]]; -+ } -+ elseif (is_string($params[0])) { -+ $priority = $params[1] ?? 0; -+ $event_subscriber_info[$event_name][$priority][] = ['service' => [$id, $params[0]]]; -+ } -+ else { -+ foreach ($params as $listener) { -+ $priority = $listener[1] ?? 0; -+ $event_subscriber_info[$event_name][$priority][] = ['service' => [$id, $listener[0]]]; -+ } -+ } - } - } -+ -+ foreach (array_keys($event_subscriber_info) as $event_name) { -+ krsort($event_subscriber_info[$event_name]); -+ } -+ -+ $definition->addArgument($event_subscriber_info); - } - - } -Index: core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php b/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php ---- a/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php (revision 6e733866fe414d0521be3d781c631c9368b818b1) -+++ b/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php (revision ed13e942030535430a4a8861cbf49808a0934a72) -@@ -4,6 +4,7 @@ - - namespace Drupal\Tests\layout_builder\Unit; - -+use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher; - use Drupal\Component\Plugin\Exception\PluginException; - use Drupal\Core\Access\AccessResult; - use Drupal\Core\Block\BlockManagerInterface; -@@ -23,7 +24,6 @@ - use Drupal\layout_builder\SectionComponent; - use Drupal\Tests\UnitTestCase; - use Prophecy\Argument; --use Symfony\Component\EventDispatcher\EventDispatcher; - - /** - * @coversDefaultClass \Drupal\layout_builder\Section -@@ -62,7 +62,7 @@ - /** - * The event dispatcher. - * -- * @var \Symfony\Component\EventDispatcher\EventDispatcher -+ * @var \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher - */ - protected $eventDispatcher; - -@@ -77,7 +77,7 @@ - $this->contextHandler = $this->prophesize(ContextHandlerInterface::class); - $this->contextRepository = $this->prophesize(ContextRepositoryInterface::class); - // @todo Refactor this into some better tests in https://www.drupal.org/node/2942605. -- $this->eventDispatcher = (new \ReflectionClass(EventDispatcher::class))->newInstanceWithoutConstructor(); -+ $this->eventDispatcher = (new \ReflectionClass(ContainerAwareEventDispatcher::class))->newInstanceWithoutConstructor(); - - $this->account = $this->prophesize(AccountInterface::class); - $subscriber = new BlockComponentRenderArray($this->account->reveal()); -Index: core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php b/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php ---- a/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php (revision 6e733866fe414d0521be3d781c631c9368b818b1) -+++ b/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php (revision ed13e942030535430a4a8861cbf49808a0934a72) -@@ -25,7 +25,6 @@ - * synchronizations. - * - * @group EventDispatcher -- * @group legacy - */ - class ContainerAwareEventDispatcherTest extends TestCase { - -Index: core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php ---- a/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php (revision 6e733866fe414d0521be3d781c631c9368b818b1) -+++ b/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php (revision ed13e942030535430a4a8861cbf49808a0934a72) -@@ -4,13 +4,13 @@ - - namespace Drupal\Tests\Core\EventSubscriber; - -+use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher as EventDispatcher; - use Drupal\Core\EventSubscriber\RedirectResponseSubscriber; - use Drupal\Core\Routing\TrustedRedirectResponse; - use Drupal\Core\Utility\UnroutedUrlAssemblerInterface; - use Drupal\Tests\UnitTestCase; - use Psr\Log\LoggerInterface; - use Symfony\Component\DependencyInjection\Container; --use Symfony\Component\EventDispatcher\EventDispatcher; - use Symfony\Component\HttpFoundation\RedirectResponse; - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpKernel\Event\ResponseEvent; -@@ -87,7 +87,7 @@ - * @dataProvider providerTestDestinationRedirect - */ - public function testDestinationRedirect(Request $request, $expected) { -- $dispatcher = new EventDispatcher(); -+ $dispatcher = new EventDispatcher(\Drupal::getContainer()); - $kernel = $this->createMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $response = new RedirectResponse('http://example.com/drupal'); - $request->headers->set('HOST', 'example.com'); -@@ -128,7 +128,7 @@ - * @dataProvider providerTestDestinationRedirectToExternalUrl - */ - public function testDestinationRedirectToExternalUrl($request, $expected) { -- $dispatcher = new EventDispatcher(); -+ $dispatcher = new EventDispatcher(\Drupal::getContainer()); - $kernel = $this->createMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $response = new RedirectResponse('http://other-example.com'); - -@@ -143,7 +143,7 @@ - * @covers ::checkRedirectUrl - */ - public function testRedirectWithOptInExternalUrl() { -- $dispatcher = new EventDispatcher(); -+ $dispatcher = new EventDispatcher(\Drupal::getContainer()); - $kernel = $this->createMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $response = new TrustedRedirectResponse('http://external-url.com'); - $request = Request::create(''); -@@ -176,7 +176,7 @@ - * @dataProvider providerTestDestinationRedirectWithInvalidUrl - */ - public function testDestinationRedirectWithInvalidUrl(Request $request) { -- $dispatcher = new EventDispatcher(); -+ $dispatcher = new EventDispatcher(\Drupal::getContainer()); - $kernel = $this->createMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $response = new RedirectResponse('http://example.com/drupal'); - diff --git a/patches/d10/2918537.diff b/patches/d10/2918537.diff deleted file mode 100644 index 9bd1c8a56a9fd3ae754a6070c85d934ec0c91ef6..0000000000000000000000000000000000000000 --- a/patches/d10/2918537.diff +++ /dev/null @@ -1,201 +0,0 @@ -diff --git a/book.module b/book.module -index 1760113fe4188101391792b45cfa93e2399089b8..cc02b76fef5e9f44a9fa594292f3bc66f9d2c5ba 100644 ---- a/book.module -+++ b/book.module -@@ -136,6 +136,9 @@ function book_node_links_alter(array &$links, NodeInterface $node, array &$conte - */ - function book_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) { - $node = $form_state->getFormObject()->getEntity(); -+ if (!book_type_is_allowed($node->getType())) { -+ return; -+ } - $account = \Drupal::currentUser(); - $access = $account->hasPermission('administer book outlines'); - if (!$access) { -@@ -290,6 +293,9 @@ function book_node_predelete(EntityInterface $node) { - * Implements hook_ENTITY_TYPE_prepare_form() for node entities. - */ - function book_node_prepare_form(NodeInterface $node, $operation, FormStateInterface $form_state) { -+ if (!book_type_is_allowed($node->getType())) { -+ return; -+ } - /** @var \Drupal\book\BookManagerInterface $book_manager */ - $book_manager = \Drupal::service('book.manager'); - -diff --git a/src/Plugin/Validation/Constraint/BookOutlineConstraintValidator.php b/src/Plugin/Validation/Constraint/BookOutlineConstraintValidator.php -index ca8114dad4a661417bed20d375d33f06127b86b8..fc7b4ef7a0ae09b7268472e7f664c23490383862 100644 ---- a/src/Plugin/Validation/Constraint/BookOutlineConstraintValidator.php -+++ b/src/Plugin/Validation/Constraint/BookOutlineConstraintValidator.php -@@ -43,7 +43,12 @@ public static function create(ContainerInterface $container) { - * {@inheritdoc} - */ - public function validate($entity, Constraint $constraint) { -- if (isset($entity) && !$entity->isNew() && !$entity->isDefaultRevision()) { -+ // Validate the book structure when the user has access to manage book -+ // outlines. When the user can manage book outlines, the book variable will -+ // be populated even if the node is not part of the book. If the user cannot -+ // manage book outlines, the book variable will be empty and we can safely -+ // ignore the constraints as the outline cannot be changed by this user. -+ if (isset($entity) && !empty($entity->book) && !$entity->isNew() && !$entity->isDefaultRevision()) { - /** @var \Drupal\Core\Entity\ContentEntityInterface $original */ - $original = $this->bookManager->loadBookLink($entity->id(), FALSE) ?: [ - 'bid' => 0, -diff --git a/tests/src/Functional/BookContentModerationTest.php b/tests/src/Functional/BookContentModerationTest.php -index 14898777d91ea52aa2630f6775a2251dc08b1530..3fc60cde1129430488bea2a258596637893bca38 100644 ---- a/tests/src/Functional/BookContentModerationTest.php -+++ b/tests/src/Functional/BookContentModerationTest.php -@@ -4,6 +4,7 @@ - - use Drupal\Tests\BrowserTestBase; - use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait; -+use Drupal\user\Entity\Role; - - /** - * Tests Book and Content Moderation integration. -@@ -15,6 +16,13 @@ class BookContentModerationTest extends BrowserTestBase { - use BookTestTrait; - use ContentModerationTestTrait; - -+ /** -+ * A user with permission to make workflow transitions but not manage books. -+ * -+ * @var \Drupal\user\UserInterface -+ */ -+ protected $nonBookAdminUser; -+ - /** - * Modules to install. - * -@@ -56,6 +64,19 @@ protected function setUp(): void { - 'use editorial transition create_new_draft', - 'use editorial transition publish', - ]); -+ -+ // Another user without manage book permissions to test updates to nodes -+ // that are -+ // 1. Not part of a book outline. -+ // 2. Part of a book outline. -+ $this->nonBookAdminUser = $this->drupalCreateUser([ -+ 'create book content', -+ 'edit own book content', -+ 'use editorial transition create_new_draft', -+ 'use editorial transition publish', -+ 'access printer-friendly version', -+ 'view any unpublished content', -+ ]); - } - - /** -@@ -163,4 +184,112 @@ public function testBookWithPendingRevisions() { - $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); - } - -+ /** -+ * Tests that users who cannot manage books can still make node updates. -+ */ -+ public function testNonBookAdminNodeUpdates() { -+ // 1. First test that users who cannot manage books can make updates to -+ // nodes that are not part of a book outline. -+ $this->drupalLogin($this->nonBookAdminUser); -+ // Create a new book page without actually attaching it to a book and create -+ // a draft. -+ $this->drupalGet('node/add/book'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'title[0][value]' => 'Some moderated content', -+ 'moderation_state[0][state]' => 'draft', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ $this->assertSession()->pageTextContains('Some moderated content has been created.'); -+ $node = $this->drupalGetNodeByTitle($edit['title[0][value]']); -+ $this->assertNotEmpty($node); -+ -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ // Publish the content. -+ $edit = [ -+ 'body[0][value]' => 'Second change non book admin user', -+ 'moderation_state[0][state]' => 'published', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); -+ $this->assertSession()->pageTextContains('Some moderated content has been updated'); -+ -+ // Now update content again, it should be successfully updated and not throw -+ // any errors. -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'moderation_state[0][state]' => 'draft', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); -+ $this->assertSession()->pageTextContains('Some moderated content has been updated'); -+ -+ // 2. Now test that users who cannot manage books can make updates to nodes -+ // that are part of a book outline. As the non admin book user, publish the -+ // content created above in order to be added to a book. -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'moderation_state[0][state]' => 'published', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ -+ // Create a book (as a book admin user). -+ $book_1_nodes = $this->createBook(['moderation_state[0][state]' => 'published']); -+ $book_1 = $this->book; -+ -+ // Now add the node created previously by the non book admin user to the -+ // book created above. We need to grant additional permission for bookAuthor -+ // to be able to edit the node owned by nonBookAdminUser. -+ $role_ids = $this->bookAuthor->getRoles(TRUE); -+ $role_id = reset($role_ids); -+ $role = Role::load($role_id); -+ $role->grantPermission('edit any book content'); -+ $role->save(); -+ $this->drupalLogin($this->bookAuthor); -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'book[bid]' => $this->book->id(), -+ 'moderation_state[0][state]' => 'published', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ -+ // Assert that the node has been added to the book. -+ $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); -+ $this->assertSession()->pageTextContains('Some moderated content has been updated'); -+ $this->checkBookNode($book_1, [ -+ $book_1_nodes[0], -+ $book_1_nodes[3], -+ $book_1_nodes[4], -+ $node, -+ ], FALSE, FALSE, $book_1_nodes[0], []); -+ -+ // Try to update the non book admin's node in the book as the user -+ // that cannot manage books, it should be successfully updated and not -+ // throw any errors. -+ $this->drupalLogin($this->nonBookAdminUser); -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'body[0][value]' => 'Change by non book admin user again', -+ 'moderation_state[0][state]' => 'draft', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); -+ $this->assertSession()->pageTextContains('Some moderated content has been updated'); -+ -+ // Check that the book outline did not change. -+ $this->book = $book_1; -+ $this->checkBookNode($book_1, [ -+ $book_1_nodes[0], -+ $book_1_nodes[3], -+ $book_1_nodes[4], -+ $node, -+ ], FALSE, FALSE, $book_1_nodes[0], []); -+ $this->checkBookNode($book_1_nodes[0], [$book_1_nodes[1], $book_1_nodes[2]], $book_1, $book_1, $book_1_nodes[1], [$book_1]); -+ } -+ - } diff --git a/patches/d10/2955321.diff b/patches/d10/2955321.diff deleted file mode 100644 index 3bbd5274935460e4bd110be28f4f9b6746b0b9b6..0000000000000000000000000000000000000000 --- a/patches/d10/2955321.diff +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityUntranslatableFieldsConstraintValidator.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityUntranslatableFieldsConstraintValidator.php -index 56f0da64fe8c6c0b2c7fd1a56b48ddad3ec69186..57e3903d5d7b17c37cd66790dd148e99fc1b6ce8 100644 ---- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityUntranslatableFieldsConstraintValidator.php -+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityUntranslatableFieldsConstraintValidator.php -@@ -71,8 +71,11 @@ public function validate($entity, Constraint $constraint) { - // in default revisions. - if ($this->hasUntranslatableFieldsChanges($entity)) { - if ($entity->isDefaultTranslationAffectedOnly()) { -+ $moderationInformation = \Drupal::hasService('content_moderation.moderation_information') ? -+ \Drupal::service('content_moderation.moderation_information') : -+ NULL; - foreach ($entity->getTranslationLanguages(FALSE) as $langcode => $language) { -- if ($entity->getTranslation($langcode)->hasTranslationChanges()) { -+ if ($entity->getTranslation($langcode)->hasTranslationChanges() && ($moderationInformation === NULL || !$moderationInformation->isModeratedEntity($entity->getTranslation($langcode)))) { - $this->context->addViolation($constraint->defaultTranslationMessage); - break; - } -diff --git a/core/modules/content_translation/src/Plugin/Validation/Constraint/ContentTranslationSynchronizedFieldsConstraintValidator.php b/core/modules/content_translation/src/Plugin/Validation/Constraint/ContentTranslationSynchronizedFieldsConstraintValidator.php -index e52a18081064ddcdcd48777a401801253f183803..51eb95e420a63e3f64b0ea3dc6be8732b3213af0 100644 ---- a/core/modules/content_translation/src/Plugin/Validation/Constraint/ContentTranslationSynchronizedFieldsConstraintValidator.php -+++ b/core/modules/content_translation/src/Plugin/Validation/Constraint/ContentTranslationSynchronizedFieldsConstraintValidator.php -@@ -111,8 +111,11 @@ public function validate($value, Constraint $constraint) { - $original_translation = $this->getOriginalTranslation($entity, $original); - if ($this->hasSynchronizedPropertyChanges($entity, $original_translation, $synchronized_properties)) { - if ($entity->isDefaultTranslationAffectedOnly()) { -+ $moderationInformation = \Drupal::hasService('content_moderation.moderation_information') ? -+ \Drupal::service('content_moderation.moderation_information') : -+ NULL; - foreach ($entity->getTranslationLanguages(FALSE) as $langcode => $language) { -- if ($entity->getTranslation($langcode)->hasTranslationChanges()) { -+ if ($entity->getTranslation($langcode)->hasTranslationChanges() && ($moderationInformation === NULL || !$moderationInformation->isModeratedEntity($entity->getTranslation($langcode)))) { - $this->context->addViolation($constraint->defaultTranslationMessage); - break; - } diff --git a/patches/d10/2966735.diff b/patches/d10/2966735.diff deleted file mode 100644 index 0222dd4462a21f7eed302b0a78c8aa4f303be0d9..0000000000000000000000000000000000000000 --- a/patches/d10/2966735.diff +++ /dev/null @@ -1,68 +0,0 @@ -diff --git a/core/modules/datetime/src/Plugin/views/filter/Date.php b/core/modules/datetime/src/Plugin/views/filter/Date.php -index f73a675fef0c72f3de88af21cdd759216713cdac..b4152bf730260e619814204140f5e18e15cd0f23 100644 ---- a/core/modules/datetime/src/Plugin/views/filter/Date.php -+++ b/core/modules/datetime/src/Plugin/views/filter/Date.php -@@ -4,6 +4,8 @@ - - use Drupal\Component\Datetime\DateTimePlus; - use Drupal\Core\Datetime\DateFormatterInterface; -+use Drupal\Core\Datetime\DrupalDateTime; -+use Drupal\Core\Form\FormStateInterface; - use Drupal\Core\Plugin\ContainerFactoryPluginInterface; - use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem; - use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; -@@ -98,6 +100,54 @@ public static function create(ContainerInterface $container, array $configuratio - ); - } - -+ /** -+ * {@inheritdoc} -+ */ -+ public function validateExposed(&$form, FormStateInterface $form_state) { -+ // Do not validate value if filter is not exposed or grouped. -+ if (empty($this->options['exposed']) || $this->options['is_grouped']) { -+ return; -+ } -+ -+ $identifier = $this->options['expose']['identifier']; -+ $input = $form_state->getValue($identifier); -+ -+ $values = []; -+ if (is_array($input)) { -+ if (!empty($input['value'])) { -+ $values[] = $input['value']; -+ } -+ else { -+ if (!empty($input['min'])) { -+ $values[] = $input['min']; -+ } -+ if (!empty($input['max'])) { -+ $values[] = $input['max']; -+ } -+ } -+ } -+ elseif (!empty($input)) { -+ $values[] = $input; -+ } -+ -+ foreach ($values as $value) { -+ try { -+ (new DrupalDateTime($value))->getTimestamp(); -+ } -+ catch (\Exception $e) { -+ if (isset($form[$identifier])) { -+ $field = &$form[$identifier]; -+ } -+ elseif (isset($form[$identifier . '_wrapper'])) { -+ $field = &$form[$identifier . '_wrapper']; -+ } -+ // Set the form error message. -+ $form_state->setError($field, $this->t('Invalid date format.')); -+ break; -+ } -+ } -+ } -+ - /** - * Override parent method, which deals with dates as integers. - */ diff --git a/patches/d10/3021431.diff b/patches/d10/3021431.diff deleted file mode 100644 index 7ddb47c1f44b0073ab07c49611c5d7b90eccf092..0000000000000000000000000000000000000000 --- a/patches/d10/3021431.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/src/Plugin/CKEditorPlugin/CodeSnippet.php b/src/Plugin/CKEditorPlugin/CodeSnippet.php -index 4cf9ab096ebdc001aaaeb918875e4af525cd001f..d5693bb409ecdec6aab495c88fc6fb799bbb0844 100644 ---- a/src/Plugin/CKEditorPlugin/CodeSnippet.php -+++ b/src/Plugin/CKEditorPlugin/CodeSnippet.php -@@ -182,6 +182,7 @@ class CodeSnippet extends CKEditorPluginBase implements CKEditorPluginConfigurab - 'vbscript' => 'VBScript', - 'xhtml' => 'XHTML', - 'xml' => 'XML', -+ 'yaml' => 'Yaml', - ]; - } - diff --git a/patches/d10/3043725-104.diff b/patches/d10/3043725-104.diff deleted file mode 100644 index 28129b99d9676b827c316ce9cf6a4c140c40a517..0000000000000000000000000000000000000000 --- a/patches/d10/3043725-104.diff +++ /dev/null @@ -1,1231 +0,0 @@ -diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module -index 0928695ed1..73bac11ffe 100644 ---- a/core/modules/comment/comment.module -+++ b/core/modules/comment/comment.module -@@ -26,7 +26,6 @@ - use Drupal\field\FieldStorageConfigInterface; - use Drupal\node\NodeInterface; - use Drupal\user\RoleInterface; --use Drupal\user\UserInterface; - - /** - * The time cutoff for comments marked as read for entity types other node. -@@ -447,50 +446,6 @@ function comment_node_search_result(EntityInterface $node) { - } - } - --/** -- * Implements hook_user_cancel(). -- */ --function comment_user_cancel($edit, UserInterface $account, $method) { -- switch ($method) { -- case 'user_cancel_block_unpublish': -- $comments = \Drupal::entityTypeManager()->getStorage('comment')->loadByProperties(['uid' => $account->id()]); -- foreach ($comments as $comment) { -- $comment->setUnpublished(); -- $comment->save(); -- } -- break; -- -- case 'user_cancel_reassign': -- /** @var \Drupal\comment\CommentInterface[] $comments */ -- $comments = \Drupal::entityTypeManager()->getStorage('comment')->loadByProperties(['uid' => $account->id()]); -- foreach ($comments as $comment) { -- $langcodes = array_keys($comment->getTranslationLanguages()); -- // For efficiency manually save the original comment before applying any -- // changes. -- $comment->original = clone $comment; -- foreach ($langcodes as $langcode) { -- $comment_translated = $comment->getTranslation($langcode); -- $comment_translated->setOwnerId(0); -- $comment_translated->setAuthorName(\Drupal::config('user.settings')->get('anonymous')); -- } -- $comment->save(); -- } -- break; -- } --} -- --/** -- * Implements hook_ENTITY_TYPE_predelete() for user entities. -- */ --function comment_user_predelete($account) { -- $entity_query = \Drupal::entityQuery('comment')->accessCheck(FALSE); -- $entity_query->condition('uid', $account->id()); -- $cids = $entity_query->execute(); -- $comment_storage = \Drupal::entityTypeManager()->getStorage('comment'); -- $comments = $comment_storage->loadMultiple($cids); -- $comment_storage->delete($comments); --} -- - /** - * Generates a comment preview. - * -diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php -index 43123b7a17..f8f145da13 100644 ---- a/core/modules/comment/src/Entity/Comment.php -+++ b/core/modules/comment/src/Entity/Comment.php -@@ -39,7 +39,8 @@ - * "default" = "Drupal\comment\CommentForm", - * "delete" = "Drupal\comment\Form\DeleteForm" - * }, -- * "translation" = "Drupal\comment\CommentTranslationHandler" -+ * "translation" = "Drupal\comment\CommentTranslationHandler", -+ * "user_cancel" = "Drupal\comment\Entity\Handler\CancellationHandler", - * }, - * base_table = "comment", - * data_table = "comment_field_data", -diff --git a/core/modules/comment/src/Entity/Handler/CancellationHandler.php b/core/modules/comment/src/Entity/Handler/CancellationHandler.php -new file mode 100644 -index 0000000000..de6f008eff ---- /dev/null -+++ b/core/modules/comment/src/Entity/Handler/CancellationHandler.php -@@ -0,0 +1,60 @@ -+<?php -+ -+namespace Drupal\comment\Entity\Handler; -+ -+use Drupal\Core\Config\ConfigFactoryInterface; -+use Drupal\Core\Entity\ContentEntityInterface; -+use Drupal\Core\Entity\ContentEntityStorageInterface; -+use Drupal\Core\Entity\ContentEntityTypeInterface; -+use Drupal\Core\Entity\EntityTypeInterface; -+use Drupal\user\Entity\Handler\DefaultCancellationHandler; -+use Symfony\Component\DependencyInjection\ContainerInterface; -+ -+/** -+ * Defines an entity handler for comments to react to user account cancellation. -+ */ -+class CancellationHandler extends DefaultCancellationHandler { -+ -+ /** -+ * The config factory service. -+ * -+ * @var \Drupal\Core\Config\ConfigFactoryInterface -+ */ -+ protected $configFactory; -+ -+ /** -+ * CancellationHandler constructor. -+ * -+ * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type -+ * The entity type definition. -+ * @param \Drupal\Core\Entity\ContentEntityStorageInterface $storage -+ * The entity storage handler. -+ * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory -+ * The config factory service. -+ */ -+ public function __construct(ContentEntityTypeInterface $entity_type, ContentEntityStorageInterface $storage, ConfigFactoryInterface $config_factory) { -+ parent::__construct($entity_type, $storage); -+ $this->configFactory = $config_factory; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { -+ return new static( -+ $entity_type, -+ $container->get('entity_type.manager')->getStorage($entity_type->id()), -+ $container->get('config.factory') -+ ); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function anonymize(ContentEntityInterface $entity): void { -+ /** @var \Drupal\comment\CommentInterface $entity */ -+ parent::anonymize($entity); -+ $entity->setAuthorName($this->configFactory->get('user.settings')->get('anonymous')); -+ } -+ -+} -diff --git a/core/modules/comment/tests/src/Kernel/UserCancellationTest.php b/core/modules/comment/tests/src/Kernel/UserCancellationTest.php -new file mode 100644 -index 0000000000..a3dd0272cf ---- /dev/null -+++ b/core/modules/comment/tests/src/Kernel/UserCancellationTest.php -@@ -0,0 +1,127 @@ -+<?php -+ -+namespace Drupal\Tests\comment\Kernel; -+ -+use Drupal\comment\Entity\Comment; -+use Drupal\comment\Entity\CommentType; -+use Drupal\field\Entity\FieldConfig; -+use Drupal\field\Entity\FieldStorageConfig; -+use Drupal\KernelTests\KernelTestBase; -+use Drupal\Tests\node\Traits\ContentTypeCreationTrait; -+use Drupal\Tests\user\Traits\UserCreationTrait; -+use Drupal\user\CancellationHandlerInterface; -+use Drupal\user\UserInterface; -+ -+/** -+ * Tests how comments react to user cancellation. -+ * -+ * @group comment -+ */ -+class UserCancellationTest extends KernelTestBase { -+ -+ use ContentTypeCreationTrait; -+ use UserCreationTrait; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected static $modules = [ -+ 'comment', -+ 'field', -+ 'node', -+ 'system', -+ 'text', -+ 'user', -+ ]; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function setUp(): void { -+ parent::setUp(); -+ -+ $this->installSchema('comment', 'comment_entity_statistics'); -+ $this->installSchema('system', 'sequences'); -+ $this->installEntitySchema('comment'); -+ $this->installEntitySchema('node'); -+ $this->installEntitySchema('user'); -+ $this->installConfig('node'); -+ -+ CommentType::create([ -+ 'id' => 'test', -+ 'target_entity_type_id' => 'node', -+ ])->save(); -+ -+ $this->createContentType(['type' => 'page']); -+ -+ FieldStorageConfig::create([ -+ 'type' => 'comment', -+ 'entity_type' => 'node', -+ 'field_name' => 'comments', -+ ])->save(); -+ -+ FieldConfig::create([ -+ 'field_name' => 'comments', -+ 'entity_type' => 'node', -+ 'bundle' => 'page', -+ ])->save(); -+ -+ $this->container->get('config.factory') -+ ->getEditable('user.settings') -+ ->set('anonymous', 'Mysterious Stranger') -+ ->save(); -+ } -+ -+ /** -+ * Tests how comment entities handle user cancellation. -+ */ -+ public function testUserCancellation(): void { -+ // With the BLOCK_UNPUBLISH method, the comment should still be associated -+ // with the cancelled account, but be unpublished. -+ $user = $this->createUser(); -+ $comment = $this->createComment($user); -+ user_cancel([], $user->id(), CancellationHandlerInterface::METHOD_BLOCK_UNPUBLISH); -+ $comment = Comment::load($comment->id()); -+ $this->assertSame($user->id(), $comment->getOwnerId()); -+ $this->assertFalse($comment->isPublished()); -+ -+ // With the REASSIGN method, the comment should still be published, but -+ // associated with the anonymous user. -+ $user = $this->createUser(); -+ $comment = $this->createComment($user); -+ user_cancel([], $user->id(), CancellationHandlerInterface::METHOD_REASSIGN); -+ $comment = Comment::load($comment->id()); -+ $this->assertTrue($comment->getOwner()->isAnonymous()); -+ $this->assertSame('Mysterious Stranger', $comment->getAuthorName()); -+ $this->assertTrue($comment->isPublished()); -+ -+ // The DELETE method should delete the comment outright. -+ $user = $this->createUser(); -+ $comment = $this->createComment($user); -+ user_cancel([], $user->id(), CancellationHandlerInterface::METHOD_DELETE); -+ $this->assertNull(Comment::load($comment->id())); -+ } -+ -+ /** -+ * Creates a published comment associated with a user account. -+ * -+ * @param \Drupal\user\UserInterface $user -+ * The user account which should own the comment. -+ * -+ * @return \Drupal\comment\Entity\Comment -+ * The saved comment entity. -+ */ -+ protected function createComment(UserInterface $user): Comment { -+ $comment = Comment::create([ -+ 'comment_type' => 'test', -+ 'entity_type' => 'node', -+ 'field_name' => 'comments', -+ ]); -+ $comment->setOwner($user)->setPublished()->save(); -+ $this->assertSame($user->id(), $comment->getOwnerId()); -+ $this->assertTrue($comment->isPublished()); -+ -+ return $comment; -+ } -+ -+} -diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php -index fee3b5e2d3..dc348fe0ac 100644 ---- a/core/modules/file/src/Entity/File.php -+++ b/core/modules/file/src/Entity/File.php -@@ -39,6 +39,7 @@ - * "route_provider" = { - * "html" = "Drupal\file\Entity\FileRouteProvider", - * }, -+ * "user_cancel" = "Drupal\user\Entity\Handler\BatchCancellationHandler", - * }, - * base_table = "file_managed", - * entity_keys = { -diff --git a/core/modules/file/tests/src/Functional/UserCancellationTest.php b/core/modules/file/tests/src/Functional/UserCancellationTest.php -new file mode 100644 -index 0000000000..934924a7a7 ---- /dev/null -+++ b/core/modules/file/tests/src/Functional/UserCancellationTest.php -@@ -0,0 +1,63 @@ -+<?php -+ -+namespace Drupal\Tests\file\Functional; -+ -+use Drupal\file\Entity\File; -+use Drupal\Tests\BrowserTestBase; -+use Drupal\Tests\user\Traits\UserCancellationTrait; -+use Drupal\user\CancellationHandlerInterface; -+ -+/** -+ * Tests how file entities react to user cancellation. -+ * -+ * @group file -+ */ -+class UserCancellationTest extends BrowserTestBase { -+ -+ use UserCancellationTrait; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected $defaultTheme = 'stark'; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected static $modules = [ -+ 'file', -+ 'views', -+ ]; -+ -+ /** -+ * Tests how nodes react to user cancellation. -+ */ -+ public function testUserCancellation(): void { -+ $alice = $this->drupalCreateUser(); -+ $bob = $this->drupalCreateUser(); -+ $charlie = $this->drupalCreateUser(); -+ -+ $uri = uniqid('public://') . '.txt'; -+ file_put_contents($uri, $this->getRandomGenerator()->paragraphs()); -+ $this->assertFileExists($uri); -+ $values = ['uri' => $uri]; -+ -+ $aliceFile = File::create($values)->setOwner($alice); -+ $aliceFile->save(); -+ -+ $bobFile = File::create($values)->setOwner($bob); -+ $bobFile->save(); -+ -+ $charlieFile = File::create($values)->setOwner($charlie); -+ $charlieFile->save(); -+ -+ $this->drupalLogin($this->rootUser); -+ $this->cancelUser($alice, CancellationHandlerInterface::METHOD_BLOCK_UNPUBLISH); -+ $this->assertSame($alice->id(), File::load($aliceFile->id())->getOwnerId()); -+ $this->cancelUser($bob, CancellationHandlerInterface::METHOD_REASSIGN); -+ $this->assertTrue(File::load($bobFile->id())->getOwner()->isAnonymous()); -+ $this->cancelUser($charlie, CancellationHandlerInterface::METHOD_DELETE); -+ $this->assertNull(File::load($charlieFile->id())); -+ } -+ -+} -diff --git a/core/modules/history/history.module b/core/modules/history/history.module -index f30ec39d88..8cad279cd3 100644 ---- a/core/modules/history/history.module -+++ b/core/modules/history/history.module -@@ -11,6 +11,7 @@ - use Drupal\Core\Entity\EntityInterface; - use Drupal\Core\Entity\Display\EntityViewDisplayInterface; - use Drupal\Core\Routing\RouteMatchInterface; -+use Drupal\user\CancellationHandlerInterface; - use Drupal\user\UserInterface; - - /** -@@ -167,7 +168,7 @@ function history_node_delete(EntityInterface $node) { - */ - function history_user_cancel($edit, UserInterface $account, $method) { - switch ($method) { -- case 'user_cancel_reassign': -+ case CancellationHandlerInterface::METHOD_REASSIGN: - \Drupal::database()->delete('history') - ->condition('uid', $account->id()) - ->execute(); -diff --git a/core/modules/media/src/Entity/Media.php b/core/modules/media/src/Entity/Media.php -index 7fcf32b94c..eb389501f6 100644 ---- a/core/modules/media/src/Entity/Media.php -+++ b/core/modules/media/src/Entity/Media.php -@@ -46,7 +46,8 @@ - * "route_provider" = { - * "html" = "Drupal\media\Routing\MediaRouteProvider", - * "revision" = \Drupal\Core\Entity\Routing\RevisionHtmlRouteProvider::class, -- * } -+ * }, -+ * "user_cancel" = "Drupal\user\Entity\Handler\BatchCancellationHandler", - * }, - * base_table = "media", - * data_table = "media_field_data", -diff --git a/core/modules/media/tests/src/Functional/UserCancellationTest.php b/core/modules/media/tests/src/Functional/UserCancellationTest.php -new file mode 100644 -index 0000000000..4a01985cc7 ---- /dev/null -+++ b/core/modules/media/tests/src/Functional/UserCancellationTest.php -@@ -0,0 +1,73 @@ -+<?php -+ -+namespace Drupal\Tests\media\Functional; -+ -+use Drupal\media\Entity\Media; -+use Drupal\Tests\user\Traits\UserCancellationTrait; -+use Drupal\user\CancellationHandlerInterface; -+use Drupal\user\UserInterface; -+ -+/** -+ * Tests how media items react to user cancellation. -+ * -+ * @group media -+ */ -+class UserCancellationTest extends MediaFunctionalTestBase { -+ -+ use UserCancellationTrait; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected $defaultTheme = 'stark'; -+ -+ /** -+ * Tests how media items react to user cancellation. -+ */ -+ public function testUserCancellation(): void { -+ $this->createMediaType('test', [ -+ 'id' => 'test', -+ ]); -+ $alice = $this->drupalCreateUser(); -+ $bob = $this->drupalCreateUser(); -+ $charlie = $this->drupalCreateUser(); -+ -+ $alice_media = $this->createMediaForUser($alice); -+ $bob_media = $this->createMediaForUser($bob); -+ $charlie_media = $this->createMediaForUser($charlie); -+ -+ $original_charlie_media_vid = $charlie_media->getRevisionId(); -+ $charlie_media->setNewRevision(); -+ $charlie_media->save(); -+ -+ $this->drupalLogin($this->rootUser); -+ $this->cancelUser($alice, CancellationHandlerInterface::METHOD_BLOCK_UNPUBLISH); -+ $alice_media = Media::load($alice_media->id()); -+ $this->assertFalse($alice_media->isPublished()); -+ $this->assertSame($alice->id(), $alice_media->getOwnerId()); -+ -+ $this->cancelUser($bob, CancellationHandlerInterface::METHOD_REASSIGN); -+ $bob_media = Media::load($bob_media->id()); -+ $this->assertTrue($bob_media->isPublished()); -+ $this->assertTrue($bob_media->getOwner()->isAnonymous()); -+ -+ $this->cancelUser($charlie, CancellationHandlerInterface::METHOD_DELETE); -+ /** @var \Drupal\media\MediaStorage $media_storage */ -+ $media_storage = $this->container->get('entity_type.manager') -+ ->getStorage('media'); -+ $this->assertNull($media_storage->load($charlie_media->id())); -+ $this->assertNull($media_storage->loadRevision($original_charlie_media_vid)); -+ } -+ -+ protected function createMediaForUser(UserInterface $user): Media { -+ $media = Media::create([ -+ 'bundle' => 'test', -+ 'uid' => $user->id(), -+ ]); -+ $media->save(); -+ $this->assertSame($user->id(), $media->getOwnerId()); -+ $this->assertTrue($media->isPublished()); -+ return $media; -+ } -+ -+} -diff --git a/core/modules/node/node.module b/core/modules/node/node.module -index 43c65f590b..6f892f23b4 100644 ---- a/core/modules/node/node.module -+++ b/core/modules/node/node.module -@@ -31,7 +31,6 @@ - use Drupal\node\Entity\NodeType; - use Drupal\node\NodeInterface; - use Drupal\node\NodeTypeInterface; --use Drupal\user\UserInterface; - use Drupal\node\Form\NodePreviewForm; - - /** -@@ -661,53 +660,6 @@ function node_ranking() { - return $ranking; - } - --/** -- * Implements hook_user_cancel(). -- */ --function node_user_cancel($edit, UserInterface $account, $method) { -- switch ($method) { -- case 'user_cancel_block_unpublish': -- // Unpublish nodes (current revisions). -- $nids = \Drupal::entityQuery('node') -- ->accessCheck(FALSE) -- ->condition('uid', $account->id()) -- ->execute(); -- \Drupal::moduleHandler()->loadInclude('node', 'inc', 'node.admin'); -- node_mass_update($nids, ['status' => 0], NULL, TRUE); -- break; -- -- case 'user_cancel_reassign': -- // Anonymize all of the nodes for this old account. -- \Drupal::moduleHandler()->loadInclude('node', 'inc', 'node.admin'); -- $vids = \Drupal::entityTypeManager()->getStorage('node')->userRevisionIds($account); -- node_mass_update($vids, [ -- 'uid' => 0, -- 'revision_uid' => 0, -- ], NULL, TRUE, TRUE); -- break; -- } --} -- --/** -- * Implements hook_ENTITY_TYPE_predelete() for user entities. -- */ --function node_user_predelete($account) { -- // Delete nodes (current revisions). -- // @todo Introduce node_mass_delete() or make node_mass_update() more flexible. -- $nids = \Drupal::entityQuery('node') -- ->condition('uid', $account->id()) -- ->accessCheck(FALSE) -- ->execute(); -- // Delete old revisions. -- $storage_controller = \Drupal::entityTypeManager()->getStorage('node'); -- $nodes = $storage_controller->loadMultiple($nids); -- $storage_controller->delete($nodes); -- $revisions = $storage_controller->userRevisionIds($account); -- foreach ($revisions as $revision) { -- $storage_controller->deleteRevision($revision); -- } --} -- - /** - * Finds the most recently changed nodes that are available to the current user. - * -diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php -index b00087d35f..409cf1b015 100644 ---- a/core/modules/node/src/Entity/Node.php -+++ b/core/modules/node/src/Entity/Node.php -@@ -40,7 +40,8 @@ - * "html" = "Drupal\node\Entity\NodeRouteProvider", - * }, - * "list_builder" = "Drupal\node\NodeListBuilder", -- * "translation" = "Drupal\node\NodeTranslationHandler" -+ * "translation" = "Drupal\node\NodeTranslationHandler", -+ * "user_cancel" = "Drupal\user\Entity\Handler\BatchCancellationHandler", - * }, - * base_table = "node", - * data_table = "node_field_data", -diff --git a/core/modules/node/tests/src/Functional/UserCancellationTest.php b/core/modules/node/tests/src/Functional/UserCancellationTest.php -new file mode 100644 -index 0000000000..8efbcf0f5c ---- /dev/null -+++ b/core/modules/node/tests/src/Functional/UserCancellationTest.php -@@ -0,0 +1,88 @@ -+<?php -+ -+namespace Drupal\Tests\node\Functional; -+ -+use Drupal\node\Entity\Node; -+use Drupal\Tests\BrowserTestBase; -+use Drupal\Tests\user\Traits\UserCancellationTrait; -+use Drupal\user\CancellationHandlerInterface; -+use Drupal\user\UserInterface; -+ -+/** -+ * Tests how nodes react to user cancellation. -+ * -+ * @group node -+ */ -+class UserCancellationTest extends BrowserTestBase { -+ -+ use UserCancellationTrait; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected $defaultTheme = 'stark'; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected static $modules = [ -+ 'node', -+ 'views', -+ ]; -+ -+ /** -+ * Tests how nodes react to user cancellation. -+ */ -+ public function testUserCancellation(): void { -+ $this->drupalCreateContentType(['type' => 'test']); -+ $alice = $this->drupalCreateUser(); -+ $bob = $this->drupalCreateUser(); -+ $charlie = $this->drupalCreateUser(); -+ -+ $alice_node = $this->createNodeForUser($alice); -+ $bob_node = $this->createNodeForUser($bob); -+ $charlie_node = $this->createNodeForUser($charlie); -+ -+ $previous_charlie_node_vid = $charlie_node->getRevisionId(); -+ $charlie_node->setNewRevision(); -+ $charlie_node->save(); -+ -+ $this->drupalLogin($this->rootUser); -+ $this->cancelUser($alice, CancellationHandlerInterface::METHOD_BLOCK_UNPUBLISH); -+ $alice_node = Node::load($alice_node->id()); -+ $this->assertFalse($alice_node->isPublished()); -+ $this->assertSame($alice->id(), $alice_node->getOwnerId()); -+ -+ $this->cancelUser($bob, CancellationHandlerInterface::METHOD_REASSIGN); -+ $bob_node = Node::load($bob_node->id()); -+ $this->assertTrue($bob_node->isPublished()); -+ $this->assertTrue($bob_node->getOwner()->isAnonymous()); -+ -+ $this->cancelUser($charlie, CancellationHandlerInterface::METHOD_DELETE); -+ /** @var \Drupal\node\NodeStorageInterface $node_storage */ -+ $node_storage = $this->container->get('entity_type.manager') -+ ->getStorage('node'); -+ $this->assertNull($node_storage->load($charlie_node->id())); -+ $this->assertNull($node_storage->loadRevision($previous_charlie_node_vid)); -+ } -+ -+ /** -+ * Creates a node associated with a user account. -+ * -+ * @param \Drupal\user\UserInterface $user -+ * The user account that will own the node. -+ * -+ * @return \Drupal\node\Entity\Node -+ * The new, saved node. -+ */ -+ protected function createNodeForUser(UserInterface $user): Node { -+ $node = $this->drupalCreateNode([ -+ 'type' => 'test', -+ 'uid' => $user->id(), -+ ]); -+ $this->assertSame($user->id(), $node->getOwnerId()); -+ $this->assertTrue($node->isPublished()); -+ return $node; -+ } -+ -+} -diff --git a/core/modules/user/src/CancellationHandlerInterface.php b/core/modules/user/src/CancellationHandlerInterface.php -new file mode 100644 -index 0000000000..bcb3f836ff ---- /dev/null -+++ b/core/modules/user/src/CancellationHandlerInterface.php -@@ -0,0 +1,64 @@ -+<?php -+ -+namespace Drupal\user; -+ -+/** -+ * Defines an interface for entity types to react to user account cancellation. -+ */ -+interface CancellationHandlerInterface { -+ -+ /** -+ * The "block" cancellation method. -+ * -+ * With this cancellation method, the user account is blocked, but not -+ * otherwise changed. -+ * -+ * @var string -+ */ -+ const METHOD_BLOCK = 'user_cancel_block'; -+ -+ /** -+ * The "block and unpublish" cancellation method. -+ * -+ * With this cancellation method, the user account is blocked and the -+ * current revision of any entity associated with the account is unpublished. -+ * -+ * @var string -+ */ -+ const METHOD_BLOCK_UNPUBLISH = 'user_cancel_block_unpublish'; -+ -+ /** -+ * The "reassign" cancellation method. -+ * -+ * With this cancellation method, the user account is deleted and all -+ * revisions of any entity associated with it are reassigned to the anonymous -+ * user. -+ * -+ * @var string -+ */ -+ const METHOD_REASSIGN = 'user_cancel_reassign'; -+ -+ /** -+ * The "delete" cancellation method. -+ * -+ * With this cancellation method, the user account is deleted. Modules may -+ * implement entity hooks to react to the deletion as they see fit; it is -+ * assumed that all entities associated with the account will be deleted. -+ * -+ * @var string -+ */ -+ const METHOD_DELETE = 'user_cancel_delete'; -+ -+ /** -+ * Reacts to user account cancellation. -+ * -+ * @param \Drupal\user\UserInterface $account -+ * The account being cancelled. -+ * @param string $method -+ * The cancellation method. Will be one of the static::METHOD_* constants, -+ * or a custom string defined by an implementation of -+ * hook_user_cancel_methods(). -+ */ -+ public function cancelAccount(UserInterface $account, string $method): void; -+ -+} -diff --git a/core/modules/user/src/Entity/Handler/BatchCancellationHandler.php b/core/modules/user/src/Entity/Handler/BatchCancellationHandler.php -new file mode 100644 -index 0000000000..316fdbfce1 ---- /dev/null -+++ b/core/modules/user/src/Entity/Handler/BatchCancellationHandler.php -@@ -0,0 +1,216 @@ -+<?php -+ -+namespace Drupal\user\Entity\Handler; -+ -+use Drupal\Core\Batch\BatchBuilder; -+use Drupal\Core\Entity\ContentEntityStorageInterface; -+use Drupal\Core\Entity\ContentEntityTypeInterface; -+use Drupal\Core\Entity\EntityTypeInterface; -+use Drupal\Core\Render\RendererInterface; -+use Symfony\Component\DependencyInjection\ContainerInterface; -+ -+/** -+ * Defines an entity handler to do a batch update on user account cancellation. -+ */ -+class BatchCancellationHandler extends DefaultCancellationHandler { -+ -+ /** -+ * The renderer service. -+ * -+ * @var \Drupal\Core\Render\RendererInterface -+ */ -+ protected $renderer; -+ -+ /** -+ * BatchCancellationHandler constructor. -+ * -+ * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type -+ * The entity type definition. -+ * @param \Drupal\Core\Entity\ContentEntityStorageInterface $storage -+ * The entity storage handler. -+ * @param \Drupal\Core\Render\RendererInterface $renderer -+ * The renderer service. -+ */ -+ public function __construct(ContentEntityTypeInterface $entity_type, ContentEntityStorageInterface $storage, RendererInterface $renderer) { -+ parent::__construct($entity_type, $storage); -+ $this->renderer = $renderer; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { -+ return new static( -+ $entity_type, -+ $container->get('entity_type.manager')->getStorage($entity_type->id()), -+ $container->get('renderer') -+ ); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function updateMultiple(array $ids, string $method): void { -+ // Use batch processing to prevent timeout when updating a large number of -+ // entities. -+ if (count($ids) > 10) { -+ $batch = $this->buildBatch($ids, $method); -+ batch_set($batch->toArray()); -+ } -+ else { -+ parent::updateMultiple($ids, $method); -+ } -+ } -+ -+ /** -+ * Builds a batch definition for updating multiple entities. -+ * -+ * @param array $ids -+ * The IDs, or revision IDs, of the entities to update. -+ * @param string $method -+ * The user account cancellation method. -+ * -+ * @return \Drupal\Core\Batch\BatchBuilder -+ * The batch definition builder. -+ */ -+ protected function buildBatch(array $ids, string $method): BatchBuilder { -+ return (new BatchBuilder()) -+ // Override the default progress message, because we use a single -+ // multi-pass operation and the default 'Remaining x of y operations' -+ // progress message would be confusing here. -+ ->setProgressMessage('') -+ ->setErrorMessage($this->t('The update has encountered an error.')) -+ ->setFinishCallback(static::class . '::batchFinished') -+ ->addOperation(static::class . '::batchProcess', [ -+ $this->entityType->id(), -+ $ids, -+ $method, -+ ]); -+ } -+ -+ /** -+ * Statically instantiates this handler during batch processing. -+ * -+ * @param string $entity_type_id -+ * The entity type ID with which the handler should be associated. -+ * -+ * @return static -+ * The entity handler. -+ */ -+ protected static function getInstance(string $entity_type_id) { -+ return \Drupal::entityTypeManager() -+ ->getHandler($entity_type_id, 'user_cancel'); -+ } -+ -+ /** -+ * Static wrapper called during batch processing. -+ * -+ * @param string $entity_type_id -+ * The entity type ID being processed. -+ * @param array $ids -+ * The entity IDs, or revision IDs, being processed. -+ * @param string $method -+ * The account cancellation method. -+ * @param array $context -+ * The current context of the batch operation. -+ */ -+ public static function batchProcess(string $entity_type_id, array $ids, string $method, array &$context): void { -+ // The entity type ID is needed by other batch callbacks, so we need to -+ // store it in a persistent place. -+ $context['results']['_entity_type_id'] = $entity_type_id; -+ -+ static::getInstance($entity_type_id) -+ ->doBatchProcess($ids, $method, $context); -+ } -+ -+ /** -+ * Processes a set of entities during a batch operation. -+ * -+ * @param array $ids -+ * The entity IDs, or revision IDs, being processed. -+ * @param string $method -+ * The account cancellation method. -+ * @param array $context -+ * The current context of the batch operation. -+ */ -+ protected function doBatchProcess(array $ids, string $method, array &$context): void { -+ if (!isset($context['sandbox']['progress'])) { -+ $context['sandbox']['progress'] = 0; -+ $context['sandbox']['max'] = count($ids); -+ $context['sandbox']['entities'] = $ids; -+ } -+ -+ // Process entities in groups of 5. -+ $count = min(5, count($context['sandbox']['entities'])); -+ for ($i = 1; $i <= $count; $i++) { -+ // For each ID, load the entity, reset the values, and save it. -+ $entity_id = array_shift($context['sandbox']['entities']); -+ /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ -+ $entity = $this->entityType->isRevisionable() -+ ? $this->storage->loadRevision($entity_id) -+ : $this->storage->load($entity_id); -+ $this->updateEntity($entity, $method); -+ -+ // Store result for post-processing in the finished callback. -+ $context['results'][] = $entity->toLink(); -+ -+ // Update our progress information. -+ $context['sandbox']['progress']++; -+ } -+ -+ // Inform the batch engine that we are not finished, and provide an -+ // estimation of the completion level we reached. -+ if ($context['sandbox']['progress'] != $context['sandbox']['max']) { -+ $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max']; -+ } -+ } -+ -+ /** -+ * Static wrapper called when the batch operation is finished. -+ * -+ * @param bool $success -+ * A boolean indicating whether the batch mass update operation successfully -+ * concluded. -+ * @param string[] $results -+ * An array of rendered links to entities updated via the batch mode -+ * process. -+ * @param array $operations -+ * An array of function calls. -+ */ -+ public static function batchFinished(bool $success, array $results, array $operations): void { -+ static::getInstance($results['_entity_type_id']) -+ ->doFinish($success, $results, $operations); -+ } -+ -+ /** -+ * Implements callback_batch_finished(). -+ * -+ * Reports the 'finished' status of the batch mass update operation. -+ * -+ * @param bool $success -+ * A boolean indicating whether the batch mass update operation successfully -+ * concluded. -+ * @param string[] $results -+ * An array of rendered links to entities updated via the batch mode -+ * process. -+ * @param array $operations -+ * An array of function calls (not used in this function). -+ */ -+ protected function doFinish(bool $success, array $results, array $operations): void { -+ if ($success) { -+ $this->messenger()->addStatus($this->t('The update has been performed.')); -+ } -+ else { -+ $this->messenger()->addError($this->t('An error occurred and processing did not complete.')); -+ $message = $this->formatPlural(count($results), '1 item successfully processed:', '@count items successfully processed:'); -+ unset($results['_entity_type_id']); -+ $item_list = [ -+ '#theme' => 'item_list', -+ '#items' => $results, -+ ]; -+ $message .= $this->renderer->render($item_list); -+ $this->messenger()->addStatus($message); -+ } -+ } -+ -+} -diff --git a/core/modules/user/src/Entity/Handler/DefaultCancellationHandler.php b/core/modules/user/src/Entity/Handler/DefaultCancellationHandler.php -new file mode 100644 -index 0000000000..8626e731b6 ---- /dev/null -+++ b/core/modules/user/src/Entity/Handler/DefaultCancellationHandler.php -@@ -0,0 +1,185 @@ -+<?php -+ -+namespace Drupal\user\Entity\Handler; -+ -+use Drupal\Core\Entity\ContentEntityInterface; -+use Drupal\Core\Entity\ContentEntityStorageInterface; -+use Drupal\Core\Entity\ContentEntityTypeInterface; -+use Drupal\Core\Entity\EntityHandlerInterface; -+use Drupal\Core\Entity\EntityPublishedInterface; -+use Drupal\Core\Entity\EntityTypeInterface; -+use Drupal\Core\Entity\Query\QueryInterface; -+use Drupal\Core\Entity\RevisionLogInterface; -+use Drupal\Core\Messenger\MessengerTrait; -+use Drupal\Core\StringTranslation\StringTranslationTrait; -+use Drupal\user\CancellationHandlerInterface; -+use Drupal\user\EntityOwnerInterface; -+use Drupal\user\UserInterface; -+use Symfony\Component\DependencyInjection\ContainerInterface; -+ -+/** -+ * Defines an entity handler to react to user account cancellation. -+ */ -+class DefaultCancellationHandler implements CancellationHandlerInterface, EntityHandlerInterface { -+ -+ use MessengerTrait; -+ use StringTranslationTrait; -+ -+ /** -+ * The entity type definition. -+ * -+ * @var \Drupal\Core\Entity\ContentEntityTypeInterface -+ */ -+ protected $entityType; -+ -+ /** -+ * The entity storage handler. -+ * -+ * @var \Drupal\Core\Entity\ContentEntityStorageInterface -+ */ -+ protected $storage; -+ -+ /** -+ * DefaultCancellationHandler constructor. -+ * -+ * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type -+ * The entity type definition. -+ * @param \Drupal\Core\Entity\ContentEntityStorageInterface $storage -+ * The entity storage handler. -+ */ -+ public function __construct(ContentEntityTypeInterface $entity_type, ContentEntityStorageInterface $storage) { -+ $this->entityType = $entity_type; -+ $this->storage = $storage; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { -+ return new static( -+ $entity_type, -+ $container->get('entity_type.manager')->getStorage($entity_type->id()) -+ ); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function cancelAccount(UserInterface $account, string $method): void { -+ $ids = $this->getQuery($account, $method)->execute(); -+ $this->updateMultiple(array_keys($ids), $method); -+ } -+ -+ /** -+ * Begins updating entities in response to user account cancellation. -+ * -+ * @param array $ids -+ * The entity IDs, or revision IDs, to update. -+ * @param string $method -+ * The cancellation method. -+ */ -+ protected function updateMultiple(array $ids, string $method): void { -+ $entities = $this->entityType->isRevisionable() -+ ? $this->storage->loadMultipleRevisions($ids) -+ : $this->storage->loadMultiple($ids); -+ -+ /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ -+ foreach ($entities as $entity) { -+ $this->updateEntity($entity, $method); -+ } -+ $this->messenger()->addStatus($this->t('The update has been performed.')); -+ } -+ -+ /** -+ * Builds an entity query to find the entities to update. -+ * -+ * @param \Drupal\user\UserInterface $account -+ * The account being cancelled. -+ * @param string $method -+ * The cancellation method. -+ * -+ * @return \Drupal\Core\Entity\Query\QueryInterface -+ * The entity query. -+ */ -+ protected function getQuery(UserInterface $account, string $method): QueryInterface { -+ $query = $this->storage->getQuery() -+ ->accessCheck(FALSE) -+ ->condition($this->entityType->getKey('owner'), $account->id()); -+ -+ if (($method === self::METHOD_REASSIGN || $method === self::METHOD_DELETE) && $this->entityType->isRevisionable()) { -+ $query->allRevisions(); -+ } -+ return $query; -+ } -+ -+ /** -+ * Updates a single entity. -+ * -+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity -+ * The entity to update. -+ * @param string $method -+ * The user account cancellation method. -+ */ -+ protected function updateEntity(ContentEntityInterface $entity, string $method): void { -+ if ($method === self::METHOD_DELETE) { -+ if ($this->entityType->isRevisionable() && !$entity->isDefaultRevision()) { -+ $this->storage->deleteRevision($entity->getRevisionId()); -+ } -+ else { -+ $this->storage->delete([$entity]); -+ } -+ return; -+ } -+ -+ // For efficiency, manually save the original entity before applying any -+ // changes. -+ $entity->original = clone $entity; -+ -+ // Update each translation individually. -+ $langcodes = array_keys($entity->getTranslationLanguages()); -+ foreach ($langcodes as $langcode) { -+ $translation = $entity->getTranslation($langcode); -+ -+ if ($method === self::METHOD_BLOCK_UNPUBLISH) { -+ $this->unpublish($translation); -+ } -+ elseif ($method === self::METHOD_REASSIGN) { -+ $this->anonymize($translation); -+ } -+ } -+ $this->storage->save($entity); -+ } -+ -+ /** -+ * Marks an entity as unpublished. -+ * -+ * Normally this is done for the self::METHOD_BLOCK_UNPUBLISH cancellation -+ * method. -+ * -+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity -+ * The entity to unpublish. -+ */ -+ protected function unpublish(ContentEntityInterface $entity): void { -+ if ($entity instanceof EntityPublishedInterface) { -+ $entity->setUnpublished(); -+ } -+ } -+ -+ /** -+ * Reassigns an entity to the anonymous user. -+ * -+ * Normally this is done for the self::METHOD_REASSIGN cancellation method. -+ * -+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity -+ * The entity to reassign to the anonymous user. -+ */ -+ protected function anonymize(ContentEntityInterface $entity): void { -+ if ($entity instanceof EntityOwnerInterface) { -+ $entity->setOwnerId(0); -+ } -+ if ($entity instanceof RevisionLogInterface) { -+ $entity->setRevisionUserId(0); -+ } -+ } -+ -+} -diff --git a/core/modules/user/tests/src/Traits/UserCancellationTrait.php b/core/modules/user/tests/src/Traits/UserCancellationTrait.php -new file mode 100644 -index 0000000000..d97c8ac5ac ---- /dev/null -+++ b/core/modules/user/tests/src/Traits/UserCancellationTrait.php -@@ -0,0 +1,34 @@ -+<?php -+ -+namespace Drupal\Tests\user\Traits; -+ -+use Drupal\user\UserInterface; -+ -+/** -+ * Provides methods to cancel user accounts in the UI. -+ */ -+trait UserCancellationTrait { -+ -+ /** -+ * Cancels a user account via the UI. -+ * -+ * @param \Drupal\user\UserInterface $user -+ * The account to cancel. -+ * @param string $method -+ * The cancellation method. Should be one of the METHOD_* constants of -+ * \Drupal\user\CancellationHandlerInterface. -+ */ -+ protected function cancelUser(UserInterface $user, string $method): void { -+ $page = $this->getSession()->getPage(); -+ -+ $this->drupalGet('/admin/people'); -+ $page->checkField('Update the user ' . $user->getDisplayName()); -+ $page->selectFieldOption('action', 'user_cancel_user_action'); -+ $page->pressButton('Apply to selected items'); -+ $page->selectFieldOption('user_cancel_method', $method); -+ $page->pressButton('Cancel accounts'); -+ $this->checkForMetaRefresh(); -+ $this->assertSession()->pageTextContains('The update has been performed.'); -+ } -+ -+} -diff --git a/core/modules/user/user.module b/core/modules/user/user.module -index 76209b00ec..f4f49bd570 100644 ---- a/core/modules/user/user.module -+++ b/core/modules/user/user.module -@@ -24,6 +24,7 @@ - use Drupal\image\Plugin\Field\FieldType\ImageItem; - use Drupal\filter\FilterFormatInterface; - use Drupal\system\Entity\Action; -+use Drupal\user\CancellationHandlerInterface; - use Drupal\user\Entity\Role; - use Drupal\user\Entity\User; - use Drupal\user\RoleInterface; -@@ -622,6 +623,14 @@ function user_cancel($edit, $uid, $method) { - ->setTitle(t('Cancelling account')); - batch_set($batch_builder->toArray()); - -+ $entity_type_manager = \Drupal::entityTypeManager(); -+ foreach ($entity_type_manager->getDefinitions() as $entity_type) { -+ if ($entity_type->hasHandlerClass('user_cancel')) { -+ $entity_type_manager->getHandler($entity_type->id(), 'user_cancel') -+ ->cancelAccount($account, $method); -+ } -+ } -+ - // When the 'user_cancel_delete' method is used, user_delete() is called, - // which invokes hook_ENTITY_TYPE_predelete() and hook_ENTITY_TYPE_delete() - // for the user entity. Modules should use those hooks to respond to the -@@ -731,19 +740,19 @@ function user_cancel_methods() { - $user_settings = \Drupal::config('user.settings'); - $anonymous_name = $user_settings->get('anonymous'); - $methods = [ -- 'user_cancel_block' => [ -+ CancellationHandlerInterface::METHOD_BLOCK => [ - 'title' => t('Disable the account and keep its content.'), - 'description' => t('Your account will be blocked and you will no longer be able to log in. All of your content will remain attributed to your username.'), - ], -- 'user_cancel_block_unpublish' => [ -+ CancellationHandlerInterface::METHOD_BLOCK_UNPUBLISH => [ - 'title' => t('Disable the account and unpublish its content.'), - 'description' => t('Your account will be blocked and you will no longer be able to log in. All of your content will be hidden from everyone but administrators.'), - ], -- 'user_cancel_reassign' => [ -+ CancellationHandlerInterface::METHOD_REASSIGN => [ - 'title' => t('Delete the account and make its content belong to the %anonymous-name user. This action cannot be undone.', ['%anonymous-name' => $anonymous_name]), - 'description' => t('Your account will be removed and all account information deleted. All of your content will be assigned to the %anonymous-name user.', ['%anonymous-name' => $anonymous_name]), - ], -- 'user_cancel_delete' => [ -+ CancellationHandlerInterface::METHOD_DELETE => [ - 'title' => t('Delete the account and its content. This action cannot be undone.'), - 'description' => t('Your account will be removed and all account information deleted. All of your content will also be deleted.'), - 'access' => \Drupal::currentUser()->hasPermission('administer users'), -diff --git a/core/modules/workspaces/src/Entity/Workspace.php b/core/modules/workspaces/src/Entity/Workspace.php -index 1e16282ed1..5fb32deaaa 100644 ---- a/core/modules/workspaces/src/Entity/Workspace.php -+++ b/core/modules/workspaces/src/Entity/Workspace.php -@@ -40,6 +40,7 @@ - * "activate" = "\Drupal\workspaces\Form\WorkspaceActivateForm", - * }, - * "workspace" = "\Drupal\workspaces\Entity\Handler\IgnoredWorkspaceHandler", -+ * "user_cancel" = "\Drupal\user\Entity\Handler\BatchCancellationHandler", - * }, - * admin_permission = "administer workspaces", - * base_table = "workspace", diff --git a/patches/d10/3043879-new.diff b/patches/d10/3043879-new.diff deleted file mode 100644 index 28ddd4fbd58764dc2c19df2c9c5ed02037a489ae..0000000000000000000000000000000000000000 --- a/patches/d10/3043879-new.diff +++ /dev/null @@ -1,251 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Layout/LayoutDefault.php b/core/lib/Drupal/Core/Layout/LayoutDefault.php -index bd3780987a..8fc99d76ea 100644 ---- a/core/lib/Drupal/Core/Layout/LayoutDefault.php -+++ b/core/lib/Drupal/Core/Layout/LayoutDefault.php -@@ -8,6 +8,7 @@ - use Drupal\Core\Plugin\ContextAwarePluginTrait; - use Drupal\Core\Plugin\PluginBase; - use Drupal\Core\Plugin\PluginFormInterface; -+use Drupal\Core\Plugin\PluginWithFormsTrait; - use Drupal\Core\Plugin\PreviewAwarePluginInterface; - - /** -@@ -17,6 +18,7 @@ class LayoutDefault extends PluginBase implements LayoutInterface, PluginFormInt - - use ContextAwarePluginAssignmentTrait; - use ContextAwarePluginTrait; -+ use PluginWithFormsTrait; - - /** - * Whether the plugin is being rendered in preview mode. -@@ -127,6 +129,13 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s - $this->configuration['label'] = $form_state->getValue('label'); - } - -+ /** -+ * {@inheritdoc} -+ */ -+ public function getFormClass($operation) { -+ return $this->getPluginDefinition()->get('forms')[$operation] ?? static::class; -+ } -+ - /** - * {@inheritdoc} - */ -diff --git a/core/lib/Drupal/Core/Layout/LayoutInterface.php b/core/lib/Drupal/Core/Layout/LayoutInterface.php -index 33858a546d..2e62ba85da 100644 ---- a/core/lib/Drupal/Core/Layout/LayoutInterface.php -+++ b/core/lib/Drupal/Core/Layout/LayoutInterface.php -@@ -7,11 +7,12 @@ - use Drupal\Component\Plugin\ConfigurableInterface; - use Drupal\Component\Plugin\DependentPluginInterface; - use Drupal\Core\Plugin\ContextAwarePluginInterface; -+use Drupal\Core\Plugin\PluginWithFormsInterface; - - /** - * Provides an interface for static Layout plugins. - */ --interface LayoutInterface extends PluginInspectionInterface, DerivativeInspectionInterface, ConfigurableInterface, DependentPluginInterface, ContextAwarePluginInterface { -+interface LayoutInterface extends PluginInspectionInterface, DerivativeInspectionInterface, ConfigurableInterface, DependentPluginInterface, ContextAwarePluginInterface, PluginWithFormsInterface { - - /** - * Build a render array for layout with regions. -diff --git a/core/modules/layout_builder/src/Controller/ChooseSectionController.php b/core/modules/layout_builder/src/Controller/ChooseSectionController.php -index 46b969e2f8..bc22d1f004 100644 ---- a/core/modules/layout_builder/src/Controller/ChooseSectionController.php -+++ b/core/modules/layout_builder/src/Controller/ChooseSectionController.php -@@ -4,8 +4,10 @@ - - use Drupal\Core\Ajax\AjaxHelperTrait; - use Drupal\Core\DependencyInjection\ContainerInjectionInterface; -+use Drupal\Core\Layout\LayoutInterface; - use Drupal\Core\Layout\LayoutPluginManagerInterface; - use Drupal\Core\Plugin\PluginFormInterface; -+use Drupal\Core\Plugin\PluginWithFormsInterface; - use Drupal\Core\StringTranslation\StringTranslationTrait; - use Drupal\Core\Url; - use Drupal\layout_builder\Context\LayoutBuilderContextTrait; -@@ -52,6 +54,23 @@ public static function create(ContainerInterface $container) { - ); - } - -+ /** -+ * Determines if the layout provides a configuration form. -+ * -+ * @param \Drupal\Core\Layout\LayoutInterface $layout -+ * The layout plugin. -+ * -+ * @return bool -+ * TRUE if the layout has a configure form, FALSE otherwise. -+ */ -+ protected function hasConfigurationForm(LayoutInterface $layout) { -+ if ($layout instanceof PluginWithFormsInterface && $layout->hasFormClass('configure')) { -+ return TRUE; -+ } -+ -+ return $layout instanceof PluginFormInterface; -+ } -+ - /** - * Choose a layout plugin to add as a section. - * -@@ -78,7 +97,7 @@ public function build(SectionStorageInterface $section_storage, int $delta) { - ], - ], - '#url' => Url::fromRoute( -- $layout instanceof PluginFormInterface ? 'layout_builder.configure_section' : 'layout_builder.add_section', -+ $this->hasConfigurationForm($layout) ? 'layout_builder.configure_section' : 'layout_builder.add_section', - [ - 'section_storage_type' => $section_storage->getStorageType(), - 'section_storage' => $section_storage->getStorageId(), -diff --git a/core/modules/layout_builder/src/Element/LayoutBuilder.php b/core/modules/layout_builder/src/Element/LayoutBuilder.php -index 451927e540..8234a476ab 100644 ---- a/core/modules/layout_builder/src/Element/LayoutBuilder.php -+++ b/core/modules/layout_builder/src/Element/LayoutBuilder.php -@@ -3,8 +3,10 @@ - namespace Drupal\layout_builder\Element; - - use Drupal\Core\Ajax\AjaxHelperTrait; -+use Drupal\Core\Layout\LayoutInterface; - use Drupal\Core\Plugin\ContainerFactoryPluginInterface; - use Drupal\Core\Plugin\PluginFormInterface; -+use Drupal\Core\Plugin\PluginWithFormsInterface; - use Drupal\Core\Render\Attribute\RenderElement; - use Drupal\Core\Render\Element; - use Drupal\Core\Render\Element\RenderElement as RenderElementBase; -@@ -325,6 +327,7 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s - $build['#attributes']['class'][] = 'layout-builder__layout'; - $build['#attributes']['data-layout-builder-highlight-id'] = $this->sectionUpdateHighlightId($delta); - -+ $has_configure_form = $this->hasConfigurationForm($layout); - return [ - '#type' => 'container', - '#attributes' => [ -@@ -354,12 +357,12 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s - // link, and is only visible when the move block dialog is open. - 'section_label' => [ - '#markup' => $this->t('<span class="layout-builder__section-label" aria-hidden="true">@section</span>', ['@section' => $section_label]), -- '#access' => !$layout instanceof PluginFormInterface, -+ '#access' => !$has_configure_form, - ], - 'configure' => [ - '#type' => 'link', - '#title' => $this->t('Configure @section', ['@section' => $section_label]), -- '#access' => $layout instanceof PluginFormInterface, -+ '#access' => $has_configure_form, - '#url' => Url::fromRoute('layout_builder.configure_section', [ - 'section_storage_type' => $storage_type, - 'section_storage' => $storage_id, -@@ -379,4 +382,21 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s - ]; - } - -+ /** -+ * Determines if the layout provides a configuration form. -+ * -+ * @param \Drupal\Core\Layout\LayoutInterface $layout -+ * The layout plugin. -+ * -+ * @return bool -+ * TRUE if the layout has a configure form, FALSE otherwise. -+ */ -+ protected function hasConfigurationForm(LayoutInterface $layout) { -+ if ($layout instanceof PluginWithFormsInterface && $layout->hasFormClass('configure')) { -+ return TRUE; -+ } -+ -+ return $layout instanceof PluginFormInterface; -+ } -+ - } -diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module -index fa027a6b01..443bbcfdfa 100644 ---- a/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module -+++ b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module -@@ -13,6 +13,7 @@ - use Drupal\Core\Link; - use Drupal\Core\Routing\RouteMatchInterface; - use Drupal\Core\Url; -+use Drupal\layout_builder_test\PluginForm\CustomLayoutForm; - - /** - * Implements hook_plugin_filter_TYPE__CONSUMER_alter(). -@@ -177,6 +178,27 @@ function layout_builder_test_module_implements_alter(&$implementations, $hook) { - } - } - -+/** -+ * Implements hook_layout_alter(). -+ */ -+function layout_builder_test_layout_alter(&$definitions) { -+ /** @var \Drupal\Core\Layout\LayoutDefinition[] $definitions */ -+ $forms['configure'] = CustomLayoutForm::class; -+ $definitions['layout_builder_test_no_form_plugin']->set('forms', $forms); -+ if (isset($definitions['layout_test_plugin'])) { -+ $definitions['layout_test_plugin']->set('forms', $forms); -+ } -+} -+ -+/** -+ * Implements hook_preprocess_HOOK() for layout templates. -+ */ -+function layout_builder_test_preprocess_layout(&$variables) { -+ if (isset($variables['settings']['custom_element'])) { -+ $variables['content']['main']['custom_element']['#markup'] = $variables['settings']['custom_element']; -+ } -+} -+ - /** - * Implements hook_theme(). - */ -diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php -index 631159a8b3..24f0ff05e7 100644 ---- a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php -+++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php -@@ -458,6 +458,46 @@ public function testPreviewAwareTemplates() { - $assert_session->pageTextNotContains('This is a preview, indeed'); - } - -+ /** -+ * Tests a layout with a custom form. -+ */ -+ public function testCustomForm() { -+ $assert_session = $this->assertSession(); -+ $page = $this->getSession()->getPage(); -+ -+ $this->drupalLogin($this->drupalCreateUser(['configure any layout'])); -+ -+ LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default') -+ ->enableLayoutBuilder() -+ ->setOverridable() -+ ->save(); -+ -+ $this->drupalGet('node/1'); -+ $page->clickLink('Layout'); -+ -+ // Test a custom plugin form that is specified by the plugin annotation. -+ $page->clickLink('Add section'); -+ $page->clickLink('Layout Builder Test Custom Form Plugin'); -+ $page->fillField('layout_settings[custom_element]', 'This is a custom form specified by the plugin'); -+ $page->pressButton('Add section'); -+ $assert_session->pageTextContains('This is a custom form specified by the plugin'); -+ -+ // Test a custom plugin form that is altered onto a plugin that has no form. -+ $page->clickLink('Add section'); -+ $page->clickLink('Layout Builder Test No Form Plugin'); -+ $page->fillField('layout_settings[custom_element]', 'This had no form now it does'); -+ $page->pressButton('Add section'); -+ $assert_session->pageTextContains('This had no form now it does'); -+ -+ // Test a custom plugin form that is altered onto a plugin that has an -+ // existing form. -+ $page->clickLink('Add section'); -+ $page->clickLink('Layout Builder Test No Form Plugin'); -+ $page->fillField('layout_settings[custom_element]', 'This had an existing form now it has more'); -+ $page->pressButton('Add section'); -+ $assert_session->pageTextContains('This had an existing form now it has more'); -+ } -+ - /** - * Tests that extra fields work before and after enabling Layout Builder. - */ diff --git a/patches/d10/3043879.diff b/patches/d10/3043879.diff deleted file mode 100644 index 44e08cc986b3dcf653bb1b0c39348b7c20e7f0a4..0000000000000000000000000000000000000000 --- a/patches/d10/3043879.diff +++ /dev/null @@ -1,413 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Layout/LayoutDefault.php b/core/lib/Drupal/Core/Layout/LayoutDefault.php -index 607b34a48cd91bf740f3e6eda563a8de8b659467..bb97f8b42ffd61d185ed25eab2d91b12dc91c3d5 100644 ---- a/core/lib/Drupal/Core/Layout/LayoutDefault.php -+++ b/core/lib/Drupal/Core/Layout/LayoutDefault.php -@@ -8,6 +8,7 @@ - use Drupal\Core\Plugin\ContextAwarePluginTrait; - use Drupal\Core\Plugin\PluginBase; - use Drupal\Core\Plugin\PluginFormInterface; -+use Drupal\Core\Plugin\PluginWithFormsTrait; - use Drupal\Core\Plugin\PreviewAwarePluginInterface; - - /** -@@ -17,6 +18,7 @@ class LayoutDefault extends PluginBase implements LayoutInterface, PluginFormInt - - use ContextAwarePluginAssignmentTrait; - use ContextAwarePluginTrait; -+ use PluginWithFormsTrait; - - /** - * Whether the plugin is being rendered in preview mode. -@@ -126,6 +128,13 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s - $this->configuration['label'] = $form_state->getValue('label'); - } - -+ /** -+ * {@inheritdoc} -+ */ -+ public function getFormClass($operation) { -+ return $this->getPluginDefinition()->get('forms')[$operation] ?? static::class; -+ } -+ - /** - * {@inheritdoc} - */ -diff --git a/core/lib/Drupal/Core/Layout/LayoutInterface.php b/core/lib/Drupal/Core/Layout/LayoutInterface.php -index 33858a546d7fc0f2696c408873f4b9bf51cb9b7c..2e62ba85da65ed7029abd5df9571348e9bfcd7fa 100644 ---- a/core/lib/Drupal/Core/Layout/LayoutInterface.php -+++ b/core/lib/Drupal/Core/Layout/LayoutInterface.php -@@ -7,11 +7,12 @@ - use Drupal\Component\Plugin\ConfigurableInterface; - use Drupal\Component\Plugin\DependentPluginInterface; - use Drupal\Core\Plugin\ContextAwarePluginInterface; -+use Drupal\Core\Plugin\PluginWithFormsInterface; - - /** - * Provides an interface for static Layout plugins. - */ --interface LayoutInterface extends PluginInspectionInterface, DerivativeInspectionInterface, ConfigurableInterface, DependentPluginInterface, ContextAwarePluginInterface { -+interface LayoutInterface extends PluginInspectionInterface, DerivativeInspectionInterface, ConfigurableInterface, DependentPluginInterface, ContextAwarePluginInterface, PluginWithFormsInterface { - - /** - * Build a render array for layout with regions. -diff --git a/core/modules/layout_builder/src/Controller/ChooseSectionController.php b/core/modules/layout_builder/src/Controller/ChooseSectionController.php -index 46b969e2f8ba15a75da1be3b0f9a3bb71f30e138..bc22d1f004b7a828155a811758026e7d631a1ed9 100644 ---- a/core/modules/layout_builder/src/Controller/ChooseSectionController.php -+++ b/core/modules/layout_builder/src/Controller/ChooseSectionController.php -@@ -4,8 +4,10 @@ - - use Drupal\Core\Ajax\AjaxHelperTrait; - use Drupal\Core\DependencyInjection\ContainerInjectionInterface; -+use Drupal\Core\Layout\LayoutInterface; - use Drupal\Core\Layout\LayoutPluginManagerInterface; - use Drupal\Core\Plugin\PluginFormInterface; -+use Drupal\Core\Plugin\PluginWithFormsInterface; - use Drupal\Core\StringTranslation\StringTranslationTrait; - use Drupal\Core\Url; - use Drupal\layout_builder\Context\LayoutBuilderContextTrait; -@@ -52,6 +54,23 @@ public static function create(ContainerInterface $container) { - ); - } - -+ /** -+ * Determines if the layout provides a configuration form. -+ * -+ * @param \Drupal\Core\Layout\LayoutInterface $layout -+ * The layout plugin. -+ * -+ * @return bool -+ * TRUE if the layout has a configure form, FALSE otherwise. -+ */ -+ protected function hasConfigurationForm(LayoutInterface $layout) { -+ if ($layout instanceof PluginWithFormsInterface && $layout->hasFormClass('configure')) { -+ return TRUE; -+ } -+ -+ return $layout instanceof PluginFormInterface; -+ } -+ - /** - * Choose a layout plugin to add as a section. - * -@@ -78,7 +97,7 @@ public function build(SectionStorageInterface $section_storage, int $delta) { - ], - ], - '#url' => Url::fromRoute( -- $layout instanceof PluginFormInterface ? 'layout_builder.configure_section' : 'layout_builder.add_section', -+ $this->hasConfigurationForm($layout) ? 'layout_builder.configure_section' : 'layout_builder.add_section', - [ - 'section_storage_type' => $section_storage->getStorageType(), - 'section_storage' => $section_storage->getStorageId(), -diff --git a/core/modules/layout_builder/src/Element/LayoutBuilder.php b/core/modules/layout_builder/src/Element/LayoutBuilder.php -index 5c91b1e39208a7895c6cb664c1f5e793c8d9522c..e8c83d975fd37c8c80af9f97b0fd47c942f1f615 100644 ---- a/core/modules/layout_builder/src/Element/LayoutBuilder.php -+++ b/core/modules/layout_builder/src/Element/LayoutBuilder.php -@@ -3,8 +3,10 @@ - namespace Drupal\layout_builder\Element; - - use Drupal\Core\Ajax\AjaxHelperTrait; -+use Drupal\Core\Layout\LayoutInterface; - use Drupal\Core\Plugin\ContainerFactoryPluginInterface; - use Drupal\Core\Plugin\PluginFormInterface; -+use Drupal\Core\Plugin\PluginWithFormsInterface; - use Drupal\Core\Render\Element; - use Drupal\Core\Render\Element\RenderElement; - use Drupal\Core\Url; -@@ -337,6 +339,7 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s - $build['#attributes']['class'][] = 'layout-builder__layout'; - $build['#attributes']['data-layout-builder-highlight-id'] = $this->sectionUpdateHighlightId($delta); - -+ $has_configure_form = $this->hasConfigurationForm($layout); - return [ - '#type' => 'container', - '#attributes' => [ -@@ -366,12 +369,12 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s - // link, and is only visible when the move block dialog is open. - 'section_label' => [ - '#markup' => $this->t('<span class="layout-builder__section-label" aria-hidden="true">@section</span>', ['@section' => $section_label]), -- '#access' => !$layout instanceof PluginFormInterface, -+ '#access' => !$has_configure_form, - ], - 'configure' => [ - '#type' => 'link', - '#title' => $this->t('Configure @section', ['@section' => $section_label]), -- '#access' => $layout instanceof PluginFormInterface, -+ '#access' => $has_configure_form, - '#url' => Url::fromRoute('layout_builder.configure_section', [ - 'section_storage_type' => $storage_type, - 'section_storage' => $storage_id, -@@ -391,4 +394,21 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s - ]; - } - -+ /** -+ * Determines if the layout provides a configuration form. -+ * -+ * @param \Drupal\Core\Layout\LayoutInterface $layout -+ * The layout plugin. -+ * -+ * @return bool -+ * TRUE if the layout has a configure form, FALSE otherwise. -+ */ -+ protected function hasConfigurationForm(LayoutInterface $layout) { -+ if ($layout instanceof PluginWithFormsInterface && $layout->hasFormClass('configure')) { -+ return TRUE; -+ } -+ -+ return $layout instanceof PluginFormInterface; -+ } -+ - } -diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module -index cd6070f53ff33d4f20ef609513ff335c57b63c60..8b6b00419be68eb7d48072f6e0d223f25f9c400c 100644 ---- a/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module -+++ b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module -@@ -13,6 +13,7 @@ - use Drupal\Core\Link; - use Drupal\Core\Routing\RouteMatchInterface; - use Drupal\Core\Url; -+use Drupal\layout_builder_test\PluginForm\CustomLayoutForm; - - /** - * Implements hook_plugin_filter_TYPE__CONSUMER_alter(). -@@ -131,3 +132,24 @@ function layout_builder_test_module_implements_alter(&$implementations, $hook) { - ] + $implementations; - } - } -+ -+/** -+ * Implements hook_layout_alter(). -+ */ -+function layout_builder_test_layout_alter(&$definitions) { -+ /** @var \Drupal\Core\Layout\LayoutDefinition[] $definitions */ -+ $forms['configure'] = CustomLayoutForm::class; -+ $definitions['layout_builder_test_no_form_plugin']->set('forms', $forms); -+ if (isset($definitions['layout_test_plugin'])) { -+ $definitions['layout_test_plugin']->set('forms', $forms); -+ } -+} -+ -+/** -+ * Implements hook_preprocess_HOOK() for layout templates. -+ */ -+function layout_builder_test_preprocess_layout(&$variables) { -+ if (isset($variables['settings']['custom_element'])) { -+ $variables['content']['main']['custom_element']['#markup'] = $variables['settings']['custom_element']; -+ } -+} -diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/Layout/LayoutBuilderTestCustomFormPlugin.php b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/Layout/LayoutBuilderTestCustomFormPlugin.php -new file mode 100644 -index 0000000000000000000000000000000000000000..059a3eb4d8847695d1cbc02fee806c72b9cf94d6 ---- /dev/null -+++ b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/Layout/LayoutBuilderTestCustomFormPlugin.php -@@ -0,0 +1,23 @@ -+<?php -+ -+namespace Drupal\layout_builder_test\Plugin\Layout; -+ -+use Drupal\Core\Layout\LayoutDefault; -+ -+/** -+ * @Layout( -+ * id = "layout_builder_test_custom_form_plugin", -+ * label = @Translation("Layout Builder Test Custom Form Plugin"), -+ * regions = { -+ * "main" = { -+ * "label" = @Translation("Main Region") -+ * } -+ * }, -+ * forms = { -+ * "configure" = "Drupal\layout_builder_test\PluginForm\CustomLayoutForm", -+ * }, -+ * ) -+ */ -+class LayoutBuilderTestCustomFormPlugin extends LayoutDefault { -+ -+} -diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/Layout/LayoutBuilderTestNoFormPlugin.php b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/Layout/LayoutBuilderTestNoFormPlugin.php -new file mode 100644 -index 0000000000000000000000000000000000000000..4cef17f51f56591ab331a465274c6fc8e241adc3 ---- /dev/null -+++ b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/Layout/LayoutBuilderTestNoFormPlugin.php -@@ -0,0 +1,75 @@ -+<?php -+ -+namespace Drupal\layout_builder_test\Plugin\Layout; -+ -+use Drupal\Component\Utility\NestedArray; -+use Drupal\Core\Layout\LayoutInterface; -+use Drupal\Core\Plugin\PluginBase; -+use Drupal\Core\Plugin\PluginWithFormsTrait; -+ -+/** -+ * Provides a plugin that does not extend \Drupal\Core\Layout\LayoutDefault. -+ * -+ * @Layout( -+ * id = "layout_builder_test_no_form_plugin", -+ * label = @Translation("Layout Builder Test No Form Plugin"), -+ * regions = { -+ * "main" = { -+ * "label" = @Translation("Main Region") -+ * } -+ * }, -+ * ) -+ */ -+class LayoutBuilderTestNoFormPlugin extends PluginBase implements LayoutInterface { -+ -+ use PluginWithFormsTrait; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function build(array $regions) { -+ $build = $regions; -+ $build['#settings'] = $this->getConfiguration(); -+ $build['#layout'] = $this->pluginDefinition; -+ $build['#theme'] = $this->pluginDefinition->getThemeHook(); -+ return $build; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function getConfiguration() { -+ return $this->configuration; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function setConfiguration(array $configuration) { -+ $this->configuration = NestedArray::mergeDeep($this->defaultConfiguration(), $configuration); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function defaultConfiguration() { -+ return [ -+ 'label' => '', -+ ]; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function calculateDependencies() { -+ return []; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function getFormClass($operation) { -+ return $this->getPluginDefinition()->get('forms')[$operation] ?? NULL; -+ } -+ -+} -diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/src/PluginForm/CustomLayoutForm.php b/core/modules/layout_builder/tests/modules/layout_builder_test/src/PluginForm/CustomLayoutForm.php -new file mode 100644 -index 0000000000000000000000000000000000000000..8499f8bde1faa443b4d8220f50599d92c43291c2 ---- /dev/null -+++ b/core/modules/layout_builder/tests/modules/layout_builder_test/src/PluginForm/CustomLayoutForm.php -@@ -0,0 +1,49 @@ -+<?php -+ -+namespace Drupal\layout_builder_test\PluginForm; -+ -+use Drupal\Core\Form\FormStateInterface; -+use Drupal\Core\Plugin\PluginFormBase; -+use Drupal\Core\Plugin\PluginFormInterface; -+use Drupal\Core\StringTranslation\StringTranslationTrait; -+ -+/** -+ * Provides a custom form for a layout plugin. -+ */ -+class CustomLayoutForm extends PluginFormBase { -+ -+ use StringTranslationTrait; -+ -+ /** -+ * The plugin. -+ * -+ * @var \Drupal\Core\Layout\LayoutInterface -+ */ -+ protected $plugin; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { -+ $form = $this->plugin instanceof PluginFormInterface ? $this->plugin->buildConfigurationForm($form, $form_state) : []; -+ $form['custom_element'] = [ -+ '#title' => $this->t('Custom element'), -+ '#type' => 'textfield', -+ '#default_value' => $this->plugin->getConfiguration()['custom_element'] ?? '', -+ ]; -+ return $form; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { -+ if ($this->plugin instanceof PluginFormInterface) { -+ $this->plugin->submitConfigurationForm($form, $form_state); -+ } -+ $configuration = $this->plugin->getConfiguration(); -+ $configuration['custom_element'] = $form_state->getValue('custom_element'); -+ $this->plugin->setConfiguration($configuration); -+ } -+ -+} -diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php -index f135ac4cb17ca6155dde107c2a04205677e82a5d..ec692e9b45cd777fdc324a366aaf7a89f6614a68 100644 ---- a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php -+++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php -@@ -963,6 +963,46 @@ public function testLayoutBuilderChooseBlocksAlter() { - $assert_session->linkExists('Changed'); - } - -+ /** -+ * Tests a layout with a custom form. -+ */ -+ public function testCustomForm() { -+ $assert_session = $this->assertSession(); -+ $page = $this->getSession()->getPage(); -+ -+ $this->drupalLogin($this->drupalCreateUser(['configure any layout'])); -+ -+ LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default') -+ ->enableLayoutBuilder() -+ ->setOverridable() -+ ->save(); -+ -+ $this->drupalGet('node/1'); -+ $page->clickLink('Layout'); -+ -+ // Test a custom plugin form that is specified by the plugin annotation. -+ $page->clickLink('Add section'); -+ $page->clickLink('Layout Builder Test Custom Form Plugin'); -+ $page->fillField('layout_settings[custom_element]', 'This is a custom form specified by the plugin'); -+ $page->pressButton('Add section'); -+ $assert_session->pageTextContains('This is a custom form specified by the plugin'); -+ -+ // Test a custom plugin form that is altered onto a plugin that has no form. -+ $page->clickLink('Add section'); -+ $page->clickLink('Layout Builder Test No Form Plugin'); -+ $page->fillField('layout_settings[custom_element]', 'This had no form now it does'); -+ $page->pressButton('Add section'); -+ $assert_session->pageTextContains('This had no form now it does'); -+ -+ // Test a custom plugin form that is altered onto a plugin that has an -+ // existing form. -+ $page->clickLink('Add section'); -+ $page->clickLink('Layout Builder Test No Form Plugin'); -+ $page->fillField('layout_settings[custom_element]', 'This had an existing form now it has more'); -+ $page->pressButton('Add section'); -+ $assert_session->pageTextContains('This had an existing form now it has more'); -+ } -+ - /** - * Tests that extra fields work before and after enabling Layout Builder. - */ diff --git a/patches/d10/3056249.diff b/patches/d10/3056249.diff deleted file mode 100644 index c42e8ab960865abd5f83b473b0961ec3e0ab5491..0000000000000000000000000000000000000000 --- a/patches/d10/3056249.diff +++ /dev/null @@ -1,48 +0,0 @@ -diff --git a/src/ConfigReverter.php b/src/ConfigReverter.php -index 90d046b8bff50f6cac969a433604a6af79de4674..81395829023ae89f07a20b3cea7734ae8786e78f 100644 ---- a/src/ConfigReverter.php -+++ b/src/ConfigReverter.php -@@ -2,6 +2,7 @@ - - namespace Drupal\config_update; - -+use Drupal\Component\Utility\Crypt; - use Drupal\Core\Config\ConfigFactoryInterface; - use Drupal\Core\Config\StorageInterface; - use Drupal\Core\Entity\EntityTypeManagerInterface; -@@ -108,11 +109,12 @@ class ConfigReverter implements ConfigRevertInterface, ConfigDeleteInterface { - - // Save it as a new config entity or simple config. - if ($type === 'system.simple') { -- $this->configFactory->getEditable($full_name)->setData($value)->save(); -+ $this->configFactory->getEditable($full_name)->setData($value)->set('_core', ['default_config_hash' => Crypt::hashBase64(serialize($value))])->save(); - } - else { - $entity_storage = $this->entityManager->getStorage($type); - $entity = $entity_storage->createFromStorageRecord($value); -+ $entity->set('_core', ['default_config_hash' => Crypt::hashBase64(serialize($value))]); - $entity->save(); - } - -@@ -156,10 +158,9 @@ class ConfigReverter implements ConfigRevertInterface, ConfigDeleteInterface { - // hash (which is part of the _core config key's value). - if ($type === 'system.simple') { - $config = $this->configFactory->getEditable($full_name); -- $core = $config->get('_core'); - $config - ->setData($value) -- ->set('_core', $core) -+ ->set('_core', ['default_config_hash' => Crypt::hashBase64(serialize($value))]) - ->save(); - } - else { -@@ -168,9 +169,8 @@ class ConfigReverter implements ConfigRevertInterface, ConfigDeleteInterface { - $id = $value[$id_key]; - $entity_storage = $this->entityManager->getStorage($type); - $entity = $entity_storage->load($id); -- $core = $entity->get('_core'); - $entity = $entity_storage->updateFromStorageRecord($entity, $value); -- $entity->set('_core', $core); -+ $entity->set('_core', ['default_config_hash' => Crypt::hashBase64(serialize($value))]); - $entity->save(); - } diff --git a/patches/d10/3061935.diff b/patches/d10/3061935.diff deleted file mode 100644 index 7d8c6a8584a453c76ed419ffc304b0b04ce8b896..0000000000000000000000000000000000000000 --- a/patches/d10/3061935.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/src/EntityImportSourceManager.php b/src/EntityImportSourceManager.php -index eec012051537b67e4079a6d0886c9fdb81620fc5..29c8668a97335ab75798f6521d7bb0e480b8cf00 100644 ---- a/src/EntityImportSourceManager.php -+++ b/src/EntityImportSourceManager.php -@@ -78,6 +78,7 @@ class EntityImportSourceManager implements EntityImportSourceManagerInterface { - protected function getDefinitions() { - $definitions = []; - -+ require_once 'core/modules/content_translation/src/Plugin/migrate/source/I18nQueryTrait.php'; - foreach ($this->migrateSourceManager->getDefinitions() as $plugin_id => $definition) { - $interface = 'Drupal\entity_import\Plugin\migrate\source\EntityImportSourceInterface'; - diff --git a/patches/d10/3090234.patch b/patches/d10/3090234.patch deleted file mode 100644 index aa2a342ec0eeb812c65f708f3287254a60e56a24..0000000000000000000000000000000000000000 --- a/patches/d10/3090234.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/src/BookExport.php b/src/BookExport.php -index d130610b..edcf8541 100644 ---- a/src/BookExport.php -+++ b/src/BookExport.php -@@ -131,7 +131,6 @@ class BookExport { - */ - protected function bookNodeExport(NodeInterface $node, $children = '') { - $build = $this->viewBuilder->view($node, 'print', NULL); -- unset($build['#theme']); - - return [ - '#theme' => 'book_node_export_html', diff --git a/patches/d10/3137889.diff b/patches/d10/3137889.diff deleted file mode 100644 index 4c91553e5dc217e006823cd523afc688ff5c2eba..0000000000000000000000000000000000000000 --- a/patches/d10/3137889.diff +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/architect.info.yml b/architect.info.yml -index 58e1fc9b3e7d9cc4d143613a0808c98a9d07ec01..d6a66420948fb0e7e9964069ec2ed18745d68053 100644 ---- a/architect.info.yml -+++ b/architect.info.yml -@@ -1,7 +1,8 @@ - name: Architect - type: theme - description: the ARCHITECT Theme - A DESIGN SAVVY THEME USING UIKITTY. --core: 8.x -+# core: 8.x -+core_version_requirement: ^8.8 || ^9 - base theme: uikitty - libraries: - - architect/default diff --git a/patches/d10/3149175.diff b/patches/d10/3149175.diff deleted file mode 100644 index 4d4cdeb17fd3acb64f3204e786206ba672273dd6..0000000000000000000000000000000000000000 --- a/patches/d10/3149175.diff +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/uikitty.info.yml b/uikitty.info.yml -index 0a44d98b8e56bf3a5065409e852d868dc8bd739c..df7117253a9b3e089023d81402ab0224b2ed264a 100644 ---- a/uikitty.info.yml -+++ b/uikitty.info.yml -@@ -1,7 +1,8 @@ - name: uikitty - type: theme - description: A building block theme using uikit awesomeness. --core: 8.x -+# core: 8.x -+core_version_requirement: ^8.8 || ^9 - base theme: classy - libraries: - - uikitty/global -@@ -22,4 +23,4 @@ regions: - offcanvas: 'Off-canvas' - - ckeditor_stylesheets: -- - uikit/css/uikit.almost-flat.min.css -\ No newline at end of file -+ - uikit/css/uikit.almost-flat.min.css -diff --git a/uikitty.theme b/uikitty.theme -index 15609cc19631ad86d47683e8a473f3542bed957d..aa61fd2c233a61f742a7e586e5e237da8fd55025 100644 ---- a/uikitty.theme -+++ b/uikitty.theme -@@ -36,5 +36,5 @@ function uikitty_theme_suggestions_menu_alter(array &$suggestions, array $variab - - function uikitty_preprocess_html(&$variables) { - $current_path = \Drupal::service('path.current')->getPath(); -- $variables['current_path'] = \Drupal::service('path.alias_manager')->getAliasByPath($current_path); --} -\ No newline at end of file -+ $variables['current_path'] = \Drupal::service('path_alias.manager')->getAliasByPath($current_path); -+} diff --git a/patches/d10/3159113.diff b/patches/d10/3159113.diff deleted file mode 100644 index 7ec353c6cf9766e6009fd7f67f6702d51e4aead6..0000000000000000000000000000000000000000 --- a/patches/d10/3159113.diff +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Database/Schema.php b/core/lib/Drupal/Core/Database/Schema.php -index 9530c8fbf8..4ac5503e69 100644 ---- a/core/lib/Drupal/Core/Database/Schema.php -+++ b/core/lib/Drupal/Core/Database/Schema.php -@@ -167,8 +167,8 @@ protected function buildTableNameCondition($table_name, $operator = '=', $add_pr - * @return bool - * TRUE if the given table exists, otherwise FALSE. - */ -- public function tableExists($table, bool $add_prefix = TRUE) { -- $condition = $this->buildTableNameCondition($table, '=', $add_prefix); -+ public function tableExists($table) { -+ $condition = $this->buildTableNameCondition($table); - $condition->compile($this->connection, $this); - // Normally, we would heartily discourage the use of string - // concatenation for conditionals like this however, we diff --git a/patches/d10/3160781-2.diff b/patches/d10/3160781-2.diff deleted file mode 100644 index 53ad63987a7c8b4f9e108c3370a1302309d797ad..0000000000000000000000000000000000000000 --- a/patches/d10/3160781-2.diff +++ /dev/null @@ -1,202 +0,0 @@ -diff --git a/content_lock.module b/content_lock.module -index 0bae073d352baebf7cb934e1243c0ad87ab61703..e11bf199b891b1019566a06104c0529054d11103 100644 ---- a/content_lock.module -+++ b/content_lock.module -@@ -95,16 +95,17 @@ function content_lock_form_alter(&$form, FormStateInterface $form_state, $form_i - - if ($lock_service->isJsLock($entity_type)) { - $form['#attached']['library'][] = 'content_lock/drupal.content_lock.lock_form'; -+ $args = [ -+ 'entity' => $entity->id(), -+ 'langcode' => $entity->language()->getId(), -+ 'form_op' => $form_op, -+ ]; - $form['#attached']['drupalSettings']['content_lock'] = [ - Html::cleanCssIdentifier($form_id) => [ -- 'lockUrl' => Url::fromRoute('content_lock.create_lock.' . $entity_type, -- [ -- 'entity' => $entity->id(), -- 'langcode' => $entity->language()->getId(), -- 'form_op' => $form_op, -- ], -- ['query' => ['destination' => Drupal::request()->getRequestUri()]] -- )->toString(), -+ 'lockUrl' => Url::fromRoute('content_lock.create_lock.' . $entity_type, $args, [ -+ 'query' => ['destination' => Drupal::request()->getRequestUri()], -+ ])->toString(), -+ 'releaseUrl' => Url::fromRoute('content_lock.release_lock.' . $entity_type, $args)->toString(), - ], - ]; - -diff --git a/js/content_lock_form.js b/js/content_lock_form.js -index 2661f311e9c689cfc7bc303edd43b96c53eec6ca..58827cb7302130a213cabd1f234501570dc99363 100644 ---- a/js/content_lock_form.js -+++ b/js/content_lock_form.js -@@ -73,5 +73,16 @@ - }); - } - }; -+ -+ var onBeforeUnLoadEvent = false; -+ -+ window.onunload = window.onbeforeunload= function() { -+ if (!onBeforeUnLoadEvent) { -+ onBeforeUnLoadEvent = true; -+ if (typeof navigator.sendBeacon === 'function') { -+ navigator.sendBeacon(settings.releaseUrl); -+ } -+ } -+ }; - }; - })(jQuery, Drupal, once); -diff --git a/src/ContentLock/ContentLock.php b/src/ContentLock/ContentLock.php -index 5d6f693fc4526e0c93f8a81a7c1063ec39e69872..dea173950eda995e41d599f03d39ca02be475ca4 100644 ---- a/src/ContentLock/ContentLock.php -+++ b/src/ContentLock/ContentLock.php -@@ -344,6 +344,7 @@ class ContentLock implements ContentLockInterface { - */ - public function locking($entity_id, $langcode, $form_op, $uid, $entity_type = 'node', $quiet = FALSE, $destination = NULL) { - $translation_lock = $this->isTranslationLockEnabled($entity_type); -+ $js_lock = $this->isJsLock($entity_type); - if (!$translation_lock) { - $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED; - } -@@ -371,12 +372,20 @@ class ContentLock implements ContentLockInterface { - - if ($this->verbose() && !$quiet) { - if ($translation_lock) { -- $this->messenger->addStatus($this->t('This content translation is now locked against simultaneous editing. This content translation will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $message = 'This content translation is now locked against simultaneous editing.'; -+ if (!$js_lock) { -+ $message .= ' This content translation will remain locked if you navigate away from this page without saving or unlocking it.'; -+ } - } - else { -- $this->messenger->addStatus($this->t('This content is now locked against simultaneous editing. This content will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $message = 'This content is now locked against simultaneous editing.'; -+ if (!$js_lock) { -+ $message .= ' This content will remain locked if you navigate away from this page without saving or unlocking it.'; -+ } - } -+ $this->messenger->addStatus($this->t($message)); - } -+ - // Post locking hook. - $this->moduleHandler->invokeAll('content_lock_locked', [ - $entity_id, -@@ -424,11 +433,18 @@ class ContentLock implements ContentLockInterface { - // Locked by current user. - if ($this->verbose() && !$quiet) { - if ($translation_lock) { -- $this->messenger->addStatus($this->t('This content translation is now locked by you against simultaneous editing. This content translation will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $message = 'This content translation is now locked by you against simultaneous editing.'; -+ if (!$js_lock) { -+ $message .= ' This content translation will remain locked if you navigate away from this page without saving or unlocking it.'; -+ } - } - else { -- $this->messenger->addStatus($this->t('This content is now locked by you against simultaneous editing. This content will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $message = 'This content is now locked by you against simultaneous editing.'; -+ if (!$js_lock) { -+ $message .= ' This content will remain locked if you navigate away from this page without saving or unlocking it.'; -+ } - } -+ $this->messenger->addStatus($this->t($message)); - } - - // Send success flag. -diff --git a/src/Controller/ContentLockController.php b/src/Controller/ContentLockController.php -index b0270fb2242492a1f0c62c8070173f9ed7e99298..5d214cac26d4fda3edbfe33366d536a484d43379 100644 ---- a/src/Controller/ContentLockController.php -+++ b/src/Controller/ContentLockController.php -@@ -5,7 +5,6 @@ namespace Drupal\content_lock\Controller; - use Drupal\content_lock\Ajax\LockFormCommand; - use Drupal\content_lock\ContentLock\ContentLockInterface; - use Drupal\Core\Ajax\AjaxResponse; --use Drupal\Core\Ajax\AppendCommand; - use Drupal\Core\Ajax\PrependCommand; - use Drupal\Core\Controller\ControllerBase; - use Drupal\Core\Entity\ContentEntityInterface; -@@ -78,19 +77,31 @@ class ContentLockController extends ControllerBase { - - // Render status messages from locking service. - $response->addCommand(new PrependCommand('', ['#type' => 'status_messages'])); -- -- if ($lock) { -- $language = $this->languageManager()->getLanguage($langcode); -- $url = $entity->toUrl('canonical', ['language' => $language]); -- $unlock_button = $this->lockService->unlockButton($entity->getEntityTypeId(), $entity->id(), $langcode, $form_op, $url->toString()); -- $response->addCommand(new AppendCommand('.content-lock-actions.form-actions', $unlock_button)); -- } - } - $response->addCommand(new LockFormCommand($lockable, $lock)); - - return $response; - } - -+ /** -+ * Custom callback for the release lock route. -+ * -+ * @param \Symfony\Component\HttpFoundation\Request $request -+ * The current request. -+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity -+ * The locked entity. -+ * @param string $langcode -+ * The langcode. -+ * @param string $form_op -+ * The form op. -+ * -+ * @see \Drupal\content_lock\Routing\ContentLockRoutes::routes() -+ */ -+ public function releaseCall(Request $request, ContentEntityInterface $entity, $langcode, $form_op) { -+ $this->lockService->release($entity->id(), $entity->language()->getId(), $form_op, $this->currentUser()->id(), $entity->getEntityTypeId()); -+ return []; -+ } -+ - /** - * Custom access checker for the create lock requirements route. - * -diff --git a/src/Form/ContentLockSettingsForm.php b/src/Form/ContentLockSettingsForm.php -index c5baea93f91a5321599cb13ee62bab0ac755a659..4f8e372b80d7ff213c2ab2a95c52a294949f2b52 100644 ---- a/src/Form/ContentLockSettingsForm.php -+++ b/src/Form/ContentLockSettingsForm.php -@@ -184,9 +184,9 @@ class ContentLockSettingsForm extends ConfigFormBase { - - $form['entities'][$definition->id()]['settings']['js_lock'] = [ - '#type' => 'checkbox', -- '#title' => $this->t('Lock form using JS.'), -- '#default_value' => in_array($definition->id(), $config->get('types_js_lock') ?: []), -- '#description' => $this->t('Activating this options activates the lock when the user is on the form. This helps if modules interacting with form without a user interacting with the form, like the prefetch_cache module.'), -+ '#title' => $this->t('Lock and release form using JS.'), -+ '#default_value' => in_array($definition->id(), $config->get('types_js_lock')?: []), -+ '#description' => $this->t('Activating this options activates the lock when the user is on the form. This helps if modules interacting with form without a user interacting with the form, like the prefetch_cache module. The form is automatically released when navigating away from the page.'), - ]; - - if (!empty($definition->getHandlerClasses()['form'])) { -diff --git a/src/Routing/ContentLockRoutes.php b/src/Routing/ContentLockRoutes.php -index a0004621619ce7f11f31bd642cf1402847fb94df..289bdff22c16b99a05ad310b3ac34dae69978b08 100644 ---- a/src/Routing/ContentLockRoutes.php -+++ b/src/Routing/ContentLockRoutes.php -@@ -76,6 +76,22 @@ class ContentLockRoutes implements ContainerInjectionInterface { - ], - ] - ); -+ $routes['content_lock.release_lock.' . $definition->id()] = new Route( -+ '/admin/lock/release/' . $definition->id() . '/{entity}/{langcode}/{form_op}', -+ [ -+ '_controller' => '\Drupal\content_lock\Controller\ContentLockController::releaseCall', -+ ], -+ [ -+ '_custom_access' => '\Drupal\content_lock\Controller\ContentLockController::access', -+ ], -+ [ -+ 'parameters' => [ -+ 'entity' => [ -+ 'type' => 'entity:' . $definition->id(), -+ ], -+ ], -+ ] -+ ); - } - } - return $routes; diff --git a/patches/d10/3160781.diff b/patches/d10/3160781.diff deleted file mode 100644 index 56298c0f6510918940196957ca19cd867c5434ec..0000000000000000000000000000000000000000 --- a/patches/d10/3160781.diff +++ /dev/null @@ -1,247 +0,0 @@ -diff --git a/content_lock.module b/content_lock.module -index 9df7b0c1b37f5e60e0262fbc76c4c172093d5a4a..e513d7f749b05a580e88aed63dee95325cd3b02d 100644 ---- a/content_lock.module -+++ b/content_lock.module -@@ -94,16 +94,17 @@ function content_lock_form_alter(&$form, FormStateInterface $form_state, $form_i - - if ($lock_service->isJsLock($entity_type)) { - $form['#attached']['library'][] = 'content_lock/drupal.content_lock.lock_form'; -+ $args = [ -+ 'entity' => $entity->id(), -+ 'langcode' => $entity->language()->getId(), -+ 'form_op' => $form_op, -+ ]; - $form['#attached']['drupalSettings']['content_lock'] = [ - Html::cleanCssIdentifier($form_id) => [ -- 'lockUrl' => Url::fromRoute('content_lock.create_lock.' . $entity_type, -- [ -- 'entity' => $entity->id(), -- 'langcode' => $entity->language()->getId(), -- 'form_op' => $form_op, -- ], -- ['query' => ['destination' => Drupal::request()->getRequestUri()]] -- )->toString(), -+ 'lockUrl' => Url::fromRoute('content_lock.create_lock.' . $entity_type, $args, [ -+ 'query' => ['destination' => Drupal::request()->getRequestUri()], -+ ])->toString(), -+ 'releaseUrl' => Url::fromRoute('content_lock.release_lock.' . $entity_type, $args)->toString(), - ], - ]; - -diff --git a/js/content_lock_form.js b/js/content_lock_form.js -index db55f99b01cbe287473fc1587f190c656f86e1cc..1c6a324fd26ca7cc6d5fd3654a3dc328ca350d08 100644 ---- a/js/content_lock_form.js -+++ b/js/content_lock_form.js -@@ -56,6 +56,17 @@ - $form.find('input, textarea, select').prop('disabled', true).addClass('is-disabled'); - $form.find('.content-lock-actions input').attr('title', Drupal.t('Action not available because content is locked.')); - }; -+ -+ var onBeforeUnLoadEvent = false; -+ -+ window.onunload = window.onbeforeunload= function() { -+ if (!onBeforeUnLoadEvent) { -+ onBeforeUnLoadEvent = true; -+ if (typeof navigator.sendBeacon === 'function') { -+ navigator.sendBeacon(settings.releaseUrl); -+ } -+ } -+ }; - }; - - }(jQuery, Drupal, once)); -diff --git a/src/ContentLock/ContentLock.php b/src/ContentLock/ContentLock.php -index a0b0a80c9dafa60e301e2ef139e59a36b1041e85..98201f010982f8a5d31535a76c5152cf800f29cb 100644 ---- a/src/ContentLock/ContentLock.php -+++ b/src/ContentLock/ContentLock.php -@@ -403,6 +403,7 @@ class ContentLock extends ServiceProviderBase { - */ - public function locking($entity_id, $langcode, $form_op, $uid, $entity_type = 'node', $quiet = FALSE, $destination = NULL) { - $translation_lock = $this->isTranslationLockEnabled($entity_type); -+ $js_lock = $this->isJsLock($entity_type); - if (!$translation_lock) { - $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED; - } -@@ -420,12 +421,20 @@ class ContentLock extends ServiceProviderBase { - - if ($this->verbose() && !$quiet) { - if ($translation_lock) { -- $this->messenger->addStatus($this->t('This content translation is now locked against simultaneous editing. This content translation will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $message = 'This content translation is now locked against simultaneous editing.'; -+ if (!$js_lock) { -+ $message .= ' This content translation will remain locked if you navigate away from this page without saving or unlocking it.'; -+ } - } - else { -- $this->messenger->addStatus($this->t('This content is now locked against simultaneous editing. This content will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $message = 'This content is now locked against simultaneous editing.'; -+ if (!$js_lock) { -+ $message .= ' This content will remain locked if you navigate away from this page without saving or unlocking it.'; -+ } - } -+ $this->messenger->addStatus($this->t($message)); - } -+ - // Post locking hook. - $this->moduleHandler->invokeAll('content_lock_locked', [ - $entity_id, -@@ -473,11 +482,18 @@ class ContentLock extends ServiceProviderBase { - // Locked by current user. - if ($this->verbose() && !$quiet) { - if ($translation_lock) { -- $this->messenger->addStatus($this->t('This content translation is now locked by you against simultaneous editing. This content translation will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $message = 'This content translation is now locked by you against simultaneous editing.'; -+ if (!$js_lock) { -+ $message .= ' This content translation will remain locked if you navigate away from this page without saving or unlocking it.'; -+ } - } - else { -- $this->messenger->addStatus($this->t('This content is now locked by you against simultaneous editing. This content will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $message = 'This content is now locked by you against simultaneous editing.'; -+ if (!$js_lock) { -+ $message .= ' This content will remain locked if you navigate away from this page without saving or unlocking it.'; -+ } - } -+ $this->messenger->addStatus($this->t($message)); - } - - // Send success flag. -diff --git a/src/Controller/ContentLockController.php b/src/Controller/ContentLockController.php -index aa02b45ac8ad14765cd892377bdd724da0693650..ef5515f54b82f73d7c5adc3adfc1d99a0223e7d1 100644 ---- a/src/Controller/ContentLockController.php -+++ b/src/Controller/ContentLockController.php -@@ -5,7 +5,6 @@ namespace Drupal\content_lock\Controller; - use Drupal\content_lock\Ajax\LockFormCommand; - use Drupal\content_lock\ContentLock\ContentLock; - use Drupal\Core\Ajax\AjaxResponse; --use Drupal\Core\Ajax\AppendCommand; - use Drupal\Core\Ajax\PrependCommand; - use Drupal\Core\Controller\ControllerBase; - use Drupal\Core\Entity\ContentEntityInterface; -@@ -71,19 +70,31 @@ class ContentLockController extends ControllerBase { - - // Render status messages from locking service. - $response->addCommand(new PrependCommand('', ['#type' => 'status_messages'])); -- -- if ($lock) { -- $language = $this->languageManager()->getLanguage($langcode); -- $url = $entity->toUrl('canonical', ['language' => $language]); -- $unlock_button = $this->lockService->unlockButton($entity->getEntityTypeId(), $entity->id(), $langcode, $form_op, $url->toString()); -- $response->addCommand(new AppendCommand('.content-lock-actions.form-actions', $unlock_button)); -- } - } - $response->addCommand(new LockFormCommand($lockable, $lock)); - - return $response; - } - -+ /** -+ * Custom callback for the release lock route. -+ * -+ * @param \Symfony\Component\HttpFoundation\Request $request -+ * The current request. -+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity -+ * The locked entity. -+ * @param string $langcode -+ * The langcode. -+ * @param string $form_op -+ * The form op. -+ * -+ * @see \Drupal\content_lock\Routing\ContentLockRoutes::routes() -+ */ -+ public function releaseCall(Request $request, ContentEntityInterface $entity, $langcode, $form_op) { -+ $this->lockService->release($entity->id(), $entity->language()->getId(), $form_op, $this->currentUser()->id(), $entity->getEntityTypeId()); -+ return []; -+ } -+ - /** - * Custom access checker for the create lock requirements route. - * -diff --git a/src/Form/ContentLockSettingsForm.php b/src/Form/ContentLockSettingsForm.php -index 29bb89a231501741ca1bc579c3295be86ebd9a4a..68888a2532cbbc51bb5e93f53840e7e8fdb91175 100644 ---- a/src/Form/ContentLockSettingsForm.php -+++ b/src/Form/ContentLockSettingsForm.php -@@ -184,9 +184,9 @@ class ContentLockSettingsForm extends ConfigFormBase { - - $form['entities'][$definition->id()]['settings']['js_lock'] = [ - '#type' => 'checkbox', -- '#title' => $this->t('Lock form using JS.'), -- '#default_value' => in_array($definition->id(), $config->get('types_js_lock') ?: []), -- '#description' => $this->t('Activating this options activates the lock when the user is on the form. This helps if modules interacting with form without a user interacting with the form, like the prefetch_cache module.'), -+ '#title' => $this->t('Lock and release form using JS.'), -+ '#default_value' => in_array($definition->id(), $config->get('types_js_lock')?: []), -+ '#description' => $this->t('Activating this options activates the lock when the user is on the form. This helps if modules interacting with form without a user interacting with the form, like the prefetch_cache module. The form is automatically released when navigating away from the page.'), - ]; - - if (!empty($definition->getHandlerClasses()['form'])) { -diff --git a/src/Routing/ContentLockRoutes.php b/src/Routing/ContentLockRoutes.php -index 54f4616d24fb5b64cf9cd7bf907105793fe8d554..71f6c2a1d8b3aae1bd1dc4bb63d0dbf256d866c1 100644 ---- a/src/Routing/ContentLockRoutes.php -+++ b/src/Routing/ContentLockRoutes.php -@@ -76,6 +76,22 @@ class ContentLockRoutes implements ContainerInjectionInterface { - ], - ] - ); -+ $routes['content_lock.release_lock.' . $definition->id()] = new Route( -+ '/admin/lock/release/' . $definition->id() . '/{entity}/{langcode}/{form_op}', -+ [ -+ '_controller' => '\Drupal\content_lock\Controller\ContentLockController::releaseCall', -+ ], -+ [ -+ '_custom_access' => '\Drupal\content_lock\Controller\ContentLockController::access', -+ ], -+ [ -+ 'parameters' => [ -+ 'entity' => [ -+ 'type' => 'entity:' . $definition->id(), -+ ], -+ ], -+ ] -+ ); - } - } - return $routes; -diff --git a/tests/src/Functional/ContentLockTranslationTest.php b/tests/src/Functional/ContentLockTranslationTest.php -index d520c0ebfb7f966e3a0d9369e09179d2c721c33b..974ef7d66f8f4f2a821efe7503218a2ec03a5c30 100644 ---- a/tests/src/Functional/ContentLockTranslationTest.php -+++ b/tests/src/Functional/ContentLockTranslationTest.php -@@ -57,7 +57,7 @@ class ContentLockTranslationTest extends ContentLockTestBase { - $this->drupalLogin($this->user1); - // Edit a entity without saving. - $this->drupalGet($this->entity->toUrl('edit-form')); -- $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing. This content translation will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing.')); - // English form locked, german not. - $this->assertNotFalse($lockService->fetchLock($this->entity->id(), $this->entity->language()->getId(), NULL, 'entity_test_mul_changed')); - $this->assertFalse($lockService->fetchLock($translation->id(), $translation->language()->getId(), NULL, 'entity_test_mul_changed')); -@@ -69,18 +69,18 @@ class ContentLockTranslationTest extends ContentLockTestBase { - $this->getSession()->getPage()->clickLink('Break lock'); - $this->getSession()->getPage()->pressButton('Confirm break lock'); - $this->assertSame($this->entity->toUrl('edit-form', ['absolute' => TRUE])->toString(), $this->getUrl()); -- $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing. This content translation will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing.')); - - // Enter translation form. - $this->drupalGet($translation->toUrl('edit-form')); -- $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing. This content translation will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing.')); - $this->assertNotFalse($lockService->fetchLock($translation->id(), $translation->language()->getId(), NULL, 'entity_test_mul_changed')); - $this->drupalGet($translation->toUrl('edit-form')); - $this->submitForm([], t('Save')); - - $this->drupalLogin($this->user1); - $this->drupalGet($translation->toUrl('edit-form')); -- $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing. This content translation will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing.')); - - $this->drupalLogin($this->user2); - $this->drupalGet($translation->toUrl('edit-form')); -@@ -88,7 +88,7 @@ class ContentLockTranslationTest extends ContentLockTestBase { - $this->getSession()->getPage()->clickLink('Break lock'); - $this->getSession()->getPage()->pressButton('Confirm break lock'); - $this->assertSame($translation->toUrl('edit-form', ['absolute' => TRUE])->toString(), $this->getUrl()); -- $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing. This content translation will remain locked if you navigate away from this page without saving or unlocking it.')); -+ $assert_session->pageTextContains(t('This content translation is now locked against simultaneous editing.')); - } - - } diff --git a/patches/d10/3166181-2.diff b/patches/d10/3166181-2.diff deleted file mode 100644 index aa6cb54ebbb079462474a61a5932891d9a61add7..0000000000000000000000000000000000000000 --- a/patches/d10/3166181-2.diff +++ /dev/null @@ -1,811 +0,0 @@ -diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml -index 8ff8fbdf4b3c8f366c4704c8b53b4b2d2039a3e4..4cb367d6ba37a0a50fa630a86f7f4fd15359d735 100644 ---- a/.gitlab-ci.yml -+++ b/.gitlab-ci.yml -@@ -57,4 +57,4 @@ variables: - OPT_IN_TEST_NEXT_MAJOR: '1' - # Temporarily disable failing analysis checks until they're working, see - # module issue queue for relevant work. -- _CSPELL_WORDS: "collapsiblock,testhtmlblock" -+ _CSPELL_WORDS: "collapsiblock,testhtmlblock,blocknode" -diff --git a/collapsiblock.module b/collapsiblock.module -index dbd06ca1d937a91e1b765a01c031ff9a610d6718..cc0cbc9ae22479d193fa6a32d2e15381231c75a2 100644 ---- a/collapsiblock.module -+++ b/collapsiblock.module -@@ -18,7 +18,16 @@ use Drupal\Core\Routing\RouteMatchInterface; - * @see collapsiblock_form_block_form_alter() - */ - function collapsiblock_block_form_form_builder($entity_type, Block $block, &$form, FormStateInterface $form_state) { -- $block->setThirdPartySetting('collapsiblock', 'collapse_action', $form_state->getValue('collapsiblock_settings')['collapse_action']); -+ $collapse_action = (int) $form_state->getValue([ -+ 'collapsiblock_settings', -+ 'collapse_action', -+ ]); -+ if ($collapse_action == 0) { -+ $block->unsetThirdPartySetting('collapsiblock', 'collapse_action'); -+ } -+ else { -+ $block->setThirdPartySetting('collapsiblock', 'collapse_action', $collapse_action); -+ } - } - - /** -@@ -30,7 +39,7 @@ function collapsiblock_block_view_alter(array &$build, BlockPluginInterface $blo - } - - $block_entity = $build['#block']; -- $action = $block_entity->getThirdPartySetting('collapsiblock', 'collapse_action'); -+ $action = (int) $block_entity->getThirdPartySetting('collapsiblock', 'collapse_action', 0); - - // If the block is set to global default, get that default and use it instead - // of 0. -@@ -55,7 +64,47 @@ function collapsiblock_block_view_alter(array &$build, BlockPluginInterface $blo - } - - /** -- * Implements hook_form_FORM_ID_alter(). -+ * Implements hook_form_alter(). -+ * -+ * Alter the layout builder "Add Block" and "Update Block" forms. -+ */ -+function collapsiblock_form_alter(&$form, FormStateInterface $form_state, $form_id) { -+ // Affect the Layout Builder's "Add Block" and "Update Block" forms. -+ if ($form['#form_id'] === 'layout_builder_add_block' || $form['#form_id'] === 'layout_builder_update_block') { -+ /** @var \Drupal\layout_builder\SectionComponent $current_layout_builder_component */ -+ $current_layout_builder_component = $form_state->getFormObject()->getCurrentComponent(); -+ $saved_settings = $current_layout_builder_component->get('collapsiblock') ?: ['collapse_action' => 0]; -+ -+ $form['settings']['collapsiblock_settings'] = [ -+ '#type' => 'details', -+ '#title' => t('Collapsible'), -+ '#open' => TRUE, -+ ]; -+ -+ $options = CollapsiblockGlobalSettings::ACTION_OPTIONS; -+ $settings = \Drupal::config('collapsiblock.settings'); -+ $default_action = $options[$settings->get('default_action')]; -+ $options = [0 => 'Global default, currently set to: ' . $default_action] + $options; -+ -+ $form['settings']['collapsiblock_settings']['collapse_action'] = [ -+ '#type' => 'radios', -+ '#title' => t('Block collapse behavior'), -+ '#options' => $options, -+ '#default_value' => $saved_settings['collapse_action'], -+ ]; -+ -+ // Add our own form submit handler at the start of the #submit callbacks -+ // so that it can modify the SectionComponent configuration before it is -+ // saved by the default form submit handler, -+ // \Drupal\layout_builder\Form\ConfigureBlockFormBase::submitForm(). -+ array_unshift($form['#submit'], 'collapsiblock_form_layout_builder_submit'); -+ } -+} -+ -+/** -+ * Implements hook_form_FORM_ID_alter() for \Drupal\block\BlockForm. -+ * -+ * Alter the "Configure block" form. - */ - function collapsiblock_form_block_form_alter(&$form, FormStateInterface $form_state, $form_id) { - -@@ -82,6 +131,32 @@ function collapsiblock_form_block_form_alter(&$form, FormStateInterface $form_st - $form['#entity_builders'][] = 'collapsiblock_block_form_form_builder'; - } - -+/** -+ * Layout builder form submit callback. -+ * -+ * @see collapsiblock_form_alter() -+ */ -+function collapsiblock_form_layout_builder_submit(array $form, FormStateInterface $form_state) { -+ /** @var \Drupal\layout_builder\SectionComponent $current_layout_builder_component */ -+ $current_layout_builder_component = $form_state->getFormObject()->getCurrentComponent(); -+ -+ // Update the settings to include our additional collapsiblock settings. -+ $additional = $current_layout_builder_component->get('additional'); -+ $collapse_action = (int) $form_state->getValue([ -+ 'settings', -+ 'collapsiblock_settings', -+ 'collapse_action', -+ ]); -+ if ($collapse_action === 0) { -+ // Don't add any new settings if using default settings. -+ unset($additional['collapsiblock']); -+ } -+ else { -+ $additional['collapsiblock']['collapse_action'] = $collapse_action; -+ } -+ $current_layout_builder_component->set('additional', $additional); -+} -+ - /** - * Implements hook_help(). - */ -@@ -153,7 +228,7 @@ function collapsiblock_page_attachments_alter(array &$attachments) { - */ - function collapsiblock_preprocess_block(&$variables) { - if (isset($variables['elements']['#collapsiblock'])) { -- $variables['title_prefix'][] = $variables['elements']['#collapsiblock']['prefix']; -- $variables['title_suffix'][] = $variables['elements']['#collapsiblock']['suffix']; -+ $variables['title_prefix']['collapsiblock'] = $variables['elements']['#collapsiblock']['prefix']; -+ $variables['title_suffix']['collapsiblock'] = $variables['elements']['#collapsiblock']['suffix']; - } - } -diff --git a/collapsiblock.services.yml b/collapsiblock.services.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..eabdfd9b209af096356476aac99dff9770a5a1b0 ---- /dev/null -+++ b/collapsiblock.services.yml -@@ -0,0 +1,8 @@ -+services: -+ -+ # An event handler to alter how Layout Builder blocks are rendered. -+ collapsiblock.layout_builder_block_component_render: -+ class: 'Drupal\collapsiblock\EventSubscriber\LayoutBuilderBlockComponentRender' -+ arguments: ['@config.factory'] -+ tags: -+ - { name: event_subscriber } -diff --git a/config/schema/collapsiblock.schema.yml b/config/schema/collapsiblock.schema.yml -index a2d6ab585703f7380644c90ab380cf17679f99f2..31d05fdbc40582fe100a2999afb159e956aa3dcd 100644 ---- a/config/schema/collapsiblock.schema.yml -+++ b/config/schema/collapsiblock.schema.yml -@@ -21,7 +21,7 @@ collapsiblock.settings: - cookie_lifetime: - type: float - label: 'Cookie expiration' -- translatable: false, -+ translatable: false - nullable: true - - # Block instance settings. -diff --git a/src/EventSubscriber/LayoutBuilderBlockComponentRender.php b/src/EventSubscriber/LayoutBuilderBlockComponentRender.php -new file mode 100644 -index 0000000000000000000000000000000000000000..78e0fb4efebb29bbab799747bfbe3597dd7673ea ---- /dev/null -+++ b/src/EventSubscriber/LayoutBuilderBlockComponentRender.php -@@ -0,0 +1,107 @@ -+<?php -+ -+namespace Drupal\collapsiblock\EventSubscriber; -+ -+use Drupal\Component\EventDispatcher\Event; -+use Drupal\Component\Utility\Html; -+use Drupal\Core\Config\ConfigFactoryInterface; -+use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent; -+use Symfony\Component\EventDispatcher\EventSubscriberInterface; -+ -+/** -+ * An event handler to alter how Layout Builder blocks are rendered. -+ */ -+class LayoutBuilderBlockComponentRender implements EventSubscriberInterface { -+ -+ /** -+ * A config object for the collapsiblock configuration. -+ * -+ * @var \Drupal\Core\Config\ImmutableConfig -+ */ -+ protected $collapsibleBlockConfig; -+ -+ /** -+ * Constructs the layout builder block event subscriber. -+ */ -+ public function __construct(ConfigFactoryInterface $config_factory) { -+ $this->collapsibleBlockConfig = $config_factory->get('collapsiblock.settings'); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public static function getSubscribedEvents() { -+ // Attach Collapsiblock settings and libraries to layout builder components -+ // when a layout builder Section Component's render array is being built. -+ // -+ // Note that 'section_component.build.render_array' is the value of -+ // \Drupal\layout_builder\LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY, -+ // but we can't use the constant directly, because it does not resolve when -+ // the layout_builder module is disabled. -+ $events = []; -+ $events['section_component.build.render_array'] = 'attachCollapsiblock'; -+ -+ return $events; -+ } -+ -+ /** -+ * Attach Collapsiblock settings and libraries to layout builder components. -+ * -+ * This acts when a layout builder Section Component's render array is being -+ * built. -+ * -+ * @param \Drupal\Component\EventDispatcher\Event $event -+ * An event object containing context about the Section Component and the -+ * render array being built. Should be an instance of -+ * \Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent, -+ * otherwise, no actions will be performed. -+ * -+ * @see \collapsiblock_block_view_alter() -+ * @see \Drupal\layout_builder\LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY -+ * @see getSubscribedEvents() -+ */ -+ public function attachCollapsiblock(Event $event) { -+ if (!($event instanceof SectionComponentBuildRenderArrayEvent)) { -+ return; -+ } -+ -+ $build = $event->getBuild(); -+ $current_lb_component = $event->getComponent(); -+ $saved_settings = $current_lb_component->get('additional'); -+ $saved_settings += [ -+ 'collapsiblock' => [ -+ 'collapse_action' => 0, -+ ], -+ ]; -+ -+ if (empty($build['#configuration']['id'])) { -+ return; -+ } -+ -+ // If the action is anything other than 'none', create our wrapper -+ // elements. -+ $collapse_action = $saved_settings['collapsiblock']['collapse_action']; -+ if ($collapse_action == 0) { -+ $collapse_action = $this->collapsibleBlockConfig->get('default_action'); -+ } -+ -+ if ($collapse_action != 1) { -+ // Generate a valid HTML ID. -+ $id = Html::getId('collapsiblock-wrapper-' . $build['#configuration']['id']); -+ $classes = []; -+ $classes[] = 'collapsiblockTitle'; -+ -+ $build['#collapsiblock']['prefix'] = [ -+ '#markup' => sprintf('<div id="%s" class="%s" data-collapsiblock-action="%s">', $id, implode(' ', $classes), $collapse_action), -+ ]; -+ $build['#collapsiblock']['suffix'] = [ -+ 'collapsiblock' => [ -+ '#markup' => '</div>', -+ ], -+ ]; -+ $event->setBuild($build); -+ } -+ $event->addCacheableDependency($this->collapsibleBlockConfig); -+ } -+ -+} -diff --git a/tests/src/FunctionalJavascript/LayoutBuilderTest.php b/tests/src/FunctionalJavascript/LayoutBuilderTest.php -new file mode 100644 -index 0000000000000000000000000000000000000000..02ad687de4ec7f30f1a739e023fe33f704e3997f ---- /dev/null -+++ b/tests/src/FunctionalJavascript/LayoutBuilderTest.php -@@ -0,0 +1,266 @@ -+<?php -+ -+namespace Drupal\Tests\collapsiblock\FunctionalJavascript; -+ -+use Drupal\Core\Url; -+use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay; -+use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface; -+use Drupal\node\Entity\NodeType; -+use Drupal\Tests\collapsiblock\Traits\LayoutBuilderInstanceSettingsTrait; -+ -+/** -+ * Test that Layout Builder blocks can be affected by Collapsiblock. -+ * -+ * @group collapsiblock -+ */ -+class LayoutBuilderTest extends CollapsiblockJavaScriptTestBase { -+ use LayoutBuilderInstanceSettingsTrait; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected static $modules = [ -+ 'block', -+ 'collapsiblock', -+ 'field_layout', -+ 'field_ui', -+ 'layout_builder', -+ 'node', -+ ]; -+ -+ /** -+ * User with permissions to administer a node type's fields w/ layout builder. -+ * -+ * @var \Drupal\user\UserInterface -+ */ -+ protected $collapsiblockAdminUser; -+ -+ /** -+ * A node whose "full" display's fields are managed by layout builder. -+ * -+ * @var \Drupal\node\NodeInterface -+ */ -+ protected $collapsiblockTestNode; -+ -+ /** -+ * A node type whose "full" display's fields are managed by layout builder. -+ * -+ * @var \Drupal\node\Entity\NodeType -+ */ -+ protected $collapsiblockTestNodeType; -+ -+ /** -+ * The "full" display whose fields are managed by layout builder. -+ * -+ * @var \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface -+ */ -+ protected $collapsiblockTestNodeTypeDisplay; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected $defaultTheme = 'stark'; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function setUp(): void { -+ parent::setUp(); -+ -+ // Create a content type. -+ $this->collapsiblockTestNodeType = $this->drupalCreateContentType(); -+ $this->collapsiblockTestNodeTypeDisplay -+ = $this->createLayoutBuilderDisplayForNodeType($this->collapsiblockTestNodeType, 'full'); -+ -+ // Create a user with permissions to administer blocks, node fields/display, -+ // and enable layout builder. -+ $this->collapsiblockAdminUser = $this->createUser([ -+ 'configure any layout', -+ 'create and edit custom blocks', -+ 'administer node display', -+ 'administer node fields', -+ 'access contextual links', -+ ]); -+ } -+ -+ /** -+ * Verify the layout builder block config form has Collapsiblock controls. -+ */ -+ public function testConfigureForm() { -+ // Prepare a URL for a component configuration page. Note we are testing the -+ // form placed into the off-canvas dialog by core/drupal.dialog.off_canvas. -+ $delta = 0; -+ $components = $this->collapsiblockTestNodeTypeDisplay->getSection($delta)->getComponents(); -+ $url = Url::fromRoute('layout_builder.update_block', [ -+ 'section_storage_type' => 'defaults', -+ 'section_storage' => $this->collapsiblockTestNodeTypeDisplay->id(), -+ 'delta' => 0, -+ 'region' => $this->getDefaultRegionFromDisplay($this->collapsiblockTestNodeTypeDisplay), -+ 'uuid' => reset($components)->getUuid(), -+ ]); -+ -+ // Log in as the administrator; and go to a Layout Builder page. -+ $this->drupalLogin($this->collapsiblockAdminUser); -+ $this->drupalGet($url); -+ -+ // Test that the form controls are present. -+ $this->assertSession()->checkboxChecked('edit-settings-collapsiblock-settings-collapse-action-0'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-1'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-2'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-3'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-4'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-5'); -+ -+ // Submit the form with updated values. -+ $configFormValues = []; -+ $configFormValues['settings[collapsiblock_settings][collapse_action]'] = '2'; -+ $this->drupalGet($url); -+ $this->submitForm($configFormValues, 'Update'); -+ -+ // Test that the form controls now show the updated configuration. -+ $this->drupalGet($url); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-0'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-1'); -+ $this->assertSession()->checkboxChecked('edit-settings-collapsiblock-settings-collapse-action-2'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-3'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-4'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-5'); -+ } -+ -+ /** -+ * Verify that a LB block configured to collapse actually does so. -+ */ -+ public function testLayoutBuilderBlockWillCollapse() { -+ // Enable the Layout builder. -+ LayoutBuilderEntityViewDisplay::load('node.' . $this->collapsiblockTestNodeType->id() . '.full') -+ ->enableLayoutBuilder() -+ ->setOverridable() -+ ->save(); -+ -+ // Add field Body as block to the section in Layout builder. -+ $this->drupalLogin($this->collapsiblockAdminUser); -+ $nid = $this->collapsiblockTestNodeType->id(); -+ $this->drupalGet('/layout_builder/add/block/defaults/node.' . $nid . '.full/0/content/field_block%3Anode%3A' . $nid . '%3A' . 'body'); -+ $configFormValues = []; -+ $configFormValues['settings[label_display]'] = '1'; -+ $configFormValues['settings[collapsiblock_settings][collapse_action]'] = '2'; -+ $this->submitForm($configFormValues, 'Add block'); -+ $this->getSession()->getPage()->pressButton('Save layout'); -+ -+ // Create a node of the test content type. -+ $this->collapsiblockTestNode = $this->drupalCreateNode([ -+ 'type' => $this->collapsiblockTestNodeType->id(), -+ 'title' => 'Test node', -+ ]); -+ -+ $this->drupalLogout(); -+ -+ $this->drupalLogout(); -+ $this->drupalLogin($this->getCollapsiblockUnprivilegedUser()); -+ -+ // Navigate to the node. -+ $this->drupalGet($this->collapsiblockTestNode->toUrl('canonical')); -+ -+ $collapsiblockTestBlockTitleXpath = $this->assertSession()->buildXPathQuery('//*[@id=:blockId]//h2', [ -+ ':blockId' => '#collapse-blocknode' . $nid . 'body', -+ ]); -+ $collapsiblockTestBlockContentXpath = $this->assertSession()->buildXPathQuery('//*[@id=:blockId]//div[2]/p', [ -+ ':blockId' => 'collapse-blocknode' . $nid . 'body-content', -+ ]); -+ -+ $beforeTitle = $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockTitleXpath); -+ $this->assertNotNull($beforeTitle); -+ $this->assertTrue($beforeTitle->isVisible()); -+ $beforeContent = $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockContentXpath); -+ $this->assertNotNull($beforeContent); -+ $this->assertTrue($beforeContent->isVisible()); -+ -+ // Click on the block title. -+ $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockTitleXpath)->click(); -+ sleep(2); -+ -+ // Check that the block title is visible but the contents are not visible -+ // after the click. -+ $afterTitle = $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockTitleXpath); -+ $this->assertNotNull($afterTitle); -+ $this->assertTrue($afterTitle->isVisible()); -+ $afterContent = $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockContentXpath); -+ $this->assertNotNull($afterContent); -+ $this->assertFalse($afterContent->isVisible()); -+ } -+ -+ /** -+ * Get the default region from a Layout Builder Section. -+ * -+ * @param \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface $display -+ * The display to get the default region from. -+ * @param int $sectionDelta -+ * The delta of the Layout Builder Section to get the default region from. -+ * -+ * @return string -+ * The name of the default region for the given display and section. -+ */ -+ protected function getDefaultRegionFromDisplay(LayoutEntityDisplayInterface $display, $sectionDelta = 0) { -+ return $display->getSection($sectionDelta)->getDefaultRegion(); -+ } -+ -+ /** -+ * Create a new Layout Builder-enabled display from an existing display. -+ * -+ * @param \Drupal\node\Entity\NodeType $nodeType -+ * The content type for both the existing and new displays. -+ * @param string $newDisplayId -+ * The machine name of the view mode for the new display. -+ * @param string $existingDisplayId -+ * The machine name of the view mode for the existing display to be cloned -+ * from. Defaults to 'default'. -+ * -+ * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface -+ * The new display. -+ */ -+ protected function createLayoutBuilderDisplayForNodeType(NodeType $nodeType, $newDisplayId, $existingDisplayId = 'default') { -+ /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $existingDisplay */ -+ $existingDisplay = $this->container->get('entity_display.repository') -+ ->getViewDisplay( -+ 'node', -+ $nodeType->id(), -+ $existingDisplayId -+ ); -+ $newDisplay = $existingDisplay->createCopy($newDisplayId); -+ $newDisplay->enableLayoutBuilder() -+ ->save(); -+ -+ return $newDisplay; -+ } -+ -+ /** -+ * Get a Layout Builder SectionComponent from a entity display. -+ * -+ * @param \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface $display -+ * The display to get the Layout Builder Component from. -+ * @param int $sectionDelta -+ * The delta of the Layout Builder Section to get the Component from. -+ * @param string $pluginId -+ * The plugin ID of the Layout Builder Component to get. -+ * -+ * @return null|\Drupal\layout_builder\SectionComponent -+ * The Layout Builder Section Component in the given display at the given -+ * delta, with the given qualifier; or NULL if one cannot be found. -+ * -+ * @throws \Drupal\Component\Plugin\Exception\PluginException -+ * Throws a Plugin exception if the Component's plugin ID cannot be found. -+ */ -+ protected function getLayoutBuilderComponentFromDisplay(LayoutEntityDisplayInterface $display, $sectionDelta, $pluginId) { -+ $section = $display->getSection($sectionDelta); -+ -+ $components = $section->getComponents(); -+ foreach ($components as $component) { -+ if ($component->getPluginId() === $pluginId) { -+ return $section->getComponent($component->getUuid()); -+ } -+ } -+ -+ return NULL; -+ } -+ -+} -diff --git a/tests/src/FunctionalJavascript/LayoutBuilderUITest.php b/tests/src/FunctionalJavascript/LayoutBuilderUITest.php -new file mode 100644 -index 0000000000000000000000000000000000000000..678f75c64860b6a6b07bf21c3e4ea81234d2555a ---- /dev/null -+++ b/tests/src/FunctionalJavascript/LayoutBuilderUITest.php -@@ -0,0 +1,176 @@ -+<?php -+ -+namespace Drupal\Tests\collapsiblock\Functional; -+ -+use Behat\Mink\Element\DocumentElement; -+use Drupal\block_content\Entity\BlockContentType; -+use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay; -+use Drupal\node\NodeInterface; -+use Drupal\Tests\collapsiblock\FunctionalJavascript\CollapsiblockJavaScriptTestBase; -+ -+/** -+ * Test the Collapsiblock for the content type configuration. -+ * -+ * @group collapsiblock -+ */ -+class LayoutBuilderUITest extends CollapsiblockJavaScriptTestBase { -+ /** -+ * {@inheritdoc} -+ */ -+ protected static $modules = [ -+ 'layout_builder', -+ 'block_content', -+ 'block', -+ 'node', -+ 'field_ui', -+ 'user', -+ ]; -+ -+ /** -+ * The body field uuid. -+ * -+ * @var string -+ */ -+ protected $bodyFieldBlockUuid; -+ -+ /** -+ * The custom default block uuid. -+ * -+ * @var string -+ */ -+ protected $customDefaultBlockUuid; -+ -+ /** -+ * The editor block UUID. -+ * -+ * @var string -+ */ -+ protected $customEditorBlockUuid; -+ -+ -+ /** -+ * The default theme to use. -+ * -+ * @var string -+ */ -+ protected $defaultTheme = 'stark'; -+ -+ /** -+ * A user with all permissions. -+ * -+ * @var \Drupal\user\UserInterface -+ */ -+ protected $adminUser; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function setUp(): void { -+ parent::setUp(); -+ -+ // Enable Layout Builder for landing page. -+ $this->createContentType(['type' => 'test_node']); -+ -+ // Create custom block. -+ $block = BlockContentType::create([ -+ 'id' => 'basic', -+ 'label' => 'Basic', -+ 'revision' => FALSE, -+ ]); -+ $block->save(); -+ block_content_add_body_field($block->id()); -+ -+ // Enable the Layout builder. -+ LayoutBuilderEntityViewDisplay::load('node.test_node.default') -+ ->enableLayoutBuilder() -+ ->setOverridable() -+ ->save(); -+ -+ $this->adminUser = $this->drupalCreateUser([ -+ 'bypass node access', -+ 'configure any layout', -+ 'create and edit custom blocks', -+ 'access contextual links', -+ 'administer node display', -+ ]); -+ -+ } -+ -+ /** -+ * Test block is collapsing in the Layout Builder UI. -+ */ -+ public function testLayoutBuilderBlockIsCollapsed() { -+ $page = $this->getSession()->getPage(); -+ -+ // Create first node. -+ $node = $this->drupalCreateNode([ -+ 'type' => 'test_node', -+ 'title' => 'Homepage 1', -+ ]); -+ -+ // Check as editor. -+ $this->drupalLogin($this->adminUser); -+ $this->drupalGet('node/' . $node->id() . '/layout'); -+ -+ // Add block to default node display. -+ $this->addBlock($node, $page); -+ -+ // Visit the node page. -+ $this->drupalGet('/node/' . $node->id()); -+ -+ $collapsiblockTestBlockTitleXpath = $this->assertSession()->buildXPathQuery('//*[@id=:blockId]//h2', [ -+ ':blockId' => '#collapse-blockbasic', -+ ]); -+ $collapsiblockTestBlockContentXpath = $this->assertSession()->buildXPathQuery('//*[@id=:blockId]//p', [ -+ ':blockId' => 'collapse-blockbasic-content', -+ ]); -+ -+ // We expected that content is hidden. -+ $beforeTitle = $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockTitleXpath); -+ $this->assertNotNull($beforeTitle); -+ $this->assertTrue($beforeTitle->isVisible()); -+ $beforeContent = $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockContentXpath); -+ $this->assertNotNull($beforeContent); -+ $this->assertFalse($beforeContent->isVisible()); -+ -+ // Click by toggle button (the block title). -+ $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockTitleXpath)->click(); -+ sleep(1); -+ -+ // We expected that content is displayed. -+ $beforeTitle = $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockTitleXpath); -+ $this->assertNotNull($beforeTitle); -+ $this->assertTrue($beforeTitle->isVisible()); -+ $beforeContent = $this->getSession()->getPage()->find('xpath', $collapsiblockTestBlockContentXpath); -+ $this->assertNotNull($beforeContent); -+ $this->assertTrue($beforeContent->isVisible()); -+ -+ } -+ -+ /** -+ * Adds custom inline block to default section. -+ * -+ * @param \Drupal\node\NodeInterface $node -+ * The node object. -+ * @param \Behat\Mink\Element\DocumentElement $page -+ * The Layout builder page. -+ */ -+ public function addBlock(NodeInterface $node, DocumentElement $page): void { -+ $this->drupalLogin($this->adminUser); -+ $this->drupalGet('/layout_builder/configure/section/defaults/node.' . $node->bundle() . '.default/0'); -+ $edit = []; -+ $this->submitForm($edit, 'Update'); -+ $this->drupalGet('/layout_builder/add/block/defaults/node.' . $node->bundle() . '.default/0/content/inline_block:basic'); -+ $edit = []; -+ -+ // Add custom block. -+ $edit['settings[label]'] = 'Default custom block title'; -+ $edit['settings[block_form][body][0][value]'] = 'Default custom block content'; -+ $edit['settings[collapsiblock_settings][collapse_action]'] = '3'; -+ $this->submitForm($edit, 'Add block'); -+ -+ // Save Layout configuration. -+ $page->pressButton('Save layout'); -+ } -+ -+} -diff --git a/tests/src/Traits/LayoutBuilderInstanceSettingsTrait.php b/tests/src/Traits/LayoutBuilderInstanceSettingsTrait.php -new file mode 100644 -index 0000000000000000000000000000000000000000..386a3f121f89b6c6fee8988ee7c8e9fcf9844151 ---- /dev/null -+++ b/tests/src/Traits/LayoutBuilderInstanceSettingsTrait.php -@@ -0,0 +1,78 @@ -+<?php -+ -+namespace Drupal\Tests\collapsiblock\Traits; -+ -+use Drupal\layout_builder\SectionComponent; -+ -+/** -+ * Simplify working with Collapsiblock and layout builder components. -+ */ -+trait LayoutBuilderInstanceSettingsTrait { -+ -+ /** -+ * Get whether or not the title is shown for a given Layout Builder Component. -+ * -+ * @param \Drupal\layout_builder\SectionComponent $component -+ * The Layout Builder Section Component to get the title visibility of. -+ * -+ * @return bool -+ * TRUE if the title is visible for the given Layout Builder Component; -+ * FALSE otherwise. -+ */ -+ protected function getLayoutBuilderComponentTitleVisibility(SectionComponent $component) { -+ $configuration = $component->get('configuration'); -+ return boolval($configuration['label_display']); -+ } -+ -+ /** -+ * Get the value of a Collapsiblock setting for a Layout Builder Component. -+ * -+ * @param \Drupal\layout_builder\SectionComponent $component -+ * The Layout Builder Section Component to get the Collapsiblock setting -+ * for. -+ * @param string $key -+ * The name of the Collapsiblock setting to get the value of. -+ * -+ * @return mixed -+ * The value of the given Collapsiblock setting for the given Layout Builder -+ * Section Component. -+ */ -+ protected function getLayoutBuilderInstanceSetting(SectionComponent $component, $key = '') { -+ $collapsiblockSettings = $component->get('collapsiblock') ?: []; -+ return $collapsiblockSettings[$key]; -+ } -+ -+ /** -+ * Set whether or not the title is shown for a given Layout Builder Component. -+ * -+ * @param \Drupal\layout_builder\SectionComponent $component -+ * The Layout Builder Section Component to change the title visibility of. -+ * @param bool $newValue -+ * The new value for the title visibility. TRUE means the title is visible; -+ * FALSE means the title is hidden. -+ */ -+ protected function setLayoutBuilderComponentTitleVisibility(SectionComponent $component, $newValue) { -+ $configuration = $component->get('configuration'); -+ $configuration['label_display'] = boolval($newValue); -+ $component->setConfiguration($configuration); -+ } -+ -+ /** -+ * Set the value of a Collapsiblock setting for a Layout Builder component. -+ * -+ * @param \Drupal\layout_builder\SectionComponent $component -+ * The Layout Builder Section Component to set the Collapsiblock setting -+ * for. -+ * @param mixed $newValue -+ * The new value for the given Collapsiblock setting for the given Layout -+ * Builder Section Component. -+ * @param string $key -+ * The name of the Collapsiblock setting to change. -+ */ -+ protected function setLayoutBuilderInstanceSetting(SectionComponent $component, $newValue, $key = '') { -+ $collapsiblockSettings = $component->get('collapsiblock') ?: []; -+ $collapsiblockSettings[$key] = $newValue; -+ $component->set('collapsiblock', $collapsiblockSettings); -+ } -+ -+} diff --git a/patches/d10/3166181.diff b/patches/d10/3166181.diff deleted file mode 100644 index f64cc878b44056b27979d3afadf9cb82c3079f2b..0000000000000000000000000000000000000000 --- a/patches/d10/3166181.diff +++ /dev/null @@ -1,737 +0,0 @@ -diff --git a/collapsiblock.module b/collapsiblock.module -index 85ab2cee0e0fb7ba582f9c0261b701031426089c..51f89d182f81227ddd1f886da4f974daedea2999 100644 ---- a/collapsiblock.module -+++ b/collapsiblock.module -@@ -18,7 +18,16 @@ use Drupal\Core\Routing\RouteMatchInterface; - * @see collapsiblock_form_block_form_alter() - */ - function collapsiblock_block_form_form_builder($entity_type, Block $block, &$form, FormStateInterface $form_state) { -- $block->setThirdPartySetting('collapsiblock', 'collapse_action', $form_state->getValue('collapsiblock_settings')['collapse_action']); -+ $collapse_action = (int) $form_state->getValue([ -+ 'collapsiblock_settings', -+ 'collapse_action', -+ ]); -+ if ($collapse_action == 0) { -+ $block->unsetThirdPartySetting('collapsiblock', 'collapse_action'); -+ } -+ else { -+ $block->setThirdPartySetting('collapsiblock', 'collapse_action', $collapse_action); -+ } - } - - /** -@@ -30,7 +39,7 @@ function collapsiblock_block_view_alter(array &$build, BlockPluginInterface $blo - } - - $block_entity = $build['#block']; -- $action = $block_entity->getThirdPartySetting('collapsiblock', 'collapse_action'); -+ $action = (int) $block_entity->getThirdPartySetting('collapsiblock', 'collapse_action', 0); - - // If the block is set to global default, get that default and use it instead - // of 0. -@@ -55,7 +64,47 @@ function collapsiblock_block_view_alter(array &$build, BlockPluginInterface $blo - } - - /** -- * Implements hook_form_FORM_ID_alter(). -+ * Implements hook_form_alter(). -+ * -+ * Alter the layout builder "Add Block" and "Update Block" forms. -+ */ -+function collapsiblock_form_alter(&$form, FormStateInterface $form_state, $form_id) { -+ // Affect the Layout Builder's "Add Block" and "Update Block" forms. -+ if ($form['#form_id'] === 'layout_builder_add_block' || $form['#form_id'] === 'layout_builder_update_block') { -+ /** @var \Drupal\layout_builder\SectionComponent $current_layout_builder_component */ -+ $current_layout_builder_component = $form_state->getFormObject()->getCurrentComponent(); -+ $saved_settings = $current_layout_builder_component->get('collapsiblock') ?: ['collapse_action' => 0]; -+ -+ $form['settings']['collapsiblock_settings'] = [ -+ '#type' => 'details', -+ '#title' => t('Collapsible'), -+ '#open' => TRUE, -+ ]; -+ -+ $options = CollapsiblockGlobalSettings::ACTION_OPTIONS; -+ $settings = \Drupal::config('collapsiblock.settings'); -+ $default_action = $options[$settings->get('default_action')]; -+ $options = [0 => 'Global default, currently set to: ' . $default_action] + $options; -+ -+ $form['settings']['collapsiblock_settings']['collapse_action'] = [ -+ '#type' => 'radios', -+ '#title' => t('Block collapse behavior'), -+ '#options' => $options, -+ '#default_value' => $saved_settings['collapse_action'], -+ ]; -+ -+ // Add our own form submit handler at the start of the #submit callbacks -+ // so that it can modify the SectionComponent configuration before it is -+ // saved by the default form submit handler, -+ // \Drupal\layout_builder\Form\ConfigureBlockFormBase::submitForm(). -+ array_unshift($form['#submit'], 'collapsiblock_form_layout_builder_submit'); -+ } -+} -+ -+/** -+ * Implements hook_form_FORM_ID_alter() for \Drupal\block\BlockForm. -+ * -+ * Alter the "Configure block" form. - */ - function collapsiblock_form_block_form_alter(&$form, FormStateInterface $form_state, $form_id) { - -@@ -82,6 +131,32 @@ function collapsiblock_form_block_form_alter(&$form, FormStateInterface $form_st - $form['#entity_builders'][] = 'collapsiblock_block_form_form_builder'; - } - -+/** -+ * Layout builder form submit callback. -+ * -+ * @see collapsiblock_form_alter() -+ */ -+function collapsiblock_form_layout_builder_submit(array $form, FormStateInterface $form_state) { -+ /** @var \Drupal\layout_builder\SectionComponent $current_layout_builder_component */ -+ $current_layout_builder_component = $form_state->getFormObject()->getCurrentComponent(); -+ -+ // Update the settings to include our additional collapsiblock settings. -+ $additional = $current_layout_builder_component->get('additional'); -+ $collapse_action = (int) $form_state->getValue([ -+ 'settings', -+ 'collapsiblock_settings', -+ 'collapse_action', -+ ]); -+ if ($collapse_action === 0) { -+ // Don't add any new settings if using default settings. -+ unset($additional['collapsiblock']); -+ } -+ else { -+ $additional['collapsiblock']['collapse_action'] = $collapse_action; -+ } -+ $current_layout_builder_component->set('additional', $additional); -+} -+ - /** - * Implements hook_help(). - */ -@@ -153,7 +228,7 @@ function collapsiblock_page_attachments_alter(array &$attachments) { - */ - function collapsiblock_preprocess_block(&$variables) { - if (isset($variables['elements']['#collapsiblock'])) { -- $variables['title_prefix'][] = $variables['elements']['#collapsiblock']['prefix']; -- $variables['title_suffix'][] = $variables['elements']['#collapsiblock']['suffix']; -+ $variables['title_prefix']['collapsiblock'] = $variables['elements']['#collapsiblock']['prefix']; -+ $variables['title_suffix']['collapsiblock'] = $variables['elements']['#collapsiblock']['suffix']; - } - } -diff --git a/collapsiblock.services.yml b/collapsiblock.services.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..eabdfd9b209af096356476aac99dff9770a5a1b0 ---- /dev/null -+++ b/collapsiblock.services.yml -@@ -0,0 +1,8 @@ -+services: -+ -+ # An event handler to alter how Layout Builder blocks are rendered. -+ collapsiblock.layout_builder_block_component_render: -+ class: 'Drupal\collapsiblock\EventSubscriber\LayoutBuilderBlockComponentRender' -+ arguments: ['@config.factory'] -+ tags: -+ - { name: event_subscriber } -diff --git a/config/install/collapsiblock.settings.yml b/config/install/collapsiblock.settings.yml -index 3d4b8c7605dde2c7a4d35f226201b2f1dbde5123..2346fcc6a8471d4b3a10697274493ce5932c9391 100644 ---- a/config/install/collapsiblock.settings.yml -+++ b/config/install/collapsiblock.settings.yml -@@ -1,4 +1,4 @@ - default_action: 1 - active_pages: FALSE - slide_speed: 200 --cookie_lifetime: -+cookie_lifetime: NULL -diff --git a/config/schema/collapsiblock.schema.yml b/config/schema/collapsiblock.schema.yml -index a2d6ab585703f7380644c90ab380cf17679f99f2..31d05fdbc40582fe100a2999afb159e956aa3dcd 100644 ---- a/config/schema/collapsiblock.schema.yml -+++ b/config/schema/collapsiblock.schema.yml -@@ -21,7 +21,7 @@ collapsiblock.settings: - cookie_lifetime: - type: float - label: 'Cookie expiration' -- translatable: false, -+ translatable: false - nullable: true - - # Block instance settings. -diff --git a/src/EventSubscriber/LayoutBuilderBlockComponentRender.php b/src/EventSubscriber/LayoutBuilderBlockComponentRender.php -new file mode 100644 -index 0000000000000000000000000000000000000000..106a10a7ecadf5e6a667f6eb17bb82c8ae11f636 ---- /dev/null -+++ b/src/EventSubscriber/LayoutBuilderBlockComponentRender.php -@@ -0,0 +1,107 @@ -+<?php -+ -+namespace Drupal\collapsiblock\EventSubscriber; -+ -+use Drupal\Component\EventDispatcher\Event; -+use Drupal\Component\Utility\Html; -+use Drupal\Core\Config\ConfigFactoryInterface; -+use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent; -+use Symfony\Component\EventDispatcher\EventSubscriberInterface; -+ -+/** -+ * An event handler to alter how Layout Builder blocks are rendered. -+ */ -+class LayoutBuilderBlockComponentRender implements EventSubscriberInterface { -+ -+ /** -+ * A config object for the collapisblock configuration. -+ * -+ * @var \Drupal\Core\Config\ImmutableConfig -+ */ -+ protected $collapsibleBlockConfig; -+ -+ /** -+ * Constructs the layout builder block event subscriber. -+ */ -+ public function __construct(ConfigFactoryInterface $config_factory) { -+ $this->collapsibleBlockConfig = $config_factory->get('collapsiblock.settings'); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public static function getSubscribedEvents() { -+ // Attach Collapsiblock settings and libraries to layout builder components -+ // when a layout builder Section Component's render array is being built. -+ // -+ // Note that 'section_component.build.render_array' is the value of -+ // \Drupal\layout_builder\LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY, -+ // but we can't use the constant directly, because it does not resolve when -+ // the layout_builder module is disabled. -+ $events = []; -+ $events['section_component.build.render_array'] = 'attachCollapsiblock'; -+ -+ return $events; -+ } -+ -+ /** -+ * Attach Collapsiblock settings and libraries to layout builder components. -+ * -+ * This acts when a layout builder Section Component's render array is being -+ * built. -+ * -+ * @param \Drupal\Component\EventDispatcher\Event $event -+ * An event object containing context about the Section Component and the -+ * render array being built. Should be an instance of -+ * \Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent, -+ * otherwise, no actions will be performed. -+ * -+ * @see \collapsiblock_block_view_alter() -+ * @see \Drupal\layout_builder\LayoutBuilderEvents::SECTION_COMPONENT_BUILD_RENDER_ARRAY -+ * @see getSubscribedEvents() -+ */ -+ public function attachCollapsiblock(Event $event) { -+ if (!($event instanceof SectionComponentBuildRenderArrayEvent)) { -+ return; -+ } -+ -+ $build = $event->getBuild(); -+ $current_lb_component = $event->getComponent(); -+ $saved_settings = $current_lb_component->get('additional'); -+ $saved_settings += [ -+ 'collapsiblock' => [ -+ 'collapse_action' => 0, -+ ], -+ ]; -+ -+ if (empty($build['#configuration']['id'])) { -+ return; -+ } -+ -+ // If the action is anything other than 'none', create our wrapper -+ // elements. -+ $collapse_action = $saved_settings['collapsiblock']['collapse_action']; -+ if ($collapse_action == 0) { -+ $collapse_action = $this->collapsibleBlockConfig->get('default_action'); -+ } -+ -+ if ($collapse_action != 1) { -+ // Generate a valid HTML ID. -+ $id = Html::getId('collapsiblock-wrapper-' . $build['#configuration']['id']); -+ $classes = []; -+ $classes[] = 'collapsiblockTitle'; -+ -+ $build['#collapsiblock']['prefix'] = [ -+ '#markup' => sprintf('<div id="%s" class="%s" data-collapsiblock-action="%s">', $id, implode(' ', $classes), $collapse_action), -+ ]; -+ $build['#collapsiblock']['suffix'] = [ -+ 'collapsiblock' => [ -+ '#markup' => '</div>', -+ ], -+ ]; -+ $event->setBuild($build); -+ } -+ $event->addCacheableDependency($this->collapsibleBlockConfig); -+ } -+ -+} -diff --git a/tests/src/FunctionalJavascript/LayoutBuilderTest.php b/tests/src/FunctionalJavascript/LayoutBuilderTest.php -new file mode 100644 -index 0000000000000000000000000000000000000000..496dca15a95250ad64a549d4136c9ac11fbcf51e ---- /dev/null -+++ b/tests/src/FunctionalJavascript/LayoutBuilderTest.php -@@ -0,0 +1,203 @@ -+<?php -+ -+namespace Drupal\Tests\collapsiblock\FunctionalJavascript; -+ -+use Drupal\Core\Url; -+use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface; -+use Drupal\node\Entity\NodeType; -+use Drupal\Tests\collapsiblock\Traits\LayoutBuilderInstanceSettingsTrait; -+ -+/** -+ * Test that Layout Builder blocks can be affected by Collapsiblock. -+ * -+ * @group collapsiblock -+ */ -+class LayoutBuilderTest extends CollapsiblockJavaScriptTestBase { -+ use LayoutBuilderInstanceSettingsTrait; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected static $modules = [ -+ 'block', -+ 'collapsiblock', -+ 'field_layout', -+ 'field_ui', -+ 'layout_builder', -+ 'node', -+ ]; -+ -+ /** -+ * User with permissions to administer a node type's fields w/ layout builder. -+ * -+ * @var \Drupal\user\UserInterface -+ */ -+ protected $collapsiblockAdminUser; -+ -+ /** -+ * A node whose "full" display's fields are managed by layout builder. -+ * -+ * @var \Drupal\node\NodeInterface -+ */ -+ protected $collapsiblockTestNode; -+ -+ /** -+ * A node type whose "full" display's fields are managed by layout builder. -+ * -+ * @var \Drupal\node\Entity\NodeType -+ */ -+ protected $collapsiblockTestNodeType; -+ -+ /** -+ * The "full" display whose fields are managed by layout builder. -+ * -+ * @var \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface -+ */ -+ protected $collapsiblockTestNodeTypeDisplay; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected $defaultTheme = 'stark'; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function setUp(): void { -+ parent::setUp(); -+ -+ // Create a content type. -+ $this->collapsiblockTestNodeType = $this->drupalCreateContentType(); -+ $this->collapsiblockTestNodeTypeDisplay -+ = $this->createLayoutBuilderDisplayForNodeType($this->collapsiblockTestNodeType, 'full'); -+ -+ // Create a user with permissions to administer blocks, node fields/display, -+ // and enable layout builder. -+ $this->collapsiblockAdminUser = $this->createUser([ -+ 'configure any layout', -+ 'create and edit custom blocks', -+ 'administer node display', -+ 'administer node fields', -+ 'access contextual links', -+ ]); -+ } -+ -+ /** -+ * Verify the layout builder block config form has Collapsiblock controls. -+ */ -+ public function testConfigureForm() { -+ // Prepare a URL for a component configuration page. Note we are testing the -+ // form placed into the off-canvas dialog by core/drupal.dialog.off_canvas. -+ $delta = 0; -+ $components = $this->collapsiblockTestNodeTypeDisplay->getSection($delta)->getComponents(); -+ $url = Url::fromRoute('layout_builder.update_block', [ -+ 'section_storage_type' => 'defaults', -+ 'section_storage' => $this->collapsiblockTestNodeTypeDisplay->id(), -+ 'delta' => 0, -+ 'region' => $this->getDefaultRegionFromDisplay($this->collapsiblockTestNodeTypeDisplay), -+ 'uuid' => reset($components)->getUuid(), -+ ]); -+ -+ // Log in as the administrator; and go to a Layout Builder page. -+ $this->drupalLogin($this->collapsiblockAdminUser); -+ $this->drupalGet($url); -+ -+ // Test that the form controls are present. -+ $this->assertSession()->checkboxChecked('edit-settings-collapsiblock-settings-collapse-action-0'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-1'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-2'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-3'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-4'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-5'); -+ -+ // Submit the form with updated values. -+ $configFormValues = []; -+ $configFormValues['settings[collapsiblock_settings][collapse_action]'] = '2'; -+ $this->drupalGet($url); -+ $this->submitForm($configFormValues, 'Update'); -+ -+ // Test that the form controls now show the updated configuration. -+ $this->drupalGet($url); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-0'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-1'); -+ $this->assertSession()->checkboxChecked('edit-settings-collapsiblock-settings-collapse-action-2'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-3'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-4'); -+ $this->assertSession()->checkboxNotChecked('edit-settings-collapsiblock-settings-collapse-action-5'); -+ } -+ -+ /** -+ * Get the default region from a Layout Builder Section. -+ * -+ * @param \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface $display -+ * The display to get the default region from. -+ * @param int $sectionDelta -+ * The delta of the Layout Builder Section to get the default region from. -+ * -+ * @return string -+ * The name of the default region for the given display and section. -+ */ -+ protected function getDefaultRegionFromDisplay(LayoutEntityDisplayInterface $display, $sectionDelta = 0) { -+ return $display->getSection($sectionDelta)->getDefaultRegion(); -+ } -+ -+ /** -+ * Create a new Layout Builder-enabled display from an existing display. -+ * -+ * @param \Drupal\node\Entity\NodeType $nodeType -+ * The content type for both the existing and new displays. -+ * @param string $newDisplayId -+ * The machine name of the view mode for the new display. -+ * @param string $existingDisplayId -+ * The machine name of the view mode for the existing display to be cloned -+ * from. Defaults to 'default'. -+ * -+ * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface -+ * The new display. -+ */ -+ protected function createLayoutBuilderDisplayForNodeType(NodeType $nodeType, $newDisplayId, $existingDisplayId = 'default') { -+ /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $existingDisplay */ -+ $existingDisplay = $this->container->get('entity_display.repository') -+ ->getViewDisplay( -+ 'node', -+ $nodeType->id(), -+ $existingDisplayId -+ ); -+ $newDisplay = $existingDisplay->createCopy($newDisplayId); -+ $newDisplay->enableLayoutBuilder() -+ ->save(); -+ -+ return $newDisplay; -+ } -+ -+ /** -+ * Get a Layout Builder SectionComponent from a entity display. -+ * -+ * @param \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface $display -+ * The display to get the Layout Builder Component from. -+ * @param int $sectionDelta -+ * The delta of the Layout Builder Section to get the Component from. -+ * @param string $pluginId -+ * The plugin ID of the Layout Builder Component to get. -+ * -+ * @return null|\Drupal\layout_builder\SectionComponent -+ * The Layout Builder Section Component in the given display at the given -+ * delta, with the given qualifier; or NULL if one cannot be found. -+ * -+ * @throws \Drupal\Component\Plugin\Exception\PluginException -+ * Throws a Plugin exception if the Component's plugin ID cannot be found. -+ */ -+ protected function getLayoutBuilderComponentFromDisplay(LayoutEntityDisplayInterface $display, $sectionDelta, $pluginId) { -+ $section = $display->getSection($sectionDelta); -+ -+ $components = $section->getComponents(); -+ foreach ($components as $component) { -+ if ($component->getPluginId() === $pluginId) { -+ return $section->getComponent($component->getUuid()); -+ } -+ } -+ -+ return NULL; -+ } -+ -+} -diff --git a/tests/src/FunctionalJavascript/LayoutBuilderUITest.php b/tests/src/FunctionalJavascript/LayoutBuilderUITest.php -new file mode 100644 -index 0000000000000000000000000000000000000000..1640c030e3b77923548ee4944fabb16d2212c60f ---- /dev/null -+++ b/tests/src/FunctionalJavascript/LayoutBuilderUITest.php -@@ -0,0 +1,165 @@ -+<?php -+ -+namespace Drupal\Tests\collapsiblock\Functional; -+ -+use Behat\Mink\Element\DocumentElement; -+use Drupal\block_content\Entity\BlockContentType; -+use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay; -+use Drupal\node\NodeInterface; -+use Drupal\Tests\collapsiblock\FunctionalJavascript\CollapsiblockJavaScriptTestBase; -+ -+/** -+ * Test the Collapsiblock for the content type configuration. -+ * -+ * @group collapsiblock -+ */ -+class LayoutBuilderUITest extends CollapsiblockJavaScriptTestBase { -+ /** -+ * {@inheritdoc} -+ */ -+ protected static $modules = [ -+ 'layout_builder', -+ 'block_content', -+ 'block', -+ 'node', -+ 'field_ui', -+ 'user', -+ ]; -+ -+ /** -+ * The body field uuid. -+ * -+ * @var string -+ */ -+ protected $bodyFieldBlockUuid; -+ -+ /** -+ * The custom default block uuid. -+ * -+ * @var string -+ */ -+ protected $customDefaultBlockUuid; -+ -+ /** -+ * The editor block UUID. -+ * -+ * @var string -+ */ -+ protected $customEditorBlockUuid; -+ -+ -+ /** -+ * The default theme to use. -+ * -+ * @var string -+ */ -+ protected $defaultTheme = 'stark'; -+ -+ /** -+ * A user with all permissions. -+ * -+ * @var \Drupal\user\UserInterface -+ */ -+ protected $adminUser; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function setUp(): void { -+ parent::setUp(); -+ -+ // Enable Layout Builder for landing page. -+ $this->createContentType(['type' => 'test_node']); -+ -+ // Create custom block. -+ $block = BlockContentType::create([ -+ 'id' => 'basic', -+ 'label' => 'Basic', -+ 'revision' => FALSE, -+ ]); -+ $block->save(); -+ block_content_add_body_field($block->id()); -+ -+ // Enable the Layout builder. -+ LayoutBuilderEntityViewDisplay::load('node.test_node.default') -+ ->enableLayoutBuilder() -+ ->setOverridable() -+ ->save(); -+ -+ $this->adminUser = $this->drupalCreateUser([ -+ 'bypass node access', -+ 'configure any layout', -+ 'create and edit custom blocks', -+ 'access contextual links', -+ 'administer node display', -+ ]); -+ -+ } -+ -+ public function testLayoutBuilderBlockIsCollapsed() { -+ $page = $this->getSession()->getPage(); -+ -+ // Create first node. -+ $node = $this->drupalCreateNode([ -+ 'type' => 'test_node', -+ 'title' => 'Homepage 1', -+ ]); -+ -+ // Check as editor. -+ $this->drupalLogin($this->adminUser); -+ $this->drupalGet('node/' . $node->id() . '/layout'); -+ -+ // Add block to default node display. -+ $this->addBlock($node, $page); -+ -+ // Visit the node page. -+ $this->drupalGet('/node/' . $node->id()); -+ -+ // We expected that content is hidden. -+ $beforeTitle = $this->getSession()->getPage()->find('css', "button h2"); -+ $this->assertNotNull($beforeTitle); -+ $this->assertTrue($beforeTitle->isVisible()); -+ $beforeContent = $this->getSession()->getPage()->find('css', ".collapsiblockContent"); -+ $this->assertNotNull($beforeContent); -+ $this->assertFalse($beforeContent->isVisible()); -+ -+ // Click by toggle button (the block title). -+ $this->getSession()->getPage()->find('css', "button h2")->click(); -+ sleep(1); -+ -+ // We expected that content is displayed. -+ $beforeTitle = $this->getSession()->getPage()->find('css', "button h2"); -+ $this->assertNotNull($beforeTitle); -+ $this->assertTrue($beforeTitle->isVisible()); -+ $beforeContent = $this->getSession()->getPage()->find('css', ".collapsiblockContent"); -+ $this->assertNotNull($beforeContent); -+ $this->assertTrue($beforeContent->isVisible()); -+ -+ } -+ -+ /** -+ * Adds custom inline block to default section. -+ * -+ * @param \Drupal\node\NodeInterface $node -+ * The node object. -+ * @param \Behat\Mink\Element\DocumentElement $page -+ * The Layout builder page. -+ */ -+ public function addBlock(NodeInterface $node, DocumentElement $page): void { -+ $this->drupalLogin($this->adminUser); -+ $this->drupalGet('/layout_builder/configure/section/defaults/node.' . $node->bundle() . '.default/0'); -+ $edit = []; -+ $this->submitForm($edit, 'Update'); -+ $this->drupalGet('/layout_builder/add/block/defaults/node.' . $node->bundle() . '.default/0/content/inline_block:basic'); -+ $edit = []; -+ -+ // Add custom block. -+ $edit['settings[label]'] = 'Default custom block title'; -+ $edit['settings[block_form][body][0][value]'] = 'Default custom block content'; -+ $edit['settings[collapsiblock_settings][collapse_action]'] = '3'; -+ $this->submitForm($edit, 'Add block'); -+ -+ // Save Layout configuration. -+ $page->pressButton('Save layout'); -+ } -+} -diff --git a/tests/src/Traits/LayoutBuilderInstanceSettingsTrait.php b/tests/src/Traits/LayoutBuilderInstanceSettingsTrait.php -new file mode 100644 -index 0000000000000000000000000000000000000000..386a3f121f89b6c6fee8988ee7c8e9fcf9844151 ---- /dev/null -+++ b/tests/src/Traits/LayoutBuilderInstanceSettingsTrait.php -@@ -0,0 +1,78 @@ -+<?php -+ -+namespace Drupal\Tests\collapsiblock\Traits; -+ -+use Drupal\layout_builder\SectionComponent; -+ -+/** -+ * Simplify working with Collapsiblock and layout builder components. -+ */ -+trait LayoutBuilderInstanceSettingsTrait { -+ -+ /** -+ * Get whether or not the title is shown for a given Layout Builder Component. -+ * -+ * @param \Drupal\layout_builder\SectionComponent $component -+ * The Layout Builder Section Component to get the title visibility of. -+ * -+ * @return bool -+ * TRUE if the title is visible for the given Layout Builder Component; -+ * FALSE otherwise. -+ */ -+ protected function getLayoutBuilderComponentTitleVisibility(SectionComponent $component) { -+ $configuration = $component->get('configuration'); -+ return boolval($configuration['label_display']); -+ } -+ -+ /** -+ * Get the value of a Collapsiblock setting for a Layout Builder Component. -+ * -+ * @param \Drupal\layout_builder\SectionComponent $component -+ * The Layout Builder Section Component to get the Collapsiblock setting -+ * for. -+ * @param string $key -+ * The name of the Collapsiblock setting to get the value of. -+ * -+ * @return mixed -+ * The value of the given Collapsiblock setting for the given Layout Builder -+ * Section Component. -+ */ -+ protected function getLayoutBuilderInstanceSetting(SectionComponent $component, $key = '') { -+ $collapsiblockSettings = $component->get('collapsiblock') ?: []; -+ return $collapsiblockSettings[$key]; -+ } -+ -+ /** -+ * Set whether or not the title is shown for a given Layout Builder Component. -+ * -+ * @param \Drupal\layout_builder\SectionComponent $component -+ * The Layout Builder Section Component to change the title visibility of. -+ * @param bool $newValue -+ * The new value for the title visibility. TRUE means the title is visible; -+ * FALSE means the title is hidden. -+ */ -+ protected function setLayoutBuilderComponentTitleVisibility(SectionComponent $component, $newValue) { -+ $configuration = $component->get('configuration'); -+ $configuration['label_display'] = boolval($newValue); -+ $component->setConfiguration($configuration); -+ } -+ -+ /** -+ * Set the value of a Collapsiblock setting for a Layout Builder component. -+ * -+ * @param \Drupal\layout_builder\SectionComponent $component -+ * The Layout Builder Section Component to set the Collapsiblock setting -+ * for. -+ * @param mixed $newValue -+ * The new value for the given Collapsiblock setting for the given Layout -+ * Builder Section Component. -+ * @param string $key -+ * The name of the Collapsiblock setting to change. -+ */ -+ protected function setLayoutBuilderInstanceSetting(SectionComponent $component, $newValue, $key = '') { -+ $collapsiblockSettings = $component->get('collapsiblock') ?: []; -+ $collapsiblockSettings[$key] = $newValue; -+ $component->set('collapsiblock', $collapsiblockSettings); -+ } -+ -+} diff --git a/patches/d10/3204051.diff b/patches/d10/3204051.diff deleted file mode 100644 index 651201b2761ddfb1464e182bf982604a3d5e5e2a..0000000000000000000000000000000000000000 --- a/patches/d10/3204051.diff +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/src/Plugin/Field/FieldWidget/InlineEntityFormSimple.php b/src/Plugin/Field/FieldWidget/InlineEntityFormSimple.php -index 51e0acca397a35054ce08568a32d1ca76cdc8912..840478e2a98964b70b28f7f61d2fdceda4a8ffea 100644 ---- a/src/Plugin/Field/FieldWidget/InlineEntityFormSimple.php -+++ b/src/Plugin/Field/FieldWidget/InlineEntityFormSimple.php -@@ -134,12 +134,12 @@ class InlineEntityFormSimple extends InlineEntityFormBase { - $submitted_values = $form_state->getValue($parents); - $values = []; - foreach ($items as $delta => $value) { -- if ($element = NestedArray::getValue( -- $form, -- [$field_name, 'widget', $delta] -- )) { -+ if ( -+ ($element = NestedArray::getValue($form, [$field_name, 'widget', $delta])) - /** @var \Drupal\Core\Entity\EntityInterface $entity */ -- $entity = $element['inline_entity_form']['#entity']; -+ && ($entity = $element['inline_entity_form']['#entity'] ?? NULL) -+ ) { -+ // @todo Can this be unset? If yes, can the '0' fallback clash? - $weight = $submitted_values[$delta]['_weight'] ?? 0; - $values[$weight] = ['entity' => $entity]; - } diff --git a/patches/d10/3208959.diff b/patches/d10/3208959.diff deleted file mode 100644 index 490c9cca69b858f681549c330292a6cf440eff53..0000000000000000000000000000000000000000 --- a/patches/d10/3208959.diff +++ /dev/null @@ -1,35 +0,0 @@ -diff --git a/fakeobjects.install b/fakeobjects.install -index c664d41134301a481031235484bb759b7b5beae1..8f6925ce601bef811ef46598251c192c9e899eae 100644 ---- a/fakeobjects.install -+++ b/fakeobjects.install -@@ -12,7 +12,7 @@ function fakeobjects_requirements($phase) { - $requirements = []; - - if ($phase == 'install' || $phase == 'runtime') { -- $plugin_detected = file_exists(DRUPAL_ROOT . '/libraries/fakeobjects/plugin.js'); -+ $plugin_detected = file_exists(DRUPAL_ROOT . '/libraries/ckeditor.fakeobjects/plugin.js'); - - if ($plugin_detected) { - $requirements['fakeobjects'] = [ -@@ -26,7 +26,7 @@ function fakeobjects_requirements($phase) { - 'title' => t('FakeObjects'), - 'value' => t('Plugin not detected'), - 'severity' => REQUIREMENT_ERROR, -- 'description' => t('Before you can use the FakeObjects module, you need to download the plugin from ckeditor.com and place it in /libraries/fakeobjects. Check the README.txt for more information. <a href=":plugin_url">Get the plugin here</a>.', [':plugin_url' => 'http://ckeditor.com/addon/fakeobjects']), -+ 'description' => t('Before you can use the FakeObjects module, you need to download the plugin from ckeditor.com and place it in /libraries/ckeditor.fakeobjects. Check the README.txt for more information. <a href=":plugin_url">Get the plugin here</a>.', [':plugin_url' => 'http://ckeditor.com/addon/fakeobjects']), - ]; - } - } -diff --git a/src/Plugin/CKEditorPlugin/FakeObjects.php b/src/Plugin/CKEditorPlugin/FakeObjects.php -index e405394fdc2a59be87974b0592f3fa38672b72d0..dd222dc702a9c22f635febf777a33cfc869ecbeb 100644 ---- a/src/Plugin/CKEditorPlugin/FakeObjects.php -+++ b/src/Plugin/CKEditorPlugin/FakeObjects.php -@@ -19,7 +19,7 @@ class FakeObjects extends CKEditorPluginBase { - * {@inheritdoc} - */ - public function getFile() { -- return 'libraries/fakeobjects/plugin.js'; -+ return 'libraries/ckeditor.fakeobjects/plugin.js'; - } - - /** diff --git a/patches/d10/3219083.diff b/patches/d10/3219083.diff deleted file mode 100644 index faefaf608f1b8c60341b662c9a03b600ff4fe770..0000000000000000000000000000000000000000 --- a/patches/d10/3219083.diff +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/src/ConfigImporterExporter.php b/src/ConfigImporterExporter.php -index 1c0196151a0d8f0d0390e4043b27a5d0b5f7e9ca..88dc5cc47600e025e7c6a825869f92ae4f2a3e4c 100644 ---- a/src/ConfigImporterExporter.php -+++ b/src/ConfigImporterExporter.php -@@ -230,7 +230,7 @@ class ConfigImporterExporter { - $written_files = []; - - if ($file_names) { -- $data = $config->get(); -+ $data = $config->getRawData(); - $config_name = $config->getName(); - unset($data['_core']); - if ($entity_type_id = $this->configManager->getEntityTypeIdByName($config_name)) { -diff --git a/src/EventSubscriber/ConfigDevelAutoExportSubscriber.php b/src/EventSubscriber/ConfigDevelAutoExportSubscriber.php -index 44dc76ecbb3de80ed6132171f9876151491a4268..406eb508aa08a00b9ddf0a399874c71e4748d7ce 100644 ---- a/src/EventSubscriber/ConfigDevelAutoExportSubscriber.php -+++ b/src/EventSubscriber/ConfigDevelAutoExportSubscriber.php -@@ -98,7 +98,7 @@ class ConfigDevelAutoExportSubscriber extends ConfigDevelSubscriberBase implemen - public function writeBackConfig(Config $config, array $file_names) { - // TODO: use the config_devel.importer_exporter service. - if ($file_names) { -- $data = $config->get(); -+ $data = $config->getRawData(); - $config_name = $config->getName(); - unset($data['_core']); - if ($entity_type_id = $this->configManager->getEntityTypeIdByName($config_name)) { diff --git a/patches/d10/3227732.diff b/patches/d10/3227732.diff deleted file mode 100644 index 37dc0c58cb5b2927f40028f437efc1c9e15e6bb9..0000000000000000000000000000000000000000 --- a/patches/d10/3227732.diff +++ /dev/null @@ -1,38 +0,0 @@ -diff --git a/core/modules/book/src/Plugin/migrate/destination/Book.php b/core/modules/book/src/Plugin/migrate/destination/Book.php -index dcc5056c89af907650b90e5b02d5bb2df8531456..55496aed7fc0693a956bbfa1634a19ed2b200132 100644 ---- a/core/modules/book/src/Plugin/migrate/destination/Book.php -+++ b/core/modules/book/src/Plugin/migrate/destination/Book.php -@@ -3,6 +3,7 @@ - namespace Drupal\book\Plugin\migrate\destination; - - use Drupal\Core\Entity\EntityInterface; -+use Drupal\menu_link_content\Entity\MenuLinkContent; - use Drupal\migrate\Plugin\migrate\destination\EntityContentBase; - use Drupal\migrate\Row; - -@@ -34,6 +35,12 @@ protected function updateEntity(EntityInterface $entity, Row $row) { - else { - $entity->book = $row->getDestinationProperty('book'); - } -+ if (($mid = $entity->book['pid']) && ($menuLink = MenuLinkContent::load($mid)) && $url = $menuLink->getUrlObject()) { -+ $parameters = $url->getRouteParameters(); -+ if (isset($parameters['node'])) { -+ $entity->book['pid'] = $parameters['node']; -+ } -+ } - return parent::updateEntity($entity, $row); - } - -diff --git a/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php b/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php -index 1b186a6e04a18749d3ccb7e09194364c5e485c44..f28ab3ec2f46c2df995ea588f0b2b316b3380ef2 100644 ---- a/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php -+++ b/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php -@@ -23,7 +23,7 @@ public function query() { - $query = $this->select('menu_links', 'ml') - ->fields('ml'); - $and = $query->andConditionGroup() -- ->condition('ml.module', 'menu') -+ ->condition('ml.module', ['menu', 'book'], 'IN') - ->condition('ml.router_path', ['admin/build/menu-customize/%', 'admin/structure/menu/manage/%'], 'NOT IN'); - $condition = $query->orConditionGroup() - ->condition('ml.customized', 1) diff --git a/patches/d10/3227813.diff b/patches/d10/3227813.diff deleted file mode 100644 index aaff701bfdbd4093cb75ac7af48375407537dc17..0000000000000000000000000000000000000000 --- a/patches/d10/3227813.diff +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/ldap_authentication/ldap_authentication.module b/ldap_authentication/ldap_authentication.module -index 07ac1740eac87f464f5801babcbbda94a07102bd..3fb89b4fec4550c70d04fde73aa6f7587c5f382b 100644 ---- a/ldap_authentication/ldap_authentication.module -+++ b/ldap_authentication/ldap_authentication.module -@@ -169,6 +169,11 @@ function ldap_authentication_form_user_form_alter(&$form, FormStateInterface $fo - */ - function ldap_authentication_show_password_field($user = NULL): bool { - -+ $config = \Drupal::config('ldap_authentication.settings'); -+ if ($config->get('authenticationMode') === 'exclusive') { -+ return FALSE; -+ } -+ - if (!$user) { - $user = \Drupal::currentUser(); - } -@@ -182,8 +187,7 @@ function ldap_authentication_show_password_field($user = NULL): bool { - $authmap = \Drupal::service('externalauth.authmap'); - $authname = $authmap->get($user->id(), 'ldap_user'); - if ($authname) { -- $password_option = \Drupal::config('ldap_authentication.settings') -- ->get('passwordOption'); -+ $password_option = $config->get('passwordOption'); - return $password_option === 'allow'; - } - diff --git a/patches/d10/3227816.diff b/patches/d10/3227816.diff deleted file mode 100644 index 97c9a13d7d35feb3ef14bf314cf695fa4b625c57..0000000000000000000000000000000000000000 --- a/patches/d10/3227816.diff +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/core/modules/user/src/Plugin/Block/UserLoginBlock.php b/core/modules/user/src/Plugin/Block/UserLoginBlock.php -index f55a70d6118f6c9e85d3384d0182cdd3de508b74..6858e46ed0c5e77f3a362e670a950c570f1edde3 100644 ---- a/core/modules/user/src/Plugin/Block/UserLoginBlock.php -+++ b/core/modules/user/src/Plugin/Block/UserLoginBlock.php -@@ -126,16 +126,19 @@ public function build() { - ]), - ]; - } -- $items['request_password'] = [ -- '#type' => 'link', -- '#title' => $this->t('Reset your password'), -- '#url' => Url::fromRoute('user.pass', [], [ -- 'attributes' => [ -- 'title' => $this->t('Send password reset instructions via email.'), -- 'class' => ['request-password-link'], -- ], -- ]), -- ]; -+ $url = Url::fromRoute('user.pass', [], [ -+ 'attributes' => [ -+ 'title' => $this->t('Send password reset instructions via email.'), -+ 'class' => ['request-password-link'], -+ ], -+ ]); -+ if ($url->access()) { -+ $items['request_password'] = [ -+ '#type' => 'link', -+ '#title' => $this->t('Reset your password'), -+ '#url' => $url, -+ ]; -+ } - return [ - 'user_login_form' => $form, - 'user_links' => [ diff --git a/patches/d10/3227881.diff b/patches/d10/3227881.diff deleted file mode 100644 index 0b72badd218387490d1a99e5dd6a9aaf65c1df6b..0000000000000000000000000000000000000000 --- a/patches/d10/3227881.diff +++ /dev/null @@ -1,118 +0,0 @@ -diff --git a/src/Commands/ConfigDevelCommands.php b/src/Commands/ConfigDevelCommands.php -index ad76144d17fa4c77d6df9318243220b0c258cfe4..9bb354fef0982f9658fb430e5f25d842c6811f87 100644 ---- a/src/Commands/ConfigDevelCommands.php -+++ b/src/Commands/ConfigDevelCommands.php -@@ -4,6 +4,7 @@ namespace Drupal\config_devel\Commands; - - use Drupal\config_devel\ConfigImporterExporter; - use Drupal\config_devel\EventSubscriber\ConfigDevelAutoImportSubscriber; -+use Drupal\config_rewrite\ConfigRewriterInterface; - use Drupal\Core\Config\ConfigFactoryInterface; - use Drupal\Core\Config\InstallStorage; - use Drupal\Core\Extension\InfoParserInterface; -@@ -115,6 +116,8 @@ class ConfigDevelCommands extends DrushCommands { - * - field.instance.node.article.body - * optional: - * - field.instance.node.article.tags -+ * rewrite: -+ * - book.settings - * - * @command config:devel-export - * @param string $extension Machine name of the module, profile or theme to export. -@@ -149,6 +152,11 @@ class ConfigDevelCommands extends DrushCommands { - if (isset($config['optional'])) { - $this->exportConfig($config['optional'], $type, $extension, InstallStorage::CONFIG_OPTIONAL_DIRECTORY); - } -+ -+ // If we have any rewrite configuration, export that as well. -+ if (isset($config['rewrite']) && $this->moduleHandler->moduleExists('config_rewrite')) { -+ $this->exportConfig($config['rewrite'], $type, $extension, ConfigRewriterInterface::CONFIG_REWRITE_DIRECTORY); -+ } - } - - /** -@@ -164,6 +172,12 @@ class ConfigDevelCommands extends DrushCommands { - * - field.instance.node.article.body - * optional: - * - field.instance.node.article.tags -+ * rewrite: -+ * - book.settings -+ * -+ * The 'rewrite' key may contain config entities that could override config -+ * from other modules and is only taking any action, if the config_rewrite -+ * module is being installed. - * - * @command config:devel-import - * @param string $extension Machine name of the module, profile or theme. -@@ -198,6 +212,11 @@ class ConfigDevelCommands extends DrushCommands { - if (isset($config['optional'])) { - $this->importConfig($config['optional'], $type, $extension, InstallStorage::CONFIG_OPTIONAL_DIRECTORY); - } -+ -+ // Import rewrite config. -+ if (isset($config['rewrite']) && $this->moduleHandler->moduleExists('config_rewrite')) { -+ $this->importConfig($config['rewrite'], $type, $extension, ConfigRewriterInterface::CONFIG_REWRITE_DIRECTORY); -+ } - } - - /** -@@ -213,6 +232,8 @@ class ConfigDevelCommands extends DrushCommands { - * - field.instance.node.article.body - * optional: - * - field.instance.node.article.tags -+ * rewrite: -+ * - book.settings - * - * @command config:devel-import-one - * @param string $path Config file name. -@@ -284,7 +305,7 @@ class ConfigDevelCommands extends DrushCommands { - * The name of the extension for which to return the config. - * - * @return array -- * An array containing install and optional config. -+ * An array containing install, optional and rewrite config. - * - * @throws \Exception - * Throws an exception if the 'config_devel' property of the extension's -@@ -302,14 +323,14 @@ class ConfigDevelCommands extends DrushCommands { - - // Keep backwards compatibility for the old format. This has config names - // listed directly beneath 'config_devel', rather than an intermediate -- // level for 'install' and 'optional'. -+ // level for 'install', 'optional' and 'rewrite'. - // Detect the old format based on whether there's neither of these two - // keys. -- if (!isset($info['config_devel']['install']) && !isset($info['config_devel']['optional'])) { -+ if (!isset($info['config_devel']['install']) && !isset($info['config_devel']['optional']) && !isset($info['config_devel']['rewrite'])) { - $info['config_devel']['install'] = $info['config_devel']; - } - -- foreach (['install', 'optional'] as $type) { -+ foreach (['install', 'optional', 'rewrite'] as $type) { - if (isset($info['config_devel'][$type])) { - $config[$type] = $info['config_devel'][$type]; - } -@@ -331,8 +352,9 @@ class ConfigDevelCommands extends DrushCommands { - * The name of the extension we're exporting. - * @param string $directory - * The subdirectory within the extension that we're exporting to. One of -- * \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_INSTALL_DIRECTORY -- * or \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_OPTIONAL_DIRECTORY. -+ * - \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_INSTALL_DIRECTORY -+ * - \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_OPTIONAL_DIRECTORY -+ * - \Drupal\config_rewrite\ConfigRewriterInterface::CONFIG_REWRITE_DIRECTORY - * - * @throws \Exception - * Thrown when the directory to export to is missing and could not be -@@ -376,8 +398,9 @@ class ConfigDevelCommands extends DrushCommands { - * The module, theme or install profile we're importing. - * @param string $directory - * The subdirectory within the extension that we're importing from. One of -- * \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_INSTALL_DIRECTORY -- * or \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_OPTIONAL_DIRECTORY. -+ * - \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_INSTALL_DIRECTORY -+ * - \Drupal\Core\Config\InstallStorage\InstallStorage::CONFIG_OPTIONAL_DIRECTORY -+ * - \Drupal\config_rewrite\ConfigRewriterInterface::CONFIG_REWRITE_DIRECTORY - */ - protected function importConfig($config_list, $type, $extension, $directory) { - $config_path = \Drupal::service('extension.path.resolver')->getPath($type, $extension) . "/$directory"; diff --git a/patches/d10/3228298.diff b/patches/d10/3228298.diff deleted file mode 100644 index 2a76ed14a105d15284804b9e07ebc31b63f765f8..0000000000000000000000000000000000000000 --- a/patches/d10/3228298.diff +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/core/modules/system/src/Plugin/Condition/RequestPath.php b/core/modules/system/src/Plugin/Condition/RequestPath.php -index 796277986b8cbbb18da7c8db77cdb44a1daa87cd..968d962401021fbf7182bf74021973e94fb62c42 100644 ---- a/core/modules/system/src/Plugin/Condition/RequestPath.php -+++ b/core/modules/system/src/Plugin/Condition/RequestPath.php -@@ -149,6 +149,11 @@ public function evaluate() { - $request = $this->requestStack->getCurrentRequest(); - // Compare the lowercase path alias (if any) and internal path. - $path = $this->currentPath->getPath($request); -+ // Remove multiple slashes as they can only be there by mistake. -+ $count = -1; -+ while ($count !== 0) { -+ $path = str_replace('//', '/', $path, $count); -+ } - // Do not trim a trailing slash if that is the complete path. - $path = $path === '/' ? $path : rtrim($path, '/'); - $path_alias = mb_strtolower($this->aliasManager->getAliasByPath($path)); diff --git a/patches/d10/3228312-13.diff b/patches/d10/3228312-13.diff deleted file mode 100644 index f2c99dd16e25c8ea162527957d156ad28d7869e2..0000000000000000000000000000000000000000 --- a/patches/d10/3228312-13.diff +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php b/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php -index 892cbcfee4918ac3448b851c0f12ffca9747c167..91d886b6c17ed44344b40dbbbd7722d595d3e0d4 100644 ---- a/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php -+++ b/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php -@@ -900,15 +900,16 @@ abstract class EntityGroupFieldWidgetBase extends WidgetBase implements Containe - } - $local_keys = [$field_name, 'add_more', 'add_relation']; - $parent_keys = array_merge($parents, $local_keys); -- $selected_group = NestedArray::getValue($form_state->getValues(), $parent_keys); -- -- $group = \Drupal::entityTypeManager()->getStorage('group')->load($selected_group); -- $group_type = $group->getGroupType()->id(); -- $group_relationship_type_id = \Drupal::entityTypeManager() -- ->getStorage(entitygroupfield_get_group_relationship_type_id()) -- ->getRelationshipTypeId($group_type, $widget_state['entity_plugin_id']); -- $widget_state['selected_group'] = $selected_group; -- $widget_state['selected_bundle'] = $group_relationship_type_id; -+ if ($selected_group = NestedArray::getValue($form_state->getValues(), $parent_keys)) { -+ if ($group = \Drupal::entityTypeManager()->getStorage('group')->load($selected_group)) { -+ $group_type = $group->getGroupType()->id(); -+ $group_relationship_type_id = \Drupal::entityTypeManager() -+ ->getStorage(entitygroupfield_get_group_relationship_type_id()) -+ ->getRelationshipTypeId($group_type, $widget_state['entity_plugin_id']); -+ $widget_state['selected_group'] = $selected_group; -+ $widget_state['selected_bundle'] = $group_relationship_type_id; -+ } -+ } - - // Clearing relation field. - $user_input = $form_state->getUserInput(); diff --git a/patches/d10/3228312-2.diff b/patches/d10/3228312-2.diff deleted file mode 100644 index 913b23c3abb9ab7ded17c9ae55ed6ed546f9c05f..0000000000000000000000000000000000000000 --- a/patches/d10/3228312-2.diff +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php b/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php -index efac04d259db57def100c0036880c264aa6dff96..2ba8ede403f6670b83deb63e6cd603281385fb31 100644 ---- a/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php -+++ b/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php -@@ -859,12 +859,13 @@ abstract class EntityGroupFieldWidgetBase extends WidgetBase implements Containe - } - $local_keys = [$field_name, 'add_more', 'add_relation']; - $parent_keys = array_merge($parents, $local_keys); -- $selected_group = NestedArray::getValue($form_state->getValues(), $parent_keys); -- -- $group = \Drupal::entityTypeManager()->getStorage('group')->load($selected_group); -- $group_content_type_id = $group->getGroupType()->getContentPlugin($widget_state['entity_plugin_id'])->getContentTypeConfigId(); -- $widget_state['selected_group'] = $selected_group; -- $widget_state['selected_bundle'] = $group_content_type_id; -+ if ($selected_group = NestedArray::getValue($form_state->getValues(), $parent_keys)) { -+ if ($group = \Drupal::entityTypeManager()->getStorage('group')->load($selected_group)) { -+ $group_content_type_id = $group->getGroupType()->getContentPlugin($widget_state['entity_plugin_id'])->getContentTypeConfigId(); -+ $widget_state['selected_group'] = $selected_group; -+ $widget_state['selected_bundle'] = $group_content_type_id; -+ } -+ } - - // Clearing relation field. - $user_input = $form_state->getUserInput(); diff --git a/patches/d10/3228312.diff b/patches/d10/3228312.diff deleted file mode 100644 index 263076ad49c24d35e4f314ba022d860f28fd316f..0000000000000000000000000000000000000000 --- a/patches/d10/3228312.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php b/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php ---- a/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php -+++ b/src/Plugin/Field/FieldWidget/EntityGroupFieldWidgetBase.php -@@ -861,1 +861,1 @@ abstract class EntityGroupFieldWidgetBase extends WidgetBase implements Containe -- $selected_group = NestedArray::getValue($form_state->getValues(), $parent_keys); -+ if ($selected_group = NestedArray::getValue($form_state->getValues(), $parent_keys)) { -@@ -862,1 +862,1 @@ abstract class EntityGroupFieldWidgetBase extends WidgetBase implements Containe -- $group = \Drupal::entityTypeManager()->getStorage('group')->load($selected_group); -+ if ($group = \Drupal::entityTypeManager()->getStorage('group')->load($selected_group)) { -@@ -869,1 +869,1 @@ abstract class EntityGroupFieldWidgetBase extends WidgetBase implements Containe -- // Clearing relation field. -+ }}// Clearing relation field. diff --git a/patches/d10/3238783.diff b/patches/d10/3238783.diff deleted file mode 100644 index abaf468d78902f4cf417f0fedba8bc2ba37952f7..0000000000000000000000000000000000000000 --- a/patches/d10/3238783.diff +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/src/Plugin/Action/FlagAction.php b/src/Plugin/Action/FlagAction.php -index fc00f7b0c0739dd08bf64ca1226c04af17234415..8ed1920059f7bb119b672830c07c7be3ed22d1e3 100644 ---- a/src/Plugin/Action/FlagAction.php -+++ b/src/Plugin/Action/FlagAction.php -@@ -59,8 +59,8 @@ class FlagAction extends ActionBase implements ContainerFactoryPluginInterface, - public function __construct(array $configuration, $plugin_id, $plugin_definition, FlagServiceInterface $flag_service) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->flagService = $flag_service; -- $this->flag = $this->flagService->getFlagById($configuration['flag_id']); -- $this->flagOperation = $configuration['flag_action']; -+ $this->flag = $this->flagService->getFlagById($configuration['flag_id'] ?? 'flag'); -+ $this->flagOperation = $configuration['flag_action'] ?? ''; - } - - /** diff --git a/patches/d10/3241310.diff b/patches/d10/3241310.diff deleted file mode 100644 index f5e9d1d4781dbf4261de2ce59e95b9fedfe34c45..0000000000000000000000000000000000000000 --- a/patches/d10/3241310.diff +++ /dev/null @@ -1,120 +0,0 @@ -diff --git a/src/Plugin/SocialLinkField/Platform/FacebookEvent.php b/src/Plugin/SocialLinkField/Platform/FacebookEvent.php -new file mode 100644 -index 0000000000000000000000000000000000000000..538f04e29768355b4d9dbfd482ac43ab7aa3baed ---- /dev/null -+++ b/src/Plugin/SocialLinkField/Platform/FacebookEvent.php -@@ -0,0 +1,18 @@ -+<?php -+ -+namespace Drupal\social_link_field\Plugin\SocialLinkField\Platform; -+ -+use Drupal\social_link_field\PlatformBase; -+ -+/** -+ * Provides 'facebook event' platform. -+ * -+ * @SocialLinkFieldPlatform( -+ * id = "facebookevent", -+ * name = @Translation("Facebook Event"), -+ * icon = "fa-facebook", -+ * iconSquare = "fa-facebook-square", -+ * urlPrefix = "https://www.facebook.com/event/", -+ * ) -+ */ -+class FacebookEvent extends PlatformBase {} -diff --git a/src/Plugin/SocialLinkField/Platform/Homepage.php b/src/Plugin/SocialLinkField/Platform/Homepage.php -new file mode 100644 -index 0000000000000000000000000000000000000000..8370807758989fec95cadba6e83c79b0d4f966ec ---- /dev/null -+++ b/src/Plugin/SocialLinkField/Platform/Homepage.php -@@ -0,0 +1,18 @@ -+<?php -+ -+namespace Drupal\social_link_field\Plugin\SocialLinkField\Platform; -+ -+use Drupal\social_link_field\PlatformBase; -+ -+/** -+ * Provides 'homepage' platform. -+ * -+ * @SocialLinkFieldPlatform( -+ * id = "homepage", -+ * name = @Translation("Homepage"), -+ * icon = "fa-home", -+ * iconSquare = "fa-home-square", -+ * urlPrefix = "https://www.", -+ * ) -+ */ -+class Homepage extends PlatformBase {} -diff --git a/src/Plugin/SocialLinkField/Platform/SpotifyAlbum.php b/src/Plugin/SocialLinkField/Platform/SpotifyAlbum.php -new file mode 100644 -index 0000000000000000000000000000000000000000..169b6c3af1deece4af1bde7603c4f36eec97a45a ---- /dev/null -+++ b/src/Plugin/SocialLinkField/Platform/SpotifyAlbum.php -@@ -0,0 +1,18 @@ -+<?php -+ -+namespace Drupal\social_link_field\Plugin\SocialLinkField\Platform; -+ -+use Drupal\social_link_field\PlatformBase; -+ -+/** -+ * Provides 'spotify album' platform. -+ * -+ * @SocialLinkFieldPlatform( -+ * id = "spotifyalbum", -+ * name = @Translation("Spotify Album"), -+ * icon = "fa-spotify", -+ * iconSquare = "spotify-square", -+ * urlPrefix = "https://open.spotify.com/album/", -+ * ) -+ */ -+class SpotifyAlbum extends PlatformBase {} -diff --git a/src/Plugin/SocialLinkField/Platform/SpotifyArtist.php b/src/Plugin/SocialLinkField/Platform/SpotifyArtist.php -new file mode 100644 -index 0000000000000000000000000000000000000000..9b94e8b807045caf758ed4390038bd21374f3176 ---- /dev/null -+++ b/src/Plugin/SocialLinkField/Platform/SpotifyArtist.php -@@ -0,0 +1,18 @@ -+<?php -+ -+namespace Drupal\social_link_field\Plugin\SocialLinkField\Platform; -+ -+use Drupal\social_link_field\PlatformBase; -+ -+/** -+ * Provides 'spotify artist' platform. -+ * -+ * @SocialLinkFieldPlatform( -+ * id = "spotifyartist", -+ * name = @Translation("Spotify Artist"), -+ * icon = "fa-spotify", -+ * iconSquare = "spotify-square", -+ * urlPrefix = "https://open.spotify.com/artist/", -+ * ) -+ */ -+class SpotifyArtist extends PlatformBase {} -diff --git a/src/Plugin/SocialLinkField/Platform/SpotifyPlaylist.php b/src/Plugin/SocialLinkField/Platform/SpotifyPlaylist.php -new file mode 100644 -index 0000000000000000000000000000000000000000..d310f601ebfc339a0ae8c23095cacbd058c785d5 ---- /dev/null -+++ b/src/Plugin/SocialLinkField/Platform/SpotifyPlaylist.php -@@ -0,0 +1,18 @@ -+<?php -+ -+namespace Drupal\social_link_field\Plugin\SocialLinkField\Platform; -+ -+use Drupal\social_link_field\PlatformBase; -+ -+/** -+ * Provides 'spotify playlist' platform. -+ * -+ * @SocialLinkFieldPlatform( -+ * id = "spotifyplaylist", -+ * name = @Translation("Spotify Playlist"), -+ * icon = "fa-spotify", -+ * iconSquare = "spotify-square", -+ * urlPrefix = "https://open.spotify.com/playlist/", -+ * ) -+ */ -+class SpotifyPlaylist extends PlatformBase {} diff --git a/patches/d10/3252081.diff b/patches/d10/3252081.diff deleted file mode 100644 index 469c4822d6f06149f487f1096c14603f5e369d07..0000000000000000000000000000000000000000 --- a/patches/d10/3252081.diff +++ /dev/null @@ -1,109 +0,0 @@ -diff --git a/README.txt b/README.txt -index ca0644d991306507e37c0fb96143aa284c4f5acd..a85b615061a55aaf59e6d70a9d8530a35550858b 100644 ---- a/README.txt -+++ b/README.txt -@@ -16,7 +16,7 @@ Manual - ------ - 1. Download the plugin from http://ckeditor.com/addon/find. - 2. Place the 'find' plugin directory within the root libraries folder in a -- CKEditor plugin subdirectory (DRUPAL_ROOT/libraries/ckeditor/plugins). -+ CKEditor plugin subdirectory (DRUPAL_ROOT/libraries/ckeditor.find). - 3. Enable the CKEditor Find/replace module. - 4. Configure your WYSIWYG toolbar to include the buttons. - -@@ -33,7 +33,7 @@ Composer - "version": "4.8.0", - "type": "drupal-library", - "extra": { -- "installer-name": "find" -+ "installer-name": "ckeditor.find" - }, - "dist": { - "url": "https://download.ckeditor.com/find/releases/find_4.8.0.zip", -@@ -49,11 +49,11 @@ Composer - { - "extra": { - "installer-paths": { -- "libraries/ckeditor/plugins/{$name}": ["ckeditor/find"] -+ "libraries/ckeditor.{$name}": ["ckeditor/find"] - } - } - } - - 3. Run `composer require drupal/ckeditor_find` - 3. Enable the CKEditor Find/replace module. --4. Configure your WYSIWYG toolbar to include the buttons. -\ No newline at end of file -+4. Configure your WYSIWYG toolbar to include the buttons. -diff --git a/ckeditor_find.install b/ckeditor_find.install -index b80e53a5002fffc4e19b263a5e3fa7a5afe31aaa..dfd7cde1d65a6eb0c0df281d9214f6a10ec2c3a1 100644 ---- a/ckeditor_find.install -+++ b/ckeditor_find.install -@@ -12,7 +12,7 @@ function ckeditor_find_requirements($phase) { - $requirements = []; - - if ($phase == 'install' || $phase == 'runtime') { -- $library_path = '/libraries/ckeditor/plugins/find'; -+ $library_path = '/libraries/ckeditor.find'; - - // Is the library found in the root libraries path. - $library_found = file_exists(DRUPAL_ROOT . $library_path); -@@ -43,4 +43,4 @@ function ckeditor_find_requirements($phase) { - - return $requirements; - --} -\ No newline at end of file -+} -diff --git a/composer.json b/composer.json -index 43a4b312492d3632b8f5beddc60a2cfb20cf27de..92900d613e8ace6657cb1ccc6c68297f03804039 100644 ---- a/composer.json -+++ b/composer.json -@@ -24,7 +24,7 @@ - "version": "4.14.1", - "type": "drupal-library", - "extra": { -- "installer-name": "ckeditor/plugins/find" -+ "installer-name": "ckeditor.find" - }, - "dist": { - "url": "https://download.ckeditor.com/find/releases/find_4.14.1.zip", -@@ -37,4 +37,4 @@ - "ckeditor/find": "^4", - "drupal/core": "^8 || ^9" - } --} -\ No newline at end of file -+} -diff --git a/src/Plugin/CKEditorPlugin/Find.php b/src/Plugin/CKEditorPlugin/Find.php -index 73fc2d859fd78058e2ca926ca88bcf352b5de620..dcb859c9ab5e8bef6fc5559e5556893a895a92c1 100644 ---- a/src/Plugin/CKEditorPlugin/Find.php -+++ b/src/Plugin/CKEditorPlugin/Find.php -@@ -20,7 +20,7 @@ class Find extends CKEditorPluginBase { - * Implements \Drupal\ckeditor\Plugin\CKEditorPluginInterface::getFile(). - */ - public function getFile() { -- return 'libraries/ckeditor/plugins/' . $this->getPluginId() . '/plugin.js'; -+ return 'libraries/ckeditor.' . $this->getPluginId() . '/plugin.js'; - } - - /** -@@ -53,15 +53,15 @@ class Find extends CKEditorPluginBase { - return [ - 'Find' => [ - 'label' => $this->t('Find'), -- 'image' => 'libraries/ckeditor/plugins/' . $this->getPluginId() . '/icons/find.png', -+ 'image' => 'libraries/ckeditor.' . $this->getPluginId() . '/icons/find.png', - ], - 'Find RTL' => [ - 'label' => $this->t('Find RTL'), -- 'image' => 'libraries/ckeditor/plugins/' . $this->getPluginId() . '/icons/find-rtl.png', -+ 'image' => 'libraries/ckeditor.' . $this->getPluginId() . '/icons/find-rtl.png', - ], - 'Replace' => [ - 'label' => $this->t('Replace'), -- 'image' => 'libraries/ckeditor/plugins/' . $this->getPluginId() . '/icons/replace.png', -+ 'image' => 'libraries/ckeditor.' . $this->getPluginId() . '/icons/replace.png', - ], - ]; - } diff --git a/patches/d10/3252427.diff b/patches/d10/3252427.diff deleted file mode 100644 index dc240a2b3f77c7b438a7063f7bd2bf1c649727e7..0000000000000000000000000000000000000000 --- a/patches/d10/3252427.diff +++ /dev/null @@ -1,69 +0,0 @@ -diff --git a/smtp.tokens.inc b/smtp.tokens.inc -new file mode 100644 -index 0000000000000000000000000000000000000000..89aa5024278227132458368cbb1f4b5a28d30ae9 ---- /dev/null -+++ b/smtp.tokens.inc -@@ -0,0 +1,63 @@ -+<?php -+ -+/** -+ * @file -+ * Builds placeholder replacement tokens for smtp data. -+ * -+ * This file handles tokens for the smtp tokens. -+ */ -+ -+/** -+ * Implements hook_token_info(). -+ */ -+function smtp_token_info(): array { -+ $types['smtp'] = [ -+ 'name' => t("SMTP data"), -+ 'description' => t("Tokens for smtp settings."), -+ ]; -+ $smtp['from'] = [ -+ 'name' => t("E-mail from address"), -+ 'description' => t("The e-mail address that all e-mails will be from."), -+ ]; -+ $smtp['fromname'] = [ -+ 'name' => t("E-mail from name"), -+ 'description' => t("The name that all e-mails will be from."), -+ ]; -+ $smtp['username'] = [ -+ 'name' => t("Username"), -+ 'description' => t("SMTP Username."), -+ ]; -+ return [ -+ 'types' => $types, -+ 'tokens' => [ -+ 'smtp' => $smtp, -+ ], -+ ]; -+} -+ -+/** -+ * Implements hook_tokens(). -+ */ -+function smtp_tokens($type, $tokens): array { -+ $replacements = []; -+ if ($type === 'smtp') { -+ $config = \Drupal::config('smtp.settings'); -+ foreach ($tokens as $name => $original) { -+ switch ($name) { -+ case 'from': -+ $replacements[$original] = $config->get('smtp_from'); -+ break; -+ -+ case 'fromname': -+ $replacements[$original] = $config->get('smtp_fromname'); -+ break; -+ -+ case 'username': -+ $replacements[$original] = $config->get('smtp_username'); -+ break; -+ -+ } -+ } -+ } -+ return $replacements; -+} diff --git a/patches/d10/3279973.diff b/patches/d10/3279973.diff deleted file mode 100644 index 70717ded6a73eb216c1df01a418cbadda30e01e3..0000000000000000000000000000000000000000 --- a/patches/d10/3279973.diff +++ /dev/null @@ -1,234 +0,0 @@ -diff --git a/config/schema/tamper.schema.yml b/config/schema/tamper.schema.yml -index 5443efdea2df71e74b4ba614ca6ab5a3b14f2cba..32260190c7c9a01ed4ae5821d057af72ff5df9b2 100644 ---- a/config/schema/tamper.schema.yml -+++ b/config/schema/tamper.schema.yml -@@ -255,3 +255,10 @@ tamper.url_encode: - method: - type: string - label: 'Encode method' -+ -+tamper.word_count: -+ mapping: -+ limit: -+ type: integer -+ label: 'Limit' -+ nullable: true -diff --git a/src/Plugin/Tamper/WordCount.php b/src/Plugin/Tamper/WordCount.php -new file mode 100644 -index 0000000000000000000000000000000000000000..7bfb6b2b6e6140e7008bf32038da6bcadee91707 ---- /dev/null -+++ b/src/Plugin/Tamper/WordCount.php -@@ -0,0 +1,76 @@ -+<?php -+ -+namespace Drupal\tamper\Plugin\Tamper; -+ -+use Drupal\Core\Form\FormStateInterface; -+use Drupal\tamper\Exception\TamperException; -+use Drupal\tamper\TamperableItemInterface; -+use Drupal\tamper\TamperBase; -+ -+/** -+ * Plugin implementation of the Word Count plugin. -+ * -+ * @Tamper( -+ * id = "word_count", -+ * label = @Translation("Get number of words"), -+ * description = @Translation("Get the number of words in a string"), -+ * category = "Text" -+ * ) -+ */ -+class WordCount extends TamperBase { -+ -+ const SETTING_LIMIT = 'limit'; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function defaultConfiguration() { -+ $config = parent::defaultConfiguration(); -+ $config[self::SETTING_LIMIT] = NULL; -+ return $config; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { -+ $form[self::SETTING_LIMIT] = [ -+ '#type' => 'number', -+ '#title' => $this->t('Limit'), -+ '#default_value' => $this->getSetting(self::SETTING_LIMIT), -+ '#description' => $this->t('If limit is set and positive, the returned items will contain a maximum of limit with the last item containing the rest of string. If limit is negative, all components except the last -limit are returned. If the limit parameter is zero, then this is treated as 1. If limit is not set, then there will be no limit on the number of items returned.'), -+ ]; -+ -+ return $form; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { -+ parent::submitConfigurationForm($form, $form_state); -+ $this->setConfiguration([ -+ self::SETTING_LIMIT => $form_state->getValue(self::SETTING_LIMIT), -+ ]); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function tamper($data, TamperableItemInterface $item = NULL) { -+ if (!is_string($data)) { -+ throw new TamperException('Input should be a string.'); -+ } -+ $limit = is_numeric($this->getSetting(self::SETTING_LIMIT)) ? $this->getSetting(self::SETTING_LIMIT) : PHP_INT_MAX; -+ $words = explode(' ', $data, $limit); -+ return count($words); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function multiple() { -+ return TRUE; -+ } -+ -+} -diff --git a/tests/src/Functional/Plugin/Tamper/WordCountTest.php b/tests/src/Functional/Plugin/Tamper/WordCountTest.php -new file mode 100644 -index 0000000000000000000000000000000000000000..202598a5518f90049050b0ed2d3467df80a7c409 ---- /dev/null -+++ b/tests/src/Functional/Plugin/Tamper/WordCountTest.php -@@ -0,0 +1,41 @@ -+<?php -+ -+namespace Drupal\Tests\tamper\Functional\Plugin\Tamper; -+ -+/** -+ * Tests the word_count plugin. -+ * -+ * @coversDefaultClass \Drupal\tamper\Plugin\Tamper\WordCount -+ * @group tamper -+ */ -+class WordCountTest extends TamperPluginTestBase { -+ -+ /** -+ * The ID of the plugin to test. -+ * -+ * @var string -+ */ -+ protected static $pluginId = 'word_count'; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function formDataProvider(): array { -+ return [ -+ 'no values' => [ -+ 'expected' => [ -+ 'limit' => NULL, -+ ], -+ ], -+ 'with values' => [ -+ 'expected' => [ -+ 'limit' => 6, -+ ], -+ 'edit' => [ -+ 'limit' => '6', -+ ], -+ ], -+ ]; -+ } -+ -+} -diff --git a/tests/src/Unit/Plugin/Tamper/WordCountTest.php b/tests/src/Unit/Plugin/Tamper/WordCountTest.php -new file mode 100644 -index 0000000000000000000000000000000000000000..4c317a930c877fe487f5b7e14431482ca804ab75 ---- /dev/null -+++ b/tests/src/Unit/Plugin/Tamper/WordCountTest.php -@@ -0,0 +1,84 @@ -+<?php -+ -+namespace Drupal\Tests\tamper\Unit\Plugin\Tamper; -+ -+use Drupal\tamper\Exception\TamperException; -+use Drupal\tamper\Plugin\Tamper\WordCount; -+ -+/** -+ * Tests the word_count plugin. -+ * -+ * @coversDefaultClass \Drupal\tamper\Plugin\Tamper\WordCount -+ * @group tamper -+ */ -+class WordCountTest extends TamperPluginTestBase { -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function instantiatePlugin() { -+ return $this->getPluginDefaultConfig(); -+ } -+ -+ /** -+ * Test explode. -+ */ -+ public function testExplodeWithSingleValue() { -+ $original = 'foo bar baz zip'; -+ $expected = 4; -+ $this->assertEquals($expected, $this->getPluginDefaultConfig()->tamper($original)); -+ } -+ -+ /** -+ * Test explode. -+ */ -+ public function testExplodeWithMultipleValues() { -+ $this->expectException(TamperException::class); -+ $this->expectExceptionMessage('Input should be a string.'); -+ $original = ['foo bar', 'baz zip']; -+ $this->getPluginDefaultConfig()->tamper($original); -+ } -+ -+ /** -+ * Text explode with limit. -+ */ -+ public function testExplodeWithSingleValueAndLimit() { -+ $original = 'foo bar baz zip'; -+ $expected = 2; -+ $this->assertEquals($expected, $this->getPluginWithLimit()->tamper($original)); -+ } -+ -+ /** -+ * Text explode with limit. -+ */ -+ public function testExplodeWithMultipleValuesAndLimit() { -+ $this->expectException(TamperException::class); -+ $this->expectExceptionMessage('Input should be a string.'); -+ $original = ['foo bar baz zip', 'fizz bang boop']; -+ $this->getPluginWithLimit()->tamper($original); -+ } -+ -+ /** -+ * Returns default configuration for the plugin for this test. -+ * -+ * @return \Drupal\tamper\Plugin\Tamper\Explode -+ * A explode tamper plugin instance. -+ */ -+ protected function getPluginDefaultConfig() { -+ return new WordCount([], 'word_count', [], $this->getMockSourceDefinition()); -+ } -+ -+ /** -+ * Returns default limit setting for the plugin for this test. -+ * -+ * @return \Drupal\tamper\Plugin\Tamper\Explode -+ * A explode tamper plugin instance. -+ */ -+ protected function getPluginWithLimit() { -+ $config = [ -+ WordCount::SETTING_LIMIT => 2, -+ ]; -+ return new WordCount($config, 'word_count', [], $this->getMockSourceDefinition()); -+ } -+ -+} diff --git a/patches/d10/3298701.diff b/patches/d10/3298701.diff deleted file mode 100644 index d7cbfdce07f2ac42b5f4cb583dae9bda84e03416..0000000000000000000000000000000000000000 --- a/patches/d10/3298701.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/core/modules/image/src/Controller/ImageStyleDownloadController.php b/core/modules/image/src/Controller/ImageStyleDownloadController.php -index cd7fc95425..079782971b 100644 ---- a/core/modules/image/src/Controller/ImageStyleDownloadController.php -+++ b/core/modules/image/src/Controller/ImageStyleDownloadController.php -@@ -108,7 +108,7 @@ public static function create(ContainerInterface $container) { - * @throws \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException - * Thrown when the file is still being generated. - */ -- public function deliver(Request $request, $scheme, ImageStyleInterface $image_style, string $required_derivative_scheme) { -+ public function deliver(Request $request, $scheme, ImageStyleInterface $image_style, string $required_derivative_scheme = 'public') { - $target = $request->query->get('file'); - $image_uri = $scheme . '://' . $target; - $image_uri = $this->streamWrapperManager->normalizeUri($image_uri); diff --git a/patches/d10/3302450.diff b/patches/d10/3302450.diff deleted file mode 100644 index 49065f1973a0de6c3ca68c5c6a492ecfbc2cb0bd..0000000000000000000000000000000000000000 --- a/patches/d10/3302450.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php -index 98351f05ce33beb42baa3cd60292ae999123df21..5377e839cbc1a4a323faa5675a0a9d679e1e1360 100644 ---- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php -+++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php -@@ -268,7 +268,7 @@ public function elementType($none_supported = FALSE, $default_empty = FALSE, $in - return $this->definition['element type']; - } - -- return 'span'; -+ return 'div'; - } - - /** diff --git a/patches/d10/3306745.diff b/patches/d10/3306745.diff deleted file mode 100644 index 1299d687f543de917933cc5b386c8192ce9f4136..0000000000000000000000000000000000000000 --- a/patches/d10/3306745.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/modules/entity_share_client/src/Plugin/EntityShareClient/Processor/PhysicalFile.php b/modules/entity_share_client/src/Plugin/EntityShareClient/Processor/PhysicalFile.php -index 938dc5716d39ac4261e6d4ff1b726f108e41ebeb..600d093da5755abd7054404ca49818a5ac7d74e2 100644 ---- a/modules/entity_share_client/src/Plugin/EntityShareClient/Processor/PhysicalFile.php -+++ b/modules/entity_share_client/src/Plugin/EntityShareClient/Processor/PhysicalFile.php -@@ -131,7 +131,6 @@ class PhysicalFile extends ImportProcessorPluginBase implements PluginFormInterf - - $file_destination = $this->fileSystem->getDestinationFilename($processed_entity->getFileUri(), $file_overwrite_mode); - $processed_entity->setFileUri($file_destination); -- $processed_entity->setFilename($this->fileSystem->basename($file_destination)); - - // Create the destination folder. - if ($this->fileSystem->prepareDirectory($directory_uri, FileSystemInterface::CREATE_DIRECTORY)) { diff --git a/patches/d10/3307775.diff b/patches/d10/3307775.diff deleted file mode 100644 index ed6b49528c7b6996a0536a4f5663c5c843c786c3..0000000000000000000000000000000000000000 --- a/patches/d10/3307775.diff +++ /dev/null @@ -1,4688 +0,0 @@ -diff --git a/.gitignore b/.gitignore -new file mode 100644 -index 0000000000000000000000000000000000000000..560d566caee74765cf8cc435aeafb49c4c37c8da ---- /dev/null -+++ b/.gitignore -@@ -0,0 +1,3 @@ -+.idea -+.node_modules -+node_modules -\ No newline at end of file -diff --git a/css/drupalentity.css b/css/drupalentity.css -new file mode 100644 -index 0000000000000000000000000000000000000000..e5bdc08f66366d285f505d8832dd43f767e33689 ---- /dev/null -+++ b/css/drupalentity.css -@@ -0,0 +1,8 @@ -+/** -+ * @file -+ * Styles for the Drupal View Embed in CKEditor 5. -+ */ -+ -+.ck .drupal-entity [data-drupal-views-preview] { -+ pointer-events: none; -+} -diff --git a/js/build/drupalviewsentity.js b/js/build/drupalviewsentity.js -new file mode 100644 -index 0000000000000000000000000000000000000000..dda47587f515185c38c1641338f7a240aec2cf5f ---- /dev/null -+++ b/js/build/drupalviewsentity.js -@@ -0,0 +1 @@ -+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalviewsentity=t())}(self,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/engine.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/engine.js")},"ckeditor5/src/ui.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/ui.js")},"ckeditor5/src/utils.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/utils.js")},"ckeditor5/src/widget.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/widget.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function i(r){var n=t[r];if(void 0!==n)return n.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,i),o.exports}i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var r={};return(()=>{"use strict";i.d(r,{default:()=>v});var e=i("ckeditor5/src/core.js"),t=i("ckeditor5/src/widget.js");class n extends e.Command{execute(e){const{model:t}=this.editor,i=this.editor.plugins.get("ViewsEntityEmbedEditing"),r=Object.fromEntries(Object.entries(i.attrs).map((([e,t])=>[t,e]))),n=Object.fromEntries(Object.keys(r).filter((t=>e[t])).map((t=>[r[t],e[t]])));t.change((e=>{t.insertContent(function(e,t){return e.createElement("drupalViewsEntity",t)}(e,n))}))}refresh(){const e=this.editor.model,t=e.document.selection,i=e.schema.findAllowedParent(t.getFirstPosition(),"drupalViewsEntity");this.isEnabled=null!==i}}class o extends e.Plugin{static get requires(){return[t.Widget]}init(){this.attrs={alt:"alt",title:"title",dataCaption:"data-caption",dataAlign:"data-align",drupalEntityLangCode:"data-langcode",drupalEntityEntityType:"data-entity-type",drupalEntityEntityUuid:"data-entity-uuid",drupalEntityViewMode:"data-view-mode",drupalEntityEmbedButton:"data-embed-button",drupalEntityEmbedDisplay:"data-entity-embed-display",drupalEntityEmbedDisplaySettings:"data-entity-embed-display-settings",dataViewName:"data-view-name",dataViewDisplay:"data-view-display",dataViewArguments:"data-view-arguments",dataViewAttributes:"data-view-attributes"};const e=this.editor.config.get("viewsEntityEmbed");if(!e)throw new Error("Error on initializing entityEmbed plugin: entityEmbed config is required.");this.options=e,this.labelError=Drupal.t("Preview failed"),this.previewError=`\n <p>${Drupal.t("An error occurred while trying to preview the embedded content. Please save your work and reload this page.")}<p>\n `,this._defineSchema(),this._defineConverters(),this.editor.commands.add("insertViewsEntityEmbed",new n(this.editor))}_defineSchema(){this.editor.model.schema.register("drupalViewsEntity",{isObject:!0,isContent:!0,isBlock:!0,allowWhere:"$block",allowAttributes:Object.keys(this.attrs)}),this.editor.editing.view.domConverter.blockElements.push("drupal-views")}_defineConverters(){const{conversion:e}=this.editor,i={model:"drupalViewsEntity",view:{name:"drupal-views"}};e.for("upcast").elementToElement(i),e.for("dataDowncast").elementToElement(i),e.for("editingDowncast").elementToElement({...i,view:(e,{writer:i})=>{const r=e.hasAttribute("dataAlign")?` align-${e.getAttribute("dataAlign")}`:"",n=i.createContainerElement("figure",{class:`drupal-views${r}`});return i.setCustomProperty("drupalViewsEntity",!0,n),(0,t.toWidget)(n,i,{label:Drupal.t("Views Entity Embed widget")})}}).add((e=>(e.on("attribute:dataViewDisplay",((e,t,i)=>{const r=i.writer,n=t.item,o=i.mapper.toViewElement(t.item);let s=this._getPreviewContainer(o.getChildren());if(s){if("ready"!==s.getAttribute("data-drupal-views-preview"))return;r.setAttribute("data-drupal-views-preview","loading",s)}else s=r.createRawElement("div",{"data-drupal-views-preview":"loading"}),r.insert(r.createPositionAt(o,0),s);this._loadPreview(n).then((({label:e,preview:t})=>{s&&this.editor.editing.view.change((i=>{const r=i.createRawElement("div",{"data-drupal-views-preview":"ready","aria-label":e},(e=>{e.innerHTML=t}));i.insert(i.createPositionBefore(s),r),i.remove(s)}))}))})),e))),Object.keys(this.attrs).forEach((t=>{const i={model:{key:t,name:"drupalViewsEntity"},view:{name:"drupal-views",key:this.attrs[t]}};e.for("dataDowncast").attributeToAttribute(i),e.for("upcast").attributeToAttribute(i)}))}async _loadPreview(e){const t={text:this._renderElement(e)},i=await fetch(Drupal.url("embed/preview/"+this.options.format+"?"+new URLSearchParams(t)),{headers:{"X-Drupal-EmbedPreview-CSRF-Token":this.options.previewCsrfToken}});if(i.ok){return{label:Drupal.t("Entity Embed widget"),preview:await i.text()}}return{label:this.labelError,preview:this.previewError}}_renderElement(e){const t=this.editor.model.change((t=>{const i=t.createDocumentFragment(),r=t.cloneElement(e,!1);return["linkHref","dataAlign"].forEach((e=>{t.removeAttribute(e,r)})),t.append(r,i),i}));return this.editor.data.stringify(t)}_getPreviewContainer(e){for(const t of e){if(t.hasAttribute("data-drupal-views-preview"))return t;if(t.childCount){const e=this._getPreviewContainer(t.getChildren());if(e)return e}}return null}static get pluginName(){return"ViewsEntityEmbedEditing"}}var s=i("ckeditor5/src/ui.js");class a extends e.Plugin{static get requires(){return[t.WidgetToolbarRepository]}init(){const t=this.editor,i=t.plugins.get("ViewsEntityEmbedEditing"),r=t.config.get("viewsEntityEmbed"),{dialogSettings:n={}}=r;t.ui.componentFactory.add("viewsEntityEmbedEdit",(o=>{let a=new s.ButtonView(o);return a.set({label:t.t("Edit"),icon:e.icons.pencil,tooltip:!0}),this.listenTo(a,"execute",(e=>{let o=t.model.document.selection.getSelectedElement(),s=Drupal.url((r.formRoot||"entity-embed/dialog/")+r.format+"/"+o.getAttribute("drupalEntityEmbedButton")),a={};for(let[e,t]of o.getAttributes()){let r=i.attrs[e];r&&(a[r]=t)}this._openDialog(s,a,(({attributes:e})=>{t.execute("insertViewsEntityEmbed",e),t.editing.view.focus()}),n)})),a})),t.ui.componentFactory.add("viewsEditEmbeddedEntity",(i=>{const r=new s.ButtonView(i),n=t.model.document.selection.getSelectedElement();if(!n)return null;if(!n.hasAttribute("dataViewName"))return console.warn(Drupal.t("Unable to create edit link. There must be a value for data-view-name.")),null;const o=n.getAttribute("dataViewName"),a=Drupal.url(`entity-embed/edit-embedded-view/${o}`);return console.log("edit url",a),r.set({isEnabled:!0,label:Drupal.t("Edit the embedded view (opens in new tab)"),icon:e.icons.cog,tooltip:!0}),fetch(a).then((e=>{e.ok||r.set({label:Drupal.t(`You do not have the permissions needed to edit the view ${o}.`),isEnabled:!1})})),this.listenTo(r,"execute",(()=>{window.open(a,"_blank")})),r}))}afterInit(){const{editor:e}=this;if(!e.plugins.has("WidgetToolbarRepository"))return;e.plugins.get(t.WidgetToolbarRepository).register("viewsEntityEmbed",{ariaLabel:Drupal.t("Entity Embed toolbar"),items:["viewsEntityEmbedEdit","entityEmbedLink","viewsEditEmbeddedEntity"],getRelatedElement(e){const i=e.getSelectedElement();return i&&(0,t.isWidget)(i)&&i.getCustomProperty("drupalViewsEntity")?i:null}})}_openDialog(e,t,i,r){const n=r.dialogClass?r.dialogClass.split(" "):[];n.push("ui-dialog--narrow"),r.dialogClass=n.join(" "),r.autoResize=window.matchMedia("(min-width: 600px)").matches,r.width="auto";Drupal.ajax({dialog:r,dialogType:"modal",selector:".ckeditor5-dialog-loading-link",url:e,progress:{type:"fullscreen"},submit:{editor_object:t}}).execute(),Drupal.ckeditor5.saveCallback=i}static get pluginName(){return"ViewsEntityEmbedToolbar"}}class l extends e.Plugin{static get requires(){return["Widget"]}init(){const e=this.editor,t=e.commands.get("insertViewsEntityEmbed"),i=e.config.get("viewsEntityEmbed");if(!i)return;const{dialogSettings:r={}}=i,n=i.buttons;Object.keys(n).forEach((o=>{const a=Drupal.url((i.formRoot||"entity-embed/dialog/")+i.format+"/"+o);e.ui.componentFactory.add(o,(i=>{const l=n[o],d=new s.ButtonView(i);let u=null;if(l.icon.endsWith("svg")){let e=new XMLHttpRequest;e.open("GET",l.icon,!1),e.send(null),u=e.response}else console.warn(`CKEditor 5 only supports enity embed icons in SVG format. The icon provided is ${l.icon}`);return d.set({label:l.label,icon:u??'<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> <image id="image0" width="16" height="16" x="0" y="0"\n href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAAAJiS0dE\nAP+Hj8y/AAAAB3RJTUUH5gkNAywZrK+VpAAAAElJREFUKM9jYKAeuMrwHwUuhwizoCj6xfALzv6J\nzYRKTIOZCNmMaoUpQzKcvZnhFX5HWmIz4SrDDTj7LbUcSVABbkfeJ9kEcgEApvsllE2X4VkAAAAl\ndEVYdGRhdGU6Y3JlYXRlADIwMjItMDktMTNUMDE6NDQ6MjUrMDI6MDCMUacyAAAAJXRFWHRkYXRl\nOm1vZGlmeQAyMDIyLTA5LTEzVDAxOjQ0OjI1KzAyOjAw/QwfjgAAAABJRU5ErkJggg==" />\n</svg>\n',tooltip:!0}),d.bind("isOn","isEnabled").to(t,"value","isEnabled"),this.listenTo(d,"execute",(()=>Drupal.ckeditor5.openDialog(a,(({attributes:t})=>{e.execute("insertViewsEntityEmbed",t)}),r))),d}))}))}static get pluginName(){return"ViewsEntityEmbedUI"}}function d(e,t,i){if(t.attributes)for(const[r,n]of Object.entries(t.attributes))e.setAttribute(r,n,i);t.styles&&e.setStyle(t.styles,i),t.classes&&e.addClass(t.classes,i)}function u(e,t,i){if(!i.consumable.consume(t.item,e.name))return;const r=i.mapper.toViewElement(t.item);d(i.writer,t.attributeNewValue,r)}class c extends e.Plugin{constructor(e){if(super(e),!e.plugins.has("GeneralHtmlSupport"))return;e.plugins.has("DataFilter")&&e.plugins.has("DataSchema")||console.error("DataFilter and DataSchema plugins are required for Entity Embed to integrate with General HTML Support plugin.");const{schema:t}=e.model,{conversion:i}=e,r=this.editor.plugins.get("DataFilter");this.editor.plugins.get("DataSchema").registerBlockElement({model:"drupalViewsEntity",view:"drupal-views"}),r.on("register:drupal-views",((e,n)=>{"drupalViewsEntity"===n.model&&(t.extend("drupalViewsEntity",{allowAttributes:["htmlLinkAttributes","htmlAttributes"]}),i.for("upcast").add(function(e){return t=>{t.on("element:drupal-views",((t,i,r)=>{function n(t,n){const o=e.processViewAttributes(t,r);o&&r.writer.setAttribute(n,o,i.modelRange)}const o=i.viewItem,s=o.parent;n(o,"htmlAttributes"),s.is("element","a")&&n(s,"htmlLinkAttributes")}),{priority:"low"})}}(r)),i.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalEntity",((e,t,i)=>{if(!i.consumable.consume(t.item,"attribute:htmlLinkAttributes:drupalEntity"))return;const r=i.mapper.toViewElement(t.item),n=function(e,t,i){const r=e.createRangeOn(t);for(const{item:e}of r.getWalker())if(e.is("element",i))return e}(i.writer,r,"a");d(i.writer,t.item.getAttribute("htmlLinkAttributes"),n)}),{priority:"low"})})),i.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalEntity",((e,t,i)=>{if(!i.consumable.consume(t.item,"attribute:htmlLinkAttributes:drupalEntity"))return;const r=i.mapper.toViewElement(t.item).parent;d(i.writer,t.item.getAttribute("htmlLinkAttributes"),r)}),{priority:"low"}),e.on("attribute:htmlAttributes:drupalEntity",u,{priority:"low"})})),e.stop())}))}static get pluginName(){return"ViewsEntityEmbedGeneralHtmlSupport"}}var m=i("ckeditor5/src/engine.js"),p=i("ckeditor5/src/utils.js");function w(e){return Array.from(e.getChildren()).find((e=>"drupal-views"===e.name))}function g(e){return t=>{t.on(`attribute:${e.id}:drupalEntity`,((t,i,r)=>{const n=r.mapper.toViewElement(i.item);let o=Array.from(n.getChildren()).find((e=>"a"===e.name));if(o=!o&&n.is("element","a")?n:Array.from(n.getAncestors()).find((e=>"a"===e.name)),o){for(const[t,i]of(0,p.toMap)(e.attributes))r.writer.setAttribute(t,i,o);e.classes&&r.writer.addClass(e.classes,o);for(const t in e.styles)Object.prototype.hasOwnProperty.call(e.styles,t)&&r.writer.setStyle(t,e.styles[t],o)}}))}}function E(e,t){return e=>{e.on("element:a",((e,i,r)=>{const n=i.viewItem;if(!w(n))return;const o=new m.Matcher(t._createPattern()).match(n);if(!o)return;if(!r.consumable.consume(n,o.match))return;const s=i.modelCursor.nodeBefore;r.writer.setAttribute(t.id,!0,s)}),{priority:"high"})}}class b extends e.Plugin{static get requires(){return["ViewsEntityEmbedEditing"]}static get pluginName(){return"ViewsEntityEmbedLinkEditing"}init(){const{editor:e}=this;e.model.schema.extend("drupalViewsEntity",{allowAttributes:["linkHref"]}),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const r=t.viewItem,n=w(r);if(!n)return;if(!i.consumable.consume(r,{attributes:["href"],name:!0}))return;const o=r.getAttribute("href");if(!o)return;const s=i.convertItem(n,t.modelCursor);t.modelRange=s.modelRange,t.modelCursor=s.modelCursor;const a=t.modelCursor.nodeBefore;a&&a.is("element","drupalViewsEntity")&&i.writer.setAttribute("linkHref",o,a)}),{priority:"high"})})),e.conversion.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalEntity",((e,t,i)=>{const{writer:r}=i;if(!i.consumable.consume(t.item,e.name))return;const n=i.mapper.toViewElement(t.item),o=Array.from(n.getChildren()).find((e=>"a"===e.name));if(o)t.attributeNewValue?r.setAttribute("href",t.attributeNewValue,o):(r.move(r.createRangeIn(o),r.createPositionAt(n,0)),r.remove(o));else{const e=Array.from(n.getChildren()).find((e=>e.getAttribute("data-drupal-views-preview"))),i=r.createContainerElement("a",{href:t.attributeNewValue});r.insert(r.createPositionAt(n,0),i),r.move(r.createRangeOn(e),r.createPositionAt(i,0))}}),{priority:"high"})})),e.conversion.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalEntity",((e,t,i)=>{const{writer:r}=i;if(!i.consumable.consume(t.item,e.name))return;const n=i.mapper.toViewElement(t.item),o=r.createContainerElement("a",{href:t.attributeNewValue});r.insert(r.createPositionBefore(n),o),r.move(r.createRangeOn(n),r.createPositionAt(o,0))}),{priority:"high"})})),this._enableManualDecorators();if(e.commands.get("link").automaticDecorators.length>0)throw new Error("The Drupal Entity plugin is not compatible with automatic link decorators. To use Drupal Entity, disable any plugins providing automatic link decorators.")}_enableManualDecorators(){const e=this.editor,t=e.commands.get("link");for(const i of t.manualDecorators)e.model.schema.extend("drupalViewsEntity",{allowAttributes:i.id}),e.conversion.for("downcast").add(g(i)),e.conversion.for("upcast").add(E(0,i))}}class h extends e.Plugin{static get requires(){return["LinkEditing","LinkUI","ViewsEntityEmbedEditing","ViewsEntityEmbedUI"]}static get pluginName(){return"ViewsEntityEmbedLinkUi"}init(){const{editor:e}=this,t=e.editing.view.document;this.listenTo(t,"click",((t,i)=>{this._isSelectedLinkedEntityEmbed(e.model.document.selection)&&(i.preventDefault(),t.stop())}),{priority:"high"}),this._createToolbarLinkEntityEmbedButton()}_createToolbarLinkEntityEmbedButton(){const{editor:e}=this;e.ui.componentFactory.add("entityEmbedLink",(t=>{const i=new s.ButtonView(t),r=e.plugins.get("LinkUI"),n=e.commands.get("link");return i.set({isEnabled:!0,label:Drupal.t("Link entity embed"),icon:'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184z"/></svg>',keystroke:"Ctrl+K",tooltip:!0,isToggleable:!0}),i.bind("isEnabled").to(n,"isEnabled"),i.bind("isOn").to(n,"value",(e=>!!e)),this.listenTo(i,"execute",(()=>{this._isSelectedLinkedEntityEmbed(e.model.document.selection)?r._addActionsView():r._showUI(!0)})),i}))}_isSelectedLinkedEntityEmbed(e){const t=e.getSelectedElement();return!!t&&t.is("element","drupalViewsEntity")&&t.hasAttribute("linkHref")}}class y extends e.Plugin{static get requires(){return[b,h]}static get pluginName(){return"ViewsEntityEmbedLink"}}class f extends e.Plugin{static get requires(){return[o,l,a,c,y]}static get pluginName(){return"ViewsEntityEmbed"}}const v={ViewsEntityEmbed:f}})(),r=r.default})())); -\ No newline at end of file -diff --git a/js/ckeditor5_plugins/drupalviewsentity/entity.svg b/js/ckeditor5_plugins/drupalviewsentity/entity.svg -new file mode 100644 -index 0000000000000000000000000000000000000000..5c6a180d1cb92d3b51907f963854883cd0069070 ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/entity.svg -@@ -0,0 +1,9 @@ -+<?xml version="1.0" encoding="UTF-8" standalone="no"?> -+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> <image id="image0" width="16" height="16" x="0" y="0" -+ href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAAAJiS0dE -+AP+Hj8y/AAAAB3RJTUUH5gkNAywZrK+VpAAAAElJREFUKM9jYKAeuMrwHwUuhwizoCj6xfALzv6J -+zYRKTIOZCNmMaoUpQzKcvZnhFX5HWmIz4SrDDTj7LbUcSVABbkfeJ9kEcgEApvsllE2X4VkAAAAl -+dEVYdGRhdGU6Y3JlYXRlADIwMjItMDktMTNUMDE6NDQ6MjUrMDI6MDCMUacyAAAAJXRFWHRkYXRl -+Om1vZGlmeQAyMDIyLTA5LTEzVDAxOjQ0OjI1KzAyOjAw/QwfjgAAAABJRU5ErkJggg==" /> -+</svg> -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/command.js b/js/ckeditor5_plugins/drupalviewsentity/src/command.js -new file mode 100644 -index 0000000000000000000000000000000000000000..b4e782ea1b6b41b031561e074e6b3e4bf8315ab5 ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/command.js -@@ -0,0 +1,45 @@ -+import {Command} from 'ckeditor5/src/core'; -+ -+export default class InsertViewsEntityEmbedCommand extends Command { -+ -+ execute(attributes) { -+ const { model } = this.editor; -+ const entityEmbedEditing = this.editor.plugins.get('ViewsEntityEmbedEditing'); -+ -+ // Create object that contains supported data-attributes in view data by -+ // flipping `ViewsEntityEmbedEditing.attrs` object (i.e. keys from object become -+ // values and values from object become keys). -+ const dataAttributeMapping = Object.fromEntries( -+ Object.entries(entityEmbedEditing.attrs).map(([key, value]) => [value, key]) -+ ); -+ -+ // \Drupal\entity_embed\Form\EntityEmbedDialog returns data in keyed by -+ // data-attributes used in view data. This converts data-attribute keys to -+ // keys used in model. -+ const modelAttributes = Object.fromEntries( -+ Object.keys(dataAttributeMapping) -+ .filter((attribute) => attributes[attribute]) -+ .map((attribute) => [dataAttributeMapping[attribute] ,attributes[attribute]]) -+ ); -+ -+ -+ model.change((writer) => { -+ model.insertContent(createEntityEmbed(writer, modelAttributes)); -+ }); -+ } -+ -+ refresh() { -+ const model = this.editor.model; -+ const selection = model.document.selection; -+ const allowedIn = model.schema.findAllowedParent( -+ selection.getFirstPosition(), -+ 'drupalViewsEntity', -+ ); -+ this.isEnabled = allowedIn !== null; -+ }; -+ -+} -+ -+function createEntityEmbed(writer, attributes) { -+ return writer.createElement('drupalViewsEntity', attributes); -+} -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/editing.js b/js/ckeditor5_plugins/drupalviewsentity/src/editing.js -new file mode 100644 -index 0000000000000000000000000000000000000000..a06ec6100a6ccdf175b71492d9f8e48ffc3498df ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/editing.js -@@ -0,0 +1,309 @@ -+import { Plugin } from 'ckeditor5/src/core'; -+import { Widget, toWidget } from 'ckeditor5/src/widget'; -+import InsertViewsEntityEmbedCommand from './command'; -+ -+export default class ViewsEntityEmbedEditing extends Plugin { -+ -+ /** -+ * @inheritdoc -+ */ -+ static get requires() { -+ return [Widget]; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ init() { -+ this.attrs = { -+ alt: 'alt', -+ title: 'title', -+ dataCaption: 'data-caption', -+ dataAlign: 'data-align', -+ drupalEntityLangCode: 'data-langcode', -+ drupalEntityEntityType: 'data-entity-type', -+ drupalEntityEntityUuid: 'data-entity-uuid', -+ drupalEntityViewMode: 'data-view-mode', -+ drupalEntityEmbedButton: 'data-embed-button', -+ drupalEntityEmbedDisplay: 'data-entity-embed-display', -+ drupalEntityEmbedDisplaySettings: 'data-entity-embed-display-settings', -+ dataViewName: 'data-view-name', -+ dataViewDisplay: 'data-view-display', -+ dataViewArguments: 'data-view-arguments', -+ dataViewAttributes: 'data-view-attributes', -+ }; -+ const options = this.editor.config.get('viewsEntityEmbed'); -+ if (!options) { -+ throw new Error('Error on initializing entityEmbed plugin: entityEmbed config is required.'); -+ } -+ this.options = options; -+ this.labelError = Drupal.t('Preview failed'); -+ this.previewError =` -+ <p>${Drupal.t( -+ 'An error occurred while trying to preview the embedded content. Please save your work and reload this page.', -+ )}<p> -+ `; -+ -+ this._defineSchema(); -+ this._defineConverters(); -+ this.editor.commands.add( -+ 'insertViewsEntityEmbed', -+ new InsertViewsEntityEmbedCommand(this.editor), -+ ); -+ } -+ -+ /** -+ * Registers drupalEntity as a block element in the DOM. -+ * -+ * @private -+ */ -+ _defineSchema() { -+ const schema = this.editor.model.schema; -+ -+ schema.register('drupalViewsEntity', { -+ isObject: true, -+ isContent: true, -+ isBlock: true, -+ allowWhere: '$block', -+ allowAttributes: Object.keys(this.attrs), -+ }); -+ this.editor.editing.view.domConverter.blockElements.push('drupal-views'); -+ } -+ -+ /** -+ * Defines handling of drupalEntity elements. -+ * -+ * @private -+ */ -+ _defineConverters() { -+ const {conversion} = this.editor; -+ const elementMapping = { -+ model: 'drupalViewsEntity', -+ view: { -+ name: 'drupal-views', -+ }, -+ }; -+ -+ conversion -+ .for('upcast') -+ .elementToElement(elementMapping); -+ -+ conversion -+ .for('dataDowncast') -+ .elementToElement(elementMapping); -+ -+ // Convert the <drupalEntity> model into an editable <drupal-views> widget. -+ conversion -+ .for('editingDowncast') -+ .elementToElement({ -+ ...elementMapping, -+ view: (modelElement, { writer }) => { -+ // Align classes should be applied to the wrapper element so the -+ // alignment is relative to the other editor contents, and not -+ // within the container. -+ const alignClass = modelElement.hasAttribute('dataAlign') ? ` align-${modelElement.getAttribute('dataAlign')}` : ''; -+ const container = writer.createContainerElement('figure', { -+ class: `drupal-views${alignClass}`, -+ }); -+ writer.setCustomProperty('drupalViewsEntity', true, container); -+ -+ return toWidget(container, writer, { -+ label: Drupal.t('Views Entity Embed widget'), -+ }) -+ }, -+ }) -+ .add((dispatcher) => { -+ const converter = (event, data, conversionApi) => { -+ const viewWriter = conversionApi.writer; -+ const modelElement = data.item; -+ const container = conversionApi.mapper.toViewElement(data.item); -+ -+ let drupalEntity = this._getPreviewContainer(container.getChildren()); -+ // Use existing container if it exists, create on if it does not. -+ if (drupalEntity) { -+ // Stop processing if a preview is already loading. -+ if (drupalEntity.getAttribute('data-drupal-views-preview') !== 'ready') { -+ return; -+ } -+ // Preview is ready meaning that a new preview can be loaded. -+ // Change the attribute to loading to prepare for the loading of -+ // the updated preview. Preview is kept intact so that it remains -+ // interactable in the UI until the new preview has been rendered. -+ viewWriter.setAttribute( -+ 'data-drupal-views-preview', -+ 'loading', -+ drupalEntity, -+ ); -+ } -+ else { -+ drupalEntity = viewWriter.createRawElement('div', { -+ 'data-drupal-views-preview': 'loading', -+ }); -+ viewWriter.insert(viewWriter.createPositionAt(container, 0), drupalEntity); -+ } -+ -+ this._loadPreview(modelElement).then(({ label, preview }) => { -+ if (!drupalEntity) { -+ // Nothing to do if associated preview wrapped no longer exist. -+ return; -+ } -+ // CKEditor 5 doesn't support async view conversion. Therefore, once -+ // the promise is fulfilled, the editing view needs to be modified -+ // manually. -+ this.editor.editing.view.change((writer) => { -+ const drupalEntityPreview = writer.createRawElement( -+ 'div', -+ {'data-drupal-views-preview': 'ready', 'aria-label': label}, -+ (domElement) => { -+ domElement.innerHTML = preview; -+ }, -+ ); -+ // Insert the new preview before the previous preview element to -+ // ensure that the location remains same even if it is wrapped -+ // with another element. -+ writer.insert(writer.createPositionBefore(drupalEntity), drupalEntityPreview); -+ writer.remove(drupalEntity); -+ }); -+ }); -+ } -+/// dataViewName: 'data-view-name', -+// dataViewDisplay: 'data-view-display', -+// dataViewArguments: 'data-view-arguments', -+ dispatcher.on('attribute:dataViewDisplay', converter); -+ -+ return dispatcher; -+ }); -+ -+ // Set attributeToAttribute conversion for all supported attributes. -+ Object.keys(this.attrs).forEach((modelKey) => { -+ const attributeMapping = { -+ model: { -+ key: modelKey, -+ name: 'drupalViewsEntity', -+ }, -+ view: { -+ name: 'drupal-views', -+ key: this.attrs[modelKey], -+ }, -+ }; -+ // Attributes should be rendered only in dataDowncast to avoid having -+ // unfiltered data-attributes on the Drupal Entity widget. -+ conversion.for('dataDowncast').attributeToAttribute(attributeMapping); -+ conversion.for('upcast').attributeToAttribute(attributeMapping); -+ }); -+ } -+ -+ /** -+ * Loads the preview. -+ * -+ * @param modelElement -+ * The model element which preview should be loaded. -+ * @returns {Promise<{preview: string, label: *}|{preview: *, label: string}>} -+ * A promise that returns an object. -+ * -+ * @private -+ * -+ * @see DrupalMediaEditing::_fetchPreview(). -+ */ -+ async _loadPreview(modelElement) { -+ const query = { -+ text: this._renderElement(modelElement), -+ }; -+ -+ const response = await fetch( -+ Drupal.url('embed/preview/' + this.options.format + '?' + new URLSearchParams(query)), -+ { -+ headers: { -+ 'X-Drupal-EmbedPreview-CSRF-Token': -+ this.options.previewCsrfToken, -+ }, -+ }, -+ ); -+ -+ if (response.ok) { -+ const label = Drupal.t('Entity Embed widget'); -+ const preview = await response.text(); -+ return { label, preview }; -+ } -+ -+ return { label: this.labelError, preview: this.previewError }; -+ } -+ -+ /** -+ * Renders the model element. -+ * -+ * @param modelElement -+ * The drupalEntity model element to be converted. -+ * @returns {*} -+ * The model element converted into HTML. -+ * -+ * @private -+ */ -+ _renderElement(modelElement) { -+ // Create model document fragment which contains the model element so that -+ // it can be stringified using the dataDowncast. -+ const modelDocumentFragment = this.editor.model.change((writer) => { -+ const modelDocumentFragment = writer.createDocumentFragment(); -+ // Create shallow clone of the model element to ensure that the original -+ // model element remains untouched and that the caption is not rendered -+ // into the preview. -+ const clonedModelElement = writer.cloneElement(modelElement, false); -+ // Remove attributes from the model element to ensure they are not -+ // downcast into the preview request. -+ // - The `linkHref` model attribute would downcast into a wrapping `<a>` -+ // element, which the preview endpoint would not be able to handle. -+ // - The dataAlign attribute results in adding an alignment class that -+ // does not work properly when applied to the preview. The alignment -+ // class is instead added to the widget container element so alignment -+ // is relative to the editor contents, and not the widget container. -+ const attributeIgnoreList = ['linkHref', 'dataAlign']; -+ -+ attributeIgnoreList.forEach((attribute) => { -+ writer.removeAttribute(attribute, clonedModelElement); -+ }); -+ -+ writer.append(clonedModelElement, modelDocumentFragment); -+ -+ return modelDocumentFragment; -+ }); -+ -+ return this.editor.data.stringify(modelDocumentFragment); -+ } -+ -+ /** -+ * Gets the preview container element. -+ * -+ * @param children -+ * The child elements. -+ * @returns {null|*} -+ * The preview child element if available. -+ * -+ * @private -+ */ -+ _getPreviewContainer(children) { -+ for (const child of children) { -+ if (child.hasAttribute('data-drupal-views-preview')) { -+ return child; -+ } -+ -+ if (child.childCount) { -+ const recursive = this._getPreviewContainer(child.getChildren()); -+ // Return only if preview container was found within this element's -+ // children. -+ if (recursive) { -+ return recursive; -+ } -+ } -+ } -+ -+ return null; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ static get pluginName() { -+ return 'ViewsEntityEmbedEditing'; -+ } -+ -+} -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/generalhtmlsupport.js b/js/ckeditor5_plugins/drupalviewsentity/src/generalhtmlsupport.js -new file mode 100644 -index 0000000000000000000000000000000000000000..5be37382f52f6b6bc860fa64eafb9ff5d750cc27 ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/generalhtmlsupport.js -@@ -0,0 +1,245 @@ -+/* eslint-disable import/no-extraneous-dependencies */ -+// cSpell:words datafilter eventinfo downcastdispatcher generalhtmlsupport -+import { Plugin } from 'ckeditor5/src/core'; -+import { setViewAttributes } from '@ckeditor/ckeditor5-html-support/src/utils'; -+ -+/** -+ * View-to-model conversion helper for Entity Embed. -+ * Used for preserving allowed attributes on the Entity Embed model. -+ * -+ * @param {module:html-support/datafilter~DataFilter} dataFilter -+ * The General HTML support data filter. -+ * -+ * @return {function} -+ * Function that adds an event listener to upcastDispatcher. -+ */ -+function viewToModelDrupalEntityAttributeConverter(dataFilter) { -+ return (dispatcher) => { -+ dispatcher.on( -+ 'element:drupal-views', -+ (evt, data, conversionApi) => { -+ function preserveElementAttributes(viewElement, attributeName) { -+ const viewAttributes = dataFilter.processViewAttributes( -+ viewElement, -+ conversionApi -+ ); -+ -+ if (viewAttributes) { -+ conversionApi.writer.setAttribute( -+ attributeName, -+ viewAttributes, -+ data.modelRange -+ ); -+ } -+ } -+ function preserveLinkAttributes(linkElement) { -+ preserveElementAttributes(linkElement, 'htmlLinkAttributes'); -+ } -+ -+ const viewEntityEmbedElement = data.viewItem; -+ const viewContainerElement = viewEntityEmbedElement.parent; -+ -+ preserveElementAttributes(viewEntityEmbedElement, 'htmlAttributes'); -+ -+ if (viewContainerElement.is('element', 'a')) { -+ preserveLinkAttributes(viewContainerElement); -+ } -+ }, -+ { priority: 'low' } -+ ); -+ }; -+} -+ -+/** -+ * Gets descendant element from a container. -+ * -+ * @param {module:engine/model/writer~Writer} writer -+ * The writer. -+ * @param {module:engine/view/element~Element} containerElement -+ * The container element. -+ * @param {string} elementName -+ * The element name. -+ * @return {module:engine/view/element~Element|undefined} -+ * The descendant element matching element name or undefined if not found. -+ */ -+function getDescendantElement(writer, containerElement, elementName) { -+ const range = writer.createRangeOn(containerElement); -+ -+ // eslint-disable-next-line no-restricted-syntax -+ for (const { item } of range.getWalker()) { -+ if (item.is('element', elementName)) { -+ return item; -+ } -+ } -+} -+ -+/** -+ * Model to view converter for the Entity Embed wrapper attributes. -+ * -+ * @param {module:utils/eventinfo~EventInfo} evt -+ * An object containing information about the fired event. -+ * @param {Object} data -+ * Additional information about the change. -+ * @param {module:engine/conversion/downcastdispatcher~DowncastDispatcher} conversionApi -+ * Conversion interface to be used by the callback. -+ */ -+function modelToDataAttributeConverter(evt, data, conversionApi) { -+ if (!conversionApi.consumable.consume(data.item, evt.name)) { -+ return; -+ } -+ -+ const viewElement = conversionApi.mapper.toViewElement(data.item); -+ -+ setViewAttributes(conversionApi.writer, data.attributeNewValue, viewElement); -+} -+ -+/** -+ * Model to editing view attribute converter. -+ * -+ * @return {function} -+ * A function that adds an event listener to downcastDispatcher. -+ */ -+function modelToEditingViewAttributeConverter() { -+ return (dispatcher) => { -+ dispatcher.on( -+ 'attribute:linkHref:drupalEntity', -+ (evt, data, conversionApi) => { -+ if ( -+ !conversionApi.consumable.consume( -+ data.item, -+ 'attribute:htmlLinkAttributes:drupalEntity' -+ ) -+ ) { -+ return; -+ } -+ -+ const containerElement = conversionApi.mapper.toViewElement(data.item); -+ const viewElement = getDescendantElement( -+ conversionApi.writer, -+ containerElement, -+ 'a' -+ ); -+ -+ setViewAttributes( -+ conversionApi.writer, -+ data.item.getAttribute('htmlLinkAttributes'), -+ viewElement -+ ); -+ }, -+ { priority: 'low' } -+ ); -+ }; -+} -+ -+/** -+ * Model to data view attribute converter. -+ * -+ * @return {function} -+ * Function that adds an event listener to downcastDispatcher. -+ */ -+function modelToDataViewAttributeConverter() { -+ return (dispatcher) => { -+ dispatcher.on( -+ 'attribute:linkHref:drupalEntity', -+ (evt, data, conversionApi) => { -+ if ( -+ !conversionApi.consumable.consume( -+ data.item, -+ 'attribute:htmlLinkAttributes:drupalEntity' -+ ) -+ ) { -+ return; -+ } -+ -+ const entityEmbedElement = conversionApi.mapper.toViewElement( -+ data.item -+ ); -+ const linkElement = entityEmbedElement.parent; -+ -+ setViewAttributes( -+ conversionApi.writer, -+ data.item.getAttribute('htmlLinkAttributes'), -+ linkElement -+ ); -+ }, -+ { priority: 'low' } -+ ); -+ -+ dispatcher.on( -+ 'attribute:htmlAttributes:drupalEntity', -+ modelToDataAttributeConverter, -+ { priority: 'low' } -+ ); -+ }; -+} -+ -+/** -+ * Integrates Entity Embed with General HTML Support. -+ * -+ * @private -+ */ -+export default class ViewsEntityEmbedGeneralHtmlSupport extends Plugin { -+ /** -+ * @inheritdoc -+ */ -+ constructor(editor) { -+ super(editor); -+ -+ // This plugin is only needed if General HTML Support plugin is loaded. -+ if (!editor.plugins.has('GeneralHtmlSupport')) { -+ return; -+ } -+ // This plugin works only if `DataFilter` and `DataSchema` plugins are -+ // loaded. These plugins are dependencies of `GeneralHtmlSupport` meaning -+ // that these should be available always when `GeneralHtmlSupport` is -+ // enabled. -+ if ( -+ !editor.plugins.has('DataFilter') || -+ !editor.plugins.has('DataSchema') -+ ) { -+ console.error( -+ 'DataFilter and DataSchema plugins are required for Entity Embed to integrate with General HTML Support plugin.' -+ ); -+ } -+ -+ const { schema } = editor.model; -+ const { conversion } = editor; -+ const dataFilter = this.editor.plugins.get('DataFilter'); -+ const dataSchema = this.editor.plugins.get('DataSchema'); -+ -+ // This needs to be initialized in ::constructor() to ensure this runs -+ // before the General HTML Support has been initialized. -+ // @see module:html-support/generalhtmlsupport~GeneralHtmlSupport -+ dataSchema.registerBlockElement({ -+ model: 'drupalViewsEntity', -+ view: 'drupal-views', -+ }); -+ -+ dataFilter.on('register:drupal-views', (evt, definition) => { -+ if (definition.model !== 'drupalViewsEntity') { -+ return; -+ } -+ -+ schema.extend('drupalViewsEntity', { -+ allowAttributes: ['htmlLinkAttributes', 'htmlAttributes'], -+ }); -+ -+ conversion -+ .for('upcast') -+ .add(viewToModelDrupalEntityAttributeConverter(dataFilter)); -+ conversion -+ .for('editingDowncast') -+ .add(modelToEditingViewAttributeConverter()); -+ conversion.for('dataDowncast').add(modelToDataViewAttributeConverter()); -+ -+ evt.stop(); -+ }); -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ static get pluginName() { -+ return 'ViewsEntityEmbedGeneralHtmlSupport'; -+ } -+} -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/index.js b/js/ckeditor5_plugins/drupalviewsentity/src/index.js -new file mode 100644 -index 0000000000000000000000000000000000000000..2ab433e207ae4943be322ebcb8bed20ca93f1f4d ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/index.js -@@ -0,0 +1,9 @@ -+/** -+ * @module entity-embed -+ */ -+ -+import ViewsEntityEmbed from './viewsentityembed'; -+ -+export default { -+ ViewsEntityEmbed, -+}; -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/link.js b/js/ckeditor5_plugins/drupalviewsentity/src/link.js -new file mode 100644 -index 0000000000000000000000000000000000000000..99f1bda8fcda4010d029cc74c7da924c4b95346e ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/link.js -@@ -0,0 +1,24 @@ -+/* eslint-disable import/no-extraneous-dependencies */ -+ -+import { Plugin } from 'ckeditor5/src/core'; -+import ViewsEntityEmbedLinkEditing from './linkediting'; -+import ViewsEntityEmbedLinkUI from './linkui'; -+ -+/** -+ * @private -+ */ -+export default class ViewsEntityEmbedLink extends Plugin { -+ /** -+ * @inheritdoc -+ */ -+ static get requires() { -+ return [ViewsEntityEmbedLinkEditing, ViewsEntityEmbedLinkUI]; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ static get pluginName() { -+ return 'ViewsEntityEmbedLink'; -+ } -+} -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/linkediting.js b/js/ckeditor5_plugins/drupalviewsentity/src/linkediting.js -new file mode 100644 -index 0000000000000000000000000000000000000000..8f982738fc376afb0d45c60823f1b02b0256a717 ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/linkediting.js -@@ -0,0 +1,406 @@ -+/* eslint-disable import/no-extraneous-dependencies */ -+/* cspell:words linkediting linkimageediting linkcommand */ -+import { Plugin } from 'ckeditor5/src/core'; -+import { Matcher } from 'ckeditor5/src/engine'; -+import { toMap } from 'ckeditor5/src/utils'; -+ -+/** -+ * Returns the first drupal-views element in a given view element. -+ * -+ * @param {module:engine/view/element~Element} viewElement -+ * The view element. -+ * -+ * @return {module:engine/view/element~Element|undefined} -+ * The first <drupal-views> element or undefined if the element doesn't have -+ * <drupal-views> as a child element. -+ */ -+function getFirstEntityEmbed(viewElement) { -+ return Array.from(viewElement.getChildren()).find( -+ (child) => child.name === 'drupal-views' -+ ); -+} -+ -+/** -+ * Returns a converter that consumes the `href` attribute if a link contains a <drupal-views>. -+ * -+ * @return {Function} -+ * A function that adds an event listener to upcastDispatcher. -+ */ -+function upcastViewsEntityEmbedLink() { -+ return (dispatcher) => { -+ dispatcher.on( -+ 'element:a', -+ (evt, data, conversionApi) => { -+ const viewLink = data.viewItem; -+ const entityEmbedInLink = getFirstEntityEmbed(viewLink); -+ -+ if (!entityEmbedInLink) { -+ return; -+ } -+ -+ // There's an <drupal-views> inside an <a> element - we consume it so it -+ // won't be picked up by the Link plugin. -+ const consumableAttributes = { attributes: ['href'], name: true }; -+ -+ // Consume the `href` attribute so the default one will not convert it to -+ // $text attribute. -+ if (!conversionApi.consumable.consume(viewLink, consumableAttributes)) { -+ // Might be consumed by something else - i.e. other converter with -+ // priority=highest - a standard check. -+ return; -+ } -+ -+ const linkHref = viewLink.getAttribute('href'); -+ -+ // Missing the `href` attribute. -+ if (!linkHref) { -+ return; -+ } -+ -+ const conversionResult = conversionApi.convertItem( -+ entityEmbedInLink, -+ data.modelCursor -+ ); -+ -+ // Set entity embed range as conversion result. -+ data.modelRange = conversionResult.modelRange; -+ -+ // Continue conversion where <drupal-views> conversion ends. -+ data.modelCursor = conversionResult.modelCursor; -+ -+ const modelElement = data.modelCursor.nodeBefore; -+ -+ if (modelElement && modelElement.is('element', 'drupalViewsEntity')) { -+ // Set the `linkHref` attribute from <a> element on model drupalEntity -+ // element. -+ conversionApi.writer.setAttribute('linkHref', linkHref, modelElement); -+ } -+ }, -+ { priority: 'high' } -+ ); -+ }; -+} -+ -+/** -+ * Return a converter that adds the <a> element to view data. -+ * -+ * @return {Function} -+ * A function that adds an event listener to downcastDispatcher. -+ */ -+function dataDowncastViewsEntityEmbedLink() { -+ return (dispatcher) => { -+ dispatcher.on( -+ 'attribute:linkHref:drupalEntity', -+ (evt, data, conversionApi) => { -+ const { writer } = conversionApi; -+ if (!conversionApi.consumable.consume(data.item, evt.name)) { -+ return; -+ } -+ -+ // The drupalEntity will be already converted - so it will be present in -+ // the view. -+ const entityEmbedElement = conversionApi.mapper.toViewElement( -+ data.item -+ ); -+ -+ // If so, update the attribute if it's defined or remove the entire link -+ // if the attribute is empty. But if it does not exist. Let's wrap already -+ // converted drupalEntity by newly created link element. -+ // 1. Create an empty <a> element. -+ const linkElement = writer.createContainerElement('a', { -+ href: data.attributeNewValue, -+ }); -+ -+ // 2. Insert <a> before the <drupal-views> element. -+ writer.insert( -+ writer.createPositionBefore(entityEmbedElement), -+ linkElement -+ ); -+ -+ // 3. Move the drupal-views element inside the <a>. -+ writer.move( -+ writer.createRangeOn(entityEmbedElement), -+ writer.createPositionAt(linkElement, 0) -+ ); -+ }, -+ { priority: 'high' } -+ ); -+ }; -+} -+ -+/** -+ * Return a converter that adds the <a> element to editing view. -+ * -+ * @return {Function} -+ * A function that adds an event listener to downcastDispatcher. -+ * -+ * @see https://github.com/ckeditor/ckeditor5/blob/v31.0.0/packages/ckeditor5-link/src/linkimageediting.js#L180 -+ */ -+function editingDowncastViewsEntityEmbedLink() { -+ return (dispatcher) => { -+ dispatcher.on( -+ 'attribute:linkHref:drupalEntity', -+ (evt, data, conversionApi) => { -+ const { writer } = conversionApi; -+ if (!conversionApi.consumable.consume(data.item, evt.name)) { -+ return; -+ } -+ -+ // The drupalEntity will be already converted - so it will be present in -+ // the view. -+ const entityEmbedContainer = conversionApi.mapper.toViewElement( -+ data.item -+ ); -+ const linkInEntityEmbed = Array.from( -+ entityEmbedContainer.getChildren() -+ ).find((child) => child.name === 'a'); -+ -+ // If link already exists, instead of creating new link from scratch, -+ // update the existing link. This makes the UI rendering much smoother. -+ if (linkInEntityEmbed) { -+ // If attribute has a new value, update it. If new value doesn't exist, -+ // the link will be removed. -+ if (data.attributeNewValue) { -+ writer.setAttribute( -+ 'href', -+ data.attributeNewValue, -+ linkInEntityEmbed -+ ); -+ } else { -+ // This is triggering elementToElement conversion for drupalEntity -+ // element which makes caused re-render of the entity embed preview, making -+ // the entity embed preview flicker once when entity embed is unlinked. -+ // @todo ensure that this doesn't cause flickering after -+ // https://www.drupal.org/i/3304834 has been addressed. -+ writer.move( -+ writer.createRangeIn(linkInEntityEmbed), -+ writer.createPositionAt(entityEmbedContainer, 0) -+ ); -+ writer.remove(linkInEntityEmbed); -+ } -+ } else { -+ const entityEmbedPreview = Array.from( -+ entityEmbedContainer.getChildren() -+ ).find((child) => child.getAttribute('data-drupal-views-preview')); -+ // 1. Create an empty <a> element. -+ const linkElement = writer.createContainerElement('a', { -+ href: data.attributeNewValue, -+ }); -+ -+ // 2. Insert <a> inside the entity embed container. -+ writer.insert( -+ writer.createPositionAt(entityEmbedContainer, 0), -+ linkElement -+ ); -+ -+ // 3. Move the entity embed preview inside the <a>. -+ writer.move( -+ writer.createRangeOn(entityEmbedPreview), -+ writer.createPositionAt(linkElement, 0) -+ ); -+ } -+ }, -+ { priority: 'high' } -+ ); -+ }; -+} -+ -+/** -+ * Returns a converter that enables manual decorators on linked Drupal Entity. -+ * -+ * @see \Drupal\editor\EditorXssFilter\Standard -+ * -+ * @param {module:link/link~LinkDecoratorDefinition} decorator -+ * The link decorator. -+ * @return {function} -+ * Function attaching event listener to dispatcher. -+ * -+ * @private -+ */ -+function downcastViewsEntityEmbedLinkManualDecorator(decorator) { -+ return (dispatcher) => { -+ dispatcher.on( -+ `attribute:${decorator.id}:drupalEntity`, -+ (evt, data, conversionApi) => { -+ const entityEmbedContainer = conversionApi.mapper.toViewElement( -+ data.item -+ ); -+ -+ // Scenario 1: `<figure>` element that contains `<a>`, generated by -+ // `dataDowncast`. -+ let entityEmbedLink = Array.from( -+ entityEmbedContainer.getChildren() -+ ).find((child) => child.name === 'a'); -+ -+ // Scenario 2: `<drupal-views>` wrapped with `<a>`, generated by -+ // `editingDowncast`. -+ if (!entityEmbedLink && entityEmbedContainer.is('element', 'a')) { -+ entityEmbedLink = entityEmbedContainer; -+ } else { -+ entityEmbedLink = Array.from( -+ entityEmbedContainer.getAncestors() -+ ).find((ancestor) => ancestor.name === 'a'); -+ } -+ -+ // The <a> element was removed by the time this converter is executed. -+ // It may happen when the base `linkHref` and decorator attributes are -+ // removed at the same time. -+ if (!entityEmbedLink) { -+ return; -+ } -+ -+ // eslint-disable-next-line no-restricted-syntax -+ for (const [key, val] of toMap(decorator.attributes)) { -+ conversionApi.writer.setAttribute(key, val, entityEmbedLink); -+ } -+ -+ if (decorator.classes) { -+ conversionApi.writer.addClass(decorator.classes, entityEmbedLink); -+ } -+ -+ // Add support for `style` attribute in manual decorators to remain -+ // consistent with CKEditor 5. This only works with text formats that -+ // have no HTMl filtering enabled. -+ // eslint-disable-next-line no-restricted-syntax -+ for (const key in decorator.styles) { -+ if (Object.prototype.hasOwnProperty.call(decorator.styles, key)) { -+ conversionApi.writer.setStyle( -+ key, -+ decorator.styles[key], -+ entityEmbedLink -+ ); -+ } -+ } -+ } -+ ); -+ }; -+} -+ -+/** -+ * Returns a converter that applies manual decorators to linked Drupal Entity. -+ * -+ * @param {module:core/editor/editor~Editor} editor -+ * The editor. -+ * @param {module:link/link~LinkDecoratorDefinition} decorator -+ * The link decorator. -+ * @return {function} -+ * Function attaching event listener to dispatcher. -+ * -+ * @private -+ */ -+function upcastViewsEntityEmbedLinkManualDecorator(editor, decorator) { -+ return (dispatcher) => { -+ dispatcher.on( -+ 'element:a', -+ (evt, data, conversionApi) => { -+ const viewLink = data.viewItem; -+ const drupalEntityEmbedInLink = getFirstEntityEmbed(viewLink); -+ -+ // We need to check whether Drupal Entity is inside a link because the -+ // converter handles only manual decorators for linked Drupal Entity. -+ if (!drupalEntityEmbedInLink) { -+ return; -+ } -+ -+ const matcher = new Matcher(decorator._createPattern()); -+ const result = matcher.match(viewLink); -+ -+ // The link element does not have required attributes or/and proper -+ // values. -+ if (!result) { -+ return; -+ } -+ -+ // Check whether we can consume those attributes. -+ if (!conversionApi.consumable.consume(viewLink, result.match)) { -+ return; -+ } -+ -+ // At this stage we can assume that we have the `<drupalEntity>` element. -+ const modelElement = data.modelCursor.nodeBefore; -+ -+ conversionApi.writer.setAttribute(decorator.id, true, modelElement); -+ }, -+ { priority: 'high' } -+ ); -+ // Using the same priority as the entity embed link upcast converter guarantees -+ // that the linked `<drupalEntity>` was already converted. -+ // @see upcastViewsEntityEmbedLink(). -+ }; -+} -+ -+/** -+ * Model to view and view to model conversions for linked entity embed elements. -+ * -+ * @private -+ * -+ * @see https://github.com/ckeditor/ckeditor5/blob/v31.0.0/packages/ckeditor5-link/src/linkimage.js -+ */ -+export default class ViewsEntityEmbedLinkEditing extends Plugin { -+ /** -+ * @inheritdoc -+ */ -+ static get requires() { -+ return ['ViewsEntityEmbedEditing']; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ static get pluginName() { -+ return 'ViewsEntityEmbedLinkEditing'; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ init() { -+ const { editor } = this; -+ editor.model.schema.extend('drupalViewsEntity', { -+ allowAttributes: ['linkHref'], -+ }); -+ -+ editor.conversion.for('upcast').add(upcastViewsEntityEmbedLink()); -+ editor.conversion -+ .for('editingDowncast') -+ .add(editingDowncastViewsEntityEmbedLink()); -+ editor.conversion.for('dataDowncast').add(dataDowncastViewsEntityEmbedLink()); -+ -+ this._enableManualDecorators(); -+ -+ const linkCommand = editor.commands.get('link'); -+ if (linkCommand.automaticDecorators.length > 0) { -+ throw new Error( -+ 'The Drupal Entity plugin is not compatible with automatic link decorators. To use Drupal Entity, disable any plugins providing automatic link decorators.' -+ ); -+ } -+ } -+ -+ /** -+ * Processes transformed manual link decorators and attaches proper converters -+ * that will work when linking Drupal Entity. -+ * -+ * @see module:link/linkimageediting~LinkImageEditing -+ * @see module:link/linkcommand~LinkCommand -+ * @see module:link/utils~ManualDecorator -+ * -+ * @private -+ */ -+ _enableManualDecorators() { -+ const editor = this.editor; -+ const command = editor.commands.get('link'); -+ -+ // eslint-disable-next-line no-restricted-syntax -+ for (const decorator of command.manualDecorators) { -+ editor.model.schema.extend('drupalViewsEntity', { -+ allowAttributes: decorator.id, -+ }); -+ editor.conversion -+ .for('downcast') -+ .add(downcastViewsEntityEmbedLinkManualDecorator(decorator)); -+ editor.conversion -+ .for('upcast') -+ .add(upcastViewsEntityEmbedLinkManualDecorator(editor, decorator)); -+ } -+ } -+} -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/linkui.js b/js/ckeditor5_plugins/drupalviewsentity/src/linkui.js -new file mode 100644 -index 0000000000000000000000000000000000000000..456de5534749baf1e81afb26f5337e96cd2164c4 ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/linkui.js -@@ -0,0 +1,116 @@ -+/* eslint-disable import/no-extraneous-dependencies */ -+// cSpell:words linkui -+import { Plugin, icons } from 'ckeditor5/src/core'; -+import { LINK_KEYSTROKE } from '@ckeditor/ckeditor5-link/src/utils'; -+import { ButtonView } from 'ckeditor5/src/ui'; -+import linkIcon from '@ckeditor/ckeditor5-link/theme/icons/link.svg'; -+ -+/** -+ * The link entity embed UI plugin. -+ * -+ * @private -+ */ -+export default class ViewsEntityEmbedLinkUI extends Plugin { -+ /** -+ * @inheritdoc -+ */ -+ static get requires() { -+ return ['LinkEditing', 'LinkUI', 'ViewsEntityEmbedEditing', 'ViewsEntityEmbedUI']; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ static get pluginName() { -+ return 'ViewsEntityEmbedLinkUi'; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ init() { -+ const { editor } = this; -+ const viewDocument = editor.editing.view.document; -+ -+ this.listenTo( -+ viewDocument, -+ 'click', -+ (evt, data) => { -+ if ( -+ this._isSelectedLinkedEntityEmbed(editor.model.document.selection) -+ ) { -+ // Prevent browser navigation when clicking a linked entity embed. -+ data.preventDefault(); -+ -+ // Block the `LinkUI` plugin when a entity embed was clicked. In such a case, -+ // we'd like to display the entity embed toolbar. -+ evt.stop(); -+ } -+ }, -+ { priority: 'high' } -+ ); -+ -+ this._createToolbarLinkEntityEmbedButton(); -+ } -+ -+ /** -+ * Creates a linking button view. -+ * -+ * Clicking this button shows a {@link module:link/linkui~LinkUI#_balloon} -+ * attached to the selection. When an embedded entity is already linked, the -+ * view shows {@link module:link/linkui~LinkUI#actionsView} or -+ * {@link module:link/linkui~LinkUI#formView} if it is not. -+ */ -+ _createToolbarLinkEntityEmbedButton() { -+ const { editor } = this; -+ -+ editor.ui.componentFactory.add('entityEmbedLink', (locale) => { -+ const button = new ButtonView(locale); -+ const plugin = editor.plugins.get('LinkUI'); -+ const linkCommand = editor.commands.get('link'); -+ -+ button.set({ -+ isEnabled: true, -+ label: Drupal.t('Link entity embed'), -+ icon: linkIcon, -+ keystroke: LINK_KEYSTROKE, -+ tooltip: true, -+ isToggleable: true, -+ }); -+ -+ // Bind button to the command. -+ button.bind('isEnabled').to(linkCommand, 'isEnabled'); -+ button.bind('isOn').to(linkCommand, 'value', (value) => !!value); -+ -+ // Show the actionsView or formView (both from LinkUI) on button click -+ // depending on whether the entity embed is already linked. -+ this.listenTo(button, 'execute', () => { -+ if ( -+ this._isSelectedLinkedEntityEmbed(editor.model.document.selection) -+ ) { -+ plugin._addActionsView(); -+ } else { -+ plugin._showUI(true); -+ } -+ }); -+ -+ return button; -+ }); -+ } -+ -+ /** -+ * Returns true if a linked entity embed is the only selected element in the model. -+ * -+ * @param {module:engine/model/selection~Selection} selection -+ * @return {Boolean} -+ */ -+ // eslint-disable-next-line class-methods-use-this -+ _isSelectedLinkedEntityEmbed(selection) { -+ const selectedModelElement = selection.getSelectedElement(); -+ return ( -+ !!selectedModelElement && -+ selectedModelElement.is('element', 'drupalViewsEntity') && -+ selectedModelElement.hasAttribute('linkHref') -+ ); -+ } -+} -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/toolbar.js b/js/ckeditor5_plugins/drupalviewsentity/src/toolbar.js -new file mode 100644 -index 0000000000000000000000000000000000000000..b6ec9d2f8fdb15e203418ca3ff076daf1d21a41e ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/toolbar.js -@@ -0,0 +1,178 @@ -+import { Plugin, icons } from 'ckeditor5/src/core'; -+import { isWidget, WidgetToolbarRepository } from 'ckeditor5/src/widget'; -+import { ButtonView } from 'ckeditor5/src/ui'; -+ -+export default class ViewsEntityEmbedToolbar extends Plugin { -+ -+ /** -+ * @inheritdoc -+ */ -+ static get requires() { -+ return [WidgetToolbarRepository]; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ init() { -+ const editor = this.editor; -+ const entityEmbedEditing = editor.plugins.get('ViewsEntityEmbedEditing'); -+ const options = editor.config.get('viewsEntityEmbed'); -+ const { dialogSettings = {} } = options; -+ -+ editor.ui.componentFactory.add('viewsEntityEmbedEdit', (locale) => { -+ let buttonView = new ButtonView(locale); -+ -+ buttonView.set({ -+ label: editor.t('Edit'), -+ icon: icons.pencil, -+ tooltip: true, -+ }); -+ -+ this.listenTo(buttonView, 'execute', (eventInfo) => { -+ let element = editor.model.document.selection.getSelectedElement(); -+ let libraryURL = Drupal.url((options.formRoot || 'entity-embed/dialog/') + options.format + '/' + element.getAttribute('drupalEntityEmbedButton')); -+ -+ let existingValues = {}; -+ -+ for (let [key, value] of element.getAttributes()) { -+ let attribute = entityEmbedEditing.attrs[key]; -+ if (attribute) { -+ existingValues[attribute] = value; -+ } -+ } -+ -+ // Open a dialog to select entity to embed. -+ this._openDialog( -+ libraryURL, -+ existingValues, -+ ({ attributes }) => { -+ editor.execute('insertViewsEntityEmbed', attributes); -+ editor.editing.view.focus(); -+ }, -+ dialogSettings, -+ ); -+ }); -+ -+ return buttonView; -+ }); -+ -+ editor.ui.componentFactory.add('viewsEditEmbeddedEntity', (locale) => { -+ const button = new ButtonView(locale); -+ const element = editor.model.document.selection.getSelectedElement(); -+ if (!element) { -+ return null; -+ } -+ if (!element.hasAttribute('dataViewName')) { -+ console.warn(Drupal.t('Unable to create edit link. There must be a value for data-view-name.')); -+ return null; -+ } -+ // const uuid = element.getAttribute('drupalEntityUuid'); -+ const type = element.getAttribute('dataViewName'); -+ const editUrl = Drupal.url(`entity-embed/edit-embedded-view/${type}`); -+ console.log('edit url', editUrl); -+ button.set({ -+ isEnabled: true, -+ label: Drupal.t('Edit the embedded view (opens in new tab)'), -+ icon: icons.cog, -+ tooltip: true, -+ }); -+ -+ // Ping the edit url and disable the button if the user does not have -+ // access. Because this is async, there's a moment where the button is -+ // clickable even if they don't have access, but the destination will -+ // remain inaccessible. -+ fetch(editUrl) -+ .then((res) => { -+ if (!res.ok) { -+ button.set({ -+ label: Drupal.t(`You do not have the permissions needed to edit the view ${type}.`), -+ isEnabled: false, -+ }); -+ } -+ }); -+ -+ this.listenTo(button, 'execute', () => { -+ window.open(editUrl, '_blank'); -+ }); -+ -+ return button; -+ }); -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ afterInit() { -+ const { editor } = this; -+ if (!editor.plugins.has('WidgetToolbarRepository')) { -+ return; -+ } -+ const widgetToolbarRepository = editor.plugins.get(WidgetToolbarRepository); -+ -+ widgetToolbarRepository.register('viewsEntityEmbed', { -+ ariaLabel: Drupal.t('Entity Embed toolbar'), -+ items: ['viewsEntityEmbedEdit', 'entityEmbedLink', 'viewsEditEmbeddedEntity'], -+ getRelatedElement(selection) { -+ const viewElement = selection.getSelectedElement(); -+ if (!viewElement) { -+ return null; -+ } -+ if (!isWidget(viewElement)) { -+ return null; -+ } -+ if (!viewElement.getCustomProperty('drupalViewsEntity')) { -+ return null; -+ } -+ -+ return viewElement; -+ }, -+ }); -+ } -+ -+ /** -+ * @param {string} url -+ * The URL that contains the contents of the dialog. -+ * @param {object} existingValues -+ * Existing values that will be sent via POST to the url for the dialog -+ * contents. -+ * @param {function} saveCallback -+ * A function to be called upon saving the dialog. -+ * @param {object} dialogSettings -+ * An object containing settings to be passed to the jQuery UI. -+ */ -+ _openDialog(url, existingValues, saveCallback, dialogSettings) { -+ // Add a consistent dialog class. -+ const classes = dialogSettings.dialogClass -+ ? dialogSettings.dialogClass.split(' ') -+ : []; -+ classes.push('ui-dialog--narrow'); -+ dialogSettings.dialogClass = classes.join(' '); -+ dialogSettings.autoResize = -+ window.matchMedia('(min-width: 600px)').matches; -+ dialogSettings.width = 'auto'; -+ -+ const ckeditorAjaxDialog = Drupal.ajax({ -+ dialog: dialogSettings, -+ dialogType: 'modal', -+ selector: '.ckeditor5-dialog-loading-link', -+ url, -+ progress: { type: 'fullscreen' }, -+ submit: { -+ editor_object: existingValues, -+ }, -+ }); -+ ckeditorAjaxDialog.execute(); -+ -+ // Store the save callback to be executed when this dialog is closed. -+ Drupal.ckeditor5.saveCallback = saveCallback; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ static get pluginName() { -+ return 'ViewsEntityEmbedToolbar'; -+ } -+ -+} -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/ui.js b/js/ckeditor5_plugins/drupalviewsentity/src/ui.js -new file mode 100644 -index 0000000000000000000000000000000000000000..e7cb8869da13b3b5a4dfa805b04ce14057687531 ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/ui.js -@@ -0,0 +1,84 @@ -+/** -+ * @file Registers the entity embed button(s) to the CKEditor instance(s) and binds functionality to it/them. -+ */ -+ -+import { Plugin } from 'ckeditor5/src/core'; -+import { ButtonView } from 'ckeditor5/src/ui'; -+import defaultIcon from '../entity.svg'; -+ -+export default class ViewsEntityEmbedUI extends Plugin { -+ -+ /** -+ * @inheritdoc -+ */ -+ static get requires() { -+ return ['Widget']; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ init() { -+ const editor = this.editor; -+ const command = editor.commands.get('insertViewsEntityEmbed'); -+ const options = editor.config.get('viewsEntityEmbed'); -+ if (!options) { -+ return; -+ } -+ const { dialogSettings = {} } = options; -+ const embed_buttons = options.buttons; -+ -+ // Register each embed button to the toolbar based on configuration. -+ Object.keys(embed_buttons).forEach(id => { -+ const libraryURL = Drupal.url((options.formRoot || 'entity-embed/dialog/') + options.format + '/' + id); -+ // Add each button to the toolbar. -+ editor.ui.componentFactory.add(id, (locale) => { -+ const button = embed_buttons[id]; -+ const buttonView = new ButtonView(locale); -+ // Set the icon to the SVG from config, or set it to the default icon. -+ // If the uploaded icon is an SVG, load it. If the icon was provided for -+ // the button in a pre-CKEditor 5 Drupal instance, it may use a format -+ // other than SVG. In that case it remains null and the default Entity -+ // embed icon will be used. -+ let icon = null; -+ if (button.icon.endsWith('svg')) { -+ let XMLrequest = new XMLHttpRequest(); -+ XMLrequest.open('GET', button.icon, false); -+ XMLrequest.send(null); -+ icon = XMLrequest.response; -+ } else { -+ console.warn(`CKEditor 5 only supports enity embed icons in SVG format. The icon provided is ${button.icon}`); -+ } -+ -+ -+ buttonView.set({ -+ label: button.label, -+ icon: icon ?? defaultIcon, -+ tooltip: true, -+ }); -+ buttonView.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled'); -+ -+ this.listenTo(buttonView, 'execute', () => -+ // Open a dialog to select entity to embed. -+ Drupal.ckeditor5.openDialog( -+ libraryURL, -+ ({ attributes }) => { -+ editor.execute('insertViewsEntityEmbed', attributes); -+ }, -+ dialogSettings, -+ ), -+ ); -+ -+ return buttonView; -+ }) -+ }); -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ static get pluginName() { -+ return 'ViewsEntityEmbedUI'; -+ } -+ -+} -diff --git a/js/ckeditor5_plugins/drupalviewsentity/src/viewsentityembed.js b/js/ckeditor5_plugins/drupalviewsentity/src/viewsentityembed.js -new file mode 100644 -index 0000000000000000000000000000000000000000..5c41d6ebe619c565ff2bbfa0b39dc80348968d54 ---- /dev/null -+++ b/js/ckeditor5_plugins/drupalviewsentity/src/viewsentityembed.js -@@ -0,0 +1,27 @@ -+import ViewsEntityEmbedEditing from './editing'; -+import ViewsEntityEmbedToolbar from './toolbar'; -+import ViewsEntityEmbedUI from './ui'; -+import ViewsEntityEmbedGeneralHtmlSupport from './generalhtmlsupport'; -+import ViewsEntityEmbedLink from './link'; -+import { Plugin } from 'ckeditor5/src/core'; -+ -+export default class ViewsEntityEmbed extends Plugin { -+ -+ static get requires() { -+ return [ -+ ViewsEntityEmbedEditing, -+ ViewsEntityEmbedUI, -+ ViewsEntityEmbedToolbar, -+ ViewsEntityEmbedGeneralHtmlSupport, -+ ViewsEntityEmbedLink, -+ ]; -+ } -+ -+ /** -+ * @inheritdoc -+ */ -+ static get pluginName() { -+ return 'ViewsEntityEmbed'; -+ } -+ -+} -diff --git a/js/plugins/drupalviews/views_entity_embed.svg b/js/plugins/drupalviews/views_entity_embed.svg -new file mode 100644 -index 0000000000000000000000000000000000000000..1c60eb2f20fa4f6118781138c0c681e00695c103 ---- /dev/null -+++ b/js/plugins/drupalviews/views_entity_embed.svg -@@ -0,0 +1,5 @@ -+<?xml version="1.0" encoding="UTF-8" standalone="no"?> -+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> <image id="image0" width="16" height="16" x="0" y="0" -+ href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAm5JREFUOBFdU01LalEUXdqtSDQrKcX8QkQaKIhDEaSfEPgLAgf6D/wlhROhgZNoEo5EhJroSMQopCBFUvGz8gMrLZ9rP+zB23Duufecvddee+19VeFweNHtdqFSqfC/8WyxWOD7+xtmsxnxeFz2/f39X1elXq8jEonA7XbL4c/PD7gURRFQAlSrVWQyGTQaDWi1WrnT6/VQq9VQ6Oz3++H1egWgWCzi+fkZPp8PFosFGo0Ge3t7yOfzeHh4QCKRwHQ6xcXFBYxGI9SMYpa1tTW0222cn5/j7OwMV1dXeHp6knOWQkZHR0c4OTnBcDjE+vq6JBSAFcjW1hYODg4kaGdnR+iK1/JBumSzubkJm80Gln5/fw+F2Vc2n8/BQJ1OJ8FkRQFpZMH3VqsFq9WK6+trUHwBIPpsNsPl5SVyuRwGg4E4jMdjoUwAgtFeXl6EwWg0EgAp4ePjA29vb2g2m9je3hYH1sgzgvGe7Jik3++LeAR3Op1/u3B7e4tKpSIOdrtdMrG0r68vFAoF0JlgzMqd3UmlUtJ+KeHm5kbqXvVeEJaPTqeDx8dHTCYTEZHq08iOzFwuFxQKQnGIzjqZlWtlFI+LPScABaYO3B0OB5RYLCYZSJlARCZlvvOMg0aRqQEnktk5bIeHh3h/f4dq6SR95EcymcTu7u5vCykay+j1ejJUbGEgEECtVkMoFMLp6SmUFVXSLpfLMq7MynI2NjZksEjf4/Hg+PhYsmazWUSjUSntlwGDqAMF4wiXSiXZ2R0qT6IshX4ETqfTMBgM/xjwkn8Yl8lkQjAYlKDPz0+8vr4K4N3dnfxU7D9BaH8AMktwtN0BAAMAAAAASUVORK5CYII=" /> -+</svg> -diff --git a/package.json b/package.json -new file mode 100644 -index 0000000000000000000000000000000000000000..0c751900428139cd0fe0838735622cab89be78f4 ---- /dev/null -+++ b/package.json -@@ -0,0 +1,21 @@ -+{ -+ "name": "drupal-vies-entity-embed", -+ "version": "1.0.0", -+ "description": "Drupal Views Entity Embed CKEditor 5 integration", -+ "author": "", -+ "license": "GPL-2.0-or-later", -+ "scripts": { -+ "watch": "webpack --mode development --watch", -+ "build": "webpack" -+ }, -+ "devDependencies": { -+ "@ckeditor/ckeditor5-dev-utils": "^30.0.0", -+ "@ckeditor/ckeditor5-html-support": "^40.0.0", -+ "@ckeditor/ckeditor5-link": "^40.0.0", -+ "ckeditor5": "~34.1.0", -+ "raw-loader": "^4.0.2", -+ "terser-webpack-plugin": "^5.2.0", -+ "webpack": "^5.51.1", -+ "webpack-cli": "^4.4.0" -+ } -+} -diff --git a/src/Controller/EditEmbeddedView.php b/src/Controller/EditEmbeddedView.php -new file mode 100644 -index 0000000000000000000000000000000000000000..62154eb45127f7594fd164a391f1904706e58cac ---- /dev/null -+++ b/src/Controller/EditEmbeddedView.php -@@ -0,0 +1,26 @@ -+<?php -+ -+namespace Drupal\views_entity_embed\Controller; -+ -+use Drupal\Core\Controller\ControllerBase; -+ -+class EditEmbeddedView extends ControllerBase { -+ -+ /** -+ * Redirects to a view edit form based on its type. -+ * -+ * @param string $type -+ * The entity type. -+ * @param string $uuid -+ * The entity uuid. -+ * -+ * @return \Symfony\Component\HttpFoundation\RedirectResponse -+ * The redirect destination response. -+ */ -+ public function edit(string $type) { -+ return $this->redirect("entity.view.edit_form", ['view' => $type]); -+ } -+ -+ -+ -+} -diff --git a/src/Plugin/CKEditor5Plugin/DrupalEntity.php b/src/Plugin/CKEditor5Plugin/DrupalEntity.php -new file mode 100644 -index 0000000000000000000000000000000000000000..7a03699cff3176cbb6693398fe12f0c882c9b2fa ---- /dev/null -+++ b/src/Plugin/CKEditor5Plugin/DrupalEntity.php -@@ -0,0 +1,132 @@ -+<?php -+ -+declare(strict_types = 1); -+ -+namespace Drupal\views_entity_embed\Plugin\CKEditor5Plugin; -+ -+use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault; -+use Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition; -+use Drupal\Component\Utility\Html; -+use Drupal\Core\Access\CsrfTokenGenerator; -+use Drupal\Core\Entity\EntityTypeManagerInterface; -+use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -+use Drupal\editor\EditorInterface; -+use Symfony\Component\DependencyInjection\ContainerInterface; -+ -+/** -+ * @CKEditor5Plugin( -+ * id = "views_entity_embed_drupalentity", -+ * ckeditor5 = @CKEditor5AspectsOfCKEditor5Plugin( -+ * plugins = {"drupalviewsentity.ViewsEntityEmbed"}, -+ * config = {}, -+ * ), -+ * drupal = @DrupalAspectsOfCKEditor5Plugin( -+ * deriver = "Drupal\entity_embed\Plugin\CKEditor5Plugin\DrupalEntityDeriver", -+ * library = "views_entity_embed/views_entity_embed", -+ * admin_library = "entity_embed/admin.entity_embed", -+ * elements = { -+ * "<drupal-views>", -+ * "<drupal-views alt title data-align data-caption data-entity-embed-display data-entity-embed-display-settings data-view-mode data-entity-uuid data-view-name data-view-display data-view-attributes data-embed-button>", -+ * "<drupal-views data-langcode>", -+ * }, -+ * conditions = { -+ * "filter" = "views_embed", -+ * }, -+ * ) -+ * ) -+ */ -+class DrupalEntity extends CKEditor5PluginDefault implements ContainerFactoryPluginInterface { -+ -+ /** -+ * The CSRF Token generator. -+ * -+ * @var \Drupal\Core\Access\CsrfTokenGenerator -+ */ -+ protected $csrfTokenGenerator; -+ -+ /** -+ * The entity type manager. -+ * -+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface -+ */ -+ protected $entityTypeManager; -+ -+ /** -+ * DrupalEntity constructor. -+ * -+ * @param array $configuration -+ * A configuration array containing information about the plugin instance. -+ * @param string $plugin_id -+ * The plugin_id for the plugin instance. -+ * @param \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition $plugin_definition -+ * The plugin implementation definition. -+ * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token_generator -+ * The CSRF Token generator service. -+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager -+ * The Entity Type Manager service. -+ */ -+ public function __construct( -+ array $configuration, -+ string $plugin_id, -+ CKEditor5PluginDefinition $plugin_definition, -+ CsrfTokenGenerator $csrf_token_generator, -+ EntityTypeManagerInterface $entity_type_manager -+ ) { -+ parent::__construct($configuration, $plugin_id, $plugin_definition); -+ $this->csrfTokenGenerator = $csrf_token_generator; -+ $this->entityTypeManager = $entity_type_manager; -+ } -+ -+ /** -+ * {@inheritDoc} -+ */ -+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { -+ return new static( -+ $configuration, -+ $plugin_id, -+ $plugin_definition, -+ $container->get('csrf_token'), -+ $container->get('entity_type.manager') -+ ); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function getDynamicPluginConfig(array $static_plugin_config, EditorInterface $editor): array { -+ // Register embed buttons as individual buttons on admin pages. -+ $dynamic_plugin_config = $static_plugin_config; -+ $entity_embed_buttons = $this -+ ->entityTypeManager -+ ->getStorage('embed_button') -+ // @see \Drupal\entity_embed\Plugin\EmbedType\Entity -+ ->loadByProperties(['type_id' => 'embed_views']); -+ $buttons = []; -+ /** @var \Drupal\embed\EmbedButtonInterface $embed_button */ -+ foreach ($entity_embed_buttons as $entity_embed_button) { -+ $id = $entity_embed_button->id(); -+ $label = Html::escape($entity_embed_button->label()); -+ $buttons[$id] = [ -+ 'id' => $id, -+ 'name' => $label, -+ 'label' => $label, -+ 'icon' => $entity_embed_button->getIconUrl(), -+ ]; -+ } -+ // Add configured embed buttons and pass it to the UI. -+ $dynamic_plugin_config['viewsEntityEmbed'] = [ -+ 'buttons' => $buttons, -+ 'format' => $editor->getFilterFormat()->id(), -+ 'dialogSettings' => [ -+ 'dialogClass' => 'entity-select-dialog', -+ 'height' => 'auto', -+ 'width' => 'auto', -+ ], -+ 'previewCsrfToken' => $this->csrfTokenGenerator->get('X-Drupal-EmbedPreview-CSRF-Token'), -+ 'formRoot' => 'views-entity-embed/dialog/', -+ ]; -+ -+ return $dynamic_plugin_config; -+ } -+ -+} -diff --git a/src/Plugin/EmbedType/EmbedViews.php b/src/Plugin/EmbedType/EmbedViews.php -index 168bcd9a33b8a950386f849dde9708b823523366..e751669b8aba2a31644a8f772078050b6bbd6546 100644 ---- a/src/Plugin/EmbedType/EmbedViews.php -+++ b/src/Plugin/EmbedType/EmbedViews.php -@@ -43,7 +43,7 @@ class EmbedViews extends EmbedTypeBase implements ContainerFactoryPluginInterfac - * {@inheritdoc} - */ - public function getDefaultIconUrl() { -- return \Drupal::service('file_url_generator')->generateAbsoluteString(\Drupal::service('extension.list.module')->getPath('views_entity_embed') . '/js/plugins/drupalviews/views_entity_embed.png'); -+ return \Drupal::service('file_url_generator')->generateAbsoluteString(\Drupal::service('extension.list.module')->getPath('views_entity_embed') . '/js/plugins/drupalviews/views_entity_embed.svg'); - } - - /** -diff --git a/views_entity_embed.libraries.yml b/views_entity_embed.libraries.yml -index 75fcd49ddee3b29ca5587a957057653d888d4e2f..e23849febd09c6fa1ceb26df56ab26bcec303f20 100644 ---- a/views_entity_embed.libraries.yml -+++ b/views_entity_embed.libraries.yml -@@ -8,3 +8,14 @@ drupal.views_entity_embed.dialog: - dependencies: - - core/drupal - - core/jquery -+ -+# CKEditor 5 -+views_entity_embed: -+ js: -+ js/build/drupalviewsentity.js: { minified: true } -+ css: -+ theme: -+ css/drupalentity.css: { } -+ dependencies: -+ - core/ckeditor5 -+ - core/drupal -diff --git a/views_entity_embed.module b/views_entity_embed.module -index 6e1c8b53a4bcffe982e6848877dc6baf51f9aea4..9eed88cee7ab7039d86a3b33116698220c2dbbed 100644 ---- a/views_entity_embed.module -+++ b/views_entity_embed.module -@@ -60,3 +60,22 @@ function views_entity_embed_preprocess_views_view(array &$variables) { - $variables['title'] = $view->element['#embed_context']['data-title']; - } - } -+ -+function views_entity_embed_library_info_alter(&$libraries, $extension) { -+ // Create the drupalSettings dynamically based on embed buttons for the -+ // admin library. -+ if ($extension === 'entity_embed' && isset($libraries['admin.entity_embed'])) { -+ $entity_embed_buttons = \Drupal::entityTypeManager() -+ ->getStorage('embed_button') -+ // @see \Drupal\entity_embed\Plugin\EmbedType\Entity -+ ->loadByProperties(['type_id' => 'embed_views']); -+ foreach ($entity_embed_buttons as $entity_embed_button) { -+ $libraries['admin.entity_embed']['drupalSettings']['embedButtons'][$entity_embed_button->id()] = [ -+ 'id' => $entity_embed_button->id(), -+ 'icon' => $entity_embed_button->getIconUrl(), -+ ]; -+ -+ } -+ $libraries['admin.entity_embed']['drupalSettings']['modulePath'] = \Drupal::service('extension.list.module')->getPath('entity_embed'); -+ } -+} -diff --git a/views_entity_embed.routing.yml b/views_entity_embed.routing.yml -index 7d38a19c029e28722ac6cd22e977a4935daef274..4706352224c723e14409a3834cdd0d58a55a8e2e 100644 ---- a/views_entity_embed.routing.yml -+++ b/views_entity_embed.routing.yml -@@ -11,4 +11,13 @@ views_entity_embed.settings: - _form: '\Drupal\views_entity_embed\Form\ViewsEmbedDialog' - _title: 'Embed Views' - requirements: -- _embed_button_editor_access: 'TRUE' -+ _embed_button_editor_access: 'TRUE' -+views_entity_embed.edit_embedded_view: -+ path: '/entity-embed/edit-embedded-view/{type}' -+ defaults: -+ _controller: '\Drupal\views_entity_embed\Controller\EditEmbeddedView::edit' -+ _title: 'Edit view' -+ requirements: -+ # This controller returns a redirect. We set access to TRUE and let the -+ # redirected-to destination determin access control. -+ _access: 'TRUE' -diff --git a/webpack.config.js b/webpack.config.js -new file mode 100644 -index 0000000000000000000000000000000000000000..bd63f7f4689e679dcf4bab8644724539eca750e6 ---- /dev/null -+++ b/webpack.config.js -@@ -0,0 +1,65 @@ -+const path = require('path'); -+const fs = require('fs'); -+const webpack = require('webpack'); -+const TerserPlugin = require('terser-webpack-plugin'); -+ -+function getDirectories(srcpath) { -+ return fs -+ .readdirSync(srcpath) -+ .filter((item) => fs.statSync(path.join(srcpath, item)).isDirectory()); -+} -+ -+module.exports = []; -+// Loop through every subdirectory in src, each a different plugin, and build -+// each one in ./build. -+getDirectories('./js/ckeditor5_plugins').forEach((dir) => { -+ const bc = { -+ mode: 'production', -+ optimization: { -+ minimize: true, -+ minimizer: [ -+ new TerserPlugin({ -+ terserOptions: { -+ format: { -+ comments: false, -+ }, -+ }, -+ test: /\.js(\?.*)?$/i, -+ extractComments: false, -+ }), -+ ], -+ moduleIds: 'named', -+ }, -+ entry: { -+ path: path.resolve( -+ __dirname, -+ 'js/ckeditor5_plugins', -+ dir, -+ 'src/index.js', -+ ), -+ }, -+ output: { -+ path: path.resolve(__dirname, './js/build'), -+ filename: `${dir}.js`, -+ library: ['CKEditor5', dir], -+ libraryTarget: 'umd', -+ libraryExport: 'default', -+ }, -+ plugins: [ -+ // It is possible to require the ckeditor5-dll.manifest.json used in -+ // core/node_modules rather than having to install CKEditor 5 here. -+ // However, that requires knowing the location of that file relative to -+ // where your module code is located. -+ new webpack.DllReferencePlugin({ -+ manifest: require('./node_modules/ckeditor5/build/ckeditor5-dll.manifest.json'), // eslint-disable-line global-require, import/no-unresolved -+ scope: 'ckeditor5/src', -+ name: 'CKEditor5.dll', -+ }), -+ ], -+ module: { -+ rules: [{ test: /\.svg$/, use: 'raw-loader' }], -+ }, -+ }; -+ -+ module.exports.push(bc); -+}); -diff --git a/yarn.lock b/yarn.lock -new file mode 100644 -index 0000000000000000000000000000000000000000..2c65fb9603161782afc7bc7425ac045ab30637d3 ---- /dev/null -+++ b/yarn.lock -@@ -0,0 +1,2775 @@ -+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -+# yarn lockfile v1 -+ -+ -+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13": -+ version "7.22.13" -+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" -+ integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== -+ dependencies: -+ "@babel/highlight" "^7.22.13" -+ chalk "^2.4.2" -+ -+"@babel/generator@^7.23.0": -+ version "7.23.0" -+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" -+ integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== -+ dependencies: -+ "@babel/types" "^7.23.0" -+ "@jridgewell/gen-mapping" "^0.3.2" -+ "@jridgewell/trace-mapping" "^0.3.17" -+ jsesc "^2.5.1" -+ -+"@babel/helper-environment-visitor@^7.22.20": -+ version "7.22.20" -+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" -+ integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -+ -+"@babel/helper-function-name@^7.23.0": -+ version "7.23.0" -+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" -+ integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== -+ dependencies: -+ "@babel/template" "^7.22.15" -+ "@babel/types" "^7.23.0" -+ -+"@babel/helper-hoist-variables@^7.22.5": -+ version "7.22.5" -+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" -+ integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== -+ dependencies: -+ "@babel/types" "^7.22.5" -+ -+"@babel/helper-split-export-declaration@^7.22.6": -+ version "7.22.6" -+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" -+ integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== -+ dependencies: -+ "@babel/types" "^7.22.5" -+ -+"@babel/helper-string-parser@^7.22.5": -+ version "7.22.5" -+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" -+ integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -+ -+"@babel/helper-validator-identifier@^7.22.20": -+ version "7.22.20" -+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" -+ integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -+ -+"@babel/highlight@^7.22.13": -+ version "7.22.20" -+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" -+ integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== -+ dependencies: -+ "@babel/helper-validator-identifier" "^7.22.20" -+ chalk "^2.4.2" -+ js-tokens "^4.0.0" -+ -+"@babel/parser@^7.18.9", "@babel/parser@^7.22.15", "@babel/parser@^7.23.0": -+ version "7.23.0" -+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" -+ integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== -+ -+"@babel/template@^7.22.15": -+ version "7.22.15" -+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" -+ integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== -+ dependencies: -+ "@babel/code-frame" "^7.22.13" -+ "@babel/parser" "^7.22.15" -+ "@babel/types" "^7.22.15" -+ -+"@babel/traverse@^7.18.9": -+ version "7.23.2" -+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" -+ integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== -+ dependencies: -+ "@babel/code-frame" "^7.22.13" -+ "@babel/generator" "^7.23.0" -+ "@babel/helper-environment-visitor" "^7.22.20" -+ "@babel/helper-function-name" "^7.23.0" -+ "@babel/helper-hoist-variables" "^7.22.5" -+ "@babel/helper-split-export-declaration" "^7.22.6" -+ "@babel/parser" "^7.23.0" -+ "@babel/types" "^7.23.0" -+ debug "^4.1.0" -+ globals "^11.1.0" -+ -+"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": -+ version "7.23.0" -+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" -+ integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== -+ dependencies: -+ "@babel/helper-string-parser" "^7.22.5" -+ "@babel/helper-validator-identifier" "^7.22.20" -+ to-fast-properties "^2.0.0" -+ -+"@ckeditor/ckeditor5-clipboard@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-clipboard/-/ckeditor5-clipboard-40.0.0.tgz#6a2b7e22dbfd001f48930ff33cb297b5c43bb875" -+ integrity sha512-Xtgjb4ZYa40XHqwQo25X6rA4Job0kgFvocNRMBH7CNrN5h4lJwJwVXlY9HSXvXPY0TBaBBS1HcMvB+sf5DYXeg== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-engine" "40.0.0" -+ "@ckeditor/ckeditor5-ui" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ "@ckeditor/ckeditor5-widget" "40.0.0" -+ lodash-es "4.17.21" -+ -+"@ckeditor/ckeditor5-clipboard@^34.1.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-clipboard/-/ckeditor5-clipboard-34.2.0.tgz#ab69636365b0bd95631817fe4f5c3cc22bc6fce3" -+ integrity sha512-OcdFj9yT7C5yKPHtTKWvjGMJLpigrkdJN4AZhdJJPigiuYG0c5mnCuTvOYxp2kVijFWRjhPlwIyPVTtDZ0vnzw== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "^34.2.0" -+ "@ckeditor/ckeditor5-engine" "^34.2.0" -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ "@ckeditor/ckeditor5-widget" "^34.2.0" -+ lodash-es "^4.17.11" -+ -+"@ckeditor/ckeditor5-core@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-core/-/ckeditor5-core-40.0.0.tgz#941654698c0714263a5aad90bb325504510eb6a5" -+ integrity sha512-8xoSDOc9/35jEikKtYbdYmBxPop7i/JYSkkZmJYbZ8XxkjQiIMAUYOJVdNntfuLGazU+THmutieEA/x3ISme4g== -+ dependencies: -+ "@ckeditor/ckeditor5-engine" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ lodash-es "4.17.21" -+ -+"@ckeditor/ckeditor5-core@^34.1.0", "@ckeditor/ckeditor5-core@^34.2.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-core/-/ckeditor5-core-34.2.0.tgz#4ba72e3407310843a784630c598501ccc42a585b" -+ integrity sha512-6K0aToibRt28sCVYpMqdSKGvMifjwziqxLxyEh38CyDZJBUf7QPEAPlEpKAFTisHNEmC4471tr8UPpvNgqUXGA== -+ dependencies: -+ "@ckeditor/ckeditor5-engine" "^34.2.0" -+ "@ckeditor/ckeditor5-ui" "^34.2.0" -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ lodash-es "^4.17.15" -+ -+"@ckeditor/ckeditor5-dev-utils@^30.0.0", "@ckeditor/ckeditor5-dev-utils@^30.5.0": -+ version "30.5.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-dev-utils/-/ckeditor5-dev-utils-30.5.0.tgz#798291a79183db716b667591a2ccebe6fcb7a7f8" -+ integrity sha512-R5oC9ka68X7NwafM5rFvIv6q0qT2kMsBkRikdEygx7cmGkV4dy7uM5HuOBUuIoLW7Md2o3QfkD3dnk6OdzuuJw== -+ dependencies: -+ "@babel/parser" "^7.18.9" -+ "@babel/traverse" "^7.18.9" -+ "@ckeditor/ckeditor5-dev-webpack-plugin" "^30.5.0" -+ chalk "^3.0.0" -+ cli-cursor "^3.1.0" -+ cli-spinners "^2.6.1" -+ cssnano "^5.0.0" -+ del "^5.0.0" -+ escodegen "^1.9.0" -+ fs-extra "^8.1.0" -+ is-interactive "^1.0.0" -+ javascript-stringify "^1.6.0" -+ pofile "^1.0.9" -+ postcss "^8.4.12" -+ postcss-import "^14.1.0" -+ postcss-loader "^4.3.0" -+ postcss-mixins "^9.0.2" -+ postcss-nesting "^10.1.4" -+ raw-loader "^4.0.1" -+ shelljs "^0.8.1" -+ style-loader "^2.0.0" -+ terser-webpack-plugin "^4.2.3" -+ through2 "^3.0.1" -+ ts-loader "^9.3.0" -+ -+"@ckeditor/ckeditor5-dev-webpack-plugin@^30.5.0": -+ version "30.5.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-dev-webpack-plugin/-/ckeditor5-dev-webpack-plugin-30.5.0.tgz#732033e117ce0b00cb0efaf009495ab767ec47de" -+ integrity sha512-mErNKfGd8XBjJxB7K7yCDnNq4pLQKbEjwJHf9g2EW4gOD1U55rgPc1XpmgfxhMj44QQ8YOZXAQ/Y/55AN7GATA== -+ dependencies: -+ "@ckeditor/ckeditor5-dev-utils" "^30.5.0" -+ chalk "^4.0.0" -+ rimraf "^3.0.2" -+ semver "^7.3.4" -+ webpack-sources "^2.0.1" -+ -+"@ckeditor/ckeditor5-engine@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-engine/-/ckeditor5-engine-40.0.0.tgz#321d19d5206dc4b4d929706c23c436e4ef6c31c7" -+ integrity sha512-zauOXFudE1B94RSziWWojdpqGprSo4rKwW3KLU6nfaz9Kq9RZkcP/TW5ADE0DxC2jWUMeItVE/3U8ES5fZ0hqQ== -+ dependencies: -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ lodash-es "4.17.21" -+ -+"@ckeditor/ckeditor5-engine@^34.1.0", "@ckeditor/ckeditor5-engine@^34.2.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-engine/-/ckeditor5-engine-34.2.0.tgz#449376625bad9e8beea9e89e201dd4fb599fc38f" -+ integrity sha512-9/i6TZ+Sy5T6hnuCtmeLTfwLSY8LaS7qFkW6gsM9NEB+LSSu930GP0Ss30Nw6dYo/JmYiQEpkiRJzKYIjrH8Pg== -+ dependencies: -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ lodash-es "^4.17.15" -+ -+"@ckeditor/ckeditor5-enter@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-enter/-/ckeditor5-enter-40.0.0.tgz#48a8d33e34622af9fdc06082d7b0d153c5e75b89" -+ integrity sha512-pu8/zyQMqzMOgJbbPLbVu6znFfbgMYQwIVT7GMmWX+pZxPSBPyM+qNlmstFLwAxeki0aHCbo27gYmR1rIYGgrg== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-engine" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ -+"@ckeditor/ckeditor5-enter@^34.1.0", "@ckeditor/ckeditor5-enter@^34.2.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-enter/-/ckeditor5-enter-34.2.0.tgz#6d90a374396ef05ecde7f6cb0fcd6d3ca38e20b1" -+ integrity sha512-QxaT3jH0qsZaE0Egj1D19o6YBz/EJKs0am5ny5hDnd5sntvIUk9PNGEu/v3mRmNqZqrhRu4BuedvdRzYWseUjw== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "^34.2.0" -+ "@ckeditor/ckeditor5-engine" "^34.2.0" -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ -+"@ckeditor/ckeditor5-html-support@^40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-html-support/-/ckeditor5-html-support-40.0.0.tgz#472ace6bcd3d844b16a0c146988cf3526db07a65" -+ integrity sha512-o/8R4i8MvpgPiZNkIfDf2aE2VAteSYSoE6jZ6UPdkc7lTpJbWoc9KPXAzCT6/+P8dZQ6FuSYL+1ftHhx27sS8w== -+ dependencies: -+ ckeditor5 "40.0.0" -+ lodash-es "4.17.21" -+ -+"@ckeditor/ckeditor5-link@^40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-link/-/ckeditor5-link-40.0.0.tgz#388a6d3eff7f7cd8fabd0f8c9cd17fd01a9012db" -+ integrity sha512-hi0JRC6N93N7vqpR80AG9wBGUJ0C86UYvwA8yPMThl5KtXxwwUs2L5UH+Ju3twrkan2MxMzjpPMTXqfaFSt+vA== -+ dependencies: -+ "@ckeditor/ckeditor5-ui" "40.0.0" -+ ckeditor5 "40.0.0" -+ lodash-es "4.17.21" -+ -+"@ckeditor/ckeditor5-paragraph@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-paragraph/-/ckeditor5-paragraph-40.0.0.tgz#50be65f332271259c6e06b46d6f5b247a0a305ba" -+ integrity sha512-j2Pm/dlu3hE08EKYvbUT9qUMyJeZtuufuZjUZaADCsVmtfFuzXlaHjIkQUvCxUeuMXQLlUpJ69OSAMzzMlJs8g== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-ui" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ -+"@ckeditor/ckeditor5-paragraph@^34.1.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-paragraph/-/ckeditor5-paragraph-34.2.0.tgz#cf981adb76bf40649ce04358674d2c1262b8753a" -+ integrity sha512-xcXUsXz3PY355gJ8u+y0qFLWcScYo0CZPZSbs5YwDz7g9lV8foVVzzdW7ITYwr5/YIpJsjjxYC+dDUqsH6EpBQ== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "^34.2.0" -+ "@ckeditor/ckeditor5-ui" "^34.2.0" -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ -+"@ckeditor/ckeditor5-select-all@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-select-all/-/ckeditor5-select-all-40.0.0.tgz#15e1eacc2cc7260b34ef98c778c5e8b0fbeb15fe" -+ integrity sha512-6FP/8DjKCfy9ha42hoshOj5fs13KFFRSbXcCDNFCUk/ZrvH42lz1BYw2S5uQYA9Xl6o4BUWdN+g4CVPQfpGm+Q== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-ui" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ -+"@ckeditor/ckeditor5-select-all@^34.1.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-select-all/-/ckeditor5-select-all-34.2.0.tgz#49ca1e239dda9d6045716ebd6707a4cf1a8a6203" -+ integrity sha512-/Va85RwNlmpgQ7vWxiAFLyzXhXrWiA5Pde7yCNcc6hJpqnaGcqvscOJoZLMk5oASTvMnPhQIgNSMDN/oq6ej0Q== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "^34.2.0" -+ "@ckeditor/ckeditor5-ui" "^34.2.0" -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ -+"@ckeditor/ckeditor5-typing@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-typing/-/ckeditor5-typing-40.0.0.tgz#0f5e5aeb261b9fe9c27acf364b83a6dbcf9df5d8" -+ integrity sha512-c0uMXkh3kJP1wEVoh/0sLPr8Ouk4EvBuaAqSYEkrvX5wKBWAoGnUotHfrFH8wRBy25m17QbZ44N7dkA+BpuMPQ== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-engine" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ lodash-es "4.17.21" -+ -+"@ckeditor/ckeditor5-typing@^34.1.0", "@ckeditor/ckeditor5-typing@^34.2.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-typing/-/ckeditor5-typing-34.2.0.tgz#6f408e671ef5acf526076128f9422a13d914776f" -+ integrity sha512-Eq8mhb8M7RwUmeVUantN+PrqxDELXCvLCcpixy+ge/5lM8wxVcn/SonfUL9PLqs2eluRc4Bx+mstMQySglkVkw== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "^34.2.0" -+ "@ckeditor/ckeditor5-engine" "^34.2.0" -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ lodash-es "^4.17.15" -+ -+"@ckeditor/ckeditor5-ui@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-ui/-/ckeditor5-ui-40.0.0.tgz#30fb7f4a2deab4097886c11c8990728f9697ed22" -+ integrity sha512-wnfC7eSqdN6i+nHTN83+PCByWeCPDgdQAXvf3HHBNdsJna6khKC8Oy/1eU8F+vR84unJMrPoaCMIx0qRvK3hCA== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ color-convert "2.0.1" -+ color-parse "1.4.2" -+ lodash-es "4.17.21" -+ vanilla-colorful "0.7.2" -+ -+"@ckeditor/ckeditor5-ui@^34.1.0", "@ckeditor/ckeditor5-ui@^34.2.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-ui/-/ckeditor5-ui-34.2.0.tgz#56a0fe2eb87c2e44b6c6d9440e6b6077a244214c" -+ integrity sha512-XL561G/e3b1YLGHNjLxS9IgoVn4BSugHmidEXYNUTMLATRCKld1XMUKFsB/wm3DwLBUfWn4d2j3qdcO2CnDuBg== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "^34.2.0" -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ lodash-es "^4.17.15" -+ -+"@ckeditor/ckeditor5-undo@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-undo/-/ckeditor5-undo-40.0.0.tgz#788cd65a56bbd9ccbc41a34f0a5a5e782ed24982" -+ integrity sha512-kt+9Ux0RdC0OeMLp95kJW1h6d1LobOblkozU2as5VN5c20tvbXviQJuoQeJsiixmJapUmi7vvDgnVsGw9obEpQ== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-engine" "40.0.0" -+ "@ckeditor/ckeditor5-ui" "40.0.0" -+ -+"@ckeditor/ckeditor5-undo@^34.1.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-undo/-/ckeditor5-undo-34.2.0.tgz#7ea368fc46d4f1f663a2e59cba7390bd90b62017" -+ integrity sha512-WW3f6ku36DpKhUxXACfNFm2DaKcJ2Rz0EFEkol0+offpOjltJnUEJ7LvfOthGdMvGz+5lmnySTbkvOvNruq1Ew== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "^34.2.0" -+ "@ckeditor/ckeditor5-engine" "^34.2.0" -+ "@ckeditor/ckeditor5-ui" "^34.2.0" -+ -+"@ckeditor/ckeditor5-upload@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-upload/-/ckeditor5-upload-40.0.0.tgz#bfed41c38b5bb6bba8c68321c931c6e304bcf79b" -+ integrity sha512-LutDg8zjhJu1UKInAyvVHIk8HyroETi61KS2PQTyiTQv/DmRvjSK32Xl83KprTxAvqZsiDdXe+Nl1kdAO8S2ag== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-ui" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ -+"@ckeditor/ckeditor5-upload@^34.1.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-upload/-/ckeditor5-upload-34.2.0.tgz#8a55065bba3e2e3f6134872f0735a4ed0257bf6b" -+ integrity sha512-HBJr0/wFE+R13aIXRF/xJVQqo6Yh34EgbnrNYYhlNiHG40Vr6079eCuoZrnY3vwEsjtFNnTRQ433+RqxJ652zw== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "^34.2.0" -+ "@ckeditor/ckeditor5-ui" "^34.2.0" -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ -+"@ckeditor/ckeditor5-utils@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-utils/-/ckeditor5-utils-40.0.0.tgz#6e287ab33d5f8cac4928f82f5621ba994c47a15e" -+ integrity sha512-52UwkeGxrZWhbwWWfixKceWhF1kuDeJNAM57wqfB7GS8CzElOpJ3AELeD/L/ZkUEBGL9asqribEH3CzgTjWKPA== -+ dependencies: -+ lodash-es "4.17.21" -+ -+"@ckeditor/ckeditor5-utils@^34.1.0", "@ckeditor/ckeditor5-utils@^34.2.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-utils/-/ckeditor5-utils-34.2.0.tgz#d84177671afbc849b0435075f00311f9cc181288" -+ integrity sha512-jHJV2S8DzmpVvd3jdercY6HsGRAwpm/MK79Rs/Mrc3NNYKzN9SVFs/NLbrELNoMZeJ1WKt5BwKgBY+PEOpfyLw== -+ dependencies: -+ lodash-es "^4.17.15" -+ -+"@ckeditor/ckeditor5-watchdog@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-watchdog/-/ckeditor5-watchdog-40.0.0.tgz#a9ab427fe9a1248cf4d2a1e98729b74cda79af09" -+ integrity sha512-bwBimEdiUCe6/1IScWhC2vomcwApkKeb804NuxX7m7QhctKaHALV+tRVG/y8Oln3qCaw3na6C4pQgaXz0qtWcQ== -+ dependencies: -+ lodash-es "4.17.21" -+ -+"@ckeditor/ckeditor5-widget@40.0.0": -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-widget/-/ckeditor5-widget-40.0.0.tgz#71330fe467fdbf66f075ee10ac7dda1f72a6a2d8" -+ integrity sha512-3dZjAQECWMvSMQhleM6iHwG2LOTKQmir4Rf3/Vulc7Aexb/brcQ06JuNQtQsIxRBZPoaEpWFAakK1PSfxxO0Sg== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-engine" "40.0.0" -+ "@ckeditor/ckeditor5-enter" "40.0.0" -+ "@ckeditor/ckeditor5-typing" "40.0.0" -+ "@ckeditor/ckeditor5-ui" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ lodash-es "4.17.21" -+ -+"@ckeditor/ckeditor5-widget@^34.1.0", "@ckeditor/ckeditor5-widget@^34.2.0": -+ version "34.2.0" -+ resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-widget/-/ckeditor5-widget-34.2.0.tgz#0df140bc4c40287251cf5f5a970f616bdbd470e3" -+ integrity sha512-h2iF/RRK+GjvVHb6VY7+slnIV+IdWdLfZS83OECQNYp2e+6kN/JZp+PxiyYC4asPTraL43zJGzlgT53Jof77vw== -+ dependencies: -+ "@ckeditor/ckeditor5-core" "^34.2.0" -+ "@ckeditor/ckeditor5-engine" "^34.2.0" -+ "@ckeditor/ckeditor5-enter" "^34.2.0" -+ "@ckeditor/ckeditor5-typing" "^34.2.0" -+ "@ckeditor/ckeditor5-ui" "^34.2.0" -+ "@ckeditor/ckeditor5-utils" "^34.2.0" -+ lodash-es "^4.17.15" -+ -+"@csstools/selector-specificity@^2.0.0": -+ version "2.2.0" -+ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016" -+ integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== -+ -+"@discoveryjs/json-ext@^0.5.0": -+ version "0.5.7" -+ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" -+ integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -+ -+"@gar/promisify@^1.0.1": -+ version "1.1.3" -+ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" -+ integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -+ -+"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": -+ version "0.3.3" -+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" -+ integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== -+ dependencies: -+ "@jridgewell/set-array" "^1.0.1" -+ "@jridgewell/sourcemap-codec" "^1.4.10" -+ "@jridgewell/trace-mapping" "^0.3.9" -+ -+"@jridgewell/resolve-uri@^3.1.0": -+ version "3.1.1" -+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" -+ integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== -+ -+"@jridgewell/set-array@^1.0.1": -+ version "1.1.2" -+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" -+ integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -+ -+"@jridgewell/source-map@^0.3.3": -+ version "0.3.5" -+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" -+ integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== -+ dependencies: -+ "@jridgewell/gen-mapping" "^0.3.0" -+ "@jridgewell/trace-mapping" "^0.3.9" -+ -+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": -+ version "1.4.15" -+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" -+ integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -+ -+"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": -+ version "0.3.20" -+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" -+ integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== -+ dependencies: -+ "@jridgewell/resolve-uri" "^3.1.0" -+ "@jridgewell/sourcemap-codec" "^1.4.14" -+ -+"@nodelib/fs.scandir@2.1.5": -+ version "2.1.5" -+ resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" -+ integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== -+ dependencies: -+ "@nodelib/fs.stat" "2.0.5" -+ run-parallel "^1.1.9" -+ -+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": -+ version "2.0.5" -+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" -+ integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -+ -+"@nodelib/fs.walk@^1.2.3": -+ version "1.2.8" -+ resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" -+ integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== -+ dependencies: -+ "@nodelib/fs.scandir" "2.1.5" -+ fastq "^1.6.0" -+ -+"@npmcli/fs@^1.0.0": -+ version "1.1.1" -+ resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" -+ integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== -+ dependencies: -+ "@gar/promisify" "^1.0.1" -+ semver "^7.3.5" -+ -+"@npmcli/move-file@^1.0.1": -+ version "1.1.2" -+ resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" -+ integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== -+ dependencies: -+ mkdirp "^1.0.4" -+ rimraf "^3.0.2" -+ -+"@trysound/sax@0.2.0": -+ version "0.2.0" -+ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" -+ integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== -+ -+"@types/eslint-scope@^3.7.3": -+ version "3.7.6" -+ resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.6.tgz#585578b368ed170e67de8aae7b93f54a1b2fdc26" -+ integrity sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ== -+ dependencies: -+ "@types/eslint" "*" -+ "@types/estree" "*" -+ -+"@types/eslint@*": -+ version "8.44.6" -+ resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.6.tgz#60e564551966dd255f4c01c459f0b4fb87068603" -+ integrity sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw== -+ dependencies: -+ "@types/estree" "*" -+ "@types/json-schema" "*" -+ -+"@types/estree@*", "@types/estree@^1.0.0": -+ version "1.0.3" -+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.3.tgz#2be19e759a3dd18c79f9f436bd7363556c1a73dd" -+ integrity sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ== -+ -+"@types/glob@^7.1.1": -+ version "7.2.0" -+ resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" -+ integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== -+ dependencies: -+ "@types/minimatch" "*" -+ "@types/node" "*" -+ -+"@types/json-schema@*", "@types/json-schema@^7.0.8": -+ version "7.0.14" -+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.14.tgz#74a97a5573980802f32c8e47b663530ab3b6b7d1" -+ integrity sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw== -+ -+"@types/minimatch@*": -+ version "5.1.2" -+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" -+ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== -+ -+"@types/node@*": -+ version "20.8.7" -+ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.7.tgz#ad23827850843de973096edfc5abc9e922492a25" -+ integrity sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ== -+ dependencies: -+ undici-types "~5.25.1" -+ -+"@types/parse-json@^4.0.0": -+ version "4.0.1" -+ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.1.tgz#27f7559836ad796cea31acb63163b203756a5b4e" -+ integrity sha512-3YmXzzPAdOTVljVMkTMBdBEvlOLg2cDQaDhnnhT3nT9uDbnJzjWhKlzb+desT12Y7tGqaN6d+AbozcKzyL36Ng== -+ -+"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" -+ integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== -+ dependencies: -+ "@webassemblyjs/helper-numbers" "1.11.6" -+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6" -+ -+"@webassemblyjs/floating-point-hex-parser@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" -+ integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== -+ -+"@webassemblyjs/helper-api-error@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" -+ integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== -+ -+"@webassemblyjs/helper-buffer@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" -+ integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== -+ -+"@webassemblyjs/helper-numbers@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" -+ integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== -+ dependencies: -+ "@webassemblyjs/floating-point-hex-parser" "1.11.6" -+ "@webassemblyjs/helper-api-error" "1.11.6" -+ "@xtuc/long" "4.2.2" -+ -+"@webassemblyjs/helper-wasm-bytecode@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" -+ integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== -+ -+"@webassemblyjs/helper-wasm-section@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" -+ integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== -+ dependencies: -+ "@webassemblyjs/ast" "1.11.6" -+ "@webassemblyjs/helper-buffer" "1.11.6" -+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6" -+ "@webassemblyjs/wasm-gen" "1.11.6" -+ -+"@webassemblyjs/ieee754@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" -+ integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== -+ dependencies: -+ "@xtuc/ieee754" "^1.2.0" -+ -+"@webassemblyjs/leb128@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" -+ integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== -+ dependencies: -+ "@xtuc/long" "4.2.2" -+ -+"@webassemblyjs/utf8@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" -+ integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== -+ -+"@webassemblyjs/wasm-edit@^1.11.5": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" -+ integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== -+ dependencies: -+ "@webassemblyjs/ast" "1.11.6" -+ "@webassemblyjs/helper-buffer" "1.11.6" -+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6" -+ "@webassemblyjs/helper-wasm-section" "1.11.6" -+ "@webassemblyjs/wasm-gen" "1.11.6" -+ "@webassemblyjs/wasm-opt" "1.11.6" -+ "@webassemblyjs/wasm-parser" "1.11.6" -+ "@webassemblyjs/wast-printer" "1.11.6" -+ -+"@webassemblyjs/wasm-gen@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" -+ integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== -+ dependencies: -+ "@webassemblyjs/ast" "1.11.6" -+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6" -+ "@webassemblyjs/ieee754" "1.11.6" -+ "@webassemblyjs/leb128" "1.11.6" -+ "@webassemblyjs/utf8" "1.11.6" -+ -+"@webassemblyjs/wasm-opt@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" -+ integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== -+ dependencies: -+ "@webassemblyjs/ast" "1.11.6" -+ "@webassemblyjs/helper-buffer" "1.11.6" -+ "@webassemblyjs/wasm-gen" "1.11.6" -+ "@webassemblyjs/wasm-parser" "1.11.6" -+ -+"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" -+ integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== -+ dependencies: -+ "@webassemblyjs/ast" "1.11.6" -+ "@webassemblyjs/helper-api-error" "1.11.6" -+ "@webassemblyjs/helper-wasm-bytecode" "1.11.6" -+ "@webassemblyjs/ieee754" "1.11.6" -+ "@webassemblyjs/leb128" "1.11.6" -+ "@webassemblyjs/utf8" "1.11.6" -+ -+"@webassemblyjs/wast-printer@1.11.6": -+ version "1.11.6" -+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" -+ integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== -+ dependencies: -+ "@webassemblyjs/ast" "1.11.6" -+ "@xtuc/long" "4.2.2" -+ -+"@webpack-cli/configtest@^1.2.0": -+ version "1.2.0" -+ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" -+ integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== -+ -+"@webpack-cli/info@^1.5.0": -+ version "1.5.0" -+ resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" -+ integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== -+ dependencies: -+ envinfo "^7.7.3" -+ -+"@webpack-cli/serve@^1.7.0": -+ version "1.7.0" -+ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" -+ integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== -+ -+"@xtuc/ieee754@^1.2.0": -+ version "1.2.0" -+ resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" -+ integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== -+ -+"@xtuc/long@4.2.2": -+ version "4.2.2" -+ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" -+ integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -+ -+acorn-import-assertions@^1.9.0: -+ version "1.9.0" -+ resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" -+ integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== -+ -+acorn@^8.7.1, acorn@^8.8.2: -+ version "8.10.0" -+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" -+ integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== -+ -+aggregate-error@^3.0.0: -+ version "3.1.0" -+ resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" -+ integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== -+ dependencies: -+ clean-stack "^2.0.0" -+ indent-string "^4.0.0" -+ -+ajv-keywords@^3.5.2: -+ version "3.5.2" -+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" -+ integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -+ -+ajv@^6.12.5: -+ version "6.12.6" -+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" -+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== -+ dependencies: -+ fast-deep-equal "^3.1.1" -+ fast-json-stable-stringify "^2.0.0" -+ json-schema-traverse "^0.4.1" -+ uri-js "^4.2.2" -+ -+ansi-styles@^3.2.1: -+ version "3.2.1" -+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" -+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== -+ dependencies: -+ color-convert "^1.9.0" -+ -+ansi-styles@^4.1.0: -+ version "4.3.0" -+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" -+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== -+ dependencies: -+ color-convert "^2.0.1" -+ -+array-union@^2.1.0: -+ version "2.1.0" -+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" -+ integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -+ -+balanced-match@^1.0.0: -+ version "1.0.2" -+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" -+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -+ -+big.js@^5.2.2: -+ version "5.2.2" -+ resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" -+ integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -+ -+boolbase@^1.0.0: -+ version "1.0.0" -+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" -+ integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== -+ -+brace-expansion@^1.1.7: -+ version "1.1.11" -+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" -+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== -+ dependencies: -+ balanced-match "^1.0.0" -+ concat-map "0.0.1" -+ -+braces@^3.0.2: -+ version "3.0.2" -+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" -+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== -+ dependencies: -+ fill-range "^7.0.1" -+ -+browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.21.4: -+ version "4.22.1" -+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" -+ integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== -+ dependencies: -+ caniuse-lite "^1.0.30001541" -+ electron-to-chromium "^1.4.535" -+ node-releases "^2.0.13" -+ update-browserslist-db "^1.0.13" -+ -+buffer-from@^1.0.0: -+ version "1.1.2" -+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" -+ integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -+ -+cacache@^15.0.5: -+ version "15.3.0" -+ resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" -+ integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== -+ dependencies: -+ "@npmcli/fs" "^1.0.0" -+ "@npmcli/move-file" "^1.0.1" -+ chownr "^2.0.0" -+ fs-minipass "^2.0.0" -+ glob "^7.1.4" -+ infer-owner "^1.0.4" -+ lru-cache "^6.0.0" -+ minipass "^3.1.1" -+ minipass-collect "^1.0.2" -+ minipass-flush "^1.0.5" -+ minipass-pipeline "^1.2.2" -+ mkdirp "^1.0.3" -+ p-map "^4.0.0" -+ promise-inflight "^1.0.1" -+ rimraf "^3.0.2" -+ ssri "^8.0.1" -+ tar "^6.0.2" -+ unique-filename "^1.1.1" -+ -+callsites@^3.0.0: -+ version "3.1.0" -+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" -+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -+ -+camelcase-css@^2.0.1: -+ version "2.0.1" -+ resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" -+ integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== -+ -+caniuse-api@^3.0.0: -+ version "3.0.0" -+ resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" -+ integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== -+ dependencies: -+ browserslist "^4.0.0" -+ caniuse-lite "^1.0.0" -+ lodash.memoize "^4.1.2" -+ lodash.uniq "^4.5.0" -+ -+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001541: -+ version "1.0.30001551" -+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz#1f2cfa8820bd97c971a57349d7fd8f6e08664a3e" -+ integrity sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg== -+ -+chalk@^2.4.2: -+ version "2.4.2" -+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" -+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== -+ dependencies: -+ ansi-styles "^3.2.1" -+ escape-string-regexp "^1.0.5" -+ supports-color "^5.3.0" -+ -+chalk@^3.0.0: -+ version "3.0.0" -+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" -+ integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== -+ dependencies: -+ ansi-styles "^4.1.0" -+ supports-color "^7.1.0" -+ -+chalk@^4.0.0, chalk@^4.1.0: -+ version "4.1.2" -+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" -+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== -+ dependencies: -+ ansi-styles "^4.1.0" -+ supports-color "^7.1.0" -+ -+chownr@^2.0.0: -+ version "2.0.0" -+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" -+ integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -+ -+chrome-trace-event@^1.0.2: -+ version "1.0.3" -+ resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" -+ integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== -+ -+ckeditor5@40.0.0: -+ version "40.0.0" -+ resolved "https://registry.yarnpkg.com/ckeditor5/-/ckeditor5-40.0.0.tgz#0415efab6b11da48cc7d58229ce282e3587d2be8" -+ integrity sha512-pyXptFXODCyICkIaiBfWl+vqNq87CwB4IodaacUDdp+7z7szK13/vMJMWyPCQyZob+lzpakqPpj29HR5Kzy4AQ== -+ dependencies: -+ "@ckeditor/ckeditor5-clipboard" "40.0.0" -+ "@ckeditor/ckeditor5-core" "40.0.0" -+ "@ckeditor/ckeditor5-engine" "40.0.0" -+ "@ckeditor/ckeditor5-enter" "40.0.0" -+ "@ckeditor/ckeditor5-paragraph" "40.0.0" -+ "@ckeditor/ckeditor5-select-all" "40.0.0" -+ "@ckeditor/ckeditor5-typing" "40.0.0" -+ "@ckeditor/ckeditor5-ui" "40.0.0" -+ "@ckeditor/ckeditor5-undo" "40.0.0" -+ "@ckeditor/ckeditor5-upload" "40.0.0" -+ "@ckeditor/ckeditor5-utils" "40.0.0" -+ "@ckeditor/ckeditor5-watchdog" "40.0.0" -+ "@ckeditor/ckeditor5-widget" "40.0.0" -+ -+ckeditor5@~34.1.0: -+ version "34.1.0" -+ resolved "https://registry.yarnpkg.com/ckeditor5/-/ckeditor5-34.1.0.tgz#3bd10c603f877891ee57e210fdd918762e2b865a" -+ integrity sha512-bWcmXEx4C7AC+FywiTrhbs63mUc8vwEualSDzyJIZLd5HULLznqJwjeON/icKtzCiBLEeUFoeDHLC5yWAEj8sA== -+ dependencies: -+ "@ckeditor/ckeditor5-clipboard" "^34.1.0" -+ "@ckeditor/ckeditor5-core" "^34.1.0" -+ "@ckeditor/ckeditor5-engine" "^34.1.0" -+ "@ckeditor/ckeditor5-enter" "^34.1.0" -+ "@ckeditor/ckeditor5-paragraph" "^34.1.0" -+ "@ckeditor/ckeditor5-select-all" "^34.1.0" -+ "@ckeditor/ckeditor5-typing" "^34.1.0" -+ "@ckeditor/ckeditor5-ui" "^34.1.0" -+ "@ckeditor/ckeditor5-undo" "^34.1.0" -+ "@ckeditor/ckeditor5-upload" "^34.1.0" -+ "@ckeditor/ckeditor5-utils" "^34.1.0" -+ "@ckeditor/ckeditor5-widget" "^34.1.0" -+ -+clean-stack@^2.0.0: -+ version "2.2.0" -+ resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" -+ integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -+ -+cli-cursor@^3.1.0: -+ version "3.1.0" -+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" -+ integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== -+ dependencies: -+ restore-cursor "^3.1.0" -+ -+cli-spinners@^2.6.1: -+ version "2.9.1" -+ resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.1.tgz#9c0b9dad69a6d47cbb4333c14319b060ed395a35" -+ integrity sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ== -+ -+clone-deep@^4.0.1: -+ version "4.0.1" -+ resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" -+ integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== -+ dependencies: -+ is-plain-object "^2.0.4" -+ kind-of "^6.0.2" -+ shallow-clone "^3.0.0" -+ -+color-convert@2.0.1, color-convert@^2.0.1: -+ version "2.0.1" -+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" -+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== -+ dependencies: -+ color-name "~1.1.4" -+ -+color-convert@^1.9.0: -+ version "1.9.3" -+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" -+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== -+ dependencies: -+ color-name "1.1.3" -+ -+color-name@1.1.3: -+ version "1.1.3" -+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" -+ integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -+ -+color-name@^1.0.0, color-name@~1.1.4: -+ version "1.1.4" -+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" -+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -+ -+color-parse@1.4.2: -+ version "1.4.2" -+ resolved "https://registry.yarnpkg.com/color-parse/-/color-parse-1.4.2.tgz#78651f5d34df1a57f997643d86f7f87268ad4eb5" -+ integrity sha512-RI7s49/8yqDj3fECFZjUI1Yi0z/Gq1py43oNJivAIIDSyJiOZLfYCRQEgn8HEVAj++PcRe8AnL2XF0fRJ3BTnA== -+ dependencies: -+ color-name "^1.0.0" -+ -+colord@^2.9.1: -+ version "2.9.3" -+ resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" -+ integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== -+ -+colorette@^2.0.14: -+ version "2.0.20" -+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" -+ integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== -+ -+commander@^2.20.0: -+ version "2.20.3" -+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" -+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -+ -+commander@^7.0.0, commander@^7.2.0: -+ version "7.2.0" -+ resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" -+ integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -+ -+commondir@^1.0.1: -+ version "1.0.1" -+ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" -+ integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -+ -+concat-map@0.0.1: -+ version "0.0.1" -+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -+ integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -+ -+cosmiconfig@^7.0.0: -+ version "7.1.0" -+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" -+ integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== -+ dependencies: -+ "@types/parse-json" "^4.0.0" -+ import-fresh "^3.2.1" -+ parse-json "^5.0.0" -+ path-type "^4.0.0" -+ yaml "^1.10.0" -+ -+cross-spawn@^7.0.3: -+ version "7.0.3" -+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" -+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== -+ dependencies: -+ path-key "^3.1.0" -+ shebang-command "^2.0.0" -+ which "^2.0.1" -+ -+css-declaration-sorter@^6.3.1: -+ version "6.4.1" -+ resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz#28beac7c20bad7f1775be3a7129d7eae409a3a71" -+ integrity sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g== -+ -+css-select@^4.1.3: -+ version "4.3.0" -+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" -+ integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== -+ dependencies: -+ boolbase "^1.0.0" -+ css-what "^6.0.1" -+ domhandler "^4.3.1" -+ domutils "^2.8.0" -+ nth-check "^2.0.1" -+ -+css-tree@^1.1.2, css-tree@^1.1.3: -+ version "1.1.3" -+ resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" -+ integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== -+ dependencies: -+ mdn-data "2.0.14" -+ source-map "^0.6.1" -+ -+css-what@^6.0.1: -+ version "6.1.0" -+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" -+ integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== -+ -+cssesc@^3.0.0: -+ version "3.0.0" -+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" -+ integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -+ -+cssnano-preset-default@^5.2.14: -+ version "5.2.14" -+ resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz#309def4f7b7e16d71ab2438052093330d9ab45d8" -+ integrity sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A== -+ dependencies: -+ css-declaration-sorter "^6.3.1" -+ cssnano-utils "^3.1.0" -+ postcss-calc "^8.2.3" -+ postcss-colormin "^5.3.1" -+ postcss-convert-values "^5.1.3" -+ postcss-discard-comments "^5.1.2" -+ postcss-discard-duplicates "^5.1.0" -+ postcss-discard-empty "^5.1.1" -+ postcss-discard-overridden "^5.1.0" -+ postcss-merge-longhand "^5.1.7" -+ postcss-merge-rules "^5.1.4" -+ postcss-minify-font-values "^5.1.0" -+ postcss-minify-gradients "^5.1.1" -+ postcss-minify-params "^5.1.4" -+ postcss-minify-selectors "^5.2.1" -+ postcss-normalize-charset "^5.1.0" -+ postcss-normalize-display-values "^5.1.0" -+ postcss-normalize-positions "^5.1.1" -+ postcss-normalize-repeat-style "^5.1.1" -+ postcss-normalize-string "^5.1.0" -+ postcss-normalize-timing-functions "^5.1.0" -+ postcss-normalize-unicode "^5.1.1" -+ postcss-normalize-url "^5.1.0" -+ postcss-normalize-whitespace "^5.1.1" -+ postcss-ordered-values "^5.1.3" -+ postcss-reduce-initial "^5.1.2" -+ postcss-reduce-transforms "^5.1.0" -+ postcss-svgo "^5.1.0" -+ postcss-unique-selectors "^5.1.1" -+ -+cssnano-utils@^3.1.0: -+ version "3.1.0" -+ resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" -+ integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== -+ -+cssnano@^5.0.0: -+ version "5.1.15" -+ resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.15.tgz#ded66b5480d5127fcb44dac12ea5a983755136bf" -+ integrity sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw== -+ dependencies: -+ cssnano-preset-default "^5.2.14" -+ lilconfig "^2.0.3" -+ yaml "^1.10.2" -+ -+csso@^4.2.0: -+ version "4.2.0" -+ resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" -+ integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== -+ dependencies: -+ css-tree "^1.1.2" -+ -+debug@^4.1.0: -+ version "4.3.4" -+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" -+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== -+ dependencies: -+ ms "2.1.2" -+ -+deep-is@~0.1.3: -+ version "0.1.4" -+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" -+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -+ -+del@^5.0.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" -+ integrity sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA== -+ dependencies: -+ globby "^10.0.1" -+ graceful-fs "^4.2.2" -+ is-glob "^4.0.1" -+ is-path-cwd "^2.2.0" -+ is-path-inside "^3.0.1" -+ p-map "^3.0.0" -+ rimraf "^3.0.0" -+ slash "^3.0.0" -+ -+dir-glob@^3.0.1: -+ version "3.0.1" -+ resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" -+ integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== -+ dependencies: -+ path-type "^4.0.0" -+ -+dom-serializer@^1.0.1: -+ version "1.4.1" -+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" -+ integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== -+ dependencies: -+ domelementtype "^2.0.1" -+ domhandler "^4.2.0" -+ entities "^2.0.0" -+ -+domelementtype@^2.0.1, domelementtype@^2.2.0: -+ version "2.3.0" -+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" -+ integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -+ -+domhandler@^4.2.0, domhandler@^4.3.1: -+ version "4.3.1" -+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" -+ integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== -+ dependencies: -+ domelementtype "^2.2.0" -+ -+domutils@^2.8.0: -+ version "2.8.0" -+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" -+ integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== -+ dependencies: -+ dom-serializer "^1.0.1" -+ domelementtype "^2.2.0" -+ domhandler "^4.2.0" -+ -+electron-to-chromium@^1.4.535: -+ version "1.4.560" -+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.560.tgz#f251409f1e8f393d0dfdf9ccb0b39de739a06a17" -+ integrity sha512-HhJH/pWAxTaPZl7R3mJ6gCd8MfjQdil9RAWk84qHaLsmPTadydfAmq0a1x8kZtOGQ6pZrWhOYj5uZ8I0meZIgg== -+ -+emojis-list@^3.0.0: -+ version "3.0.0" -+ resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" -+ integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -+ -+enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: -+ version "5.15.0" -+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" -+ integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== -+ dependencies: -+ graceful-fs "^4.2.4" -+ tapable "^2.2.0" -+ -+entities@^2.0.0: -+ version "2.2.0" -+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" -+ integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -+ -+envinfo@^7.7.3: -+ version "7.10.0" -+ resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" -+ integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw== -+ -+error-ex@^1.3.1: -+ version "1.3.2" -+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" -+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== -+ dependencies: -+ is-arrayish "^0.2.1" -+ -+es-module-lexer@^1.2.1: -+ version "1.3.1" -+ resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.1.tgz#c1b0dd5ada807a3b3155315911f364dc4e909db1" -+ integrity sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q== -+ -+escalade@^3.1.1: -+ version "3.1.1" -+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" -+ integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -+ -+escape-string-regexp@^1.0.5: -+ version "1.0.5" -+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -+ integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -+ -+escodegen@^1.9.0: -+ version "1.14.3" -+ resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" -+ integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== -+ dependencies: -+ esprima "^4.0.1" -+ estraverse "^4.2.0" -+ esutils "^2.0.2" -+ optionator "^0.8.1" -+ optionalDependencies: -+ source-map "~0.6.1" -+ -+eslint-scope@5.1.1: -+ version "5.1.1" -+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" -+ integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== -+ dependencies: -+ esrecurse "^4.3.0" -+ estraverse "^4.1.1" -+ -+esprima@^4.0.1: -+ version "4.0.1" -+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" -+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -+ -+esrecurse@^4.3.0: -+ version "4.3.0" -+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" -+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== -+ dependencies: -+ estraverse "^5.2.0" -+ -+estraverse@^4.1.1, estraverse@^4.2.0: -+ version "4.3.0" -+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" -+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -+ -+estraverse@^5.2.0: -+ version "5.3.0" -+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" -+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -+ -+esutils@^2.0.2: -+ version "2.0.3" -+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" -+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -+ -+events@^3.2.0: -+ version "3.3.0" -+ resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" -+ integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -+ -+fast-deep-equal@^3.1.1: -+ version "3.1.3" -+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" -+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -+ -+fast-glob@^3.0.3, fast-glob@^3.2.11: -+ version "3.3.1" -+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" -+ integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== -+ dependencies: -+ "@nodelib/fs.stat" "^2.0.2" -+ "@nodelib/fs.walk" "^1.2.3" -+ glob-parent "^5.1.2" -+ merge2 "^1.3.0" -+ micromatch "^4.0.4" -+ -+fast-json-stable-stringify@^2.0.0: -+ version "2.1.0" -+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" -+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -+ -+fast-levenshtein@~2.0.6: -+ version "2.0.6" -+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" -+ integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -+ -+fastest-levenshtein@^1.0.12: -+ version "1.0.16" -+ resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" -+ integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== -+ -+fastq@^1.6.0: -+ version "1.15.0" -+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" -+ integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== -+ dependencies: -+ reusify "^1.0.4" -+ -+fill-range@^7.0.1: -+ version "7.0.1" -+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" -+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== -+ dependencies: -+ to-regex-range "^5.0.1" -+ -+find-cache-dir@^3.3.1: -+ version "3.3.2" -+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" -+ integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== -+ dependencies: -+ commondir "^1.0.1" -+ make-dir "^3.0.2" -+ pkg-dir "^4.1.0" -+ -+find-up@^4.0.0: -+ version "4.1.0" -+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" -+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== -+ dependencies: -+ locate-path "^5.0.0" -+ path-exists "^4.0.0" -+ -+flat@^5.0.2: -+ version "5.0.2" -+ resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" -+ integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -+ -+fs-extra@^8.1.0: -+ version "8.1.0" -+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" -+ integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== -+ dependencies: -+ graceful-fs "^4.2.0" -+ jsonfile "^4.0.0" -+ universalify "^0.1.0" -+ -+fs-minipass@^2.0.0: -+ version "2.1.0" -+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" -+ integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== -+ dependencies: -+ minipass "^3.0.0" -+ -+fs.realpath@^1.0.0: -+ version "1.0.0" -+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" -+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -+ -+glob-parent@^5.1.2: -+ version "5.1.2" -+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" -+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== -+ dependencies: -+ is-glob "^4.0.1" -+ -+glob-to-regexp@^0.4.1: -+ version "0.4.1" -+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" -+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -+ -+glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: -+ version "7.2.3" -+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" -+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== -+ dependencies: -+ fs.realpath "^1.0.0" -+ inflight "^1.0.4" -+ inherits "2" -+ minimatch "^3.1.1" -+ once "^1.3.0" -+ path-is-absolute "^1.0.0" -+ -+globals@^11.1.0: -+ version "11.12.0" -+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" -+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -+ -+globby@^10.0.1: -+ version "10.0.2" -+ resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" -+ integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== -+ dependencies: -+ "@types/glob" "^7.1.1" -+ array-union "^2.1.0" -+ dir-glob "^3.0.1" -+ fast-glob "^3.0.3" -+ glob "^7.1.3" -+ ignore "^5.1.1" -+ merge2 "^1.2.3" -+ slash "^3.0.0" -+ -+graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: -+ version "4.2.11" -+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" -+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -+ -+has-flag@^3.0.0: -+ version "3.0.0" -+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" -+ integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== -+ -+has-flag@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" -+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -+ -+has@^1.0.3: -+ version "1.0.4" -+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6" -+ integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== -+ -+ignore@^5.1.1: -+ version "5.2.4" -+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" -+ integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -+ -+import-fresh@^3.2.1: -+ version "3.3.0" -+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" -+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== -+ dependencies: -+ parent-module "^1.0.0" -+ resolve-from "^4.0.0" -+ -+import-local@^3.0.2: -+ version "3.1.0" -+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" -+ integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== -+ dependencies: -+ pkg-dir "^4.2.0" -+ resolve-cwd "^3.0.0" -+ -+imurmurhash@^0.1.4: -+ version "0.1.4" -+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" -+ integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -+ -+indent-string@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" -+ integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -+ -+infer-owner@^1.0.4: -+ version "1.0.4" -+ resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" -+ integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== -+ -+inflight@^1.0.4: -+ version "1.0.6" -+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" -+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== -+ dependencies: -+ once "^1.3.0" -+ wrappy "1" -+ -+inherits@2, inherits@^2.0.3, inherits@^2.0.4: -+ version "2.0.4" -+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" -+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -+ -+interpret@^1.0.0: -+ version "1.4.0" -+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" -+ integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -+ -+interpret@^2.2.0: -+ version "2.2.0" -+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" -+ integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== -+ -+is-arrayish@^0.2.1: -+ version "0.2.1" -+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" -+ integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== -+ -+is-core-module@^2.13.0: -+ version "2.13.0" -+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" -+ integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== -+ dependencies: -+ has "^1.0.3" -+ -+is-extglob@^2.1.1: -+ version "2.1.1" -+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" -+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -+ -+is-glob@^4.0.1: -+ version "4.0.3" -+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" -+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== -+ dependencies: -+ is-extglob "^2.1.1" -+ -+is-interactive@^1.0.0: -+ version "1.0.0" -+ resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" -+ integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -+ -+is-number@^7.0.0: -+ version "7.0.0" -+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" -+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -+ -+is-path-cwd@^2.2.0: -+ version "2.2.0" -+ resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" -+ integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== -+ -+is-path-inside@^3.0.1: -+ version "3.0.3" -+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" -+ integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -+ -+is-plain-object@^2.0.4: -+ version "2.0.4" -+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" -+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== -+ dependencies: -+ isobject "^3.0.1" -+ -+isexe@^2.0.0: -+ version "2.0.0" -+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" -+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -+ -+isobject@^3.0.1: -+ version "3.0.1" -+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" -+ integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -+ -+javascript-stringify@^1.6.0: -+ version "1.6.0" -+ resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" -+ integrity sha512-fnjC0up+0SjEJtgmmG+teeel68kutkvzfctO/KxE3qJlbunkJYAshgH3boU++gSBHP8z5/r0ts0qRIrHf0RTQQ== -+ -+jest-worker@^26.5.0: -+ version "26.6.2" -+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" -+ integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== -+ dependencies: -+ "@types/node" "*" -+ merge-stream "^2.0.0" -+ supports-color "^7.0.0" -+ -+jest-worker@^27.4.5: -+ version "27.5.1" -+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" -+ integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== -+ dependencies: -+ "@types/node" "*" -+ merge-stream "^2.0.0" -+ supports-color "^8.0.0" -+ -+js-tokens@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" -+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -+ -+jsesc@^2.5.1: -+ version "2.5.2" -+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" -+ integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -+ -+json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: -+ version "2.3.1" -+ resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" -+ integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -+ -+json-schema-traverse@^0.4.1: -+ version "0.4.1" -+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" -+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -+ -+json5@^2.1.2: -+ version "2.2.3" -+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" -+ integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -+ -+jsonfile@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" -+ integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== -+ optionalDependencies: -+ graceful-fs "^4.1.6" -+ -+kind-of@^6.0.2: -+ version "6.0.3" -+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" -+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -+ -+klona@^2.0.4: -+ version "2.0.6" -+ resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" -+ integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== -+ -+levn@~0.3.0: -+ version "0.3.0" -+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" -+ integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== -+ dependencies: -+ prelude-ls "~1.1.2" -+ type-check "~0.3.2" -+ -+lilconfig@^2.0.3: -+ version "2.1.0" -+ resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" -+ integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== -+ -+lines-and-columns@^1.1.6: -+ version "1.2.4" -+ resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" -+ integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -+ -+loader-runner@^4.2.0: -+ version "4.3.0" -+ resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" -+ integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== -+ -+loader-utils@^2.0.0: -+ version "2.0.4" -+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" -+ integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== -+ dependencies: -+ big.js "^5.2.2" -+ emojis-list "^3.0.0" -+ json5 "^2.1.2" -+ -+locate-path@^5.0.0: -+ version "5.0.0" -+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" -+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== -+ dependencies: -+ p-locate "^4.1.0" -+ -+lodash-es@4.17.21, lodash-es@^4.17.11, lodash-es@^4.17.15: -+ version "4.17.21" -+ resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" -+ integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -+ -+lodash.memoize@^4.1.2: -+ version "4.1.2" -+ resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" -+ integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== -+ -+lodash.uniq@^4.5.0: -+ version "4.5.0" -+ resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -+ integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -+ -+lru-cache@^6.0.0: -+ version "6.0.0" -+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" -+ integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== -+ dependencies: -+ yallist "^4.0.0" -+ -+make-dir@^3.0.2: -+ version "3.1.0" -+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" -+ integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== -+ dependencies: -+ semver "^6.0.0" -+ -+mdn-data@2.0.14: -+ version "2.0.14" -+ resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" -+ integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== -+ -+merge-stream@^2.0.0: -+ version "2.0.0" -+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" -+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -+ -+merge2@^1.2.3, merge2@^1.3.0: -+ version "1.4.1" -+ resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" -+ integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -+ -+micromatch@^4.0.0, micromatch@^4.0.4: -+ version "4.0.5" -+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" -+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== -+ dependencies: -+ braces "^3.0.2" -+ picomatch "^2.3.1" -+ -+mime-db@1.52.0: -+ version "1.52.0" -+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" -+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -+ -+mime-types@^2.1.27: -+ version "2.1.35" -+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" -+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== -+ dependencies: -+ mime-db "1.52.0" -+ -+mimic-fn@^2.1.0: -+ version "2.1.0" -+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" -+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -+ -+minimatch@^3.1.1: -+ version "3.1.2" -+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" -+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== -+ dependencies: -+ brace-expansion "^1.1.7" -+ -+minipass-collect@^1.0.2: -+ version "1.0.2" -+ resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" -+ integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== -+ dependencies: -+ minipass "^3.0.0" -+ -+minipass-flush@^1.0.5: -+ version "1.0.5" -+ resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" -+ integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== -+ dependencies: -+ minipass "^3.0.0" -+ -+minipass-pipeline@^1.2.2: -+ version "1.2.4" -+ resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" -+ integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== -+ dependencies: -+ minipass "^3.0.0" -+ -+minipass@^3.0.0, minipass@^3.1.1: -+ version "3.3.6" -+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" -+ integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== -+ dependencies: -+ yallist "^4.0.0" -+ -+minipass@^5.0.0: -+ version "5.0.0" -+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" -+ integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== -+ -+minizlib@^2.1.1: -+ version "2.1.2" -+ resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" -+ integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== -+ dependencies: -+ minipass "^3.0.0" -+ yallist "^4.0.0" -+ -+mkdirp@^1.0.3, mkdirp@^1.0.4: -+ version "1.0.4" -+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" -+ integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -+ -+ms@2.1.2: -+ version "2.1.2" -+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" -+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -+ -+nanoid@^3.3.6: -+ version "3.3.6" -+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" -+ integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== -+ -+neo-async@^2.6.2: -+ version "2.6.2" -+ resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" -+ integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -+ -+node-releases@^2.0.13: -+ version "2.0.13" -+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" -+ integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== -+ -+normalize-url@^6.0.1: -+ version "6.1.0" -+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" -+ integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== -+ -+nth-check@^2.0.1: -+ version "2.1.1" -+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" -+ integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== -+ dependencies: -+ boolbase "^1.0.0" -+ -+once@^1.3.0: -+ version "1.4.0" -+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" -+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== -+ dependencies: -+ wrappy "1" -+ -+onetime@^5.1.0: -+ version "5.1.2" -+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" -+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== -+ dependencies: -+ mimic-fn "^2.1.0" -+ -+optionator@^0.8.1: -+ version "0.8.3" -+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" -+ integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== -+ dependencies: -+ deep-is "~0.1.3" -+ fast-levenshtein "~2.0.6" -+ levn "~0.3.0" -+ prelude-ls "~1.1.2" -+ type-check "~0.3.2" -+ word-wrap "~1.2.3" -+ -+p-limit@^2.2.0: -+ version "2.3.0" -+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" -+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== -+ dependencies: -+ p-try "^2.0.0" -+ -+p-limit@^3.0.2: -+ version "3.1.0" -+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" -+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== -+ dependencies: -+ yocto-queue "^0.1.0" -+ -+p-locate@^4.1.0: -+ version "4.1.0" -+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" -+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== -+ dependencies: -+ p-limit "^2.2.0" -+ -+p-map@^3.0.0: -+ version "3.0.0" -+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" -+ integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== -+ dependencies: -+ aggregate-error "^3.0.0" -+ -+p-map@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" -+ integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== -+ dependencies: -+ aggregate-error "^3.0.0" -+ -+p-try@^2.0.0: -+ version "2.2.0" -+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" -+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -+ -+parent-module@^1.0.0: -+ version "1.0.1" -+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" -+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== -+ dependencies: -+ callsites "^3.0.0" -+ -+parse-json@^5.0.0: -+ version "5.2.0" -+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" -+ integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== -+ dependencies: -+ "@babel/code-frame" "^7.0.0" -+ error-ex "^1.3.1" -+ json-parse-even-better-errors "^2.3.0" -+ lines-and-columns "^1.1.6" -+ -+path-exists@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" -+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -+ -+path-is-absolute@^1.0.0: -+ version "1.0.1" -+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -+ integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -+ -+path-key@^3.1.0: -+ version "3.1.1" -+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" -+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -+ -+path-parse@^1.0.7: -+ version "1.0.7" -+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" -+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -+ -+path-type@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" -+ integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -+ -+picocolors@^1.0.0: -+ version "1.0.0" -+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" -+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -+ -+picomatch@^2.3.1: -+ version "2.3.1" -+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" -+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -+ -+pify@^2.3.0: -+ version "2.3.0" -+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" -+ integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== -+ -+pkg-dir@^4.1.0, pkg-dir@^4.2.0: -+ version "4.2.0" -+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" -+ integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== -+ dependencies: -+ find-up "^4.0.0" -+ -+pofile@^1.0.9: -+ version "1.1.4" -+ resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.1.4.tgz#eab7e29f5017589b2a61b2259dff608c0cad76a2" -+ integrity sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g== -+ -+postcss-calc@^8.2.3: -+ version "8.2.4" -+ resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" -+ integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== -+ dependencies: -+ postcss-selector-parser "^6.0.9" -+ postcss-value-parser "^4.2.0" -+ -+postcss-colormin@^5.3.1: -+ version "5.3.1" -+ resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz#86c27c26ed6ba00d96c79e08f3ffb418d1d1988f" -+ integrity sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ== -+ dependencies: -+ browserslist "^4.21.4" -+ caniuse-api "^3.0.0" -+ colord "^2.9.1" -+ postcss-value-parser "^4.2.0" -+ -+postcss-convert-values@^5.1.3: -+ version "5.1.3" -+ resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz#04998bb9ba6b65aa31035d669a6af342c5f9d393" -+ integrity sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA== -+ dependencies: -+ browserslist "^4.21.4" -+ postcss-value-parser "^4.2.0" -+ -+postcss-discard-comments@^5.1.2: -+ version "5.1.2" -+ resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" -+ integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== -+ -+postcss-discard-duplicates@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" -+ integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== -+ -+postcss-discard-empty@^5.1.1: -+ version "5.1.1" -+ resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" -+ integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== -+ -+postcss-discard-overridden@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" -+ integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== -+ -+postcss-import@^14.1.0: -+ version "14.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" -+ integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== -+ dependencies: -+ postcss-value-parser "^4.0.0" -+ read-cache "^1.0.0" -+ resolve "^1.1.7" -+ -+postcss-js@^4.0.0: -+ version "4.0.1" -+ resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" -+ integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== -+ dependencies: -+ camelcase-css "^2.0.1" -+ -+postcss-loader@^4.3.0: -+ version "4.3.0" -+ resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.3.0.tgz#2c4de9657cd4f07af5ab42bd60a673004da1b8cc" -+ integrity sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q== -+ dependencies: -+ cosmiconfig "^7.0.0" -+ klona "^2.0.4" -+ loader-utils "^2.0.0" -+ schema-utils "^3.0.0" -+ semver "^7.3.4" -+ -+postcss-merge-longhand@^5.1.7: -+ version "5.1.7" -+ resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz#24a1bdf402d9ef0e70f568f39bdc0344d568fb16" -+ integrity sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ stylehacks "^5.1.1" -+ -+postcss-merge-rules@^5.1.4: -+ version "5.1.4" -+ resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz#2f26fa5cacb75b1402e213789f6766ae5e40313c" -+ integrity sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g== -+ dependencies: -+ browserslist "^4.21.4" -+ caniuse-api "^3.0.0" -+ cssnano-utils "^3.1.0" -+ postcss-selector-parser "^6.0.5" -+ -+postcss-minify-font-values@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" -+ integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ -+postcss-minify-gradients@^5.1.1: -+ version "5.1.1" -+ resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" -+ integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== -+ dependencies: -+ colord "^2.9.1" -+ cssnano-utils "^3.1.0" -+ postcss-value-parser "^4.2.0" -+ -+postcss-minify-params@^5.1.4: -+ version "5.1.4" -+ resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz#c06a6c787128b3208b38c9364cfc40c8aa5d7352" -+ integrity sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw== -+ dependencies: -+ browserslist "^4.21.4" -+ cssnano-utils "^3.1.0" -+ postcss-value-parser "^4.2.0" -+ -+postcss-minify-selectors@^5.2.1: -+ version "5.2.1" -+ resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" -+ integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== -+ dependencies: -+ postcss-selector-parser "^6.0.5" -+ -+postcss-mixins@^9.0.2: -+ version "9.0.4" -+ resolved "https://registry.yarnpkg.com/postcss-mixins/-/postcss-mixins-9.0.4.tgz#75cd3cdb619a7e08c4c51ebb094db5f6d65b3831" -+ integrity sha512-XVq5jwQJDRu5M1XGkdpgASqLk37OqkH4JCFDXl/Dn7janOJjCTEKL+36cnRVy7bMtoBzALfO7bV7nTIsFnUWLA== -+ dependencies: -+ fast-glob "^3.2.11" -+ postcss-js "^4.0.0" -+ postcss-simple-vars "^7.0.0" -+ sugarss "^4.0.1" -+ -+postcss-nesting@^10.1.4: -+ version "10.2.0" -+ resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-10.2.0.tgz#0b12ce0db8edfd2d8ae0aaf86427370b898890be" -+ integrity sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA== -+ dependencies: -+ "@csstools/selector-specificity" "^2.0.0" -+ postcss-selector-parser "^6.0.10" -+ -+postcss-normalize-charset@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" -+ integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== -+ -+postcss-normalize-display-values@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" -+ integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ -+postcss-normalize-positions@^5.1.1: -+ version "5.1.1" -+ resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" -+ integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ -+postcss-normalize-repeat-style@^5.1.1: -+ version "5.1.1" -+ resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" -+ integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ -+postcss-normalize-string@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" -+ integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ -+postcss-normalize-timing-functions@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" -+ integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ -+postcss-normalize-unicode@^5.1.1: -+ version "5.1.1" -+ resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz#f67297fca3fea7f17e0d2caa40769afc487aa030" -+ integrity sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA== -+ dependencies: -+ browserslist "^4.21.4" -+ postcss-value-parser "^4.2.0" -+ -+postcss-normalize-url@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" -+ integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== -+ dependencies: -+ normalize-url "^6.0.1" -+ postcss-value-parser "^4.2.0" -+ -+postcss-normalize-whitespace@^5.1.1: -+ version "5.1.1" -+ resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" -+ integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ -+postcss-ordered-values@^5.1.3: -+ version "5.1.3" -+ resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" -+ integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== -+ dependencies: -+ cssnano-utils "^3.1.0" -+ postcss-value-parser "^4.2.0" -+ -+postcss-reduce-initial@^5.1.2: -+ version "5.1.2" -+ resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz#798cd77b3e033eae7105c18c9d371d989e1382d6" -+ integrity sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg== -+ dependencies: -+ browserslist "^4.21.4" -+ caniuse-api "^3.0.0" -+ -+postcss-reduce-transforms@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" -+ integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ -+postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: -+ version "6.0.13" -+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" -+ integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== -+ dependencies: -+ cssesc "^3.0.0" -+ util-deprecate "^1.0.2" -+ -+postcss-simple-vars@^7.0.0: -+ version "7.0.1" -+ resolved "https://registry.yarnpkg.com/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz#836b3097a54dcd13dbd3c36a5dbdd512fad2954c" -+ integrity sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A== -+ -+postcss-svgo@^5.1.0: -+ version "5.1.0" -+ resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" -+ integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== -+ dependencies: -+ postcss-value-parser "^4.2.0" -+ svgo "^2.7.0" -+ -+postcss-unique-selectors@^5.1.1: -+ version "5.1.1" -+ resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" -+ integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== -+ dependencies: -+ postcss-selector-parser "^6.0.5" -+ -+postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: -+ version "4.2.0" -+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" -+ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -+ -+postcss@^8.4.12: -+ version "8.4.31" -+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" -+ integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== -+ dependencies: -+ nanoid "^3.3.6" -+ picocolors "^1.0.0" -+ source-map-js "^1.0.2" -+ -+prelude-ls@~1.1.2: -+ version "1.1.2" -+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" -+ integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== -+ -+promise-inflight@^1.0.1: -+ version "1.0.1" -+ resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" -+ integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== -+ -+punycode@^2.1.0: -+ version "2.3.0" -+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" -+ integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -+ -+queue-microtask@^1.2.2: -+ version "1.2.3" -+ resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" -+ integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -+ -+randombytes@^2.1.0: -+ version "2.1.0" -+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" -+ integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== -+ dependencies: -+ safe-buffer "^5.1.0" -+ -+raw-loader@^4.0.1, raw-loader@^4.0.2: -+ version "4.0.2" -+ resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" -+ integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== -+ dependencies: -+ loader-utils "^2.0.0" -+ schema-utils "^3.0.0" -+ -+read-cache@^1.0.0: -+ version "1.0.0" -+ resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" -+ integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== -+ dependencies: -+ pify "^2.3.0" -+ -+"readable-stream@2 || 3": -+ version "3.6.2" -+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" -+ integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== -+ dependencies: -+ inherits "^2.0.3" -+ string_decoder "^1.1.1" -+ util-deprecate "^1.0.1" -+ -+rechoir@^0.6.2: -+ version "0.6.2" -+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" -+ integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== -+ dependencies: -+ resolve "^1.1.6" -+ -+rechoir@^0.7.0: -+ version "0.7.1" -+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" -+ integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== -+ dependencies: -+ resolve "^1.9.0" -+ -+resolve-cwd@^3.0.0: -+ version "3.0.0" -+ resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" -+ integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== -+ dependencies: -+ resolve-from "^5.0.0" -+ -+resolve-from@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" -+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -+ -+resolve-from@^5.0.0: -+ version "5.0.0" -+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" -+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -+ -+resolve@^1.1.6, resolve@^1.1.7, resolve@^1.9.0: -+ version "1.22.8" -+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" -+ integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== -+ dependencies: -+ is-core-module "^2.13.0" -+ path-parse "^1.0.7" -+ supports-preserve-symlinks-flag "^1.0.0" -+ -+restore-cursor@^3.1.0: -+ version "3.1.0" -+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" -+ integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== -+ dependencies: -+ onetime "^5.1.0" -+ signal-exit "^3.0.2" -+ -+reusify@^1.0.4: -+ version "1.0.4" -+ resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" -+ integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -+ -+rimraf@^3.0.0, rimraf@^3.0.2: -+ version "3.0.2" -+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" -+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== -+ dependencies: -+ glob "^7.1.3" -+ -+run-parallel@^1.1.9: -+ version "1.2.0" -+ resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" -+ integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== -+ dependencies: -+ queue-microtask "^1.2.2" -+ -+safe-buffer@^5.1.0, safe-buffer@~5.2.0: -+ version "5.2.1" -+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" -+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -+ -+schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: -+ version "3.3.0" -+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" -+ integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== -+ dependencies: -+ "@types/json-schema" "^7.0.8" -+ ajv "^6.12.5" -+ ajv-keywords "^3.5.2" -+ -+semver@^6.0.0: -+ version "6.3.1" -+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" -+ integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -+ -+semver@^7.3.4, semver@^7.3.5: -+ version "7.5.4" -+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" -+ integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== -+ dependencies: -+ lru-cache "^6.0.0" -+ -+serialize-javascript@^5.0.1: -+ version "5.0.1" -+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" -+ integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== -+ dependencies: -+ randombytes "^2.1.0" -+ -+serialize-javascript@^6.0.1: -+ version "6.0.1" -+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" -+ integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== -+ dependencies: -+ randombytes "^2.1.0" -+ -+shallow-clone@^3.0.0: -+ version "3.0.1" -+ resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" -+ integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== -+ dependencies: -+ kind-of "^6.0.2" -+ -+shebang-command@^2.0.0: -+ version "2.0.0" -+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" -+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== -+ dependencies: -+ shebang-regex "^3.0.0" -+ -+shebang-regex@^3.0.0: -+ version "3.0.0" -+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" -+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -+ -+shelljs@^0.8.1: -+ version "0.8.5" -+ resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" -+ integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== -+ dependencies: -+ glob "^7.0.0" -+ interpret "^1.0.0" -+ rechoir "^0.6.2" -+ -+signal-exit@^3.0.2: -+ version "3.0.7" -+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" -+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -+ -+slash@^3.0.0: -+ version "3.0.0" -+ resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" -+ integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -+ -+source-list-map@^2.0.0, source-list-map@^2.0.1: -+ version "2.0.1" -+ resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" -+ integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -+ -+source-map-js@^1.0.2: -+ version "1.0.2" -+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" -+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -+ -+source-map-support@~0.5.20: -+ version "0.5.21" -+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" -+ integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== -+ dependencies: -+ buffer-from "^1.0.0" -+ source-map "^0.6.0" -+ -+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: -+ version "0.6.1" -+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" -+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -+ -+source-map@^0.7.4: -+ version "0.7.4" -+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" -+ integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== -+ -+ssri@^8.0.1: -+ version "8.0.1" -+ resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" -+ integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== -+ dependencies: -+ minipass "^3.1.1" -+ -+stable@^0.1.8: -+ version "0.1.8" -+ resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" -+ integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -+ -+string_decoder@^1.1.1: -+ version "1.3.0" -+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" -+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== -+ dependencies: -+ safe-buffer "~5.2.0" -+ -+style-loader@^2.0.0: -+ version "2.0.0" -+ resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" -+ integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== -+ dependencies: -+ loader-utils "^2.0.0" -+ schema-utils "^3.0.0" -+ -+stylehacks@^5.1.1: -+ version "5.1.1" -+ resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" -+ integrity sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw== -+ dependencies: -+ browserslist "^4.21.4" -+ postcss-selector-parser "^6.0.4" -+ -+sugarss@^4.0.1: -+ version "4.0.1" -+ resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-4.0.1.tgz#128a783ed71ee0fc3b489ce1f7d5a89bc1e24383" -+ integrity sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw== -+ -+supports-color@^5.3.0: -+ version "5.5.0" -+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" -+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== -+ dependencies: -+ has-flag "^3.0.0" -+ -+supports-color@^7.0.0, supports-color@^7.1.0: -+ version "7.2.0" -+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" -+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== -+ dependencies: -+ has-flag "^4.0.0" -+ -+supports-color@^8.0.0: -+ version "8.1.1" -+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" -+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== -+ dependencies: -+ has-flag "^4.0.0" -+ -+supports-preserve-symlinks-flag@^1.0.0: -+ version "1.0.0" -+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" -+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -+ -+svgo@^2.7.0: -+ version "2.8.0" -+ resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" -+ integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== -+ dependencies: -+ "@trysound/sax" "0.2.0" -+ commander "^7.2.0" -+ css-select "^4.1.3" -+ css-tree "^1.1.3" -+ csso "^4.2.0" -+ picocolors "^1.0.0" -+ stable "^0.1.8" -+ -+tapable@^2.1.1, tapable@^2.2.0: -+ version "2.2.1" -+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" -+ integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -+ -+tar@^6.0.2: -+ version "6.2.0" -+ resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" -+ integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== -+ dependencies: -+ chownr "^2.0.0" -+ fs-minipass "^2.0.0" -+ minipass "^5.0.0" -+ minizlib "^2.1.1" -+ mkdirp "^1.0.3" -+ yallist "^4.0.0" -+ -+terser-webpack-plugin@^4.2.3: -+ version "4.2.3" -+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a" -+ integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ== -+ dependencies: -+ cacache "^15.0.5" -+ find-cache-dir "^3.3.1" -+ jest-worker "^26.5.0" -+ p-limit "^3.0.2" -+ schema-utils "^3.0.0" -+ serialize-javascript "^5.0.1" -+ source-map "^0.6.1" -+ terser "^5.3.4" -+ webpack-sources "^1.4.3" -+ -+terser-webpack-plugin@^5.2.0, terser-webpack-plugin@^5.3.7: -+ version "5.3.9" -+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" -+ integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== -+ dependencies: -+ "@jridgewell/trace-mapping" "^0.3.17" -+ jest-worker "^27.4.5" -+ schema-utils "^3.1.1" -+ serialize-javascript "^6.0.1" -+ terser "^5.16.8" -+ -+terser@^5.16.8, terser@^5.3.4: -+ version "5.22.0" -+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.22.0.tgz#4f18103f84c5c9437aafb7a14918273310a8a49d" -+ integrity sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw== -+ dependencies: -+ "@jridgewell/source-map" "^0.3.3" -+ acorn "^8.8.2" -+ commander "^2.20.0" -+ source-map-support "~0.5.20" -+ -+through2@^3.0.1: -+ version "3.0.2" -+ resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" -+ integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== -+ dependencies: -+ inherits "^2.0.4" -+ readable-stream "2 || 3" -+ -+to-fast-properties@^2.0.0: -+ version "2.0.0" -+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" -+ integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== -+ -+to-regex-range@^5.0.1: -+ version "5.0.1" -+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" -+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== -+ dependencies: -+ is-number "^7.0.0" -+ -+ts-loader@^9.3.0: -+ version "9.5.0" -+ resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.0.tgz#f0a51dda37cc4d8e43e6cb14edebbc599b0c3aa2" -+ integrity sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg== -+ dependencies: -+ chalk "^4.1.0" -+ enhanced-resolve "^5.0.0" -+ micromatch "^4.0.0" -+ semver "^7.3.4" -+ source-map "^0.7.4" -+ -+type-check@~0.3.2: -+ version "0.3.2" -+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" -+ integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== -+ dependencies: -+ prelude-ls "~1.1.2" -+ -+undici-types@~5.25.1: -+ version "5.25.3" -+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.25.3.tgz#e044115914c85f0bcbb229f346ab739f064998c3" -+ integrity sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA== -+ -+unique-filename@^1.1.1: -+ version "1.1.1" -+ resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" -+ integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== -+ dependencies: -+ unique-slug "^2.0.0" -+ -+unique-slug@^2.0.0: -+ version "2.0.2" -+ resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" -+ integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== -+ dependencies: -+ imurmurhash "^0.1.4" -+ -+universalify@^0.1.0: -+ version "0.1.2" -+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" -+ integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -+ -+update-browserslist-db@^1.0.13: -+ version "1.0.13" -+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" -+ integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== -+ dependencies: -+ escalade "^3.1.1" -+ picocolors "^1.0.0" -+ -+uri-js@^4.2.2: -+ version "4.4.1" -+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" -+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== -+ dependencies: -+ punycode "^2.1.0" -+ -+util-deprecate@^1.0.1, util-deprecate@^1.0.2: -+ version "1.0.2" -+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -+ integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -+ -+vanilla-colorful@0.7.2: -+ version "0.7.2" -+ resolved "https://registry.yarnpkg.com/vanilla-colorful/-/vanilla-colorful-0.7.2.tgz#3fb1f4b9f15b797e20fd1ce8e0364f33b073f4a2" -+ integrity sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg== -+ -+watchpack@^2.4.0: -+ version "2.4.0" -+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" -+ integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== -+ dependencies: -+ glob-to-regexp "^0.4.1" -+ graceful-fs "^4.1.2" -+ -+webpack-cli@^4.4.0: -+ version "4.10.0" -+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" -+ integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== -+ dependencies: -+ "@discoveryjs/json-ext" "^0.5.0" -+ "@webpack-cli/configtest" "^1.2.0" -+ "@webpack-cli/info" "^1.5.0" -+ "@webpack-cli/serve" "^1.7.0" -+ colorette "^2.0.14" -+ commander "^7.0.0" -+ cross-spawn "^7.0.3" -+ fastest-levenshtein "^1.0.12" -+ import-local "^3.0.2" -+ interpret "^2.2.0" -+ rechoir "^0.7.0" -+ webpack-merge "^5.7.3" -+ -+webpack-merge@^5.7.3: -+ version "5.10.0" -+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" -+ integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== -+ dependencies: -+ clone-deep "^4.0.1" -+ flat "^5.0.2" -+ wildcard "^2.0.0" -+ -+webpack-sources@^1.4.3: -+ version "1.4.3" -+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" -+ integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== -+ dependencies: -+ source-list-map "^2.0.0" -+ source-map "~0.6.1" -+ -+webpack-sources@^2.0.1: -+ version "2.3.1" -+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd" -+ integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA== -+ dependencies: -+ source-list-map "^2.0.1" -+ source-map "^0.6.1" -+ -+webpack-sources@^3.2.3: -+ version "3.2.3" -+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" -+ integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -+ -+webpack@^5.51.1: -+ version "5.89.0" -+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" -+ integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== -+ dependencies: -+ "@types/eslint-scope" "^3.7.3" -+ "@types/estree" "^1.0.0" -+ "@webassemblyjs/ast" "^1.11.5" -+ "@webassemblyjs/wasm-edit" "^1.11.5" -+ "@webassemblyjs/wasm-parser" "^1.11.5" -+ acorn "^8.7.1" -+ acorn-import-assertions "^1.9.0" -+ browserslist "^4.14.5" -+ chrome-trace-event "^1.0.2" -+ enhanced-resolve "^5.15.0" -+ es-module-lexer "^1.2.1" -+ eslint-scope "5.1.1" -+ events "^3.2.0" -+ glob-to-regexp "^0.4.1" -+ graceful-fs "^4.2.9" -+ json-parse-even-better-errors "^2.3.1" -+ loader-runner "^4.2.0" -+ mime-types "^2.1.27" -+ neo-async "^2.6.2" -+ schema-utils "^3.2.0" -+ tapable "^2.1.1" -+ terser-webpack-plugin "^5.3.7" -+ watchpack "^2.4.0" -+ webpack-sources "^3.2.3" -+ -+which@^2.0.1: -+ version "2.0.2" -+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" -+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== -+ dependencies: -+ isexe "^2.0.0" -+ -+wildcard@^2.0.0: -+ version "2.0.1" -+ resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" -+ integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== -+ -+word-wrap@~1.2.3: -+ version "1.2.5" -+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" -+ integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -+ -+wrappy@1: -+ version "1.0.2" -+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -+ -+yallist@^4.0.0: -+ version "4.0.0" -+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" -+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -+ -+yaml@^1.10.0, yaml@^1.10.2: -+ version "1.10.2" -+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" -+ integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -+ -+yocto-queue@^0.1.0: -+ version "0.1.0" -+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" -+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/patches/d10/3311234.diff b/patches/d10/3311234.diff deleted file mode 100644 index 013cd9df970ef333fd4adef8a63dcd861bc4e41c..0000000000000000000000000000000000000000 --- a/patches/d10/3311234.diff +++ /dev/null @@ -1,145 +0,0 @@ -diff --git a/diff.views.inc b/diff.views.inc -index 1c9e8765a7a008fba3f0925a1df35452c6744548..1c23dc7c9cc494a3322046d5f94d82a864a7f3e3 100644 ---- a/diff.views.inc -+++ b/diff.views.inc -@@ -31,7 +31,18 @@ function diff_views_data() { - 'id' => 'diff__to', - ], - ]; -+ $data[$revision_base_table]['diff_previous'] = [ -+ 'title' => t('Diff with previous revision'), -+ 'help' => 'Provides a link to diff this revision to the previous one.', -+ 'field' => [ -+ 'id' => 'diff_previous', -+ ], -+ 'argument' => [ -+ 'additional fields' => [$entity_type->getKey('revision')], -+ ] -+ ]; - } -+ - } - - return $data; -diff --git a/src/Plugin/views/field/DiffPrevious.php b/src/Plugin/views/field/DiffPrevious.php -new file mode 100644 -index 0000000000000000000000000000000000000000..ede4a4bc229d0403ae02579db1856aca5965cb34 ---- /dev/null -+++ b/src/Plugin/views/field/DiffPrevious.php -@@ -0,0 +1,116 @@ -+<?php -+ -+namespace Drupal\diff\Plugin\views\field; -+ -+use Drupal\Core\Database\Connection; -+use Drupal\Core\Form\FormStateInterface; -+use Drupal\Core\Url; -+use Drupal\diff\DiffLayoutManager; -+use Drupal\views\Plugin\views\field\LinkBase; -+use Drupal\views\ResultRow; -+use Symfony\Component\DependencyInjection\ContainerInterface; -+ -+/** -+ * Provides link to diff with previous revision. -+ * -+ * @ViewsField("diff_previous") -+ */ -+class DiffPrevious extends LinkBase { -+ -+ /** -+ * @var \Drupal\Core\Database\Connection -+ */ -+ protected Connection $database; -+ -+ /** -+ * @var \Drupal\diff\DiffLayoutManager -+ */ -+ protected DiffLayoutManager $diffLayoutManager; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): DiffPrevious { -+ $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); -+ $instance->database = $container->get('database'); -+ $instance->diffLayoutManager = $container->get('plugin.manager.diff.layout'); -+ return $instance; -+ } -+ -+ protected function defineOptions() { -+ $options = parent::defineOptions(); -+ $options['filter'] = ['default' => $this->diffLayoutManager->getDefaultLayout()]; -+ $options['previous_published'] = ['default' => FALSE]; -+ $options['new_label'] = ['default' => 'new']; -+ return $options; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function getDefaultLabel() { -+ return $this->t('diff'); -+ } -+ -+ public function buildOptionsForm(&$form, FormStateInterface $form_state) { -+ parent::buildOptionsForm($form, $form_state); -+ $form['filter'] = [ -+ '#type' => 'select', -+ '#title' => $this->t('Filter'), -+ '#options' => $this->diffLayoutManager->getPluginOptions(), -+ '#default_value' => $this->options['filter'], -+ '#required' => TRUE, -+ ]; -+ $form['previous_published'] = [ -+ '#type' => 'checkbox', -+ '#title' => $this->t('Only compare with previously published revision'), -+ '#default_value' => $this->options['previous_published'], -+ ]; -+ $form['new_label'] = [ -+ '#type' => 'textfield', -+ '#title' => $this->t('Text to output if no previous revision is available'), -+ '#default_value' => $this->options['new_label'], -+ ]; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function render(ResultRow $row) { -+ $entity = $row->_entity; -+ $id_key = $entity->getEntityType()->getKey('id'); -+ $rev_key = $entity->getEntityType()->getKey('revision'); -+ $rid = $row->{$rev_key}; -+ $table = $entity->getEntityType()->getRevisionTable(); -+ $query = $this->database->select($table, 't') -+ ->fields('t', [$rev_key]) -+ ->condition('t.' . $id_key, $entity->id()) -+ ->condition('t.' . $rev_key, $rid, '<') -+ ->orderBy('t.' . $rev_key, 'DESC') -+ ->range(0, 1); -+ if ($this->options['previous_published']) { -+ $query->join($entity->getEntityType()->getDataTable(), 'd', 't.vid = d.vid'); -+ $query->condition('d.status', 1); -+ } -+ $prev_rid = $query -+ ->execute() -+ ->fetchField(); -+ if (empty($prev_rid)) { -+ $this->options['alter']['make_link'] = FALSE; -+ return $this->sanitizeValue($this->options['new_label']); -+ } -+ $row->_revision_id_previous = $prev_rid; -+ $row->_revision_id_current = $rid; -+ return parent::render($row); -+ } -+ -+ protected function getUrlInfo(ResultRow $row): Url { -+ return Url::fromRoute('diff.revisions_diff', [ -+ 'node' => $row->_entity->id(), -+ 'left_revision' => $row->_revision_id_previous, -+ 'right_revision' => $row->_revision_id_current, -+ 'filter' => $this->options['filter'], -+ ]); -+ } -+ -+} diff --git a/patches/d10/3311372.diff b/patches/d10/3311372.diff deleted file mode 100644 index 446a59a2e67902e16c530b48494b6dce975697d5..0000000000000000000000000000000000000000 --- a/patches/d10/3311372.diff +++ /dev/null @@ -1,184 +0,0 @@ -diff --git a/config/install/diff.settings.yml b/config/install/diff.settings.yml -index db5ba4a773f86c5c5fb94afe1625cf7f804adc92..6eb1e09c428148c2ba57c1aa2e3b502eb7f65a98 100644 ---- a/config/install/diff.settings.yml -+++ b/config/install/diff.settings.yml -@@ -1,5 +1,6 @@ - general_settings: - radio_behavior: simple -+ date_format: short - context_lines_leading: 1 - context_lines_trailing: 1 - revision_pager_limit: 50 -diff --git a/src/DiffLayoutBase.php b/src/DiffLayoutBase.php -index 11ce99201cbe5711c6d96f51db352857cb3fa8cb..bab210e34c6f0e208e1e59a09a5488f1f70fddf6 100644 ---- a/src/DiffLayoutBase.php -+++ b/src/DiffLayoutBase.php -@@ -115,7 +115,7 @@ abstract class DiffLayoutBase extends PluginBase implements DiffLayoutInterface, - */ - protected function buildRevisionLink(ContentEntityInterface $revision) { - if ($revision instanceof RevisionLogInterface) { -- $revision_date = $this->date->format($revision->getRevisionCreationTime(), 'short'); -+ $revision_date = $this->date->format($revision->getRevisionCreationTime(), $this->configFactory->get('diff.settings')->get('general_settings.date_format') ?? 'short'); - $revision_link = Link::fromTextAndUrl($revision_date, $revision->toUrl('revision'))->toString(); - } - else { -@@ -179,7 +179,7 @@ abstract class DiffLayoutBase extends PluginBase implements DiffLayoutInterface, - - $revision_link['date'] = [ - '#type' => 'link', -- '#title' => $this->date->format($revision->getRevisionCreationTime(), 'short'), -+ '#title' => $this->date->format($revision->getRevisionCreationTime(), $this->configFactory->get('diff.settings')->get('general_settings.date_format') ?? 'short'), - '#url' => $revision->toUrl('revision'), - '#prefix' => '<div class="diff-revision__item diff-revision__item-date">', - '#suffix' => '</div>', -diff --git a/src/Form/GeneralSettingsForm.php b/src/Form/GeneralSettingsForm.php -index 74c5d53ff3f5fcda7cf42ff9d08d301d43be57af..3209702d96eb88a06573b14e42d6fb08d1b0b55f 100644 ---- a/src/Form/GeneralSettingsForm.php -+++ b/src/Form/GeneralSettingsForm.php -@@ -80,6 +80,17 @@ class GeneralSettingsForm extends ConfigFormBase { - '#description' => $this->t('<em>Simple exclusion</em> means that users will not be able to select the same revision, <em>Linear restrictions</em> means that users can only select older or newer revisions of the current selections.'), - ]; - -+ $date_formats = []; -+ foreach (\Drupal::entityTypeManager()->getStorage('date_format')->loadMultiple() as $machine_name => $value) { -+ $date_formats[$machine_name] = $value->label(); -+ } -+ $form['date_format'] = array( -+ '#type' => 'select', -+ '#title' => $this->t('Date format'), -+ '#default_value' => $config->get('general_settings.date_format'), -+ '#options' => $date_formats, -+ ); -+ - $layout_plugins = $this->diffLayoutManager->getDefinitions(); - $weight = count($layout_plugins) + 1; - $layout_plugins_order = []; -@@ -242,6 +253,7 @@ class GeneralSettingsForm extends ConfigFormBase { - - $keys = [ - 'radio_behavior', -+ 'date_format', - 'context_lines_leading', - 'context_lines_trailing', - 'layout_plugins', -diff --git a/src/Form/RevisionOverviewForm.php b/src/Form/RevisionOverviewForm.php -index be03826f4c1e5f07722be0a9157e67e1e4c85bbb..522fdc420b5e777781834937ba58e1d677a62b62 100755 ---- a/src/Form/RevisionOverviewForm.php -+++ b/src/Form/RevisionOverviewForm.php -@@ -222,7 +222,7 @@ class RevisionOverviewForm extends FormBase { - '#theme' => 'username', - '#account' => $revision->getRevisionUser(), - ]; -- $revision_date = $this->date->format($revision->getRevisionCreationTime(), 'short'); -+ $revision_date = $this->date->format($revision->getRevisionCreationTime(), $this->configFactory->get('diff.settings')->get('general_settings.date_format') ?? 'short'); - // Use revision link to link to revisions that are not active. - if ($vid != $node->getRevisionId()) { - $link = Link::fromTextAndUrl($revision_date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid])); -diff --git a/src/Plugin/diff/Field/DateFieldBuilder.php b/src/Plugin/diff/Field/DateFieldBuilder.php -new file mode 100644 -index 0000000000000000000000000000000000000000..78d39378bedfeed0da987f5aab4eca471a9c4519 ---- /dev/null -+++ b/src/Plugin/diff/Field/DateFieldBuilder.php -@@ -0,0 +1,102 @@ -+<?php -+ -+namespace Drupal\diff\Plugin\diff\Field; -+ -+use Drupal\Core\Datetime\DateFormatter; -+use Drupal\diff\FieldDiffBuilderBase; -+use Drupal\Core\Field\FieldItemListInterface; -+use Drupal\Core\Form\FormStateInterface; -+use Symfony\Component\DependencyInjection\ContainerInterface; -+ -+/** -+ * Plugin to compare date fields. -+ * -+ * @FieldDiffBuilder( -+ * id = "date_field_diff_builder", -+ * label = @Translation("Date Field Diff"), -+ * field_types = { -+ * "changed", -+ * "created", -+ * "datetime", -+ * "daterange", -+ * "timestamp" -+ * }, -+ * ) -+ */ -+class DateFieldBuilder extends FieldDiffBuilderBase { -+ -+ /** -+ * The date formatter service. -+ * -+ * @var \Drupal\Core\Datetime\DateFormatter -+ */ -+ protected DateFormatter $date; -+ -+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { -+ /** @var \Drupal\diff\Plugin\diff\Field\DateFieldBuilder $instance */ -+ $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); -+ $instance->date = $container->get('date.formatter'); -+ return $instance; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function build(FieldItemListInterface $field_items) { -+ $result = array(); -+ -+ // Every item from $field_items is of type FieldItemInterface. -+ foreach ($field_items as $field_key => $field_item) { -+ if (!$field_item->isEmpty()) { -+ foreach ($field_item->getValue() as $value) { -+ if (!is_numeric($value)) { -+ $value = (new \DateTime($value))->getTimestamp(); -+ } -+ $result[$field_key][] = $this->date->format((int) $value, $this->configuration['format']); -+ } -+ } -+ } -+ -+ return $result; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { -+ $date_formats = []; -+ foreach (\Drupal::entityTypeManager()->getStorage('date_format')->loadMultiple() as $machine_name => $value) { -+ $date_formats[$machine_name] = $value->label(); -+ } -+ $form['format'] = array( -+ '#type' => 'select', -+ '#title' => $this->t('Format'), -+ '#default_value' => $this->configuration['format'], -+ '#options' => $date_formats, -+ ); -+ -+ return parent::buildConfigurationForm($form, $form_state); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { -+ $this->configuration['format'] = $form_state->getValue('format'); -+ -+ parent::submitConfigurationForm($form, $form_state); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function defaultConfiguration() { -+ $default_configuration = array( -+ 'format' => 'short', -+ ); -+ $default_configuration += parent::defaultConfiguration(); -+ -+ return $default_configuration; -+ } -+ -+} diff --git a/patches/d10/3311849.diff b/patches/d10/3311849.diff deleted file mode 100644 index 6ee5389eba37e0ad168aecfd3e088b5c9e2d9251..0000000000000000000000000000000000000000 --- a/patches/d10/3311849.diff +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/core/modules/views/src/Plugin/views/filter/InOperator.php b/core/modules/views/src/Plugin/views/filter/InOperator.php -index 5fdeeb958b65889c5ce397cb41fa2a455d270909..9b538444f5267a1cce263b617c8eb12120de74b2 100644 ---- a/core/modules/views/src/Plugin/views/filter/InOperator.php -+++ b/core/modules/views/src/Plugin/views/filter/InOperator.php -@@ -270,6 +270,9 @@ public function reduceValueOptions($input = NULL) { - foreach ($input as $id => $option) { - if (is_array($option)) { - $options[$id] = $this->reduceValueOptions($option); -+ if (empty($options[$id])) { -+ unset($options[$id]); -+ } - continue; - } - elseif (is_object($option) && !$option instanceof MarkupInterface) { diff --git a/patches/d10/3313343-3.diff b/patches/d10/3313343-3.diff deleted file mode 100644 index 0b9408581eaab8f60ad187b39bda90b84e23baf8..0000000000000000000000000000000000000000 --- a/patches/d10/3313343-3.diff +++ /dev/null @@ -1,100 +0,0 @@ -diff --git a/config/install/linkchecker.settings.yml b/config/install/linkchecker.settings.yml -index f3311437c5beb3072a353b5047d769850442f95b..649a0131a1681b1ac2c01fda257dd5b34b61b5f5 100644 ---- a/config/install/linkchecker.settings.yml -+++ b/config/install/linkchecker.settings.yml -@@ -26,6 +26,7 @@ check: - library: core - interval: 2419200 - useragent: 'Drupal (+http://drupal.org/)' -+ disable_cron: false - error: - action_status_code_301: 0 - action_status_code_404: 0 -diff --git a/config/schema/linkchecker.schema.yml b/config/schema/linkchecker.schema.yml -index ef877deae66f38e145048b9d4a14104479107e56..357ec2eeed64da1dab200f64199fc8a6e8a7f0ad 100644 ---- a/config/schema/linkchecker.schema.yml -+++ b/config/schema/linkchecker.schema.yml -@@ -72,6 +72,9 @@ linkchecker.settings: - useragent: - type: string - label: 'User-Agent' -+ disable_cron: -+ type: boolean -+ label: 'Disable cron processing and queueing' - error: - type: mapping - label: 'Error handling' -diff --git a/linkchecker.module b/linkchecker.module -index 7b305e40e472338cd2b6a8ee2a354843d4c149dc..e2b7b88da60cb4809298342cec0fdc0716673de7 100644 ---- a/linkchecker.module -+++ b/linkchecker.module -@@ -148,6 +148,9 @@ function linkchecker_form_field_config_form_builder($entity_type, FieldConfigInt - * Implements hook_cron(). - */ - function linkchecker_cron() { -+ if (\Drupal::config('linkchecker.settings')->get('check.disable_cron')) { -+ return; -+ } - \Drupal::service('linkchecker.extractor_batch')->processEntities(); - \Drupal::service('linkchecker.checker')->queueLinks(); - } -diff --git a/src/Form/LinkCheckerAdminSettingsForm.php b/src/Form/LinkCheckerAdminSettingsForm.php -index f17e6667a482b57ddfaba34ec9d87cb51aec6d95..247bd82ad256719c62495750d1af8e5aba5d7d2d 100644 ---- a/src/Form/LinkCheckerAdminSettingsForm.php -+++ b/src/Form/LinkCheckerAdminSettingsForm.php -@@ -74,6 +74,11 @@ class LinkCheckerAdminSettingsForm extends ConfigFormBase { - */ - protected $linkCheckerResponseCodes; - -+ /** -+ * The module handler. -+ */ -+ protected ModuleHandlerInterface $moduleHandler; -+ - /** - * The current user. - * -@@ -357,6 +362,12 @@ class LinkCheckerAdminSettingsForm extends ConfigFormBase { - RfcLogLevel::ERROR => $this->t('Errors only'), - ], - ]; -+ $form['check']['linkchecker_check_disable_cron'] = [ -+ '#default_value' => $config->get('check.disable_cron'), -+ '#type' => 'checkbox', -+ '#title' => $this->t('Disable cron processing and queueing'), -+ '#description' => $this->t('Check this box if you will be queueing and processing links via Drush or some other method. <strong>Enabling this will completely disable automatic link queueing and processing.</strong>'), -+ ]; - - $form['error'] = [ - '#type' => 'details', -@@ -364,11 +375,12 @@ class LinkCheckerAdminSettingsForm extends ConfigFormBase { - '#description' => $this->t('Defines error handling and custom actions to be executed if specific HTTP requests are failing.'), - '#open' => TRUE, - ]; -+ /** @var \Drupal\user\UserInterface|null $linkchecker_default_impersonate_account */ - $linkchecker_default_impersonate_account = $this->userStorage->load(1); - $form['error']['linkchecker_impersonate_account'] = [ - '#type' => 'textfield', - '#title' => $this->t('Impersonate user account'), -- '#description' => $this->t('If below error handling actions are executed they can be impersonated with a custom user account. By default this is user %name, but you are able to assign a custom user to allow easier identification of these automatic revision updates. Make sure you select a user with <em>full</em> permissions on your site or the user may not able to access and save all content.', ['%name' => $linkchecker_default_impersonate_account->getAccountName()]), -+ '#description' => $this->t('If below error handling actions are executed they can be impersonated with a custom user account. By default this is user %name, but you are able to assign a custom user to allow easier identification of these automatic revision updates. Make sure you select a user with <em>full</em> permissions on your site or the user may not able to access and save all content.', ['%name' => $linkchecker_default_impersonate_account?->getAccountName() ?? '[UID 1 not found]']), - '#size' => 30, - '#maxlength' => 60, - '#autocomplete_path' => 'user/autocomplete', -@@ -388,7 +400,7 @@ class LinkCheckerAdminSettingsForm extends ConfigFormBase { - ], - ]; - if ($this->moduleHandler->moduleExists('dblog') && $this->currentUser->hasPermission('access site reports')) { -- $form['error']['#description'] = $this->t('If enabled, outdated links in content providing a status -+ $form['error']['#description'] = $this->t('If enabled, outdated links in content providing a status - <em>Moved Permanently</em> (status code 301) are automatically updated to the most recent URL. If used, - it is recommended to use a value of <em>three</em> to make sure this is not only a temporarily change. - This feature trust sites to provide a valid permanent redirect. -@@ -506,6 +518,7 @@ class LinkCheckerAdminSettingsForm extends ConfigFormBase { - ->set('check.library', $form_state->getValue('linkchecker_check_library')) - ->set('check.interval', $form_state->getValue('linkchecker_check_interval')) - ->set('check.useragent', $form_state->getValue('linkchecker_check_useragent')) -+ ->set('check.disable_cron', $form_state->getValue('linkchecker_check_disable_cron')) - ->set('search_published_contents_only', $form_state->getValue('search_published_contents_only')) - ->set('error.action_status_code_301', $form_state->getValue('linkchecker_action_status_code_301')) - ->set('error.action_status_code_404', $form_state->getValue('linkchecker_action_status_code_404')) diff --git a/patches/d10/3315413.diff b/patches/d10/3315413.diff deleted file mode 100644 index 58309b20c8feb669eb0884edf8e9452f97ac3f25..0000000000000000000000000000000000000000 --- a/patches/d10/3315413.diff +++ /dev/null @@ -1,607 +0,0 @@ -diff --git a/README.md b/README.md -index e61293de4dd3a9ee40e1ee5c45e6c7f690a92be8..0c4eb6e21cbe65176a5863910e5bef93d69338a9 100755 ---- a/README.md -+++ b/README.md -@@ -1,7 +1,7 @@ - # CAPTCHA: Friendly Captcha - - The Friendly Captcha module uses the Friendly Captcha web service. --For more information about Friendly Captcha is, please visit: -+For more information about Friendly Captcha, please visit: - https://friendlycaptcha.com/ - - For a full description of the module, visit the -@@ -38,9 +38,11 @@ This module requires the following modules: - 2. You'll now find the configuration tab in the CAPTCHA - administration page available at: - `admin/config/people/captcha/friendlycaptcha` --3. Register your web site at -+3. Select the "API endpoint". For the option "This Drupal site - limited to captches -+ only.", you can skip step 4 and 5 and jump to step 6. -+4. Register your web site at - `https://app.friendlycaptcha.com/account` --4. Input the sitekey and API Key into the Friendly Captcha settings. --5. Visit the Captcha administration page and set where you -+5. Input the sitekey and API Key into the Friendly Captcha settings. -+6. Visit the Captcha administration page and set where you - want the Friendly Captcha form to be presented: - `admin/config/people/captcha` -diff --git a/friendlycaptcha.module b/friendlycaptcha.module -index d793d07e4485113fba9aa3ff58486db04f58cdb7..e2b60577acfca3b21eba921a10aabd80f47e7fd1 100644 ---- a/friendlycaptcha.module -+++ b/friendlycaptcha.module -@@ -75,6 +75,9 @@ function friendlycaptcha_captcha($op, $captcha_type = '') { - elseif ($friendlycaptcha_api_endpoint === 'eu_fallback') { - $friendlycaptcha_puzzle_endpoint = 'https://eu-api.friendlycaptcha.eu/api/v1/puzzle,https://api.friendlycaptcha.com/api/v1/puzzle'; - } -+ elseif ($friendlycaptcha_api_endpoint === 'local') { -+ $friendlycaptcha_puzzle_endpoint = Url::fromRoute('friendlycaptcha.api_puzzle', [], ['absolute' => TRUE])->toString(); -+ } - - if (!empty($friendlycaptcha_site_key) && !empty($friendlycaptcha_api_key)) { - // Build the friendlycaptcha captcha form if site_key and api_key are -@@ -134,20 +137,25 @@ function friendlycaptcha_captcha_validation($solution, $response, $element, $for - return FALSE; - } - -- $options = [ -- 'json' => [ -- 'solution' => $_POST['frc-captcha-solution'], -- 'secret' => $config->get('api_key'), -- 'sitekey' => $config->get('site_key'), -- ], -- // Stop firing exception on response status code >= 300. -- // See http://docs.guzzlephp.org/en/stable/handlers-and-middleware.html -- 'http_errors' => FALSE, -- ]; -- -- $response = \Drupal::httpClient()->post('https://api.friendlycaptcha.com/api/v1/siteverify', $options); -- $responseContent = $response->getBody()->getContents(); -- $json = json_decode($responseContent, TRUE); -+ if ($config->get('api_endpoint') === 'local') { -+ $json = \Drupal::service('friendlycaptcha.siteverify')->verify($_POST['frc-captcha-solution']); -+ } -+ else { -+ $options = [ -+ 'json' => [ -+ 'solution' => $_POST['frc-captcha-solution'], -+ 'secret' => $config->get('api_key'), -+ 'sitekey' => $config->get('site_key'), -+ ], -+ // Stop firing exception on response status code >= 300. -+ // See http://docs.guzzlephp.org/en/stable/handlers-and-middleware.html -+ 'http_errors' => FALSE, -+ ]; -+ -+ $response = \Drupal::httpClient()->post('https://api.friendlycaptcha.com/api/v1/siteverify', $options); -+ $responseContent = $response->getBody()->getContents(); -+ $json = json_decode($responseContent, TRUE); -+ } - - if (isset($json['success']) && $json['success'] == TRUE) { - return TRUE; -diff --git a/friendlycaptcha.routing.yml b/friendlycaptcha.routing.yml -index 62a0743828aa8c6bb8d1ea189966b3a567afbdcd..03003016093e1ad1d1c5d03cb2d99f7348a65d16 100644 ---- a/friendlycaptcha.routing.yml -+++ b/friendlycaptcha.routing.yml -@@ -5,3 +5,11 @@ friendlycaptcha.admin_settings_form: - _title: 'Friendly Captcha' - requirements: - _permission: 'administer CAPTCHA settings' -+ -+friendlycaptcha.api_puzzle: -+ path: '/api/v1/puzzle' -+ defaults: -+ _controller: '\Drupal\friendlycaptcha\Controller\Puzzle::execute' -+ # @todo Implement access control according to Polite::cors() -+ requirements: -+ _permission: 'access content' -diff --git a/friendlycaptcha.services.yml b/friendlycaptcha.services.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..e1591df93d0eb2f215dcc27804abd51d31174bea ---- /dev/null -+++ b/friendlycaptcha.services.yml -@@ -0,0 +1,5 @@ -+services: -+ -+ friendlycaptcha.siteverify: -+ class: Drupal\friendlycaptcha\SiteVerify -+ arguments: ['@keyvalue.expirable', '@logger.channel.php', '@config.factory'] -diff --git a/src/Controller/Puzzle.php b/src/Controller/Puzzle.php -new file mode 100644 -index 0000000000000000000000000000000000000000..5f62c145ee2dc0aadd22d2c1e93594c8a56e6e3d ---- /dev/null -+++ b/src/Controller/Puzzle.php -@@ -0,0 +1,135 @@ -+<?php -+ -+namespace Drupal\friendlycaptcha\Controller; -+ -+use Drupal\Core\Config\ConfigFactoryInterface; -+use Drupal\Core\Config\ImmutableConfig; -+use Drupal\Core\Controller\ControllerBase; -+use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; -+use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; -+use Drupal\Core\Logger\LoggerChannel; -+use Drupal\friendlycaptcha\SiteVerify; -+use Symfony\Component\DependencyInjection\ContainerInterface; -+use Symfony\Component\HttpFoundation\JsonResponse; -+use Symfony\Component\HttpFoundation\Request; -+use Symfony\Component\HttpFoundation\RequestStack; -+ -+/** -+ * Controller providing the puzzles for the user in the browser. -+ */ -+class Puzzle extends ControllerBase { -+ -+ /** -+ * The request service. -+ * -+ * @var \Symfony\Component\HttpFoundation\Request -+ */ -+ protected Request $request; -+ -+ /** -+ * The expirable key value store. -+ * -+ * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface -+ */ -+ protected KeyValueStoreExpirableInterface $keyValueStore; -+ -+ /** -+ * The logger service. -+ * -+ * @var \Drupal\Core\Logger\LoggerChannel -+ */ -+ protected LoggerChannel $logger; -+ -+ /** -+ * The config for this module. -+ * -+ * @var \Drupal\Core\Config\ImmutableConfig -+ */ -+ protected ImmutableConfig $config; -+ -+ /** -+ * Puzzle constructor. -+ * -+ * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack -+ * The request service. -+ * @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $keyValueFactory -+ * The key value factory. -+ * @param \Drupal\Core\Logger\LoggerChannel $logger -+ * The logger service. -+ * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory -+ * The config factory. -+ */ -+ public function __construct(RequestStack $requestStack, KeyValueExpirableFactoryInterface $keyValueFactory, LoggerChannel $logger, ConfigFactoryInterface $configFactory) { -+ $this->request = $requestStack->getCurrentRequest(); -+ $this->keyValueStore = $keyValueFactory->get('friendlycaptcha'); -+ $this->logger = $logger; -+ $this->config = $configFactory->get('friendlycaptcha.settings'); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public static function create(ContainerInterface $container): Puzzle { -+ return new static( -+ $container->get('request_stack'), -+ $container->get('keyvalue.expirable'), -+ $container->get('logger.channel.php'), -+ $container->get('config.factory') -+ ); -+ } -+ -+ /** -+ * Executes the controller request. -+ * -+ * @return \Symfony\Component\HttpFoundation\JsonResponse -+ * The controller response. -+ * -+ * @throws \Exception -+ */ -+ public function execute(): JsonResponse { -+ $accountId = 1; -+ $appId = 1; -+ $puzzleExpiry = SiteVerify::EXPIRY_TIMES_5_MINUTES; -+ $anonymizedIp = SiteVerify::anonymizeIp($this->request->getClientIp()); -+ $requestTimes = $this->keyValueStore->get($anonymizedIp, 0) + 1; -+ $this->keyValueStore->setWithExpire($anonymizedIp, $requestTimes, SiteVerify::SCALING_TTL_SECOUNDS); -+ $this->logger->info('This is request %count from IP %ip in the last 30 minutes (or longer, if there were subsequent requests)', [ -+ '%count' => $requestTimes, -+ '%ip' => $anonymizedIp, -+ ]); -+ foreach (array_reverse(SiteVerify::SCALING, TRUE) as $threshold => $scale) { -+ if ($requestTimes > $threshold) { -+ $numberOfSolutions = $scale['solutions']; -+ $puzzleDifficulty = $scale['difficulty']; -+ break; -+ } -+ } -+ if (!isset($numberOfSolutions, $puzzleDifficulty)) { -+ return new JsonResponse('Error in configuration', 503); -+ } -+ $this->logger->info('Configured with %solutions solutions of %difficulty difficulty', [ -+ '%solutions' => $numberOfSolutions, -+ '%difficulty' => $puzzleDifficulty, -+ ]); -+ $nonce = random_bytes(8); -+ $timeHex = dechex(time()); -+ $accountIdHex = SiteVerify::padHex(dechex($accountId), 4); -+ $appIdHex = SiteVerify::padHex(dechex($appId), 4); -+ $puzzleVersionHex = SiteVerify::padHex(dechex($appId), 1); -+ $puzzleExpiryHex = SiteVerify::padHex(dechex($puzzleExpiry), 1); -+ $numberOfSolutionsHex = SiteVerify::padHex(dechex($numberOfSolutions), 1); -+ $puzzleDifficultyHex = SiteVerify::padHex(dechex($puzzleDifficulty), 1); -+ $reservedHex = SiteVerify::padHex('', 8); -+ $puzzleNonceHex = SiteVerify::padHex(bin2hex($nonce), 8); -+ $bufferHex = SiteVerify::padHex($timeHex, 4) . $accountIdHex . $appIdHex . $puzzleVersionHex . $puzzleExpiryHex . $numberOfSolutionsHex . $puzzleDifficultyHex . $reservedHex . $puzzleNonceHex; -+ $buffer = hex2bin($bufferHex); -+ $hash = SiteVerify::signBuffer($buffer, $this->config->get('site_key')); -+ $puzzle = $hash . '.' . base64_encode($buffer); -+ return new JsonResponse([ -+ 'data' => [ -+ 'puzzle' => $puzzle, -+ ], -+ ]); -+ } -+ -+} -diff --git a/src/Form/FriendlyCaptchaAdminSettingsForm.php b/src/Form/FriendlyCaptchaAdminSettingsForm.php -index d36f66dd6e26c75d7bbf801bd531bbc0242558a6..17519182e66af17ceae4c71fa9bf9f63a92af628 100644 ---- a/src/Form/FriendlyCaptchaAdminSettingsForm.php -+++ b/src/Form/FriendlyCaptchaAdminSettingsForm.php -@@ -2,6 +2,7 @@ - - namespace Drupal\friendlycaptcha\Form; - -+use Drupal\Component\Utility\Random; - use Drupal\Core\Asset\LibrariesDirectoryFileFinder; - use Drupal\Core\Form\ConfigFormBase; - use Drupal\Core\Form\FormStateInterface; -@@ -55,6 +56,11 @@ class FriendlyCaptchaAdminSettingsForm extends ConfigFormBase { - public function buildForm(array $form, FormStateInterface $form_state) { - $config = $this->config('friendlycaptcha.settings'); - -+ $localEndpoint = $config->get('api_endpoint') === 'local'; -+ if ($userInput = $form_state->getUserInput()) { -+ $localEndpoint = $userInput['friendlycaptcha_api_endpoint'] === 'local'; -+ } -+ - $form['general'] = [ - '#type' => 'details', - '#title' => $this->t('General settings'), -@@ -65,18 +71,34 @@ class FriendlyCaptchaAdminSettingsForm extends ConfigFormBase { - '#default_value' => $config->get('site_key'), - '#description' => $this->t('The site key given to you when you <a href=":url">register for Friendly Captcha</a>.', [':url' => 'https://app.friendlycaptcha.com/account']), - '#maxlength' => 64, -- '#required' => TRUE, -+ '#required' => !$localEndpoint, - '#title' => $this->t('Site key'), - '#type' => 'textfield', -+ '#states' => [ -+ 'invisible' => [ -+ ':input[name="friendlycaptcha_api_endpoint"]' => ['value' => 'local'], -+ ], -+ 'optional' => [ -+ ':input[name="friendlycaptcha_api_endpoint"]' => ['value' => 'local'], -+ ], -+ ], - ]; - - $form['general']['friendlycaptcha_api_key'] = [ - '#default_value' => $config->get('api_key'), - '#description' => $this->t('The API key given to you when you <a href=":url">register for Friendly Captcha</a>.', [':url' => 'https://www.google.com/friendlycaptcha/admin']), - '#maxlength' => 128, -- '#required' => TRUE, -+ '#required' => !$localEndpoint, - '#title' => $this->t('API key'), - '#type' => 'textfield', -+ '#states' => [ -+ 'invisible' => [ -+ ':input[name="friendlycaptcha_api_endpoint"]' => ['value' => 'local'], -+ ], -+ 'optional' => [ -+ ':input[name="friendlycaptcha_api_endpoint"]' => ['value' => 'local'], -+ ], -+ ], - ]; - - $form['general']['friendlycaptcha_api_endpoint'] = [ -@@ -85,6 +107,7 @@ class FriendlyCaptchaAdminSettingsForm extends ConfigFormBase { - 'global' => $this->t('Global API-Endpoint'), - 'eu' => $this->t('EU API-Endpoint'), - 'eu_fallback' => $this->t('EU API-Endpoint with fallback to Global APO-Endpoint if not available'), -+ 'local' => $this->t('This Drupal site - limited to captches only.'), - ], - '#description' => $this->t('Select the Friendly Captcha API endpoint for your site. Both, the EU API-Endpoint (and "with fallback to Global API-Endpoint if not available") require a Friendly Captcha Business or Enterprise plan. <a href=":url">See docs for further information.</a>', [':url' => 'https://docs.friendlycaptcha.com/#/eu_endpoint']), - '#required' => TRUE, -@@ -99,6 +122,19 @@ class FriendlyCaptchaAdminSettingsForm extends ConfigFormBase { - return parent::buildForm($form, $form_state); - } - -+ /** -+ * {@inheritdoc} -+ */ -+ public function validateForm(array &$form, FormStateInterface $form_state) { -+ if ($form_state->getValue('friendlycaptcha_api_endpoint') === 'local' && $form_state->getValue('friendlycaptcha_site_key') === '') { -+ // Let's initially create random keys. -+ $random = new Random(); -+ $form_state->setValue('friendlycaptcha_site_key', $random->string(32)); -+ $form_state->setValue('friendlycaptcha_api_key', $random->string(32)); -+ } -+ parent::validateForm($form, $form_state); -+ } -+ - /** - * {@inheritdoc} - */ -diff --git a/src/SiteVerify.php b/src/SiteVerify.php -new file mode 100644 -index 0000000000000000000000000000000000000000..a773d6434aa999509693841fb5bba01f465fa7ba ---- /dev/null -+++ b/src/SiteVerify.php -@@ -0,0 +1,262 @@ -+<?php -+ -+namespace Drupal\friendlycaptcha; -+ -+use Drupal\Core\Config\ConfigFactoryInterface; -+use Drupal\Core\Config\ImmutableConfig; -+use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; -+use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; -+use Drupal\Core\Logger\LoggerChannel; -+ -+/** -+ * Service class for the local endpoint. -+ */ -+class SiteVerify { -+ -+ public const SCALING_TTL_SECOUNDS = 30 * 60; -+ public const SCALING = [ -+ 0 => ['solutions' => 51, 'difficulty' => 122], -+ 4 => ['solutions' => 51, 'difficulty' => 130], -+ 10 => ['solutions' => 45, 'difficulty' => 141], -+ 20 => ['solutions' => 45, 'difficulty' => 149], -+ ]; -+ public const EXPIRY_TIMES_5_MINUTES = 12; -+ -+ /** -+ * TBD. -+ * -+ * @param string $hexValue -+ * TBD. -+ * @param int $bytes -+ * TBD. -+ * @param int $where -+ * TBD. -+ * -+ * @return string -+ * TBD. -+ */ -+ public static function padHex(string $hexValue, int $bytes, int $where = STR_PAD_LEFT): string { -+ return str_pad($hexValue, $bytes * 2, '0', $where); -+ } -+ -+ /** -+ * TBD. -+ * -+ * @param string $string -+ * TBD. -+ * @param int $offset -+ * TBD. -+ * @param int $count -+ * TBD. -+ * -+ * @return false|string -+ * TBD. -+ */ -+ public static function extractHexBytes(string $string, int $offset, int $count) { -+ return substr($string, $offset * 2, $count * 2); -+ } -+ -+ /** -+ * TBD. -+ * -+ * @param string $hexValue -+ * TBD. -+ * -+ * @return int -+ * TBD. -+ */ -+ public static function littleEndianHexToDec(string $hexValue): int { -+ $bigEndianHex = implode('', array_reverse(str_split($hexValue, 2))); -+ return hexdec($bigEndianHex); -+ } -+ -+ /** -+ * TBD. -+ * -+ * @param string $ip -+ * TBD. -+ * -+ * @return string -+ * TBD. -+ */ -+ public static function anonymizeIp(string $ip): string { -+ return preg_replace( -+ ['/\.\d*$/', '/[\da-f]*:[\da-f]*$/'], -+ ['.XXX', 'XXXX:XXXX'], -+ $ip -+ ); -+ } -+ -+ /** -+ * TBD. -+ * -+ * @param string $buffer -+ * TBD. -+ * @param string $key -+ * TBD. -+ * -+ * @return string -+ * TBD. -+ */ -+ public static function signBuffer(string $buffer, string $key): string { -+ return hash_hmac('sha256', $buffer, $key); -+ } -+ -+ /** -+ * The expirable key value store. -+ * -+ * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface -+ */ -+ protected KeyValueStoreExpirableInterface $keyValueStore; -+ -+ /** -+ * The logger service. -+ * -+ * @var \Drupal\Core\Logger\LoggerChannel -+ */ -+ protected LoggerChannel $logger; -+ -+ /** -+ * The config for this module. -+ * -+ * @var \Drupal\Core\Config\ImmutableConfig -+ */ -+ protected ImmutableConfig $config; -+ -+ /** -+ * SiteVerify service constructor. -+ * -+ * @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $keyValueFactory -+ * The expirable key value factory. -+ * @param \Drupal\Core\Logger\LoggerChannel $logger -+ * The logger service. -+ * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory -+ * The config factory. -+ */ -+ public function __construct(KeyValueExpirableFactoryInterface $keyValueFactory, LoggerChannel $logger, ConfigFactoryInterface $configFactory) { -+ $this->keyValueStore = $keyValueFactory->get('friendlycaptcha'); -+ $this->logger = $logger; -+ $this->config = $configFactory->get('friendlycaptcha.settings'); -+ } -+ -+ /** -+ * Verifies the given solution. -+ * -+ * @param string $solution -+ * TBD. -+ * -+ * @return array -+ * TBD. -+ * -+ * @throws \JsonException -+ * @throws \SodiumException -+ */ -+ public function verify(string $solution): array { -+ if (empty($solution)) { -+ return self::returnErrorEmptySolution(); -+ } -+ [$signature, $puzzle, $solutions] = explode('.', $solution); -+ $puzzleBin = base64_decode($puzzle); -+ $puzzleHex = bin2hex($puzzleBin); -+ if (($calculated = self::signBuffer($puzzleBin, $this->config->get('site_key'))) !== $signature) { -+ $this->logger->critical('Signature mismatch. Calculated "%calculated", given "%given"', [ -+ '%calculated' => $calculated, -+ '%given' => $signature, -+ ]); -+ return self::returnSolutionInvalid(); -+ } -+ // Only need to store as long as valid, after that the timeout will kick in. -+ if (!$this->keyValueStore->setWithExpireIfNotExists($puzzleHex, TRUE, self::EXPIRY_TIMES_5_MINUTES * 300)) { -+ $this->logger->critical('Puzzle "%puzzle" was already successfully used before', ['%puzzle' => $puzzleHex]); -+ return self::returnSolutionTimeoutOrDuplicate(); -+ } -+ -+ $numberOfSolutions = hexdec(self::extractHexBytes($puzzleHex, 14, 1)); -+ $timeStamp = hexdec(self::extractHexBytes($puzzleHex, 0, 4)); -+ $expiry = hexdec(self::extractHexBytes($puzzleHex, 13, 1)); -+ $expiryInSeconds = $expiry * 300; -+ $solutionsHex = bin2hex(base64_decode($solutions)); -+ $age = time() - $timeStamp; -+ if (($expiry !== 0) && $age > $expiryInSeconds) { -+ $this->logger->critical('Puzzle is too old (%age seconds, allowed: %expiry', [ -+ '%age' => $age, -+ '%expiry' => $expiry, -+ ]); -+ return self::returnSolutionTimeoutOrDuplicate(); -+ } -+ -+ $d = hexdec(self::extractHexBytes($puzzleHex, 15, 1)); -+ $t = floor(2 ** ((255.999 - $d) / 8.0)); -+ $solutionSeenInThisRequest = []; -+ for ($solutionIndex = 0; $solutionIndex < $numberOfSolutions; $solutionIndex++) { -+ $currentSolution = self::extractHexBytes($solutionsHex, $solutionIndex * 8, 8); -+ -+ if (isset($solutionSeenInThisRequest[$currentSolution])) { -+ $this->logger->critical('Solution seen in this request before'); -+ return self::returnSolutionInvalid(); -+ } -+ $solutionSeenInThisRequest[$currentSolution] = TRUE; -+ $fullSolution = self::padHex($puzzleHex, 120, STR_PAD_RIGHT) . $currentSolution; -+ $blake2b256hash = bin2hex(sodium_crypto_generichash(hex2bin($fullSolution))); -+ $first4Bytes = self::extractHexBytes($blake2b256hash, 0, 4); -+ $first4Int = self::littleEndianHexToDec($first4Bytes); -+ if ($first4Int >= $t) { -+ $this->logger->critical($currentSolution . ' (index: ' . $solutionIndex . ') is invalid (' . $first4Int . ' >= ' . $t . ')'); -+ return self::returnSolutionInvalid(); -+ } -+ } -+ return self::returnResponse(TRUE); -+ } -+ -+ /** -+ * TBD. -+ * -+ * @return array -+ * TBD. -+ */ -+ private static function returnSolutionInvalid(): array { -+ return self::returnResponse(FALSE, 'solution_invalid'); -+ } -+ -+ /** -+ * TBD. -+ * -+ * @return array -+ * TBD. -+ */ -+ private static function returnSolutionTimeoutOrDuplicate(): array { -+ return self::returnResponse(FALSE, 'solution_timeout_or_duplicate'); -+ } -+ -+ /** -+ * TBD. -+ * -+ * @return array -+ * TBD. -+ */ -+ private static function returnErrorEmptySolution(): array { -+ return self::returnResponse(FALSE, 'solution_missing'); -+ } -+ -+ /** -+ * TBD. -+ * -+ * @param bool $success -+ * TBD. -+ * @param string|null $error -+ * TBD. -+ * -+ * @return array -+ * TBD. -+ */ -+ private static function returnResponse(bool $success, string $error = NULL): array { -+ $result = [ -+ 'success' => $success, -+ ]; -+ if ($error !== NULL) { -+ $result['error'] = $error; -+ } -+ return $result; -+ } -+ -+} diff --git a/patches/d10/3319445.diff b/patches/d10/3319445.diff deleted file mode 100644 index c97724d8a489d36d684491b73aed4ed18f89b0be..0000000000000000000000000000000000000000 --- a/patches/d10/3319445.diff +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/gin_toolbar.module b/gin_toolbar.module -index f30970a7d3b6a88c0b45e4c7343da3356f293b82..77e96c4c118f75965ea1968826e4c0feda79157d 100644 ---- a/gin_toolbar.module -+++ b/gin_toolbar.module -@@ -8,6 +8,7 @@ - use Drupal\Component\Utility\Html; - use Drupal\Core\Entity\EntityInterface; - use Drupal\Core\Routing\RouteMatchInterface; -+use Drupal\Core\Url; - use Drupal\gin\GinNavigation; - use Drupal\gin\GinSettings; - use Drupal\gin\GinUserPicture; -@@ -256,8 +257,13 @@ function gin_toolbar_preprocess_toolbar(&$variables) { - $entity = \Drupal::request()->attributes->get($matches['entity_type_id']); - - if ($entity instanceof EntityInterface && $entity->hasLinkTemplate('edit-form')) { -- $variables['entity_title'] = $entity->label(); -- $variables['entity_edit_url'] = $entity->toUrl('edit-form'); -+ if ($entity->access('update')) { -+ $variables['entity_title'] = $entity->label(); -+ $variables['entity_edit_url'] = $entity->toUrl('edit-form'); -+ } -+ elseif (Url::fromRoute('system.admin_content')->access(\Drupal::currentUser())) { -+ $variables['access_admin_content'] = TRUE; -+ } - } - } - } diff --git a/patches/d10/3327582.diff b/patches/d10/3327582.diff deleted file mode 100644 index 0ed22fbd9c1696255f263b721a631275999d2331..0000000000000000000000000000000000000000 --- a/patches/d10/3327582.diff +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/src/Plugin/migrate/source/EntityImportSourceCSV.php b/src/Plugin/migrate/source/EntityImportSourceCSV.php -index 5430bd64c798e1c737f9d781883b1c5acae299d7..701bb3b8b65e15d6b695743329f706ab312602b1 100644 ---- a/src/Plugin/migrate/source/EntityImportSourceCSV.php -+++ b/src/Plugin/migrate/source/EntityImportSourceCSV.php -@@ -26,6 +26,9 @@ class EntityImportSourceCSV extends EntityImportSourceLimitIteratorBase implemen - */ - protected $fileIterator; - -+ protected const DELIMITER = ','; -+ protected const FILEEXTENSION = 'csv'; -+ - /** - * {@inheritdoc} - */ -@@ -91,7 +94,7 @@ class EntityImportSourceCSV extends EntityImportSourceLimitIteratorBase implemen - '#type' => 'managed_file', - '#title' => $this->t('Upload File'), - '#upload_validators' => [ -- 'file_validate_extensions' => ['csv'], -+ 'file_validate_extensions' => [$this::FILEEXTENSION], - ], - '#required' => $this->isRequired(), - '#multiple' => $this->hasMultiple(), -@@ -308,6 +311,7 @@ class EntityImportSourceCSV extends EntityImportSourceLimitIteratorBase implemen - /** @var \League\Csv\Reader $csv */ - $csv = Reader::createFromPath($file->getFileUri()); - $csv->setHeaderOffset(0); -+ $csv->setDelimiter($this::DELIMITER); - - if ($this->hasCsvEncoding()) { - $filter = "{$this->getCsvFromEncoding()}/{$this->getCsvToEncoding()}"; diff --git a/patches/d10/3327593.diff b/patches/d10/3327593.diff deleted file mode 100644 index aa5123eda5059c353d47e9a7e482ba147f97f9b0..0000000000000000000000000000000000000000 --- a/patches/d10/3327593.diff +++ /dev/null @@ -1,68 +0,0 @@ -diff --git a/modules/entity_import_plus/src/Plugin/migrate/process/EntityImportPlusEntityLookup.php b/modules/entity_import_plus/src/Plugin/migrate/process/EntityImportPlusEntityLookup.php -index c73b459d7122dc5947dfeeec655b0c9fc654e742..017c530b6b875fdaaa7549288c62c5a7019470bc 100644 ---- a/modules/entity_import_plus/src/Plugin/migrate/process/EntityImportPlusEntityLookup.php -+++ b/modules/entity_import_plus/src/Plugin/migrate/process/EntityImportPlusEntityLookup.php -@@ -12,6 +12,7 @@ use Drupal\Core\Form\FormStateInterface; - use Drupal\entity_import\Plugin\migrate\process\EntityImportProcessInterface; - use Drupal\entity_import\Plugin\migrate\process\EntityImportProcessTrait; - use Drupal\field\FieldStorageConfigInterface; -+use Drupal\migrate\MigrateException; - use Drupal\migrate\Plugin\MigrationInterface; - use Drupal\migrate_plus\Plugin\migrate\process\EntityLookup; - use Symfony\Component\DependencyInjection\ContainerInterface; -@@ -88,6 +89,7 @@ class EntityImportPlusEntityLookup extends EntityLookup implements EntityImportP - 'bundle_key' => NULL, - 'entity_type' => NULL, - 'ignore_case' => FALSE, -+ 'operator' => 'IN', - ]; - } - -@@ -155,12 +157,26 @@ class EntityImportPlusEntityLookup extends EntityLookup implements EntityImportP - '#description' => $this->t('If checked then value casing is irrelevant.'), - '#default_value' => $this->getFormStateValue('ignore_case', $form_state, FALSE), - ]; -+ $form['operator'] = [ -+ '#type' => 'select', -+ '#title' => $this->t('Operator'), -+ '#options' => [ -+ 'IN' => $this->t('Match'), -+ 'STARTS_WITH' => $this->t('Starts with'), -+ 'ENDS_WITH' => $this->t('Ends with'), -+ 'CONTAINS' => $this->t('Contains'), -+ ], -+ '#required' => TRUE, -+ '#default_value' => $this->getFormStateValue('operator', $form_state, 'IN'), -+ ]; - - return $form; - } - - /** - * {@inheritdoc} -+ * -+ * @throws \Drupal\migrate\MigrateException - */ - protected function query($value) { - // Entity queries typically are case-insensitive. Therefore, we need to -@@ -170,10 +186,19 @@ class EntityImportPlusEntityLookup extends EntityLookup implements EntityImportP - $ignoreCase = !empty($this->configuration['ignore_case']) ?: FALSE; - - $multiple = is_array($value); -+ $operator = $this->configuration['operator']; -+ if ($multiple) { -+ if ($operator !== 'IN') { -+ throw new MigrateException('This chosen operator does not work with multiple values.'); -+ } -+ } -+ else if ($operator === 'IN') { -+ $operator = NULL; -+ } - - $query = $this->entityTypeManager->getStorage($this->lookupEntityType) - ->getQuery() -- ->condition($this->lookupValueKey, $value, $multiple ? 'IN' : NULL); -+ ->condition($this->lookupValueKey, $value, $operator); - // Sqlite and possibly others returns data in a non-deterministic order. - // Make it deterministic. - if ($multiple) { diff --git a/patches/d10/3344199.diff b/patches/d10/3344199.diff deleted file mode 100644 index d666e1644de7604fbd8dc54a69be115ad47f1a47..0000000000000000000000000000000000000000 --- a/patches/d10/3344199.diff +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/js/collapsible.js b/js/collapsible.js -index 1d1ee04943d8277ac9bf365d87fab8e4f507b69d..f91c176ef7dc821f3cb836971c19649b391e6e81 100644 ---- a/js/collapsible.js -+++ b/js/collapsible.js -@@ -33,7 +33,9 @@ - }); - - } -- $('.views_tree_link a', context).on('click', function (e) { -+ $('.views_tree_link a:not(.view-tree-processed)', context) -+ .addClass('view-tree-processed') -+ .on('click', function (e) { - e.preventDefault(); - - if ($(this).parent().hasClass('views_tree_link_expanded')) { diff --git a/patches/d10/3346430.diff b/patches/d10/3346430.diff deleted file mode 100644 index 8a4beb279a99cec65eb0f55225316bf860896eb2..0000000000000000000000000000000000000000 --- a/patches/d10/3346430.diff +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Entity/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php -index 85d7ef4359a0d6b1682514cffef0067c3ec03015..7f18cbd9ee18737009d628d2d56f67f84e2ee73b 100644 ---- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php -+++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php -@@ -519,6 +519,11 @@ protected function doPreSave(EntityInterface $entity) { - throw new EntityStorageException("'{$this->entityTypeId}' entity with ID '$id' already exists."); - } - -+ // Use the loaded revision instead of default if this is not the default revision. -+ if ($id_exists && !isset($entity->original) && $entity instanceof RevisionableInterface && !$entity->isDefaultRevision()) { -+ $entity->original = $this->loadRevision($entity->getLoadedRevisionId()); -+ } -+ - // Load the original entity, if any. - if ($id_exists && !isset($entity->original)) { - $entity->original = $this->loadUnchanged($id); diff --git a/patches/d10/3347453.diff b/patches/d10/3347453.diff deleted file mode 100644 index 992f9097c101c634073bfb99377158dacbcc3d17..0000000000000000000000000000000000000000 --- a/patches/d10/3347453.diff +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/modules/entity_share_client_vbo/entity_share_client_vbo.info.yml b/modules/entity_share_client_vbo/entity_share_client_vbo.info.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..69209cd5219ac3fe207a97da9f8a977df65a9a92 ---- /dev/null -+++ b/modules/entity_share_client_vbo/entity_share_client_vbo.info.yml -@@ -0,0 +1,8 @@ -+name: 'Entity Share Client VBO' -+type: module -+description: 'Provides plugin for Entity Share Client integration with VBO' -+core_version_requirement: ^8 || ^9 -+package: 'Web services' -+dependencies: -+ - entity_share_vbo:entity_share__vbo -+ - views_bulk_operations:views_bulk_operations -diff --git a/modules/entity_share_client/src/Plugin/Action/UpdatePolicyAction.php b/modules/entity_share_client_vbo/src/Plugin/Action/UpdatePolicyAction.php -similarity index 98% -rename from modules/entity_share_client/src/Plugin/Action/UpdatePolicyAction.php -rename to modules/entity_share_client_vbo/src/Plugin/Action/UpdatePolicyAction.php -index 25077a8dcd42c040bf5a264aecaf8f78466065d3..dac74ad77a789d0d02b6bfacba3cf1692643c97a 100644 ---- a/modules/entity_share_client/src/Plugin/Action/UpdatePolicyAction.php -+++ b/modules/entity_share_client_vbo/src/Plugin/Action/UpdatePolicyAction.php -@@ -2,7 +2,7 @@ - - declare(strict_types = 1); - --namespace Drupal\entity_share_client\Plugin\Action; -+namespace Drupal\entity_share_client_vbo\Plugin\Action; - - use Drupal\Core\Plugin\ContainerFactoryPluginInterface; - use Drupal\entity_share_client\ImportPolicy\ImportPolicyPluginManager; diff --git a/patches/d10/3352586.diff b/patches/d10/3352586.diff deleted file mode 100644 index 8f446b27712963490c8e10ddf7cb798744479a06..0000000000000000000000000000000000000000 --- a/patches/d10/3352586.diff +++ /dev/null @@ -1,121 +0,0 @@ -diff --git a/config/install/taxonomy_machine_name.settings.yml b/config/install/taxonomy_machine_name.settings.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..4593ad4f74557433d1b3a98bed866d0917c3a142 ---- /dev/null -+++ b/config/install/taxonomy_machine_name.settings.yml -@@ -0,0 +1 @@ -+hide_machine_name_in_terms_overview: 0 -diff --git a/src/Form/Settings.php b/src/Form/Settings.php -new file mode 100644 -index 0000000000000000000000000000000000000000..0476b6b19e4e8053c0fb73742c1e8e75cd531cc8 ---- /dev/null -+++ b/src/Form/Settings.php -@@ -0,0 +1,54 @@ -+<?php -+ -+namespace Drupal\taxonomy_machine_name\Form; -+ -+use Drupal\Core\Form\ConfigFormBase; -+use Drupal\Core\Form\FormStateInterface; -+ -+/** -+ * Configure Taxonomy Machine Name settings for this site. -+ */ -+final class Settings extends ConfigFormBase { -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function getFormId(): string { -+ return 'taxonomy_machine_name_settings'; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function getEditableConfigNames(): array { -+ return ['taxonomy_machine_name.settings']; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function buildForm(array $form, FormStateInterface $form_state): array { -+ $config = $this->config('taxonomy_machine_name.settings'); -+ -+ $form['hide_machine_name_in_terms_overview'] = [ -+ '#type' => 'checkbox', -+ '#title' => $this->t('This hides the machine name in the terms overview.'), -+ '#default_value' => $config->get('hide_machine_name_in_terms_overview'), -+ '#description' => $this->t('By default, the machine name is displayed.'), -+ ]; -+ -+ return parent::buildForm($form, $form_state); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function submitForm(array &$form, FormStateInterface $form_state): void { -+ $config = $this->config('taxonomy_machine_name.settings'); -+ $config->set('hide_machine_name_in_terms_overview', -+ $form_state->getValue('hide_machine_name_in_terms_overview')); -+ $config->save(); -+ parent::submitForm($form, $form_state); -+ } -+ -+} -diff --git a/taxonomy_machine_name.info.yml b/taxonomy_machine_name.info.yml -index 1ec84a8daba43059a61f8345c987e8524f05e206..901b25ec7e9508a6ffa77408641f20148a4425da 100644 ---- a/taxonomy_machine_name.info.yml -+++ b/taxonomy_machine_name.info.yml -@@ -3,6 +3,7 @@ description: Provides a new property to store machine name for taxonomy terms. - type: module - package: Other - core_version_requirement: ^9.4 || ^10 -+configure: taxonomy_machine_name.settings - - dependencies: - - drupal:taxonomy -diff --git a/taxonomy_machine_name.links.menu.yml b/taxonomy_machine_name.links.menu.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..d06f5425102a95f63c60439aba59e77e52636051 ---- /dev/null -+++ b/taxonomy_machine_name.links.menu.yml -@@ -0,0 +1,6 @@ -+taxonomy_machine_name.settings: -+ title: Taxonomy Machine Name -+ description: Configure Taxonomy Machine Name -+ parent: system.admin_config_ui -+ route_name: taxonomy_machine_name.settings -+ weight: 10 -diff --git a/taxonomy_machine_name.module b/taxonomy_machine_name.module -index de7468cbabc03d32016c4b721098871482153ffb..1765908f715f042e8d6559af6380858a6835fa1c 100644 ---- a/taxonomy_machine_name.module -+++ b/taxonomy_machine_name.module -@@ -37,6 +37,11 @@ function taxonomy_machine_name_entity_base_field_info(EntityTypeInterface $entit - * Implements hook_form_FORM_ID_alter(). - */ - function taxonomy_machine_name_form_taxonomy_overview_terms_alter(&$form, FormStateInterface $form_state, $form_id) { -+ $shouldHide = Drupal::config('taxonomy_machine_name.settings') -+ ->get('hide_machine_name_in_terms_overview'); -+ if($shouldHide) { -+ return; -+ } - $user = \Drupal::currentUser(); - if($user->hasPermission('view machine name overview page')) { - if (isset($form['terms']['#header'])) { -diff --git a/taxonomy_machine_name.routing.yml b/taxonomy_machine_name.routing.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..837855b11344d084993c967e1af32591e059a5ce ---- /dev/null -+++ b/taxonomy_machine_name.routing.yml -@@ -0,0 +1,7 @@ -+taxonomy_machine_name.settings: -+ path: '/admin/config/user-interface/taxonomy_machine_name' -+ defaults: -+ _title: 'Taxonomy Machine Name' -+ _form: 'Drupal\taxonomy_machine_name\Form\Settings' -+ requirements: -+ _permission: 'administer site configuration' -\ No newline at end of file diff --git a/patches/d10/33525862-2.diff b/patches/d10/33525862-2.diff deleted file mode 100644 index 78fa1824633adc8192f5d88f1b76423eac2d07c3..0000000000000000000000000000000000000000 --- a/patches/d10/33525862-2.diff +++ /dev/null @@ -1,109 +0,0 @@ -diff --git a/config/install/taxonomy_machine_name.settings.yml b/config/install/taxonomy_machine_name.settings.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..4593ad4f74557433d1b3a98bed866d0917c3a142 ---- /dev/null -+++ b/config/install/taxonomy_machine_name.settings.yml -@@ -0,0 +1 @@ -+hide_machine_name_in_terms_overview: 0 -diff --git a/src/Form/Settings.php b/src/Form/Settings.php -new file mode 100644 -index 0000000000000000000000000000000000000000..0476b6b19e4e8053c0fb73742c1e8e75cd531cc8 ---- /dev/null -+++ b/src/Form/Settings.php -@@ -0,0 +1,54 @@ -+<?php -+ -+namespace Drupal\taxonomy_machine_name\Form; -+ -+use Drupal\Core\Form\ConfigFormBase; -+use Drupal\Core\Form\FormStateInterface; -+ -+/** -+ * Configure Taxonomy Machine Name settings for this site. -+ */ -+final class Settings extends ConfigFormBase { -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function getFormId(): string { -+ return 'taxonomy_machine_name_settings'; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function getEditableConfigNames(): array { -+ return ['taxonomy_machine_name.settings']; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function buildForm(array $form, FormStateInterface $form_state): array { -+ $config = $this->config('taxonomy_machine_name.settings'); -+ -+ $form['hide_machine_name_in_terms_overview'] = [ -+ '#type' => 'checkbox', -+ '#title' => $this->t('This hides the machine name in the terms overview.'), -+ '#default_value' => $config->get('hide_machine_name_in_terms_overview'), -+ '#description' => $this->t('By default, the machine name is displayed.'), -+ ]; -+ -+ return parent::buildForm($form, $form_state); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function submitForm(array &$form, FormStateInterface $form_state): void { -+ $config = $this->config('taxonomy_machine_name.settings'); -+ $config->set('hide_machine_name_in_terms_overview', -+ $form_state->getValue('hide_machine_name_in_terms_overview')); -+ $config->save(); -+ parent::submitForm($form, $form_state); -+ } -+ -+} -diff --git a/taxonomy_machine_name.links.menu.yml b/taxonomy_machine_name.links.menu.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..d06f5425102a95f63c60439aba59e77e52636051 ---- /dev/null -+++ b/taxonomy_machine_name.links.menu.yml -@@ -0,0 +1,6 @@ -+taxonomy_machine_name.settings: -+ title: Taxonomy Machine Name -+ description: Configure Taxonomy Machine Name -+ parent: system.admin_config_ui -+ route_name: taxonomy_machine_name.settings -+ weight: 10 -diff --git a/taxonomy_machine_name.module b/taxonomy_machine_name.module -index de7468cbabc03d32016c4b721098871482153ffb..1765908f715f042e8d6559af6380858a6835fa1c 100644 ---- a/taxonomy_machine_name.module -+++ b/taxonomy_machine_name.module -@@ -37,6 +37,11 @@ function taxonomy_machine_name_entity_base_field_info(EntityTypeInterface $entit - * Implements hook_form_FORM_ID_alter(). - */ - function taxonomy_machine_name_form_taxonomy_overview_terms_alter(&$form, FormStateInterface $form_state, $form_id) { -+ $shouldHide = Drupal::config('taxonomy_machine_name.settings') -+ ->get('hide_machine_name_in_terms_overview'); -+ if($shouldHide) { -+ return; -+ } - $user = \Drupal::currentUser(); - if($user->hasPermission('view machine name overview page')) { - if (isset($form['terms']['#header'])) { -diff --git a/taxonomy_machine_name.routing.yml b/taxonomy_machine_name.routing.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..837855b11344d084993c967e1af32591e059a5ce ---- /dev/null -+++ b/taxonomy_machine_name.routing.yml -@@ -0,0 +1,7 @@ -+taxonomy_machine_name.settings: -+ path: '/admin/config/user-interface/taxonomy_machine_name' -+ defaults: -+ _title: 'Taxonomy Machine Name' -+ _form: 'Drupal\taxonomy_machine_name\Form\Settings' -+ requirements: -+ _permission: 'administer site configuration' -\ No newline at end of file diff --git a/patches/d10/3352648.diff b/patches/d10/3352648.diff deleted file mode 100644 index 0a45d9ae45f902bdf6d6997f10d225c8c61668aa..0000000000000000000000000000000000000000 --- a/patches/d10/3352648.diff +++ /dev/null @@ -1,44 +0,0 @@ -diff --git a/js/ajax.js b/js/ajax.js -index b6a2eed22888c58b54b0b1cc73aca2ed057999c6..04fc7e086d9715e656350925cf3e4e301ddebd2a 100644 ---- a/js/ajax.js -+++ b/js/ajax.js -@@ -51,19 +51,33 @@ - submit: view_info, - url: drupalSettings.path.baseUrl + 'views/ajax', - element: item, -- event: execute || 'click', - progress: { type: 'fullscreen' }, - }; - -- var ajax_call = Drupal.ajax(ajax_settings); -- -- // Load right away. - if (execute) { -- ajax_call.execute(); -+ // Load right away. -+ this.loadLazyAjax(ajax_settings); -+ } -+ else { -+ // Load and unload on click. -+ $el.on(execute || 'click', function (e) { -+ e.preventDefault(); -+ if ($el.hasClass('loaded')) { -+ $el.removeClass('loaded'); -+ $('.js-view-dom-id-' + view_dom_id).empty(); -+ } -+ else { -+ $el.addClass('loaded'); -+ Drupal.behaviors.lazyViewsAjax.loadLazyAjax(ajax_settings); -+ } -+ }) - } - } -+ }, -+ loadLazyAjax: function (ajax_settings) { -+ let ajax_call = Drupal.ajax(ajax_settings); -+ ajax_call.execute(); - } - }; - - })(jQuery, Drupal); -- diff --git a/patches/d10/3354588.diff b/patches/d10/3354588.diff deleted file mode 100644 index b281f0bac7470c78eb5db5275aae148a9de83fe5..0000000000000000000000000000000000000000 --- a/patches/d10/3354588.diff +++ /dev/null @@ -1,52 +0,0 @@ -diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..9f34c760eecdedf94199745a6944277cebb90ff1 ---- /dev/null -+++ b/.gitlab-ci.yml -@@ -0,0 +1,16 @@ -+include: -+ - project: $_GITLAB_TEMPLATES_REPO -+ ref: $_GITLAB_TEMPLATES_REF -+ file: -+ - '/includes/include.drupalci.main.yml' -+ - '/includes/include.drupalci.variables.yml' -+ - '/includes/include.drupalci.workflows.yml' -+ -+variables: -+ SKIP_ESLINT: '1' -+ -+phpcs: -+ allow_failure: false -+ -+phpstan: -+ allow_failure: false -diff --git a/phpstan.neon b/phpstan.neon -new file mode 100644 -index 0000000000000000000000000000000000000000..3161509b8f29bd8024d5bfcd8231375650e31b4b ---- /dev/null -+++ b/phpstan.neon -@@ -0,0 +1,2 @@ -+parameters: -+ level: 6 -diff --git a/src/Plugin/CKEditorPlugin/TemplateSelector.php b/src/Plugin/CKEditorPlugin/TemplateSelector.php -index 0d60a9198c370509e0c2c2f60ead1b583d47c803..58e54318b5ba196d82090a22e769575d5915f626 100644 ---- a/src/Plugin/CKEditorPlugin/TemplateSelector.php -+++ b/src/Plugin/CKEditorPlugin/TemplateSelector.php -@@ -24,7 +24,7 @@ class TemplateSelector extends CKEditorPluginBase { - return [ - 'TemplateSelector' => [ - 'label' => $this->t('Insert templates'), -- 'image' => drupal_get_path('module', 'wysiwyg_template') . '/js/plugins/templateselector/icons/templateselector.png', -+ 'image' => $this->getModulePath('wysiwyg_template') . '/js/plugins/templateselector/icons/templateselector.png', - ], - ]; - } -@@ -33,7 +33,7 @@ class TemplateSelector extends CKEditorPluginBase { - * {@inheritdoc} - */ - public function getFile() { -- return drupal_get_path('module', 'wysiwyg_template') . '/js/plugins/templateselector/plugin.js'; -+ return $this->getModulePath('wysiwyg_template') . '/js/plugins/templateselector/plugin.js'; - } - - /** diff --git a/patches/d10/3355054.diff b/patches/d10/3355054.diff deleted file mode 100644 index 2e62de2f60a008c147eb9de05fb28b595a1963a4..0000000000000000000000000000000000000000 --- a/patches/d10/3355054.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/GinContentFormHelper.php b/src/GinContentFormHelper.php -index 4c7de93eb2a4b7e41741e66adf2f744c8698a242..2844ac25a16f7fa20353372bfa0759942bfca403 100644 ---- a/src/GinContentFormHelper.php -+++ b/src/GinContentFormHelper.php -@@ -180,7 +180,7 @@ class GinContentFormHelper implements ContainerInjectionInterface { - $form['gin_sidebar']['footer'] = ($form['footer']) ?? []; - // Copy actions. - $form['gin_sidebar']['actions'] = []; -- $form['gin_sidebar']['actions']['#type'] = ($form['actions']['#type']) ?? []; -+ $form['gin_sidebar']['actions']['#type'] = ($form['actions']['#type']) ?? 'actions'; - // Copy delete action. - $form['gin_sidebar']['actions']['delete'] = ($form['actions']['delete']) ?? []; - // Copy delete_translation action. diff --git a/patches/d10/3360589.diff b/patches/d10/3360589.diff deleted file mode 100644 index 4b93d4eec41d8bd5524970e747efba63d9037e97..0000000000000000000000000000000000000000 --- a/patches/d10/3360589.diff +++ /dev/null @@ -1,44 +0,0 @@ -diff --git a/src/Plugin/views/field/DiffFrom.php b/src/Plugin/views/field/DiffFrom.php -index 8af0ecc0b009cd821bf981152953d7e7a2476047..eb54962d4474454b00a06a6728c60518585855f6 100644 ---- a/src/Plugin/views/field/DiffFrom.php -+++ b/src/Plugin/views/field/DiffFrom.php -@@ -32,6 +32,7 @@ class DiffFrom extends DiffPluginBase { - public function viewsForm(array &$form, FormStateInterface $form_state) { - // Replace the form submit button label. - $form['actions']['submit']['#value'] = $this->t('Compare'); -+ $form['#validate'][] = [$this, 'viewsFormValidate']; - parent::viewsForm($form, $form_state); - } - -@@ -49,6 +50,31 @@ class DiffFrom extends DiffPluginBase { - } - } - -+ /** -+ * Validation handler for the comparison form. -+ * -+ * @param array $form -+ * An associative array containing the structure of the form. -+ * @param \Drupal\Core\Form\FormStateInterface $form_state -+ * The current state of the form. -+ */ -+ public function viewsFormValidate(array &$form, FormStateInterface $form_state) { -+ if ($form_state->get('step') === 'views_form_views_form') { -+ $diff_from = $form_state->getValue($this->options['id']); -+ $diff_to = $form_state->getValue($this->getToFieldId()); -+ if ($diff_from === '' || $diff_to === '') { -+ $form_state->setError($form, $this->t('No revisions selected for comparison.')); -+ } -+ else { -+ $diff_from_entity = $this->loadEntityFromDiffFormKey($diff_from); -+ $diff_to_entity = $this->loadEntityFromDiffFormKey($diff_to); -+ if ($diff_from_entity instanceof RevisionableInterface && $diff_to_entity instanceof RevisionableInterface && (int) $diff_from_entity->getRevisionId() >= (int) $diff_to_entity->getRevisionId()) { -+ $form_state->setError($form, $this->t('The from revision needs to be older than the to revision.')); -+ } -+ } -+ } -+ } -+ - /** - * Submit handler for the bulk form. - * diff --git a/patches/d10/3360633.diff b/patches/d10/3360633.diff deleted file mode 100644 index c810d632bccf6338845053610def11faea7dbf08..0000000000000000000000000000000000000000 --- a/patches/d10/3360633.diff +++ /dev/null @@ -1,23 +0,0 @@ -diff --git a/src/GlossifyBase.php b/src/GlossifyBase.php -index d9442f344bb68fa072e6547b0041b035f1b49cd3..7de926ecce501db162aaa8703300b1622a3dd119 100644 ---- a/src/GlossifyBase.php -+++ b/src/GlossifyBase.php -@@ -181,6 +181,7 @@ abstract class GlossifyBase extends FilterBase { - ]; - $word = $this->renderTip($word_tip); - } -+ $word = str_replace('&#013;', '
', $word); - $dom_fragment->appendXML($word); - $replaced[] = $term_txt; - } -@@ -235,6 +236,10 @@ abstract class GlossifyBase extends FilterBase { - if ($tip === NULL) { - return ''; - } -+ // Replace HTML entities with their UTF-8 characters. -+ $tip = html_entity_decode($tip, ENT_QUOTES|ENT_SUBSTITUTE, "UTF-8"); -+ // Replace br-tag with line break entity. -+ $tip = str_replace('<br />', '
', $tip); - // Get rid of HTML. - $tip = strip_tags($tip); - diff --git a/patches/d10/3362719.diff b/patches/d10/3362719.diff deleted file mode 100644 index ac912eb5678d0218723dbf6f441db028b03e5f2f..0000000000000000000000000000000000000000 --- a/patches/d10/3362719.diff +++ /dev/null @@ -1,59 +0,0 @@ -diff --git a/js/bpmn_io.js b/js/bpmn_io.js -index 9e7be1c8c12884173cd9416bcc14b7ab73f6b193..3f3c5ba50b38e4a67aec7806f4e1e3ede9e53a07 100644 ---- a/js/bpmn_io.js -+++ b/js/bpmn_io.js -@@ -40,6 +40,10 @@ - Drupal.bpmn_io.export(); - return false; - }); -+ $('input.button.eca-export').click(function () { -+ Drupal.bpmn_io.saveSVG(); -+ return false; -+ }); - $('input.button.eca-close').click(function () { - window.location = drupalSettings.bpmn_io.collection_url; - return false; -@@ -123,4 +127,25 @@ - }, false); - }; - -+ Drupal.bpmn_io.saveSVG = function () { -+ Drupal.bpmn_io.modeller.saveSVG({ format: true }, function (error, svg) { -+ if (error) { -+ return; -+ } -+ let svgBlob = new Blob([svg], { -+ type: 'image/svg+xml' -+ }); -+ let downloadLink = document.createElement('a'); -+ downloadLink.download = drupalSettings.bpmn_io.id + '.svg'; -+ downloadLink.innerHTML = 'Get BPMN SVG'; -+ downloadLink.href = window.URL.createObjectURL(svgBlob); -+ downloadLink.onclick = function (event) { -+ document.body.removeChild(event.target); -+ }; -+ downloadLink.style.visibility = 'hidden'; -+ document.body.appendChild(downloadLink); -+ downloadLink.click(); -+ }); -+ } -+ - })(jQuery, Drupal, drupalSettings); -diff --git a/src/Form/Modeller.php b/src/Form/Modeller.php -index a2cea66cd3f543f21eb72a72c587c1be7f0f20f3..37a217f38ec7d63247d1c6ad1e37fbeacf41c0e5 100644 ---- a/src/Form/Modeller.php -+++ b/src/Form/Modeller.php -@@ -32,6 +32,13 @@ final class Modeller extends FormBase { - 'class' => ['button--primary eca-save'], - ], - ], -+ 'export' => [ -+ '#type' => 'submit', -+ '#value' => $this->t('Export as SVG'), -+ '#attributes' => [ -+ 'class' => ['eca-export'], -+ ], -+ ], - 'close' => [ - '#type' => 'submit', - '#value' => $this->t('Close'), diff --git a/patches/d10/3375253.diff b/patches/d10/3375253.diff deleted file mode 100644 index a36c58c40b2e8f701c999a98fc6138846654d2b6..0000000000000000000000000000000000000000 --- a/patches/d10/3375253.diff +++ /dev/null @@ -1,170 +0,0 @@ -diff --git a/linkchecker.services.yml b/linkchecker.services.yml -index c1232ffbb7a0a5afe829e04f4e5f619e6f51d613..afff51148f52524ac38b12d4801149e82e4052b4 100644 ---- a/linkchecker.services.yml -+++ b/linkchecker.services.yml -@@ -34,6 +34,7 @@ services: - - '@datetime.time' - - '@queue' - - '@plugin.manager.link_status_handler' -+ - '@event_dispatcher' - - plugin.manager.link_status_handler: - class: Drupal\linkchecker\Plugin\LinkStatusHandlerManager -diff --git a/src/Event/BuildHeader.php b/src/Event/BuildHeader.php -new file mode 100644 -index 0000000000000000000000000000000000000000..3458f01fa3d1cf90ef0899a799208c3d7e59f03c ---- /dev/null -+++ b/src/Event/BuildHeader.php -@@ -0,0 +1,71 @@ -+<?php -+ -+namespace Drupal\linkchecker\Event; -+ -+use Drupal\Component\EventDispatcher\Event; -+ -+/** -+ * Provides an event for linkchecker headers. -+ */ -+class BuildHeader extends Event { -+ -+ /** -+ * The headers. -+ * -+ * @var array -+ */ -+ protected array $headers; -+ -+ /** -+ * The context. -+ * -+ * @var array -+ */ -+ protected array $context; -+ -+ /** -+ * Constructs the event to build the request headers. -+ * -+ * @param array $headers -+ * The headers. -+ * @param array $context -+ * The context. It contains 2 keys and their values: "method" and "url". -+ * -+ * @see \Drupal\linkchecker\LinkCheckerService::check -+ */ -+ public function __construct(array $headers, array $context) { -+ $this->headers = $headers; -+ $this->context = $context; -+ } -+ -+ /** -+ * Get the headers array. -+ * -+ * @return array -+ * The headers. -+ */ -+ public function getHeaders(): array { -+ return $this->headers; -+ } -+ -+ /** -+ * Set the headers array. -+ * -+ * @param array $headers -+ * The headers. -+ */ -+ public function setHeaders(array $headers): void { -+ $this->headers = $headers; -+ } -+ -+ /** -+ * Get the contexts array. -+ * -+ * @return array -+ * The context. -+ */ -+ public function getContext(): array { -+ return $this->context; -+ } -+ -+} -diff --git a/src/Event/LinkcheckerEvents.php b/src/Event/LinkcheckerEvents.php -new file mode 100644 -index 0000000000000000000000000000000000000000..4fa2e70787794d4d4de34fa0ffce41fe69937ce6 ---- /dev/null -+++ b/src/Event/LinkcheckerEvents.php -@@ -0,0 +1,15 @@ -+<?php -+ -+namespace Drupal\linkchecker\Event; -+ -+/** -+ * Provides the event names of the Linkchecker module. -+ */ -+final class LinkcheckerEvents { -+ -+ /** -+ * Dispatched when the linkchecker builds the headers for its requests. -+ */ -+ public const BUILD_HEADER = 'linkchecker.build_header'; -+ -+} -diff --git a/src/LinkCheckerService.php b/src/LinkCheckerService.php -index 6e08845a7d64d451d03c12797b16beb11bbca3e9..e672546064af73737756e62370b2e660bcfe1064 100644 ---- a/src/LinkCheckerService.php -+++ b/src/LinkCheckerService.php -@@ -10,10 +10,13 @@ use Drupal\Core\Logger\RfcLogLevel; - use Drupal\Core\Queue\QueueFactory; - use Drupal\Core\StringTranslation\StringTranslationTrait; - use Drupal\Core\Url; -+use Drupal\linkchecker\Event\BuildHeader; -+use Drupal\linkchecker\Event\LinkcheckerEvents; - use Drupal\linkchecker\Plugin\LinkStatusHandlerManager; - use GuzzleHttp\Client; - use Psr\Http\Message\ResponseInterface; - use GuzzleHttp\Exception\RequestException; -+use Symfony\Component\EventDispatcher\EventDispatcherInterface; - - /** - * Class LinkCheckerService. -@@ -71,16 +74,24 @@ class LinkCheckerService { - */ - protected $statusHandlerManager; - -+ /** -+ * The event dispatcher. -+ * -+ * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface -+ */ -+ protected EventDispatcherInterface $eventDispatcher; -+ - /** - * Constructs a new LinkCheckerService object. - */ -- public function __construct(EntityTypeManagerInterface $entityTypeManager, ConfigFactory $config, Client $httpClient, TimeInterface $time, QueueFactory $queueFactory, LinkStatusHandlerManager $statusHandlerManager) { -+ public function __construct(EntityTypeManagerInterface $entityTypeManager, ConfigFactory $config, Client $httpClient, TimeInterface $time, QueueFactory $queueFactory, LinkStatusHandlerManager $statusHandlerManager, EventDispatcherInterface $eventDispatcher) { - $this->entityTypeManager = $entityTypeManager; - $this->linkcheckerSetting = $config->get('linkchecker.settings'); - $this->httpClient = $httpClient; - $this->time = $time; - $this->queue = $queueFactory->get('linkchecker_check'); - $this->statusHandlerManager = $statusHandlerManager; -+ $this->eventDispatcher = $eventDispatcher; - } - - /** -@@ -161,6 +172,15 @@ class LinkCheckerService { - $headers['Range'] = 'bytes=0-1024'; - } - -+ // Allow other modules to alter the header. -+ $context = [ -+ 'method' => $link->getRequestMethod(), -+ 'url' => $link->getUrl(), -+ ]; -+ $event = new BuildHeader($headers, $context); -+ $this->eventDispatcher->dispatch($event, LinkcheckerEvents::BUILD_HEADER); -+ $headers = $event->getHeaders(); -+ - // Add in the headers. - $options = [ - 'headers' => $headers, diff --git a/patches/d10/3376854.diff b/patches/d10/3376854.diff deleted file mode 100644 index eb323805e83dcf1935b5b226707c2892bb82d29d..0000000000000000000000000000000000000000 --- a/patches/d10/3376854.diff +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/src/LinkExtractorService.php b/src/LinkExtractorService.php -index 6a0ee108afbeeb268baa3c6d0c05f8fa501f175e..8b08d6473dc07f268fb679862dff081aba23a250 100644 ---- a/src/LinkExtractorService.php -+++ b/src/LinkExtractorService.php -@@ -183,7 +183,7 @@ class LinkExtractorService { - $checkLinksType = $this->linkcheckerSetting->get('check_links_types'); - if (isset($this->request)) { - $httpProtocol = $this->request->getScheme() . '://'; -- $baseUrl = $this->request->getSchemeAndHttpHost(); -+ $baseUrl = $this->request->getSchemeAndHttpHost() . $this->request->getBasePath(); - } - else { - $httpProtocol = $this->linkcheckerSetting->get('default_url_scheme'); -diff --git a/src/Plugin/LinkStatusHandler/Repair301.php b/src/Plugin/LinkStatusHandler/Repair301.php -index 0255fe14d391999d67ce7f25ad819fceab7b3802..5a53adbbe5af5d47ef92d5bb2c1572bf151cdd30 100644 ---- a/src/Plugin/LinkStatusHandler/Repair301.php -+++ b/src/Plugin/LinkStatusHandler/Repair301.php -@@ -124,7 +124,7 @@ class Repair301 extends LinkStatusHandlerBase { - } - - if (isset($this->request)) { -- $baseUrl = $this->request->getSchemeAndHttpHost(); -+ $baseUrl = $this->request->getSchemeAndHttpHost() . $this->request->getBasePath(); - } - else { - $httpProtocol = $this->linkcheckerSetting->get('default_url_scheme'); diff --git a/patches/d10/3379128.diff b/patches/d10/3379128.diff deleted file mode 100644 index 2e462630335970dff416697415503968157e1404..0000000000000000000000000000000000000000 --- a/patches/d10/3379128.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/simple_menu_icons.module b/simple_menu_icons.module -index 99ef85119b7f702cf2e14dbdbd506e7b14855feb..1d8662a004a0e8b42263344743bbbd5b99f3636e 100755 ---- a/simple_menu_icons.module -+++ b/simple_menu_icons.module -@@ -162,7 +162,7 @@ function simple_menu_icons_css_generate() { - } - - // Either way, we should flush CSS cache so that aggregated CSS gets rebuilt. -- \Drupal::service('asset.css.collection_optimizer')->deleteAll(); -+ \Drupal::state()->delete('drupal_css_cache_files'); - _drupal_flush_css_js(); - } - diff --git a/patches/d10/3385117.diff b/patches/d10/3385117.diff deleted file mode 100644 index d4c346b33d73c8f5185f84483cebe081ef203014..0000000000000000000000000000000000000000 --- a/patches/d10/3385117.diff +++ /dev/null @@ -1,99 +0,0 @@ -diff --git a/src/Drush/Generators/ServiceGenerator.php b/src/Drush/Generators/ServiceGenerator.php -new file mode 100644 -index 0000000000000000000000000000000000000000..e27e8b7dd093b1851d4eda8b54a7fa3911f67885 ---- /dev/null -+++ b/src/Drush/Generators/ServiceGenerator.php -@@ -0,0 +1,43 @@ -+<?php -+ -+namespace Drupal\http_client_manager\Drush\Generators; -+ -+use DrupalCodeGenerator\Asset\AssetCollection as Assets; -+use DrupalCodeGenerator\Attribute\Generator; -+use DrupalCodeGenerator\Command\BaseGenerator; -+use DrupalCodeGenerator\GeneratorType; -+ -+/** -+ * Code generator for a HTTP Client Manager service component. -+ */ -+#[Generator( -+ name: 'http_client_manager:service', -+ description: 'Generates an HTTP Client Manager service.', -+ aliases: ['http-service'], -+ templatePath: __DIR__ . '/templates/service', -+ type: GeneratorType::MODULE_COMPONENT, -+)] -+class ServiceGenerator extends BaseGenerator { -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function generate(array &$vars, Assets $assets): void { -+ $ir = $this->createInterviewer($vars); -+ $vars['machine_name'] = $ir->askMachineName(); -+ $vars['name'] = $ir->ask('Name'); -+ $vars['description'] = $ir->ask('Description'); -+ $vars['method'] = $ir->ask('Method (get or post)', 'get'); -+ $vars['base_uri'] = $ir->ask('Base URI', 'https://example.com'); -+ $vars['operation'] = $ir->ask('Name of operation'); -+ $vars['path'] = $ir->ask('Path (e.g. api/name/of/path)'); -+ $vars['summary'] = $ir->ask('Summary'); -+ -+ $vars['id'] = mb_strtolower(str_replace([':', ' ', '-', '.', ',', '__'], '_', $vars['name'])); -+ -+ $assets->addFile('{machine_name}.http_services_api.yml', 'http_services_api.yml.twig'); -+ $assets->addFile('src/api/{id}.yml', 'api.yml.twig'); -+ $assets->addFile('src/api/resources/{id}.yml', 'resources.yml.twig'); -+ } -+ -+} -diff --git a/src/Drush/Generators/templates/service/api.yml.twig b/src/Drush/Generators/templates/service/api.yml.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..986ce90bb8288e1dd89faa25fbcf9c301cb3f8f4 ---- /dev/null -+++ b/src/Drush/Generators/templates/service/api.yml.twig -@@ -0,0 +1,5 @@ -+name: "{{ name }}" -+apiVersion: "2.0" -+description: "{{ description }}" -+imports: -+ - "resources/{{ id }}.yml" -diff --git a/src/Drush/Generators/templates/service/http_services_api.yml.twig b/src/Drush/Generators/templates/service/http_services_api.yml.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..07373ff305a04d13fd5a3585b2f99407cc6f3b61 ---- /dev/null -+++ b/src/Drush/Generators/templates/service/http_services_api.yml.twig -@@ -0,0 +1,5 @@ -+{{ machine_name }}_services: -+ title: "{{ name }}" -+ api_path: "src/api/{{ id }}.yml" -+ config: -+ base_uri: "{{ base_uri }}" -diff --git a/src/Drush/Generators/templates/service/resources.yml.twig b/src/Drush/Generators/templates/service/resources.yml.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..fa47c1e273f275810651081ae851fd7d37d071f4 ---- /dev/null -+++ b/src/Drush/Generators/templates/service/resources.yml.twig -@@ -0,0 +1,22 @@ -+operations: -+ {{ operation }}: -+ httpMethod: "{{ method|upper }}" -+ uri: "{{ path }}" -+ summary: "{{ summary }}" -+ responseClass: "Result" -+ -+models: -+ ResultRow: -+ type: "object" -+ properties: -+ prop1: -+ location: "json" -+ type: "string" -+ prop2: -+ location: "json" -+ type: "string" -+ Result: -+ type: "array" -+ location: "json" -+ items: -+ "$ref": "ResultRow" diff --git a/patches/d10/3387653.diff b/patches/d10/3387653.diff deleted file mode 100644 index aec11a8fe9296d3138714976f594f00bf649faef..0000000000000000000000000000000000000000 --- a/patches/d10/3387653.diff +++ /dev/null @@ -1,44 +0,0 @@ -diff --git a/dist/js/init.js b/dist/js/init.js -index 9871b15ec228d11ab443a3f86504ab1b573cde45..0bee63ffd6cc1b23e61071064cf35aba82e8e5ea 100644 ---- a/dist/js/init.js -+++ b/dist/js/init.js -@@ -5,7 +5,7 @@ - if (localStorage.getItem("GinDarkMode") && (localStorage.setItem("Drupal.gin.darkmode", localStorage.getItem("GinDarkMode")), - localStorage.removeItem("GinDarkMode")), localStorage.getItem("GinSidebarOpen") && (localStorage.setItem("Drupal.gin.toolbarExpanded", localStorage.getItem("GinSidebarOpen")), - localStorage.removeItem("GinSidebarOpen")), ginInitDarkmode(), window.addEventListener("DOMContentLoaded", (() => { -- localStorage.getItem("Drupal.gin.darkmode") || (localStorage.setItem("Drupal.gin.darkmode", drupalSettings.gin.darkmode), -+ localStorage.getItem("Drupal.gin.darkmode") && (drupalSettings.gin.darkmode == localStorage.getItem("Drupal.gin.darkmode") || drupalSettings.gin.show_user_theme_settings) || (localStorage.setItem("Drupal.gin.darkmode", drupalSettings.gin.darkmode), - ginInitDarkmode()); - })), localStorage.getItem("Drupal.gin.toolbarExpanded")) { - const style = document.createElement("style"), className = "gin-toolbar-inline-styles"; -diff --git a/includes/page.theme b/includes/page.theme -index 38dee471f4b6a0ab22e9a4b466919fe42567cbc2..ef61dbbb483609e7d3dade8a7d72ed876502b08f 100644 ---- a/includes/page.theme -+++ b/includes/page.theme -@@ -99,6 +99,7 @@ function gin_page_attachments_alter(&$page) { - $page['#attached']['drupalSettings']['gin']['highcontrastmode'] = $settings->get('high_contrast_mode'); - $page['#attached']['drupalSettings']['gin']['highcontrastmode_class'] = 'gin--high-contrast-mode'; - $page['#attached']['drupalSettings']['gin']['toolbar_variant'] = $settings->get('classic_toolbar'); -+ $page['#attached']['drupalSettings']['gin']['show_user_theme_settings'] = $settings->get('show_user_theme_settings'); - - // Expose stylesheets to JS. - $basethemeurl = '/' . \Drupal::service('extension.list.theme')->getPath('gin'); -diff --git a/js/init.js b/js/init.js -index 10412e892c0cd0c48ca95bb651eac6c42350c637..860de79c61bd0601dc284519fe42624f7e03df75 100644 ---- a/js/init.js -+++ b/js/init.js -@@ -32,9 +32,12 @@ function ginInitDarkmode() { - - ginInitDarkmode(); - --// GinDarkMode is not set yet. -+// GinDarkMode is not set yet or config changes detected. - window.addEventListener('DOMContentLoaded', () => { -- if (!localStorage.getItem('Drupal.gin.darkmode')) { -+ if ( -+ !localStorage.getItem('Drupal.gin.darkmode') || -+ (drupalSettings.gin.darkmode != localStorage.getItem('Drupal.gin.darkmode') && !drupalSettings.gin.show_user_theme_settings) -+ ) { - localStorage.setItem('Drupal.gin.darkmode', drupalSettings.gin.darkmode); - ginInitDarkmode(); - } diff --git a/patches/d10/3388703.diff b/patches/d10/3388703.diff deleted file mode 100644 index df4d658bb46f48c9238b74b932c7e0c2dbcbf368..0000000000000000000000000000000000000000 --- a/patches/d10/3388703.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/Plugin/views/display/DataExport.php b/src/Plugin/views/display/DataExport.php -index 169fa701955d8b3ba74fc7ada454392cdc84d84e..1f3d5ecdc8e555a876eed908914344afdf406a9c 100644 ---- a/src/Plugin/views/display/DataExport.php -+++ b/src/Plugin/views/display/DataExport.php -@@ -835,7 +835,7 @@ class DataExport extends RestExport { - $rowIndex++; - $colIndex = 0; - foreach ($row->getCellIterator() as $cell) { -- $previousExcel->getActiveSheet()->setCellValueByColumnAndRow(++$colIndex, $rowIndex, $cell->getValue()); -+ $previousExcel->getActiveSheet()->setCellValue([++$colIndex, $rowIndex], $cell->getValue()); - } - } - diff --git a/patches/d10/3392567.diff b/patches/d10/3392567.diff deleted file mode 100644 index c4e8f5e6a8a968251e8a039693ff37a524efb4c5..0000000000000000000000000000000000000000 --- a/patches/d10/3392567.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/templates/views-view-fullcalendar.html.twig b/templates/views-view-fullcalendar.html.twig -index 13c07c5feda750afd0ceebbaf9d70705395f9a55..93b76c756e37927779c54b3700ba64a10c630373 100644 ---- a/templates/views-view-fullcalendar.html.twig -+++ b/templates/views-view-fullcalendar.html.twig -@@ -28,7 +28,7 @@ - <div class="bottom-buttons fc-button-group"> - {% if showAddEvent %} - <div class="fullcalendar-bottom-btn add-event-btn"> -- <a id="calendar-add-event" href="fullcalendar-view-event-add?entity={{ entity_id }}&bundle={{ options.bundle_type }}&start_field={{ options.start }}&end_field={{ options.end }}&destination={{ path('<current>') }}" class="use-ajax" data-dialog-type="dialog" data-dialog-renderer="off_canvas" -+ <a id="calendar-add-event" href="{{ path('fullcalendar_view.add_event') }}?entity={{ entity_id }}&bundle={{ options.bundle_type }}&start_field={{ options.start }}&end_field={{ options.end }}&destination={{ path('<current>') }}" class="use-ajax" data-dialog-type="dialog" data-dialog-renderer="off_canvas" - data-dialog-options="{"width":400}">{{ 'Add event'|t }}</a> - </div> - {% endif %} diff --git a/patches/d10/3392903-follow-up.diff b/patches/d10/3392903-follow-up.diff deleted file mode 100644 index 6a2adff74f494d9cf3b2e6cdd150d47e0f119f29..0000000000000000000000000000000000000000 --- a/patches/d10/3392903-follow-up.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Config/Schema/TypeResolver.php b/core/lib/Drupal/Core/Config/Schema/TypeResolver.php -index af9c1c53ab..daec17be82 100644 ---- a/core/lib/Drupal/Core/Config/Schema/TypeResolver.php -+++ b/core/lib/Drupal/Core/Config/Schema/TypeResolver.php -@@ -91,7 +91,7 @@ public static function resolveExpression(string $expression, array|TypedDataInte - $previous_name = NULL; - // Process each value part, one at a time. - while ($name = array_shift($parts)) { -- if (str_starts_with($name, '%') && !in_array($name, ['%parent', '%key', '%type'], TRUE)) { -+ if (str_starts_with($name, '%') && !in_array($name, ['%parent', '%key', '%type', '%formatter'], TRUE)) { - throw new \LogicException('`' . $expression . '` is not a valid dynamic type expression. Dynamic type expressions must contain at least `%parent`, `%key`, or `%type`.`'); - } - if ($name === '%type' && $previous_name !== '%parent') { diff --git a/patches/d10/3404050-2.diff b/patches/d10/3404050-2.diff deleted file mode 100644 index 0bfc39647a92fe76e0fabddce28573c5bb4d7b5a..0000000000000000000000000000000000000000 --- a/patches/d10/3404050-2.diff +++ /dev/null @@ -1,50 +0,0 @@ -diff --git a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockAdjust.php b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockAdjust.php -index 962306739933d5b3b8561870775044487b5fcdbf..fb487c14fa3991de5bcb6aa6d83d16ac6d6fbef2 100644 ---- a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockAdjust.php -+++ b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockAdjust.php -@@ -33,10 +33,13 @@ class VariationXquantityStockAdjust extends ConfigurableActionBase { - public function buildConfigurationForm(array $form, FormStateInterface $form_state) { - $request = \Drupal::request(); - $storage = \Drupal::service('entity_type.manager')->getStorage('commerce_product_variation'); -- if ($ids = explode('|', $request->query->get('ids'))) { -+ if ($ids = explode('|', $request->query->get('ids') ?? '')) { - $variations = $storage->loadMultiple($ids); - $variation = reset($variations); - $xquantity_stock = FALSE; -+ if (!$variation) { -+ return $form; -+ } - foreach (array_reverse($variation->getFieldDefinitions()) as $definition) { - if ($definition->getType() == 'xquantity_stock') { - $xquantity_stock = $definition->getName(); -diff --git a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockRotate.php b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockRotate.php -index 37b7cca993f362aee025690ca8af0fa3153aa511..0752293f9abe794c19491fa98a1637d7fd452554 100644 ---- a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockRotate.php -+++ b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockRotate.php -@@ -32,7 +32,7 @@ class VariationXquantityStockRotate extends ConfigurableActionBase { - public function buildConfigurationForm(array $form, FormStateInterface $form_state) { - $request = \Drupal::request(); - $storage = \Drupal::service('entity_type.manager')->getStorage('commerce_product_variation'); -- if ($ids = explode('|', $request->query->get('ids'))) { -+ if ($ids = explode('|', $request->query->get('ids') ?? '')) { - $variations = $storage->loadMultiple($ids); - $xquantity_stock = []; - foreach ($variations as $variation) { -diff --git a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockSet.php b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockSet.php -index ecb63025c27683c91ab150bc7365ecc400c04474..e9a2d8b668565abafd3aaf232152ec859baf682f 100644 ---- a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockSet.php -+++ b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockSet.php -@@ -32,9 +32,12 @@ class VariationXquantityStockSet extends ConfigurableActionBase { - public function buildConfigurationForm(array $form, FormStateInterface $form_state) { - $request = \Drupal::request(); - $storage = \Drupal::service('entity_type.manager')->getStorage('commerce_product_variation'); -- if ($ids = explode('|', $request->query->get('ids'))) { -+ if ($ids = explode('|', $request->query->get('ids') ?? '')) { - $variations = $storage->loadMultiple($ids); - $variation = reset($variations); -+ if (!$variation) { -+ return $form; -+ } - $xquantity_stock = FALSE; - foreach (array_reverse($variation->getFieldDefinitions()) as $definition) { - if ($definition->getType() == 'xquantity_stock') { diff --git a/patches/d10/3404050.diff b/patches/d10/3404050.diff deleted file mode 100644 index 23226bbaba72b95cc225af1d61aeba5ef05741f9..0000000000000000000000000000000000000000 --- a/patches/d10/3404050.diff +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockAdjust.php b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockAdjust.php -index 962306739933d5b3b8561870775044487b5fcdbf..45cd152a8025306ece08433d8738d8cbbb489821 100644 ---- a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockAdjust.php -+++ b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockAdjust.php -@@ -37,6 +37,9 @@ class VariationXquantityStockAdjust extends ConfigurableActionBase { - $variations = $storage->loadMultiple($ids); - $variation = reset($variations); - $xquantity_stock = FALSE; -+ if($variation) { -+ return $form; -+ } - foreach (array_reverse($variation->getFieldDefinitions()) as $definition) { - if ($definition->getType() == 'xquantity_stock') { - $xquantity_stock = $definition->getName(); -diff --git a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockSet.php b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockSet.php -index ecb63025c27683c91ab150bc7365ecc400c04474..fccd8394a74ce8b4f84b946460310be22cfb48f4 100644 ---- a/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockSet.php -+++ b/modules/xquantity_stock/src/Plugin/Action/VariationXquantityStockSet.php -@@ -35,6 +35,9 @@ class VariationXquantityStockSet extends ConfigurableActionBase { - if ($ids = explode('|', $request->query->get('ids'))) { - $variations = $storage->loadMultiple($ids); - $variation = reset($variations); -+ if(!$variation) { -+ return $form; -+ } - $xquantity_stock = FALSE; - foreach (array_reverse($variation->getFieldDefinitions()) as $definition) { - if ($definition->getType() == 'xquantity_stock') { diff --git a/patches/d10/3404501.diff b/patches/d10/3404501.diff deleted file mode 100644 index 650fb007849e618b647e49f797a0fbc9a875f25e..0000000000000000000000000000000000000000 --- a/patches/d10/3404501.diff +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/simple_menu_icons.module b/simple_menu_icons.module -index aa928f37e63b5d0353f3132f344b00836b1da668..73fcb45ddb602e13013eb21f61b97e43ba788435 100755 ---- a/simple_menu_icons.module -+++ b/simple_menu_icons.module -@@ -201,6 +201,12 @@ function simple_menu_icons_css_alter(&$css, AttachedAssetsInterface $assets) { - 'IE' => TRUE, - '!IE' => TRUE, - ], -+ 'version' => -1, -+ 'license' => [ -+ 'name' => 'GNU-GPL-2.0-or-later', -+ 'url' => 'https://www.drupal.org/licensing/faq', -+ 'gpl-compatible' => TRUE, -+ ], - ]; - } - } diff --git a/patches/d10/3409287.diff b/patches/d10/3409287.diff deleted file mode 100644 index 55513a84945f5d09302d444ea9f4c759a65600ef..0000000000000000000000000000000000000000 --- a/patches/d10/3409287.diff +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/src/Plugin/Field/FieldType/SmartDateListItemBase.php b/src/Plugin/Field/FieldType/SmartDateListItemBase.php -index 3c4c5490c64eb72e8da15c92c1a566099531ae7c..b5c451ee5952d8072b674d1a9ca633e48ba55013 100644 ---- a/src/Plugin/Field/FieldType/SmartDateListItemBase.php -+++ b/src/Plugin/Field/FieldType/SmartDateListItemBase.php -@@ -14,7 +14,10 @@ abstract class SmartDateListItemBase extends ListItemBase { - */ - public static function parseValues($values) { - // Use the ListItemBase parsing function, but don't allow generated keys. -- $result = static::extractAllowedValues($values, 1); -+ $list = explode("\n", $values); -+ $list = array_map('trim', $list); -+ $list = array_filter($list, 'strlen'); -+ $result = static::extractAllowedValues($list, 1); - return $result; - } - diff --git a/patches/d10/3410547-2.diff b/patches/d10/3410547-2.diff deleted file mode 100644 index 56c277e9ff63e6146402cf79e276c48847df8af3..0000000000000000000000000000000000000000 --- a/patches/d10/3410547-2.diff +++ /dev/null @@ -1,344 +0,0 @@ -diff --git a/config/schema/smart_date.schema.yml b/config/schema/smart_date.schema.yml -index 53d9410678d1a54000f4c5318caafab81445fbb4..e97fa3f2dacb4bd1ff97c66ee6f0f31925dc9e1d 100644 ---- a/config/schema/smart_date.schema.yml -+++ b/config/schema/smart_date.schema.yml -@@ -300,3 +300,11 @@ smart_date.smart_date_format.*: - translatable: true - form_element_class: \Drupal\smart_date\FormElement\Boolean - label: 'Site Time display' -+ -+views.filter_value.date_range_contains: -+ type: views.filter_value.numeric -+ label: 'Daterange contains' -+ mapping: -+ type: -+ type: string -+ label: 'Type' -diff --git a/src/Plugin/views/filter/Date.php b/src/Plugin/views/filter/Date.php -index 969b6a0a005f96ed133d49247748c5efb2b6525b..c7e7986c8dff5325a61619e5db1cf840f86f9d9a 100644 ---- a/src/Plugin/views/filter/Date.php -+++ b/src/Plugin/views/filter/Date.php -@@ -125,127 +125,40 @@ class Date extends CoreDate implements ContainerFactoryPluginInterface { - * Override parent method, which deals with dates as integers. - */ - protected function opBetween($field) { -- $timezone = $this->getTimezone(); -- $granularity = $this->options['value']['granularity']; -- -- // Convert value to DateTimePlus for additional processing. -- $a = new DateTimePlus($this->value['min'], new \DateTimeZone($timezone)); -- $b = new DateTimePlus($this->value['max'], new \DateTimeZone($timezone)); -- // Granularity requires some conversion. -- if ($granularity != 'second') { -- $min = [ -- 'year' => $a->format('Y'), -- 'month' => $a->format('n'), -- 'day' => $a->format('j'), -- 'hour' => $a->format('G'), -- 'minute' => $a->format('i'), -- 'second' => $a->format('s'), -- ]; -- $max = [ -- 'year' => $b->format('Y'), -- 'month' => $b->format('n'), -- 'day' => $b->format('j'), -- 'hour' => $b->format('G'), -- 'minute' => $b->format('i'), -- 'second' => $b->format('s'), -- ]; -- switch ($granularity) { -- case 'year': -- $min['month'] = '01'; -- $max['month'] = '12'; -- $max['day'] = '31'; -- case 'month': -- $min['day'] = '01'; -- if ($granularity != 'year') { -- $max['day'] = $b->format('t'); -- } -- case 'day': -- $min['hour'] = '00'; -- $max['hour'] = '23'; -- case 'hour': -- $min['minute'] = '00'; -- $max['minute'] = '59'; -- case 'minute': -- $min['second'] = '00'; -- $max['second'] = '59'; -- } -- // Update the range with our altered values. -- $a = $a->createFromArray($min); -- $b = $b->createFromArray($max); -- } -- -- // This is safe because we forced the provided values to DateTimePlus. -+ [$min_value, $max_value] = $this->getMinAndMax(FALSE); - $operator = strtoupper($this->operator); -- $start = $a->format('U'); -- $end = $b->format('U'); -- $this->query->addWhereExpression($this->options['group'], "$field $operator $start AND $end"); -+ $this->query->addWhereExpression($this->options['group'], "$field $operator $min_value AND $max_value"); - } - - /** - * Override parent method, to add granularity options. - */ - protected function opSimple($field) { -- $timezone = $this->getTimezone(); -- $granularity = $this->options['value']['granularity']; -- -- // Convert value to DateTimePlus for additional processing. -- $date_value = $this->value['value']; -- $value = new DateTimePlus($date_value, new \DateTimeZone($timezone)); -- // Granularity requires some conversion. -- if ($granularity != 'second') { -- $value_array = [ -- 'year' => $value->format('Y'), -- 'month' => $value->format('n'), -- 'day' => $value->format('j'), -- 'hour' => $value->format('G'), -- 'minute' => $value->format('i'), -- 'second' => $value->format('s'), -- ]; -- $min = $max = $value_array; -- switch ($granularity) { -- case 'year': -- $min['month'] = '01'; -- $max['month'] = '12'; -- $max['day'] = '31'; -- case 'month': -- $min['day'] = '01'; -- if ($granularity != 'year') { -- $max['day'] = $value->format('t'); -- } -- case 'day': -- $min['hour'] = '00'; -- $max['hour'] = '23'; -- case 'hour': -- $min['minute'] = '00'; -- $max['minute'] = '59'; -- case 'minute': -- $min['second'] = '00'; -- $max['second'] = '59'; -- } -- -+ [$min_value, $max_value] = $this->getMinAndMax(); -+ if ($this->options['value']['granularity'] !== 'second') { - // Additional, operator-specific logic. -- if (substr($this->operator, 0, 1) == '>') { -- $value = $value->createFromArray($min, $timezone); -+ if ($this->operator[0] === '>') { -+ $value = $min_value; - } -- elseif (substr($this->operator, 0, 1) == '<') { -- $value = $value->createFromArray($max, $timezone); -+ elseif ($this->operator[0] === '<') { -+ $value = $max_value; - } - else { -- $min_value = $value->createFromArray($min, $timezone)->format('U'); -- $max_value = $value->createFromArray($max, $timezone)->format('U'); -- if ($this->operator == '=') { -+ if ($this->operator === '=') { - $operator = 'BETWEEN'; - } -- elseif ($this->operator == '!=') { -+ elseif ($this->operator === '!=') { - $operator = 'NOT BETWEEN'; - } -- $this->query->addWhereExpression($this->options['group'], "$field $operator $min_value AND $max_value"); -+ $this->query->addWhereExpression($this->options['group'], "$field $this->operator $min_value AND $max_value"); - return; - } - } -- -+ else { -+ $value = $min_value; -+ } - // This is safe because we forced the provided value to a DateTimePlus. -- $this->query->addWhereExpression($this->options['group'], "$field $this->operator " . $value->format('U')); -+ $this->query->addWhereExpression($this->options['group'], "$field $this->operator $value"); - } - - /** -@@ -262,6 +175,62 @@ class Date extends CoreDate implements ContainerFactoryPluginInterface { - return date_default_timezone_get(); - } - -+ /** -+ * Helper function to prepare min and max values for op* callbacks. -+ * -+ * @param bool $singleValueMode -+ * TRUE, if the values should be calculated on the single "value" field. -+ * Otherwise, set to FALSE to calculate based on "min" and "max" values. -+ * -+ * @return array -+ * An array containing the fully prepared min and max values ready to be -+ * used by query conditions. -+ */ -+ protected function getMinAndMax(bool $singleValueMode = TRUE): array { -+ $timezone = $this->getTimezone(); -+ $granularity = $this->options['value']['granularity']; -+ -+ // Convert form field value(s) to DateTimePlus for additional processing. -+ if ($singleValueMode) { -+ $a = $b = new DateTimePlus($this->value['value'], new \DateTimeZone($timezone)); -+ } -+ else { -+ $a = new DateTimePlus($this->value['min'], new \DateTimeZone($timezone)); -+ $b = new DateTimePlus($this->value['max'], new \DateTimeZone($timezone)); -+ } -+ $min = $max = [ -+ 'year' => $a->format('Y'), -+ 'month' => $a->format('n'), -+ 'day' => $a->format('j'), -+ 'hour' => $a->format('G'), -+ 'minute' => $a->format('i'), -+ 'second' => $a->format('s'), -+ ]; -+ switch ($granularity) { -+ case 'year': -+ $min['month'] = '01'; -+ $max['month'] = '12'; -+ $max['day'] = '31'; -+ case 'month': -+ $min['day'] = '01'; -+ if ($granularity !== 'year') { -+ $max['day'] = $b->format('t'); -+ } -+ case 'day': -+ $min['hour'] = '00'; -+ $max['hour'] = '23'; -+ case 'hour': -+ $min['minute'] = '00'; -+ $max['minute'] = '59'; -+ case 'minute': -+ $min['second'] = '00'; -+ $max['second'] = '59'; -+ } -+ $min_value = $a::createFromArray($min, $timezone)->format('U'); -+ $max_value = $b::createFromArray($max, $timezone)->format('U'); -+ return [$min_value, $max_value]; -+ } -+ - /** - * {@inheritdoc} - */ -diff --git a/src/Plugin/views/filter/DateRangeContains.php b/src/Plugin/views/filter/DateRangeContains.php -new file mode 100644 -index 0000000000000000000000000000000000000000..56f739022ec94b69b37637a208644ddf0560807d ---- /dev/null -+++ b/src/Plugin/views/filter/DateRangeContains.php -@@ -0,0 +1,112 @@ -+<?php -+ -+namespace Drupal\smart_date\Plugin\views\filter; -+ -+use Drupal\Component\Datetime\DateTimePlus; -+ -+/** -+ * Date range views filter to filter records that contain a given value. -+ * -+ * @ingroup views_filter_handlers -+ * -+ * @ViewsFilter("date_range_contains") -+ */ -+class DateRangeContains extends Date { -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function defineOptions(): array { -+ $options = parent::defineOptions(); -+ $options['operator']['default'] = 'daterange_contains'; -+ $options['value']['contains']['granularity']['default'] = 'day'; -+ return $options; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function operators(): array { -+ return [ -+ 'daterange_contains' => [ -+ 'title' => $this->t('Contains'), -+ 'method' => 'opContains', -+ 'short' => $this->t('contains'), -+ 'values' => 1, -+ ], -+ 'daterange_not_contains' => [ -+ 'title' => $this->t('Does not contain'), -+ 'method' => 'opContains', -+ 'short' => $this->t('not contains'), -+ 'values' => 1, -+ ], -+ 'daterange_starts_or_ends' => [ -+ 'title' => $this->t('Starts or ends at'), -+ 'method' => 'opContains', -+ 'short' => $this->t('starts or ends'), -+ 'values' => 1, -+ ], -+ 'daterange_contains_range' => [ -+ 'title' => $this->t('Contains range'), -+ 'method' => 'opContainsRange', -+ 'short' => $this->t('contains range'), -+ 'values' => 2, -+ ], -+ 'daterange_not_contains_range' => [ -+ 'title' => $this->t('Does not contain range'), -+ 'method' => 'opContainsRange', -+ 'short' => $this->t('not contains range'), -+ 'values' => 2, -+ ], -+ 'daterange_starts_or_ends_range' => [ -+ 'title' => $this->t('Starts or ends at range'), -+ 'method' => 'opContainsRange', -+ 'short' => $this->t('starts or ends range'), -+ 'values' => 2, -+ ], -+ ]; -+ } -+ -+ /** -+ * Add conditions to the query. -+ */ -+ protected function opContains($field): void { -+ [$min_value, $max_value] = $this->getMinAndMax(); -+ $this->containsConditions($field, $min_value, $max_value); -+ } -+ -+ /** -+ * Add conditions to the query. -+ */ -+ protected function opContainsRange($field): void { -+ [$min_value, $max_value] = $this->getMinAndMax(FALSE); -+ $this->containsConditions($field, $min_value, $max_value); -+ } -+ -+ /** -+ * Helper function to add the conditions to the query. -+ * -+ * @param string $field -+ * The field name. -+ * @param string $min_value -+ * The minimum date(time) value. -+ * @param string $max_value -+ * The maximum date(time) value. -+ */ -+ protected function containsConditions(string $field, string $min_value, string $max_value): void { -+ switch ($this->operator) { -+ case 'daterange_contains': -+ $this->query->addWhereExpression($this->options['group'], "{$field}__value <= $min_value AND {$field}__end_value >= $max_value"); -+ break; -+ -+ case 'daterange_not_contains': -+ $this->query->addWhereExpression($this->options['group'], "{$field}__value >= $max_value OR {$field}__end_value <= $min_value"); -+ break; -+ -+ case 'daterange_starts_or_ends': -+ $this->query->addWhereExpression($this->options['group'], "({$field}__value >= $min_value AND {$field}__value <= $max_value) OR ({$field}__end_value >= $min_value AND {$field}__end_value <= $max_value)"); -+ break; -+ } -+ } -+ -+} diff --git a/patches/d10/3410547.diff b/patches/d10/3410547.diff deleted file mode 100644 index 8c2833c1fa5bc922668caae53846d6f604dc62ba..0000000000000000000000000000000000000000 --- a/patches/d10/3410547.diff +++ /dev/null @@ -1,361 +0,0 @@ -diff --git a/config/schema/smart_date.schema.yml b/config/schema/smart_date.schema.yml -index 53d9410678d1a54000f4c5318caafab81445fbb4..e97fa3f2dacb4bd1ff97c66ee6f0f31925dc9e1d 100644 ---- a/config/schema/smart_date.schema.yml -+++ b/config/schema/smart_date.schema.yml -@@ -300,3 +300,11 @@ smart_date.smart_date_format.*: - translatable: true - form_element_class: \Drupal\smart_date\FormElement\Boolean - label: 'Site Time display' -+ -+views.filter_value.date_range_contains: -+ type: views.filter_value.numeric -+ label: 'Daterange contains' -+ mapping: -+ type: -+ type: string -+ label: 'Type' -diff --git a/src/Plugin/views/filter/Date.php b/src/Plugin/views/filter/Date.php -index feb42f874b66977601aa9b3d82e93d2bb785e8a2..c7e7986c8dff5325a61619e5db1cf840f86f9d9a 100644 ---- a/src/Plugin/views/filter/Date.php -+++ b/src/Plugin/views/filter/Date.php -@@ -26,14 +26,14 @@ use Drupal\date_popup\DatePopupHelper; - class Date extends CoreDate implements ContainerFactoryPluginInterface { - - use FieldAPIHandlerTrait; -- -+ - /** - * The date formatter. - * - * @var \Drupal\Core\Datetime\DateFormatterInterface - */ - protected $dateFormatter; -- -+ - /** - * The request stack used to determine current time. - * -@@ -125,127 +125,40 @@ class Date extends CoreDate implements ContainerFactoryPluginInterface { - * Override parent method, which deals with dates as integers. - */ - protected function opBetween($field) { -- $timezone = $this->getTimezone(); -- $granularity = $this->options['value']['granularity']; -- -- // Convert value to DateTimePlus for additional processing. -- $a = new DateTimePlus($this->value['min'], new \DateTimeZone($timezone)); -- $b = new DateTimePlus($this->value['max'], new \DateTimeZone($timezone)); -- // Granularity requires some conversion. -- if ($granularity != 'second') { -- $min = [ -- 'year' => $a->format('Y'), -- 'month' => $a->format('n'), -- 'day' => $a->format('j'), -- 'hour' => $a->format('G'), -- 'minute' => $a->format('i'), -- 'second' => $a->format('s'), -- ]; -- $max = [ -- 'year' => $b->format('Y'), -- 'month' => $b->format('n'), -- 'day' => $b->format('j'), -- 'hour' => $b->format('G'), -- 'minute' => $b->format('i'), -- 'second' => $b->format('s'), -- ]; -- switch ($granularity) { -- case 'year': -- $min['month'] = '01'; -- $max['month'] = '12'; -- $max['day'] = '31'; -- case 'month': -- $min['day'] = '01'; -- if ($granularity != 'year') { -- $max['day'] = $b->format('t'); -- } -- case 'day': -- $min['hour'] = '00'; -- $max['hour'] = '23'; -- case 'hour': -- $min['minute'] = '00'; -- $max['minute'] = '59'; -- case 'minute': -- $min['second'] = '00'; -- $max['second'] = '59'; -- } -- // Update the range with our altered values. -- $a = $a->createFromArray($min); -- $b = $b->createFromArray($max); -- } -- -- // This is safe because we forced the provided values to DateTimePlus. -+ [$min_value, $max_value] = $this->getMinAndMax(FALSE); - $operator = strtoupper($this->operator); -- $start = $a->format('U'); -- $end = $b->format('U'); -- $this->query->addWhereExpression($this->options['group'], "$field $operator $start AND $end"); -+ $this->query->addWhereExpression($this->options['group'], "$field $operator $min_value AND $max_value"); - } - - /** - * Override parent method, to add granularity options. - */ - protected function opSimple($field) { -- $timezone = $this->getTimezone(); -- $granularity = $this->options['value']['granularity']; -- -- // Convert value to DateTimePlus for additional processing. -- $date_value = $this->value['value']; -- $value = new DateTimePlus($date_value, new \DateTimeZone($timezone)); -- // Granularity requires some conversion. -- if ($granularity != 'second') { -- $value_array = [ -- 'year' => $value->format('Y'), -- 'month' => $value->format('n'), -- 'day' => $value->format('j'), -- 'hour' => $value->format('G'), -- 'minute' => $value->format('i'), -- 'second' => $value->format('s'), -- ]; -- $min = $max = $value_array; -- switch ($granularity) { -- case 'year': -- $min['month'] = '01'; -- $max['month'] = '12'; -- $max['day'] = '31'; -- case 'month': -- $min['day'] = '01'; -- if ($granularity != 'year') { -- $max['day'] = $value->format('t'); -- } -- case 'day': -- $min['hour'] = '00'; -- $max['hour'] = '23'; -- case 'hour': -- $min['minute'] = '00'; -- $max['minute'] = '59'; -- case 'minute': -- $min['second'] = '00'; -- $max['second'] = '59'; -- } -- -+ [$min_value, $max_value] = $this->getMinAndMax(); -+ if ($this->options['value']['granularity'] !== 'second') { - // Additional, operator-specific logic. -- if (substr($this->operator, 0, 1) == '>') { -- $value = $value->createFromArray($min, $timezone); -+ if ($this->operator[0] === '>') { -+ $value = $min_value; - } -- elseif (substr($this->operator, 0, 1) == '<') { -- $value = $value->createFromArray($max, $timezone); -+ elseif ($this->operator[0] === '<') { -+ $value = $max_value; - } - else { -- $min_value = $value->createFromArray($min, $timezone)->format('U'); -- $max_value = $value->createFromArray($max, $timezone)->format('U'); -- if ($this->operator == '=') { -+ if ($this->operator === '=') { - $operator = 'BETWEEN'; - } -- elseif ($this->operator == '!=') { -+ elseif ($this->operator === '!=') { - $operator = 'NOT BETWEEN'; - } -- $this->query->addWhereExpression($this->options['group'], "$field $operator $min_value AND $max_value"); -+ $this->query->addWhereExpression($this->options['group'], "$field $this->operator $min_value AND $max_value"); - return; - } - } -- -+ else { -+ $value = $min_value; -+ } - // This is safe because we forced the provided value to a DateTimePlus. -- $this->query->addWhereExpression($this->options['group'], "$field $this->operator " . $value->format('U')); -+ $this->query->addWhereExpression($this->options['group'], "$field $this->operator $value"); - } - - /** -@@ -262,6 +175,62 @@ class Date extends CoreDate implements ContainerFactoryPluginInterface { - return date_default_timezone_get(); - } - -+ /** -+ * Helper function to prepare min and max values for op* callbacks. -+ * -+ * @param bool $singleValueMode -+ * TRUE, if the values should be calculated on the single "value" field. -+ * Otherwise, set to FALSE to calculate based on "min" and "max" values. -+ * -+ * @return array -+ * An array containing the fully prepared min and max values ready to be -+ * used by query conditions. -+ */ -+ protected function getMinAndMax(bool $singleValueMode = TRUE): array { -+ $timezone = $this->getTimezone(); -+ $granularity = $this->options['value']['granularity']; -+ -+ // Convert form field value(s) to DateTimePlus for additional processing. -+ if ($singleValueMode) { -+ $a = $b = new DateTimePlus($this->value['value'], new \DateTimeZone($timezone)); -+ } -+ else { -+ $a = new DateTimePlus($this->value['min'], new \DateTimeZone($timezone)); -+ $b = new DateTimePlus($this->value['max'], new \DateTimeZone($timezone)); -+ } -+ $min = $max = [ -+ 'year' => $a->format('Y'), -+ 'month' => $a->format('n'), -+ 'day' => $a->format('j'), -+ 'hour' => $a->format('G'), -+ 'minute' => $a->format('i'), -+ 'second' => $a->format('s'), -+ ]; -+ switch ($granularity) { -+ case 'year': -+ $min['month'] = '01'; -+ $max['month'] = '12'; -+ $max['day'] = '31'; -+ case 'month': -+ $min['day'] = '01'; -+ if ($granularity !== 'year') { -+ $max['day'] = $b->format('t'); -+ } -+ case 'day': -+ $min['hour'] = '00'; -+ $max['hour'] = '23'; -+ case 'hour': -+ $min['minute'] = '00'; -+ $max['minute'] = '59'; -+ case 'minute': -+ $min['second'] = '00'; -+ $max['second'] = '59'; -+ } -+ $min_value = $a::createFromArray($min, $timezone)->format('U'); -+ $max_value = $b::createFromArray($max, $timezone)->format('U'); -+ return [$min_value, $max_value]; -+ } -+ - /** - * {@inheritdoc} - */ -diff --git a/src/Plugin/views/filter/DateRangeContains.php b/src/Plugin/views/filter/DateRangeContains.php -new file mode 100644 -index 0000000000000000000000000000000000000000..56f739022ec94b69b37637a208644ddf0560807d ---- /dev/null -+++ b/src/Plugin/views/filter/DateRangeContains.php -@@ -0,0 +1,112 @@ -+<?php -+ -+namespace Drupal\smart_date\Plugin\views\filter; -+ -+use Drupal\Component\Datetime\DateTimePlus; -+ -+/** -+ * Date range views filter to filter records that contain a given value. -+ * -+ * @ingroup views_filter_handlers -+ * -+ * @ViewsFilter("date_range_contains") -+ */ -+class DateRangeContains extends Date { -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function defineOptions(): array { -+ $options = parent::defineOptions(); -+ $options['operator']['default'] = 'daterange_contains'; -+ $options['value']['contains']['granularity']['default'] = 'day'; -+ return $options; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function operators(): array { -+ return [ -+ 'daterange_contains' => [ -+ 'title' => $this->t('Contains'), -+ 'method' => 'opContains', -+ 'short' => $this->t('contains'), -+ 'values' => 1, -+ ], -+ 'daterange_not_contains' => [ -+ 'title' => $this->t('Does not contain'), -+ 'method' => 'opContains', -+ 'short' => $this->t('not contains'), -+ 'values' => 1, -+ ], -+ 'daterange_starts_or_ends' => [ -+ 'title' => $this->t('Starts or ends at'), -+ 'method' => 'opContains', -+ 'short' => $this->t('starts or ends'), -+ 'values' => 1, -+ ], -+ 'daterange_contains_range' => [ -+ 'title' => $this->t('Contains range'), -+ 'method' => 'opContainsRange', -+ 'short' => $this->t('contains range'), -+ 'values' => 2, -+ ], -+ 'daterange_not_contains_range' => [ -+ 'title' => $this->t('Does not contain range'), -+ 'method' => 'opContainsRange', -+ 'short' => $this->t('not contains range'), -+ 'values' => 2, -+ ], -+ 'daterange_starts_or_ends_range' => [ -+ 'title' => $this->t('Starts or ends at range'), -+ 'method' => 'opContainsRange', -+ 'short' => $this->t('starts or ends range'), -+ 'values' => 2, -+ ], -+ ]; -+ } -+ -+ /** -+ * Add conditions to the query. -+ */ -+ protected function opContains($field): void { -+ [$min_value, $max_value] = $this->getMinAndMax(); -+ $this->containsConditions($field, $min_value, $max_value); -+ } -+ -+ /** -+ * Add conditions to the query. -+ */ -+ protected function opContainsRange($field): void { -+ [$min_value, $max_value] = $this->getMinAndMax(FALSE); -+ $this->containsConditions($field, $min_value, $max_value); -+ } -+ -+ /** -+ * Helper function to add the conditions to the query. -+ * -+ * @param string $field -+ * The field name. -+ * @param string $min_value -+ * The minimum date(time) value. -+ * @param string $max_value -+ * The maximum date(time) value. -+ */ -+ protected function containsConditions(string $field, string $min_value, string $max_value): void { -+ switch ($this->operator) { -+ case 'daterange_contains': -+ $this->query->addWhereExpression($this->options['group'], "{$field}__value <= $min_value AND {$field}__end_value >= $max_value"); -+ break; -+ -+ case 'daterange_not_contains': -+ $this->query->addWhereExpression($this->options['group'], "{$field}__value >= $max_value OR {$field}__end_value <= $min_value"); -+ break; -+ -+ case 'daterange_starts_or_ends': -+ $this->query->addWhereExpression($this->options['group'], "({$field}__value >= $min_value AND {$field}__value <= $max_value) OR ({$field}__end_value >= $min_value AND {$field}__end_value <= $max_value)"); -+ break; -+ } -+ } -+ -+} diff --git a/patches/d10/3413508.diff b/patches/d10/3413508.diff deleted file mode 100644 index 2473d57814effd9b3e02364c1bf7fa9a80c4b304..0000000000000000000000000000000000000000 --- a/patches/d10/3413508.diff +++ /dev/null @@ -1,38 +0,0 @@ -diff --git a/core/modules/system/src/Access/SystemAdminMenuBlockAccessCheck.php b/core/modules/system/src/Access/SystemAdminMenuBlockAccessCheck.php -index a527605ea7..147b8d59b3 100644 ---- a/core/modules/system/src/Access/SystemAdminMenuBlockAccessCheck.php -+++ b/core/modules/system/src/Access/SystemAdminMenuBlockAccessCheck.php -@@ -82,24 +82,21 @@ protected function hasAccessToChildMenuItems(MenuLinkInterface $link, AccountInt - ->setTopLevelOnly() - ->onlyEnabledLinks(); - -- $tree = $this->menuLinkTree->load(NULL, $parameters); -- -- if (empty($tree)) { -- $route = $this->router->getRouteCollection()->get($link->getRouteName()); -- if ($route) { -- return AccessResult::allowedIf(empty($route->getRequirement('_access_admin_menu_block_page'))); -- } -- return AccessResult::neutral(); -+ $route = $this->router->getRouteCollection()->get($link->getRouteName()); -+ if ($route && empty($route->getRequirement('_access_admin_menu_block_page')) && empty($route->getRequirement('_access_admin_overview_page'))) { -+ return AccessResult::allowed(); - } - -- foreach ($tree as $element) { -+ foreach ($this->menuLinkTree->load(NULL, $parameters) as $element) { - if (!$this->accessManager->checkNamedRoute($element->link->getRouteName(), $element->link->getRouteParameters(), $account)) { - continue; - } - -- // If access is allowed to this element in the tree check for access to -- // its own children. -- return AccessResult::allowedIf($this->hasAccessToChildMenuItems($element->link, $account)->isAllowed()); -+ // If access is allowed to this element in the tree, check for access to -+ // any of its own children. -+ if ($this->hasAccessToChildMenuItems($element->link, $account)->isAllowed()) { -+ return AccessResult::allowed(); -+ } - } - return AccessResult::neutral(); - } diff --git a/patches/d10/3414975.diff b/patches/d10/3414975.diff deleted file mode 100644 index dc54325c2fe3e445f1209dd41dc1f200e3cbcffe..0000000000000000000000000000000000000000 --- a/patches/d10/3414975.diff +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/src/Theme/RoleThemeSwitcherNegotiator.php b/src/Theme/RoleThemeSwitcherNegotiator.php -index 2dface464f6bafec320c940bf69d4c7c1e8a169b..eb651dd53f200c26e38ed9537b3d94be01a770a0 100644 ---- a/src/Theme/RoleThemeSwitcherNegotiator.php -+++ b/src/Theme/RoleThemeSwitcherNegotiator.php -@@ -81,9 +81,9 @@ class RoleThemeSwitcherNegotiator implements ThemeNegotiatorInterface { - $user_roles = $this->currentUser->getRoles(); - foreach ($roles as $rid => $config) { - if (in_array($rid, $user_roles)) { -- $this->theme = $config['theme']; -+ $this->theme = $config['theme'] ?? $this->theme; - if ($admin_path) { -- $this->theme = $config['admin_theme']; -+ $this->theme = $config['admin_theme'] ?? $this->theme; - } - break; - } diff --git a/patches/d10/3416324.diff b/patches/d10/3416324.diff deleted file mode 100644 index e7c7eda4c8ae3bfb27787983d249704a899e8a9e..0000000000000000000000000000000000000000 --- a/patches/d10/3416324.diff +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/group.tokens.inc b/group.tokens.inc -index 686b2756bd7d49bbd89b4ea7a3633473c933bc7b..1f904fdcb11ee3c11fa628c4304eeb2a44ecdca4 100644 ---- a/group.tokens.inc -+++ b/group.tokens.inc -@@ -272,5 +272,21 @@ function group_tokens($type, $tokens, array $data, array $options, BubbleableMet - if ($changed_tokens = $token_service->findWithPrefix($tokens, 'changed')) { - $replacements += $token_service->generate('date', $changed_tokens, ['date' => $group_relationship->getChangedTime()], $options, $bubbleable_metadata); - } -+ -+ // Provide access to tokens of the related entity. -+ if (str_starts_with($name, 'entity:')) { -+ $entity = $group_relationship->getEntity(); -+ $entityTypeId = $entity->getEntityTypeId(); -+ $bubbleable_metadata->addCacheableDependency($entity); -+ $parts = explode(':', $name); -+ $parts[0] = $entityTypeId; -+ $realTokens = [ -+ implode(':', $parts) => $original, -+ ]; -+ if ($entity_tokens = $token_service->findWithPrefix($realTokens, $entityTypeId)) { -+ $replacements += $token_service->generate($entityTypeId, $entity_tokens, [$entityTypeId => $entity], $options, $bubbleable_metadata); -+ } -+ } -+ - } - } diff --git a/patches/d10/3419076.diff b/patches/d10/3419076.diff deleted file mode 100644 index 7e60f2c40654a5e91d98c182f23cd3868bfbcac6..0000000000000000000000000000000000000000 --- a/patches/d10/3419076.diff +++ /dev/null @@ -1,36 +0,0 @@ -diff --git a/gin.libraries.yml b/gin.libraries.yml -index 07e1716c04a43dae28af7990b518c62362370052..631035543c82028d62ca89498ab4ac0a20cb5302 100644 ---- a/gin.libraries.yml -+++ b/gin.libraries.yml -@@ -43,6 +43,7 @@ navigation: - - gin/node_preview - - gin/responsive_preview - - gin/contextual_links -+ - gin/breadcrumb - - gin/settings_tray - - gin/settings_tray_edit - - core/drupalSettings -@@ -63,6 +64,7 @@ gin_toolbar: - - gin/node_preview - - gin/responsive_preview - - gin/contextual_links -+ - gin/breadcrumb - - gin/settings_tray - - gin/settings_tray_edit - - core/drupalSettings -@@ -83,6 +85,7 @@ gin_horizontal_toolbar: - - gin/node_preview - - gin/responsive_preview - - gin/contextual_links -+ - gin/breadcrumb - - gin/settings_tray - - gin/settings_tray_edit - - core/once -@@ -99,6 +102,7 @@ gin_classic_toolbar: - - gin/coffee - - gin/node_preview - - gin/contextual_links -+ - gin/breadcrumb - - gin/settings_tray_edit - - core/once - diff --git a/patches/d10/3419914.diff b/patches/d10/3419914.diff deleted file mode 100644 index 7ee33e61b5cad9585a6b579ffd960432fc93e9c7..0000000000000000000000000000000000000000 --- a/patches/d10/3419914.diff +++ /dev/null @@ -1,10 +0,0 @@ -diff --git a/src/Commands/core/DeployHookCommands.php b/src/Commands/core/DeployHookCommands.php -@@ -37,7 +37,7 @@ - \Drupal::getContainer()->getParameter('app.root'), - \Drupal::getContainer()->getParameter('site.path'), - array_keys(\Drupal::service('module_handler')->getModuleList()), -- \Drupal::service('keyvalue')->get('deploy_hook') -+ \Drupal::service('keyvalue') - ) extends UpdateRegistry { - public function setUpdateType(string $type): void - { diff --git a/patches/d10/3420862.diff b/patches/d10/3420862.diff deleted file mode 100644 index c13587ae5fb4f509989fa1d75007c39b65241064..0000000000000000000000000000000000000000 --- a/patches/d10/3420862.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php -index 1b2d8f02201df0131d12a8a2587680032054c034..5ba5d79cdb9f6855b7c239957a8b026f1824f2da 100644 ---- a/core/modules/comment/src/CommentForm.php -+++ b/core/modules/comment/src/CommentForm.php -@@ -325,7 +325,7 @@ public function buildEntity(array $form, FormStateInterface $form_state) { - // 1) Filter it into HTML - // 2) Strip out all HTML tags - // 3) Convert entities back to plain-text. -- $comment_text = $comment->comment_body->processed; -+ $comment_text = $comment->comment_body->processed ?? ''; - $comment->setSubject(Unicode::truncate(trim(Html::decodeEntities(strip_tags($comment_text))), 29, TRUE, TRUE)); - } - // Edge cases where the comment body is populated only by HTML tags will diff --git a/patches/d10/3426621.diff b/patches/d10/3426621.diff deleted file mode 100644 index 2dd60108f7ffa36a0218ebc0c239ff86d3b582d0..0000000000000000000000000000000000000000 --- a/patches/d10/3426621.diff +++ /dev/null @@ -1,48 +0,0 @@ -diff --git a/src/Commands/ConfigSplitCommands.php b/src/Commands/ConfigSplitCommands.php -index ed6540858eaae341e59473cec939fd32a9872159..040f67ec2fb5856e70b6ed587d348226298471d1 100644 ---- a/src/Commands/ConfigSplitCommands.php -+++ b/src/Commands/ConfigSplitCommands.php -@@ -93,14 +93,17 @@ public function splitActivate($split) { - * - * @command config-split:deactivate - * -+ * @option override -+ * Allows the deactivation via override. -+ * - * @usage drush config-split:deactivate development - * Deactivate configuration of the "development" split - * - * Propose an alias at: - * https://www.drupal.org/project/config_split/issues/3181368 - */ -- public function splitDeactivate($split) { -- return $this->cliService->ioDeactivate($split, $this->io(), 'dt') ? DrushCommands::EXIT_SUCCESS : DrushCommands::EXIT_FAILURE; -+ public function splitDeactivate($split, $options = ['override' => FALSE]) { -+ return $this->cliService->ioDeactivate($split, $this->io(), 'dt', FALSE, $options['override']) ? DrushCommands::EXIT_SUCCESS : DrushCommands::EXIT_FAILURE; - } - - /** -diff --git a/src/ConfigSplitCliService.php b/src/ConfigSplitCliService.php -index 35dff63b844ad4131a1e960692b5f8f28cf54f03..42dc957df683c4a0822a88f79af5dedb31d3bae6 100644 ---- a/src/ConfigSplitCliService.php -+++ b/src/ConfigSplitCliService.php -@@ -186,15 +186,17 @@ public function ioActivate(string $split, $io, callable $t, $confirmed = FALSE): - * The translation function akin to t(). - * @param bool $confirmed - * Whether the import is already confirmed by the console input. -+ * @param bool $override -+ * Allows the deactivation via override. - */ -- public function ioDeactivate(string $split, $io, callable $t, $confirmed = FALSE): bool { -+ public function ioDeactivate(string $split, $io, callable $t, $confirmed = FALSE, $override = FALSE): bool { - $config = $this->getSplitFromArgument($split, $io, $t); - if ($config === NULL) { - return FALSE; - } - - $message = $t('Deactivate the split config configuration?'); -- $storage = $this->manager->singleDeactivate($config, FALSE); -+ $storage = $this->manager->singleDeactivate($config, FALSE, $override); - - if ($confirmed || $io->confirm($message)) { - return $this->tryImport($storage, $io, $t); diff --git a/patches/d10/3427112.diff b/patches/d10/3427112.diff deleted file mode 100644 index 6c71631b7b204330a30b12e3b8843b6e797151ec..0000000000000000000000000000000000000000 --- a/patches/d10/3427112.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/admin_toolbar_links_access_filter/admin_toolbar_links_access_filter.module b/admin_toolbar_links_access_filter/admin_toolbar_links_access_filter.module -index 7197b740c0719db1948cbea36316a192baaa2333..9a47e758108e306fc669640f69b7a511a423134b 100644 ---- a/admin_toolbar_links_access_filter/admin_toolbar_links_access_filter.module -+++ b/admin_toolbar_links_access_filter/admin_toolbar_links_access_filter.module -@@ -90,7 +90,7 @@ function admin_toolbar_links_access_filter_filter_non_accessible_links(array &$i - } - - // Check, if user has access rights to the route. -- if (!$access_manager->checkNamedRoute($route_name, $route_params)) { -+ if ($route_name === NULL || !$access_manager->checkNamedRoute($route_name, $route_params)) { - unset($items[$menu_id]); - } - else { diff --git a/patches/d10/3433267.diff b/patches/d10/3433267.diff deleted file mode 100644 index d12fc92543c2250a3340e1d91a10a16ce0505ab6..0000000000000000000000000000000000000000 --- a/patches/d10/3433267.diff +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/smart_date.module b/smart_date.module -index 040d7d44aefc093d19eaf54a4cde9d4110bbe1e4..0487dbefaf7e3b9244522ba19c608dd37fcf398c 100644 ---- a/smart_date.module -+++ b/smart_date.module -@@ -177,3 +177,19 @@ function smart_date_views_plugins_filter_alter(array &$plugins) { - function smart_date_views_plugins_argument_alter(array &$plugins) { - $plugins['date']['class'] = 'Drupal\smart_date\Plugin\views\argument\Date'; - } -+ -+/** -+ * Implements hook_module_implements_alter(). -+ * -+ * Move smart_date hook_tokens() implementation to the end of the list to avoid -+ * conflict with the Tokens module's remplacement for custom date format/values. -+ */ -+function smart_date_module_implements_alter(&$implementations, $hook) { -+ switch ($hook) { -+ case 'tokens': -+ $group = $implementations['smart_date']; -+ unset($implementations['smart_date']); -+ $implementations['smart_date'] = $group; -+ break; -+ } -+} diff --git a/patches/d10/3436790.diff b/patches/d10/3436790.diff deleted file mode 100644 index 2700d5a53e3a5609cf18a961b26f68756304439f..0000000000000000000000000000000000000000 --- a/patches/d10/3436790.diff +++ /dev/null @@ -1,25 +0,0 @@ -diff --git a/config_update_ui/src/Commands/ConfigUpdateUiCommands.php b/config_update_ui/src/Commands/ConfigUpdateUiCommands.php -index 7c4c7eba12de987fa9904c0ed3c9990776b0b161..97356e53363f007c60a4f25a61fca59495aba7f5 100644 ---- a/config_update_ui/src/Commands/ConfigUpdateUiCommands.php -+++ b/config_update_ui/src/Commands/ConfigUpdateUiCommands.php -@@ -339,10 +339,18 @@ class ConfigUpdateUiCommands extends DrushCommands { - * @aliases cfrm,config-revert-multiple - */ - public function revertMultiple(string $type, string $name) { -+ if ($type === 'module') { -+ $extension_config = \Drupal::configFactory()->get('core.extension'); -+ $orgWeight = (int) $extension_config->get("module.$name"); -+ module_set_weight($name, 999); -+ } - $different = $this->getDifferentItems($type, $name); -- foreach ($different as $name) { -- $this->revert($name); -+ foreach ($different as $itemName) { -+ $this->revert($itemName); - } -+ if ($type === 'module') { -+ module_set_weight($name, $orgWeight); -+ } - } - - /** diff --git a/patches/d10/3437013.diff b/patches/d10/3437013.diff deleted file mode 100644 index 44aa0318fa034153635439018ee159c803a4cf4e..0000000000000000000000000000000000000000 --- a/patches/d10/3437013.diff +++ /dev/null @@ -1,62 +0,0 @@ -diff --git a/token.tokens.inc b/token.tokens.inc -index f8925aa61c359a1983c3dd9fcf2dba5e798c8035..6a6f9b88eccf877fcda3ddb4cc64b924090c9c68 100644 ---- a/token.tokens.inc -+++ b/token.tokens.inc -@@ -339,6 +339,11 @@ function token_token_info() { - 'description' => t('The URL of the current page.'), - 'type' => 'url', - ]; -+ $info['tokens']['current-page']['referrer-url'] = [ -+ 'name' => t('Referrer URL'), -+ 'description' => t('The URL of the current page referrer.'), -+ 'type' => 'url', -+ ]; - $info['tokens']['current-page']['page-number'] = [ - 'name' => t('Page number'), - 'description' => t('The page number of the current page when viewing paged lists.'), -@@ -348,6 +353,11 @@ function token_token_info() { - 'description' => t('The value of a specific query string field of the current page.'), - 'dynamic' => TRUE, - ]; -+ $info['tokens']['current-page']['referrer-query'] = [ -+ 'name' => t('Referrer query string value'), -+ 'description' => t('The value of a specific query string field of the current page referrer.'), -+ 'dynamic' => TRUE, -+ ]; - $info['tokens']['current-page']['interface-language'] = [ - 'name' => t('Interface language'), - 'description' => t('The active user interface language.'), -@@ -904,6 +914,10 @@ function token_tokens($type, array $tokens, array $data, array $options, Bubblea - } - break; - -+ case 'referrer-url': -+ $replacements[$original] = \Drupal::request()->headers->get('referer'); -+ break; -+ - case 'page-number': - if ($page = $request->query->get('page')) { - // @see PagerDefault::execute() -@@ -952,6 +966,22 @@ function token_tokens($type, array $tokens, array $data, array $options, Bubblea - } - } - -+ // [current-page:referrer-query] dynamic tokens. -+ if ($query_tokens = \Drupal::token()->findWithPrefix($tokens, 'referrer-query')) { -+ if ($referrer = \Drupal::request()->headers->get('referer')) { -+ if ($query = parse_url($referrer, PHP_URL_QUERY)) { -+ $queryArgs = []; -+ foreach (explode('&', $query) as $item) { -+ [$key, $value] = explode('=', $item); -+ $queryArgs[$key] = urldecode($value); -+ } -+ foreach ($query_tokens as $name => $original) { -+ $replacements[$original] = $queryArgs[$name] ?? ''; -+ } -+ } -+ } -+ } -+ - // Chained token relationships. - if ($url_tokens = \Drupal::token()->findWithPrefix($tokens, 'url')) { - $url = NULL; diff --git a/patches/d10/3437380.diff b/patches/d10/3437380.diff deleted file mode 100644 index ee6a0fdc95a50394830d2800a6389ecef8473542..0000000000000000000000000000000000000000 --- a/patches/d10/3437380.diff +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/src/PathautoFieldItemList.php b/src/PathautoFieldItemList.php -index 300675ca97c3a327d74a686743d8124c2c7805b0..45fb0ab0fcced41b694941bcf041d9890f6a70fa 100644 ---- a/src/PathautoFieldItemList.php -+++ b/src/PathautoFieldItemList.php -@@ -9,7 +9,7 @@ class PathautoFieldItemList extends PathFieldItemList { - /** - * @{inheritdoc} - */ -- protected function delegateMethod($method) { -+ protected function delegateMethod($method, ...$args) { - // @todo Workaround until this is fixed, see - // https://www.drupal.org/project/drupal/issues/2946289. - $this->ensureComputedValue(); -@@ -17,7 +17,6 @@ class PathautoFieldItemList extends PathFieldItemList { - // Duplicate the logic instead of calling the parent due to the dynamic - // arguments. - $result = []; -- $args = array_slice(func_get_args(), 1); - foreach ($this->list as $delta => $item) { - // call_user_func_array() is way slower than a direct call so we avoid - // using it if have no parameters. diff --git a/patches/d10/3438893.diff b/patches/d10/3438893.diff deleted file mode 100644 index a1114f5135b1b6bf90a0d24042bec3d65a57aaf1..0000000000000000000000000000000000000000 --- a/patches/d10/3438893.diff +++ /dev/null @@ -1,68 +0,0 @@ -diff --git a/core/modules/views/src/Plugin/views/relationship/EntityReverse.php b/core/modules/views/src/Plugin/views/relationship/EntityReverse.php -index 123b5c0dd4bbe6689b6a8c3dee414bbfda281ec4..da6b944df366280db25cc2b9d1a6ff41dc8a6264 100644 ---- a/core/modules/views/src/Plugin/views/relationship/EntityReverse.php -+++ b/core/modules/views/src/Plugin/views/relationship/EntityReverse.php -@@ -59,35 +59,41 @@ public static function create(ContainerInterface $container, array $configuratio - */ - public function query() { - $this->ensureMyTable(); -- // First, relate our base table to the current base table to the -- // field, using the base table's id field to the field's column. -- $views_data = Views::viewsData()->get($this->table); -- $left_field = $views_data['table']['base']['field']; -- -- $first = [ -- 'left_table' => $this->tableAlias, -- 'left_field' => $left_field, -- 'table' => $this->definition['field table'], -- 'field' => $this->definition['field field'], -- 'adjusted' => TRUE, -- ]; -- if (!empty($this->options['required'])) { -- $first['type'] = 'INNER'; -+ if ($this->definition['field table'] !== $this->definition['base']) { -+ // First, relate our base table to the current base table to the -+ // field, using the base table's id field to the field's column. -+ $views_data = Views::viewsData()->get($this->table); -+ $left_field = $views_data['table']['base']['field']; -+ $first = [ -+ 'left_table' => $this->tableAlias, -+ 'left_field' => $left_field, -+ 'table' => $this->definition['field table'], -+ 'field' => $this->definition['field field'], -+ 'adjusted' => TRUE, -+ ]; -+ if (!empty($this->options['required'])) { -+ $first['type'] = 'INNER'; -+ } -+ -+ if (!empty($this->definition['join_extra'])) { -+ $first['extra'] = $this->definition['join_extra']; -+ } -+ -+ $first_join = $this->joinManager->createInstance('standard', $first); -+ -+ $this->first_alias = $this->query->addTable($this->definition['field table'], $this->relationship, $first_join); -+ $entity_id = 'entity_id'; - } -- -- if (!empty($this->definition['join_extra'])) { -- $first['extra'] = $this->definition['join_extra']; -+ else { -+ $this->first_alias = $this->tableAlias; -+ $entity_id = $this->definition['field field']; - } - -- $first_join = $this->joinManager->createInstance('standard', $first); -- -- $this->first_alias = $this->query->addTable($this->definition['field table'], $this->relationship, $first_join); -- - // Second, relate the field table to the entity specified using - // the entity id on the field table and the entity's id field. - $second = [ - 'left_table' => $this->first_alias, -- 'left_field' => 'entity_id', -+ 'left_field' => $entity_id, - 'table' => $this->definition['base'], - 'field' => $this->definition['base field'], - 'adjusted' => TRUE, diff --git a/patches/d10/3439988.diff b/patches/d10/3439988.diff deleted file mode 100644 index 719ba92be7216bf1576bf64ab61df2c88975a950..0000000000000000000000000000000000000000 --- a/patches/d10/3439988.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/js/navigation/sidebar.js b/js/navigation/sidebar.js -index c8c699b01e12199487bb82f4ea930be2fb700e55..3f830c738a43bad1b20e2ad19ba1567d3663beb4 100644 ---- a/js/navigation/sidebar.js -+++ b/js/navigation/sidebar.js -@@ -110,7 +110,7 @@ - - // Gin Custom start --------------------- - if (toState === true && window.innerWidth < breakpointLarge) { -- Drupal.ginSidebar.collapseSidebar(); -+ Drupal.ginSidebar?.collapseSidebar(); - } - // Gin Custom end ------------------------ - } diff --git a/patches/d10/3441940.diff b/patches/d10/3441940.diff deleted file mode 100644 index 87b441d3457d58dd1085a38518de20fb3519392d..0000000000000000000000000000000000000000 --- a/patches/d10/3441940.diff +++ /dev/null @@ -1,1699 +0,0 @@ -diff --git a/dist/css/base/gin.css b/dist/css/base/gin.css -index e20f8508dda41502c6857d1f875cc70a542f297d..4dc5cdab9745ec2668bec7694b0b94ff674ea387 100644 ---- a/dist/css/base/gin.css -+++ b/dist/css/base/gin.css -@@ -1486,7 +1486,7 @@ table td .group-label { - } - - .sortable-heading > a::before { -- bottom: -1px; -+ bottom: -2px; - } - - .sortable-heading > a::after { -diff --git a/dist/css/layout/core_navigation.css b/dist/css/layout/core_navigation.css -new file mode 100644 -index 0000000000000000000000000000000000000000..69310413c7d080b7119fe9efd322cfe7099d9fbd ---- /dev/null -+++ b/dist/css/layout/core_navigation.css -@@ -0,0 +1,349 @@ -+:root { -+ --gin-toolbar-height: 39px; -+ --gin-toolbar-secondary-height: 48px; -+ --gin-scroll-offset: 72px; -+ --gin-toolbar-y-offset: var(--gin-toolbar-height); -+ --gin-toolbar-x-offset: 0px; -+ --gin-sticky-offset: 0px; -+ --gin-icon-size-toolbar: 20px; -+} -+ -+@media (min-width: 61em) { -+ :root { -+ --gin-toolbar-height: 0px; -+ --gin-toolbar-secondary-height: 52px; -+ --gin-toolbar-x-offset: var(--drupal-displace-offset-left, 64px); -+ --gin-scroll-offset: 130px; -+ --gin-sticky-offset: var(--gin-height-sticky); -+ } -+} -+ -+@media (min-width: 61em) { -+ html.admin-toolbar-expanded { -+ --gin-toolbar-x-offset: var(--drupal-displace-offset-left, 264px); -+ } -+} -+ -+.admin-toolbar { -+ --admin-toolbar-sidebar-width: 4rem; -+ --admin-toolbar-font-family: var(--gin-font); -+ --admin-toolbar-color-white: var(--gin-bg-layer); -+ --admin-toolbar-color-gray-050: var(--gin-bg-layer2); -+ --admin-toolbar-color-gray-100: var(--gin-border-color-layer); -+ --admin-toolbar-color-gray-200: var(--gin-border-color-layer2); -+ --admin-toolbar-color-gray-800: var(--gin-color-text); -+ --admin-toolbar-color-gray-990: var(--gin-color-primary-active); -+} -+ -+[data-admin-toolbar=expanded] .admin-toolbar { -+ --admin-toolbar-sidebar-width: 16.5rem; -+} -+ -+@media (--admin-toolbar-tablet) { -+ [data-admin-toolbar=expanded] .admin-toolbar { -+ --admin-toolbar-sidebar-width: calc(16.5 * var(--admin-toolbar-rem)); -+ } -+} -+ -+.gin--core-navigation .gin-secondary-toolbar:not(.gin-secondary-toolbar--frontend) { -+ margin-bottom: var(--gin-spacing-xxs); -+} -+ -+.gin--core-navigation .gin-secondary-toolbar--frontend { -+ z-index: 102; -+ -webkit-margin-start: var(--gin-toolbar-x-offset); -+ margin-inline-start: var(--gin-toolbar-x-offset); -+} -+ -+.toolbar-menu__link:hover::before { -+ inline-size: var(--gin-spacing-xxs); -+} -+ -+.admin-toolbar__content, -+.admin-toolbar__footer { -+ padding: var(--gin-spacing-s); -+} -+ -+.admin-toolbar__footer, -+.admin-toolbar__content .toolbar-block:nth-last-child(n+2)::after { -+ border-block-start-color: var(--gin-border-color-secondary); -+} -+ -+.toolbar-anti-flicker.toolbar-loading.toolbar-fixed body.gin--core-navigation, -+body.gin--core-navigation { -+ padding-top: unset !important; -+} -+ -+.admin-toolbar__logo { -+ line-height: 0; -+ border-radius: 10px; -+} -+ -+.admin-toolbar__logo svg rect { -+ fill: var(--gin-color-primary); -+} -+ -+.admin-toolbar__logo svg path { -+ fill: var(--gin-bg-app); -+} -+ -+.admin-toolbar__logo:hover svg rect { -+ fill: var(--gin-color-primary-hover); -+} -+ -+.admin-toolbar__logo:active svg rect, .admin-toolbar__logo:focus svg rect { -+ fill: var(--gin-color-primary-active); -+} -+ -+.toolbar-button, -+.toolbar-menu__link { -+ color: var(--gin-color-text); -+ font-weight: var(--gin-font-weight-normal); -+ font-variation-settings: unset; -+} -+ -+.toolbar-button, .toolbar-button:hover, .toolbar-button:active, -+.toolbar-menu__link, -+.toolbar-menu__link:hover, -+.toolbar-menu__link:active { -+ outline: none; -+ box-shadow: none; -+} -+ -+.toolbar-button:hover, -+.toolbar-menu__link:hover { -+ color: var(--gin-color-primary); -+ background-color: var(--gin-color-primary-light); -+} -+ -+.toolbar-button:active, .toolbar-button:focus, .toolbar-button:hover:focus, -+.toolbar-menu__link:active, -+.toolbar-menu__link:focus, -+.toolbar-menu__link:hover:focus { -+ color: var(--gin-color-primary-hover); -+ background-color: var(--gin-color-primary-light-hover); -+} -+ -+.toolbar-button[aria-expanded=true], -+.toolbar-button--large { -+ font-weight: var(--gin-font-weight-bold); -+} -+ -+.admin-toolbar__logo:focus, -+.toolbar-button:focus, -+.admin-toolbar__expand-button:focus { -+ outline: none; -+ box-shadow: 0 0 0 1px var(--gin-color-focus-border), 0 0 0 4px var(--gin-color-focus); -+} -+ -+.toolbar-menu__link:focus { -+ outline: none; -+ box-shadow: inset 0 0 0 1px var(--gin-color-focus-border), inset 0 0 0 3px var(--gin-color-focus); -+} -+ -+.toolbar-button.current { -+ background-color: var(--gin-bg-item-hover); -+} -+ -+.toolbar-button.current:hover { -+ background-color: var(--gin-color-primary-light); -+} -+ -+.toolbar-button.current:active, .toolbar-button.current:focus, .toolbar-button.current:hover:focus { -+ background-color: var(--gin-color-primary-light-hover); -+} -+ -+.toolbar-button.current.is-active, -+.toolbar-button:has(+ .toolbar-popover__wrapper .is-active) { -+ color: var(--gin-color-primary); -+ background-color: var(--gin-color-primary-light); -+} -+ -+.toolbar-button.current.is-active::before, -+.toolbar-button:has(+ .toolbar-popover__wrapper .is-active)::before { -+ background: var(--gin-color-primary); -+} -+ -+[class*=toolbar-button--icon] { -+ --icon: url("../../media/sprite.svg#fallback-view"); /* Default icon, aka --basic */ -+} -+ -+[class*=toolbar-button--icon]::before { -+ width: var(--gin-icon-size-toolbar); -+ height: var(--gin-icon-size-toolbar); -+ background-color: var(--gin-icon-color); -+ -webkit-mask-size: 100% 100%; -+ mask-size: 100% 100%; -+ -webkit-mask-image: var(--icon); -+ mask-image: var(--icon); -+} -+ -+[class*=toolbar-button--icon]:hover::before, [class*=toolbar-button--icon]:focus::before { -+ background-color: var(--gin-color-primary); -+} -+ -+.toolbar-button--icon--admin-toolbar-tools-help::before { -+ --icon: url("../../media/sprite.svg#tool-view"); -+} -+ -+.toolbar-button--icon--system-themes-page::before { -+ --icon: url("../../media/sprite.svg#appearance-view"); -+} -+ -+.toolbar-button--icon--navigation-blocks::before { -+ --icon: url("../../media/sprite.svg#blocks-view"); -+} -+ -+.toolbar-button--icon--shortcuts::before { -+ --icon: url("../../media/sprite.svg#shortcut-view"); -+} -+ -+.toolbar-button--icon--system-admin-config::before { -+ --icon: url("../../media/sprite.svg#config-view"); -+} -+ -+.toolbar-button--icon--navigation-content::before { -+ --icon: url("../../media/sprite.svg#content-view"); -+} -+ -+.toolbar-button--icon--navigation-create::before { -+ --icon: url("../../media/sprite.svg#create-view"); -+} -+ -+.toolbar-button--icon--system-modules-list::before { -+ --icon: url("../../media/sprite.svg#extend-new-view"); -+} -+ -+.toolbar-button--icon--navigation-files::before { -+ --icon: url("../../media/sprite.svg#files-view"); -+} -+ -+.toolbar-button--icon--help::before { -+ --icon: url("../../media/sprite.svg#help-view"); -+} -+ -+.toolbar-button--icon--navigation-media::before { -+ --icon: url("../../media/sprite.svg#media-view"); -+} -+ -+.toolbar-button--icon--entity-user-collection::before { -+ --icon: url("../../media/sprite.svg#people-new-view"); -+} -+ -+.toolbar-button--icon--system-admin-reports::before { -+ --icon: url("../../media/sprite.svg#reports-view"); -+} -+ -+.toolbar-button--icon--system-admin-structure::before { -+ --icon: url("../../media/sprite.svg#structure-view"); -+} -+ -+.toolbar-button--icon--announcements-feed-announcement::before { -+ --icon: url("../../media/sprite.svg#announcement-view"); -+} -+ -+.toolbar-link--sidebar-toggle[aria-expanded=false]::before, -+.toolbar-link--sidebar-toggle[aria-expanded=true]::before { -+ --icon: url("../../media/sprite.svg#chevron-down-view"); -+} -+ -+.toolbar-button--icon--user::before { -+ --icon: url("../../media/sprite.svg#user-view"); -+} -+ -+.toolbar-button--icon--burger::before { -+ --icon: url("../../media/sprite.svg#hamburger-view"); -+} -+ -+.toolbar-button--icon--back::before { -+ --icon: url("../../media/sprite.svg#nav-toggle-toleft-view"); -+} -+ -+.toolbar-button--icon--cross::before { -+ --icon: url("../../media/sprite.svg#close-view"); -+} -+ -+#navigation-link-navigationcreate:first-child { -+ -webkit-margin-after: var(--admin-toolbar-space-8); -+ margin-block-end: var(--admin-toolbar-space-8); -+} -+ -+#navigation-link-navigationcreate:first-child::after { -+ content: ""; -+ display: block; -+ -webkit-margin-before: var(--admin-toolbar-space-12); -+ margin-block-start: var(--admin-toolbar-space-12); -+ -webkit-border-after: 1px solid var(--gin-border-color-secondary); -+ border-block-end: 1px solid var(--gin-border-color-secondary); -+} -+ -+.admin-toolbar__expand-button { -+ -webkit-margin-before: calc(var(--gin-spacing-xs) * -1); -+ margin-block-start: calc(var(--gin-spacing-xs) * -1); -+} -+ -+.admin-toolbar__expand-button, .admin-toolbar__expand-button:hover { -+ color: var(--gin-color-text-light); -+ border-color: var(--gin-border-color); -+} -+ -+.toolbar-popover--expanded { -+ background-color: transparent; -+} -+ -+.toolbar-popover--expanded > .toolbar-button { -+ background-color: var(--gin-bg-item-hover); -+} -+ -+.gin--dark-mode .toolbar-popover__wrapper { -+ -webkit-border-start: 2px solid var(--gin-border-color-secondary); -+ border-inline-start: 2px solid var(--gin-border-color-secondary); -+} -+ -+.admin-toolbar__tooltip { -+ font-size: var(--gin-font-size-xs); -+ color: #fff; -+ background-color: var(--gin-tooltip-bg); -+} -+ -+.toolbar-block__list-item[id*=navigation-link-admin-toolbar-toolsextra-links] { -+ display: none; -+} -+ -+#navigation-block-shortcuts, -+#navigation-link-announcements-feedannouncement { -+ display: none; -+} -+ -+.gin-secondary-toolbar .toolbar-id--toolbar-icon-user { -+ display: none !important; -+} -+ -+.gin-secondary-toolbar .toolbar-menu { -+ gap: 0; -+} -+ -+.gin-secondary-toolbar .toolbar-menu__trigger { -+ display: none; -+} -+ -+[dir="ltr"] .gin-secondary-toolbar .toolbar-tray .menu-item + .menu-item { -+ border-left: none; -+} -+ -+[dir="rtl"] .gin-secondary-toolbar .toolbar-tray .menu-item + .menu-item { -+ border-right: none; -+} -+ -+[dir="ltr"] .gin-secondary-toolbar .toolbar-secondary .toolbar-bar .toolbar-id--toolbar-icon-user { -+ margin-right: -1em; -+} -+ -+[dir="rtl"] .gin-secondary-toolbar .toolbar-secondary .toolbar-bar .toolbar-id--toolbar-icon-user { -+ margin-left: -1em; -+} -+ -+.toolbar-loading #toolbar-item-shortcuts-tray { -+ visibility: hidden; -+} -+ -diff --git a/dist/css/layout/navigation.css b/dist/css/layout/navigation.css -index da4cc4473f0ae8f22c850ef4a24aa8cf6c6eb6e5..07c84e357bb8a84735b0275c8a34bb804f6ac4a1 100644 ---- a/dist/css/layout/navigation.css -+++ b/dist/css/layout/navigation.css -@@ -773,7 +773,7 @@ html:not(.admin-toolbar-expanded) .toolbar-menu__item--to-title > button.toolbar - .cloned-tooltip { - position: absolute; - top: var(--space-xs); -- background-color: var(--color-gray-950); -+ background-color: var(--gin-tooltip-bg); - color: white; - padding: .25rem var(--space-xs); - border-radius: var(--space-xs); -@@ -894,6 +894,10 @@ html:not(.admin-toolbar-expanded) .toolbar-menu__item--level-1:not(.toolbar-menu - } - } - -+.gin--navigation .admin-toolbar::after { -+ display: none; -+} -+ - .gin--navigation .admin-toolbar__content { - padding-block: var(--gin-spacing-xxs); - padding-inline: var(--gin-spacing-xs); -@@ -933,7 +937,11 @@ html:not(.admin-toolbar-expanded) .toolbar-menu__item--level-1:not(.toolbar-menu - } - - .gin--navigation .admin-toolbar__header { -- margin-bottom: -4px; -+ margin-bottom: -0.5rem; -+} -+ -+.gin--navigation .admin-toolbar__header:has(img) { -+ margin-bottom: -1.25rem; - } - - .gin--navigation .toolbar-block__content.toolbar-block__content--content { -@@ -960,6 +968,14 @@ html:not(.admin-toolbar-expanded) .toolbar-menu__item--level-1:not(.toolbar-menu - box-shadow: 0 0 0 1px var(--gin-color-focus-border), 0 0 0 4px var(--gin-color-focus); - } - -+.gin--navigation .toolbar-menu__item--level-1 > .toolbar-link--create { -+ margin-top: var(--gin-spacing-l); -+} -+ -+.gin--navigation .toolbar-menu__item--level-1 > .toolbar-link--sidebar-toggle { -+ margin-bottom: calc(var(--gin-spacing-xxs) * -1); -+} -+ - .gin--navigation .navigation-menu-wrapper.menu--logo { - margin-bottom: -5px; - } -@@ -1042,8 +1058,9 @@ html:not(.admin-toolbar-expanded) .toolbar-menu__item--level-1:not(.toolbar-menu - .gin--navigation .toolbar-menu__item--level-1 > .toolbar-link--manage-display, - .gin--navigation .toolbar-menu__item--level-1 > .toolbar-link--manage-permissions, - .gin--navigation .toolbar-menu__item--level-1 > .toolbar-link--delete, --.gin--navigation .toolbar-menu__item--level-1 > .toolbar-link[class*=toolbar-link--admin-toolbar-tools-extra-linksentity], --.gin--navigation .toolbar-menu__item--level-1 > .toolbar-link--announcements-feed-announcement { -+.gin--navigation .toolbar-menu__item--level-1 > .toolbar-link--announcements-feed-announcement, -+.gin--navigation .toolbar-menu__item--level-1 > .toolbar-link[class*=toolbar-link--admin-toolbar-tools-extra-], -+.gin--navigation .toolbar-menu__item--level-1[data-url*="/media/add/"] { - display: none; - } - -@@ -1072,6 +1089,16 @@ html:not(.admin-toolbar-expanded) .toolbar-menu__item--level-1:not(.toolbar-menu - color: var(--gin-color-text); - } - -+.gin--navigation .admin-toolbar__item { -+ flex-basis: unset; -+ flex-grow: unset; -+} -+ -+.gin--navigation .admin-toolbar__item > .toolbar-menu { -+ -webkit-margin-before: 0; -+ margin-block-start: 0; -+} -+ - html:not(.admin-toolbar-expanded) .gin--navigation .cloned-flyout, - html:not(.admin-toolbar-expanded) .gin--navigation .cloned-flyout .toolbar-menu__arrow-ref::before, - html:not(.admin-toolbar-expanded) .gin--navigation .toolbar-menu__item--level-1 > .toolbar-menu-wrapper, -@@ -1149,7 +1176,7 @@ button.toolbar-link--sidebar-toggle:hover::before, button.toolbar-link--sidebar- - } - - .toolbar-link--has-icon { -- --icon: icon("fallback"); /* Default icon, aka --basic */ -+ --icon: url("../../media/sprite.svg#fallback-view"); /* Default icon, aka --basic */ - } - - .toolbar-link--has-icon::before { -@@ -1164,39 +1191,62 @@ button.toolbar-link--sidebar-toggle:hover::before, button.toolbar-link--sidebar- - background-color: var(--gin-color-primary); - } - --.toolbar-link--gin-home { -+.admin-toolbar__logo { -+ margin: 0; -+ overflow: unset; -+} -+ -+.admin-toolbar__logo img { -+ width: 40px; -+ height: auto; -+} -+ -+.admin-toolbar__logo .toolbar-link:not(.toolbar-link--gin-home) { -+ padding: var(--gin-spacing-xxs) !important; -+ margin-top: var(--gin-spacing-xxs); -+} -+ -+.admin-toolbar__logo .toolbar-link--gin-home { - color: var(--gin-bg-layer2); - box-sizing: border-box; - width: 40px; - height: 40px; - max-width: 40px; - padding: 10px !important; -- margin: 6px 4px 0 !important; -+ margin: 6px var(--gin-spacing-xxs) 0 !important; - } - --.toolbar-link--gin-home, .toolbar-link--gin-home:hover, .toolbar-link--gin-home:focus { -+.admin-toolbar__logo .toolbar-link--gin-home, .admin-toolbar__logo .toolbar-link--gin-home:hover, .admin-toolbar__logo .toolbar-link--gin-home:focus { - background: var(--gin-color-primary) !important; - } - --.toolbar-link--gin-home::before { -+.admin-toolbar__logo .toolbar-link--gin-home::before { - --icon: url("../../media/sprite.svg#drupal-view"); - background: var(--gin-bg-layer2) !important; - } - --.toolbar-link--gin-home span { -+.admin-toolbar__logo .toolbar-link--gin-home span { - display: none; - } - --.admin-toolbar-expanded .toolbar-link--gin-home { -+.admin-toolbar-expanded .admin-toolbar__logo .toolbar-link--gin-home { - -webkit-margin-start: 0; - margin-inline-start: 0; - } - --.admin-toolbar-expanded .toolbar-link--gin-home::before { -+.admin-toolbar-expanded .admin-toolbar__logo .toolbar-link--gin-home::before { - -webkit-margin-end: 0; - margin-inline-end: 0; - } - -+.admin-toolbar__content .toolbar-block:nth-last-child(n+2)::after { -+ margin-top: calc(var(--gin-spacing-s) - var(--gin-spacing-xxs)); -+ padding-top: calc(var(--gin-spacing-xs) + var(--gin-spacing-xxs)); -+ content: ""; -+ display: block; -+ border-top: 1px solid var(--gin-border-color-table); -+} -+ - .toolbar-link--admin-toolbar-tools-help::before { - --icon: url("../../media/sprite.svg#tool-view"); - } -diff --git a/dist/css/theme/variables.css b/dist/css/theme/variables.css -index a5be43fbd62f6900f04fea8892dc0521ec2b001d..4b5c3653199816d7bef93a88b772a76f6c2d8a3a 100644 ---- a/dist/css/theme/variables.css -+++ b/dist/css/theme/variables.css -@@ -110,6 +110,7 @@ - --input-line-height: var(--gin-spacing-l); - --input-padding-horizontal: var(--gin-spacing-s); - --input-padding-vertical: var(--gin-spacing-xs); -+ --gin-tooltip-bg: #232429; - --jui-dialog-z-index: 1260; - } - -diff --git a/gin.libraries.yml b/gin.libraries.yml -index 750c97fc44d7a32cf0482730dd17887bf887a392..fd13cf67cc9c344181360ae2107fcc1849329ded 100644 ---- a/gin.libraries.yml -+++ b/gin.libraries.yml -@@ -50,6 +50,22 @@ navigation: - - core/once - - core/drupal.displace - -+core_navigation: -+ css: -+ component: -+ dist/css/components/toolbar.css: { minified: false } -+ dist/css/components/toolbar_secondary.css: { minified: false } -+ theme: -+ dist/css/layout/core_navigation.css: { minified: false } -+ dependencies: -+ - gin/coffee -+ - gin/node_preview -+ - gin/responsive_preview -+ - gin/contextual_links -+ - gin/breadcrumb -+ - gin/settings_tray -+ - gin/settings_tray_edit -+ - gin_toolbar: - css: - component: -diff --git a/includes/breadcrumb.theme b/includes/breadcrumb.theme -index 9f8cc497bbea751644042dfaab839fc4f71af036..6e218043d1dc603bd7628030dab003ebe94d4e19 100644 ---- a/includes/breadcrumb.theme -+++ b/includes/breadcrumb.theme -@@ -14,7 +14,7 @@ use Drupal\node\NodeInterface; - */ - function gin_preprocess_breadcrumb(&$variables) { - // Alter node breadcrumb. -- if ($variables['breadcrumb']) { -+ if (isset($variables['breadcrumb'])) { - $entity = _gin_get_route_entity(); - $entity_id = $entity ? $entity->getEntityTypeId() : NULL; - $url = $entity ? $entity->toUrl() : NULL; -diff --git a/includes/form.theme b/includes/form.theme -index f038902da58dc1f257e44c907e4107b8bf27975b..fd95ba2302ca492d490c2b42875a4ebfcaadce98 100644 ---- a/includes/form.theme -+++ b/includes/form.theme -@@ -113,11 +113,7 @@ function gin_theme_suggestions_form_alter(array &$suggestions, array $variables) - */ - function gin_theme_suggestions_input_alter(array &$suggestions, array $variables) { - if ($variables['element']['#type'] === 'checkbox') { -- // Way to identify if checkbox is in a checkboxes group -- // as Drupal doesn't provide one yet (see #2643012) -- if (!isset($variables['element']['#error_no_message'])) { -- $suggestions[] = 'input__checkbox__toggle'; -- } -+ $suggestions[] = 'input__checkbox__toggle'; - } - } - -diff --git a/includes/helper.theme b/includes/helper.theme -index 3085434bbf839c7d7552d23a72a92511ce8683b0..2f556833f195bb44981f23eeddc7df8ebc6d2870 100644 ---- a/includes/helper.theme -+++ b/includes/helper.theme -@@ -100,3 +100,19 @@ function _gin_is_active() { - - return $gin_activated; - } -+ -+/** -+ * Helper function for check if a module is active. -+ */ -+function _gin_module_is_active($module) { -+ $module_handler = \Drupal::service('module_handler'); -+ -+ // Check if Navigation module is active. -+ $check_module = $module_handler->moduleExists($module); -+ -+ if ($check_module) { -+ return TRUE; -+ } -+ -+ return FALSE; -+} -diff --git a/includes/html.theme b/includes/html.theme -index c02c6423dc5e980c44a2678e172bb41921c97e1f..cfe49111afdbb74aa44ec7f783f5a679f1e1dfd3 100644 ---- a/includes/html.theme -+++ b/includes/html.theme -@@ -46,24 +46,8 @@ function gin_preprocess_html(&$variables) { - return; - } - -- // Check for new Drupal navigation. -- if ($toolbar === 'new') { -- /** @var \Drupal\gin\GinNavigaton $navigation */ -- $navigation = \Drupal::classResolver(GinNavigation::class); -- // Get new navigation. -- $variables['page_top']['navigation'] = $navigation->getNavigationStructure(); -- // Get active trail. -- $variables['#attached']['drupalSettings']['active_trail_paths'] = $navigation->getNavigationActiveTrail(); -- // Set toolbar class. -- $variables['attributes']['class'][] = 'gin--navigation'; -- } -- else { -- // Set toolbar class. -- $variables['attributes']['class'][] = 'gin--' . $toolbar . '-toolbar'; -- } -- - // Gin secondary toolbar. -- if ($toolbar !== 'classic') { -+ if ($toolbar !== 'classic' || _gin_module_is_active('navigation')) { - $variables['page']['gin_secondary_toolbar'] = [ - '#type' => 'toolbar', - '#access' => \Drupal::currentUser()->hasPermission('access toolbar'), -@@ -76,6 +60,25 @@ function gin_preprocess_html(&$variables) { - ], - ]; - } -+ -+ // Check if Navigation module is active. -+ if (_gin_module_is_active('navigation')) { -+ $variables['attributes']['class'][] = 'gin--core-navigation'; -+ } -+ elseif ($toolbar === 'new') { -+ /** @var \Drupal\gin\GinNavigaton $navigation */ -+ $navigation = \Drupal::classResolver(GinNavigation::class); -+ // Get new navigation. -+ $variables['page_top']['navigation'] = $navigation->getNavigationStructure(); -+ // Get active trail. -+ $variables['#attached']['drupalSettings']['active_trail_paths'] = $navigation->getNavigationActiveTrail(); -+ // Set toolbar class. -+ $variables['attributes']['class'][] = 'gin--navigation'; -+ } -+ else { -+ // Set toolbar class. -+ $variables['attributes']['class'][] = 'gin--' . $toolbar . '-toolbar'; -+ } - } - } - -diff --git a/includes/theme.theme b/includes/theme.theme -index b3044260a6f0e26f9176cbd19384b6d7b771b0f2..de1901a7d334d8ae1e721a510e0ac5091e1c8005 100644 ---- a/includes/theme.theme -+++ b/includes/theme.theme -@@ -5,66 +5,65 @@ - * theme.theme - */ - --use Drupal\gin\GinSettings; -- - /** - * Implements hook_theme(). - */ - function gin_theme() { -- // Get theme configs. -- /** @var \Drupal\gin\GinSettings $settings */ -- $settings = \Drupal::classResolver(GinSettings::class); -- $logo_default = $settings->getDefault('logo.use_default'); -- $icon_path = ''; -- -- if (!$logo_default) { -- $icon_path = $settings->getDefault('logo.path'); -+ // Check if Navigation module is active. -+ if (_gin_module_is_active('navigation')) { -+ $items['navigation'] = [ -+ 'template' => 'navigation', -+ 'preprocess functions' => ['gin_preprocess_navigation'], -+ 'variables' => [ -+ 'icon_path' => NULL, -+ 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -+ ], -+ ]; - } -+ else { -+ $items['navigation'] = [ -+ 'template' => 'navigation--gin', -+ 'preprocess functions' => ['gin_preprocess_navigation'], -+ 'variables' => [ -+ 'icon_path' => NULL, -+ 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -+ 'menu_middle' => [], -+ 'menu_top' => [], -+ 'menu_bottom' => [], -+ ], -+ ]; - -- // Check if help is enabled. -- $help_enabled = FALSE; -- $module_handler = \Drupal::service('module_handler'); -- if ($module_handler->moduleExists('help')) { -- $help_enabled = TRUE; -- } -+ $items['menu_region__top'] = [ -+ 'variables' => [ -+ 'links' => [], -+ 'title' => NULL, -+ 'menu_name' => NULL, -+ ], -+ ]; - -- $items['navigation'] = [ -- 'variables' => [ -- 'icon_path' => $icon_path, -- 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -- 'menu_middle' => [], -- 'menu_top' => [], -- 'menu_bottom' => [], -- ], -- ]; -+ $items['menu_region__middle'] = [ -+ 'base hook' => 'menu', -+ 'variables' => [ -+ 'menu_name' => NULL, -+ 'items' => [], -+ 'attributes' => [], -+ 'title' => NULL, -+ ], -+ ]; - -- $items['menu_region__top'] = [ -- 'variables' => [ -- 'links' => [], -- 'title' => NULL, -- 'menu_name' => NULL, -- ], -- ]; -+ // Check if help is enabled. -+ $help_enabled = _gin_module_is_active('help'); - -- $items['menu_region__middle'] = [ -- 'base hook' => 'menu', -- 'variables' => [ -- 'menu_name' => NULL, -- 'items' => [], -- 'attributes' => [], -- 'title' => NULL, -- ], -- ]; -- -- $items['menu_region__bottom'] = [ -- 'variables' => [ -- 'help_enabled' => $help_enabled, -- 'items' => [], -- 'title' => NULL, -- 'menu_name' => NULL, -- 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -- ], -- ]; -+ $items['menu_region__bottom'] = [ -+ 'variables' => [ -+ 'help_enabled' => $help_enabled, -+ 'items' => [], -+ 'title' => NULL, -+ 'menu_name' => NULL, -+ 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -+ ], -+ ]; -+ } - - return $items; - } -diff --git a/includes/toolbar.theme b/includes/toolbar.theme -index f06240cdac6ae8e90c9664b75aea7fd9ec3bc2f3..0c7cad1e67143205811743716794f1f51b7893df 100644 ---- a/includes/toolbar.theme -+++ b/includes/toolbar.theme -@@ -63,6 +63,13 @@ function gin_preprocess_toolbar(&$variables) { - '#create_placeholder' => TRUE, - ]; - -+ // Check if Navigation module is active. -+ if (_gin_module_is_active('navigation')) { -+ // Attach the new drupal navigation styles. -+ $variables['#attached']['library'][] = 'gin/core_navigation'; -+ return; -+ } -+ - // Expose Toolbar variant. - /** @var \Drupal\gin\GinSettings $settings */ - $settings = \Drupal::classResolver(GinSettings::class); -@@ -80,7 +87,7 @@ function gin_preprocess_toolbar(&$variables) { - break; - - case 'new': -- // Attach the new drupal navigation styles. -+ // Attach the experimental drupal navigation styles. - $variables['#attached']['library'][] = 'gin/navigation'; - break; - -@@ -100,7 +107,7 @@ function gin_preprocess_toolbar__gin__secondary(&$variables) { - $settings = \Drupal::classResolver(GinSettings::class); - $variables['toolbar_variant'] = $settings->get('classic_toolbar'); - -- if ($variables['toolbar_variant'] !== 'classic') { -+ if ($variables['toolbar_variant'] !== 'classic' || _gin_module_is_active('navigation')) { - // Move Admin Toolbar Search to start. - $toolbar_search = array_search('administration_search', array_keys($variables['tabs'])); - if (is_numeric($toolbar_search)) { -@@ -127,17 +134,29 @@ function gin_preprocess_toolbar__gin__secondary(&$variables) { - } - - /** -- * Toolbar alter(). -+ * Navigation alter(). -+ */ -+function gin_preprocess_navigation(&$variables) { -+ // Get theme configs. -+ /** @var \Drupal\gin\GinSettings $settings */ -+ $settings = \Drupal::classResolver(GinSettings::class); -+ $logo_default = $settings->getDefault('logo.use_default'); -+ $variables['icon_path'] = !$logo_default ? $settings->getDefault('logo.path') : ''; -+ $variables['toolbar_variant'] = $settings->get('classic_toolbar'); -+ $variables['core_navigation'] = _gin_module_is_active('navigation'); -+} -+ -+/** -+ * Toolbar theme suggestions(). - */ - function gin_theme_suggestions_toolbar_alter(array &$suggestions, array $variables) { - /** @var \Drupal\gin\GinSettings $settings */ - $settings = \Drupal::classResolver(GinSettings::class); -- $toolbar = $settings->get('classic_toolbar'); - - $suggestions[] = 'toolbar__gin'; - - // Only if Classic Toolbar is disabled. -- if ($toolbar !== 'classic') { -+ if ($settings->get('classic_toolbar') !== 'classic' || _gin_module_is_active('navigation')) { - if ($variables['element']['#attributes']['id'] === 'toolbar-administration-secondary') { - $suggestions[] = 'toolbar__gin__secondary'; - } -diff --git a/styles/base/_table.scss b/styles/base/_table.scss -index a91dbc8bb071e2495d594735e63b9edfd2e9614f..d4b454ab5f694bccd989ef7c0a76ba568bbdb549 100644 ---- a/styles/base/_table.scss -+++ b/styles/base/_table.scss -@@ -135,7 +135,7 @@ table { - padding: var(--gin-spacing-xs) var(--gin-spacing-m); - - > a::before { -- bottom: -1px; -+ bottom: -2px; - } - - > a::after { -diff --git a/styles/layout/core_navigation.scss b/styles/layout/core_navigation.scss -new file mode 100644 -index 0000000000000000000000000000000000000000..06f9052866450bef1af839c6aeab24659c790961 ---- /dev/null -+++ b/styles/layout/core_navigation.scss -@@ -0,0 +1,384 @@ -+// -------------------------------------------------- -+// Toolbar Variables -+// -------------------------------------------------- -+:root { -+ --gin-toolbar-height: 39px; -+ --gin-toolbar-secondary-height: 48px; -+ --gin-scroll-offset: 72px; -+ --gin-toolbar-y-offset: var(--gin-toolbar-height); -+ --gin-toolbar-x-offset: 0px; // Needs px to work in calc() functions. -+ --gin-sticky-offset: 0px; // Needs px to work in calc() functions. -+ --gin-icon-size-toolbar: 20px; -+ -+ @include mq(medium) { -+ --gin-toolbar-height: 0px; // Needs px to work in calc() functions. -+ --gin-toolbar-secondary-height: 52px; -+ --gin-toolbar-x-offset: var(--drupal-displace-offset-left, 64px); -+ --gin-scroll-offset: 130px; -+ --gin-sticky-offset: var(--gin-height-sticky); -+ } -+} -+ -+html.admin-toolbar-expanded { -+ @include mq(medium) { -+ --gin-toolbar-x-offset: var(--drupal-displace-offset-left, 264px); -+ } -+} -+ -+// -------------------------------------------------- -+// Map CSS3 variables -+// -------------------------------------------------- -+.admin-toolbar { -+ --admin-toolbar-sidebar-width: #{rem(64px)}; -+ --admin-toolbar-font-family: var(--gin-font); -+ --admin-toolbar-color-white: var(--gin-bg-layer); -+ --admin-toolbar-color-gray-050: var(--gin-bg-layer2); -+ --admin-toolbar-color-gray-100: var(--gin-border-color-layer); -+ --admin-toolbar-color-gray-200: var(--gin-border-color-layer2); -+ --admin-toolbar-color-gray-800: var(--gin-color-text); -+ --admin-toolbar-color-gray-990: var(--gin-color-primary-active); -+ //var(--gin-border-color-table) -+ -+ [data-admin-toolbar='expanded'] & { -+ --admin-toolbar-sidebar-width: #{rem(264px)}; -+ -+ @media (--admin-toolbar-tablet) { -+ --admin-toolbar-sidebar-width: calc(16.5 * var(--admin-toolbar-rem)); -+ } -+ } -+} -+ -+// -------------------------------------------------- -+// Custom overrides -+// -------------------------------------------------- -+ -+.gin--core-navigation .gin-secondary-toolbar:not(.gin-secondary-toolbar--frontend) { -+ margin-bottom: var(--gin-spacing-xxs); -+} -+ -+.gin--core-navigation .gin-secondary-toolbar--frontend { -+ z-index: 102; -+ margin-inline-start: var(--gin-toolbar-x-offset); -+} -+ -+.toolbar-menu__link:hover::before { -+ inline-size: var(--gin-spacing-xxs); -+} -+ -+.admin-toolbar__content, -+.admin-toolbar__footer { -+ padding: var(--gin-spacing-s); -+} -+ -+.admin-toolbar__footer, -+.admin-toolbar__content .toolbar-block:nth-last-child(n+2)::after { -+ border-block-start-color: var(--gin-border-color-secondary); -+} -+ -+// -------------------------------------------------- -+// Remove anti-flicker -+// -------------------------------------------------- -+.toolbar-anti-flicker.toolbar-loading.toolbar-fixed body.gin--core-navigation, -+body.gin--core-navigation { -+ padding-top: unset !important; -+} -+ -+// -------------------------------------------------- -+// Logo override -+// -------------------------------------------------- -+.admin-toolbar__logo { -+ line-height: 0; -+ border-radius: 10px; -+ -+ svg rect { -+ fill: var(--gin-color-primary); -+ } -+ -+ svg path { -+ fill: var(--gin-bg-app); -+ } -+ -+ &:hover svg rect { -+ fill: var(--gin-color-primary-hover); -+ } -+ -+ &:active svg rect, -+ &:focus svg rect { -+ fill: var(--gin-color-primary-active); -+ } -+} -+ -+// -------------------------------------------------- -+// Button style overrides -+// -------------------------------------------------- -+.toolbar-button, -+.toolbar-menu__link { -+ color: var(--gin-color-text); -+ font-weight: var(--gin-font-weight-normal); -+ font-variation-settings: unset; -+ -+ &, -+ &:hover, -+ &:active { -+ outline: none; -+ box-shadow: none; -+ } -+ -+ &:hover { -+ color: var(--gin-color-primary); -+ background-color: var(--gin-color-primary-light); -+ } -+ -+ &:active, -+ &:focus, -+ &:hover:focus { -+ color: var(--gin-color-primary-hover); -+ background-color: var(--gin-color-primary-light-hover); -+ } -+} -+ -+.toolbar-button[aria-expanded="true"], -+.toolbar-button--large { -+ font-weight: var(--gin-font-weight-bold); -+} -+ -+// -------------------------------------------------- -+// Focus style overrides -+// -------------------------------------------------- -+.admin-toolbar__logo:focus, -+.toolbar-button:focus, -+.admin-toolbar__expand-button:focus { -+ outline: none; -+ box-shadow: 0 0 0 1px var(--gin-color-focus-border), 0 0 0 4px var(--gin-color-focus); -+} -+ -+.toolbar-menu__link:focus { -+ outline: none; -+ box-shadow: inset 0 0 0 1px var(--gin-color-focus-border), inset 0 0 0 3px var(--gin-color-focus); -+} -+ -+// -------------------------------------------------- -+// Active style overrides -+// -------------------------------------------------- -+.toolbar-button.current { -+ background-color: var(--gin-bg-item-hover); -+ -+ &:hover { -+ background-color: var(--gin-color-primary-light); -+ } -+ -+ &:active, -+ &:focus, -+ &:hover:focus { -+ background-color: var(--gin-color-primary-light-hover); -+ } -+} -+ -+.toolbar-button.current.is-active, -+.toolbar-button:has(+ .toolbar-popover__wrapper .is-active) { -+ color: var(--gin-color-primary); -+ background-color: var(--gin-color-primary-light); -+ -+ &::before { -+ background: var(--gin-color-primary); -+ } -+} -+ -+// -------------------------------------------------- -+// Icon overrides -+// -------------------------------------------------- -+[class*="toolbar-button--icon"] { -+ --icon: #{icon('fallback')}; /* Default icon, aka --basic */ -+ -+ &::before { -+ width: var(--gin-icon-size-toolbar); -+ height: var(--gin-icon-size-toolbar); -+ background-color: var(--gin-icon-color); -+ mask-size: 100% 100%; -+ mask-image: var(--icon); -+ } -+ -+ &:hover::before, -+ &:focus::before { -+ background-color: var(--gin-color-primary); -+ } -+} -+ -+.toolbar-button--icon--admin-toolbar-tools-help::before { -+ --icon: #{icon('tool')}; -+} -+ -+.toolbar-button--icon--system-themes-page::before { -+ --icon: #{icon('appearance')}; -+} -+ -+.toolbar-button--icon--navigation-blocks::before { -+ --icon: #{icon('blocks')}; -+} -+ -+.toolbar-button--icon--shortcuts::before { -+ --icon: #{icon('shortcut')}; -+} -+ -+.toolbar-button--icon--system-admin-config::before { -+ --icon: #{icon('config')}; -+} -+ -+.toolbar-button--icon--navigation-content::before { -+ --icon: #{icon('content')}; -+} -+ -+.toolbar-button--icon--navigation-create::before { -+ --icon: #{icon('create')}; -+} -+ -+.toolbar-button--icon--system-modules-list::before { -+ --icon: #{icon('extend-new')}; -+} -+ -+.toolbar-button--icon--navigation-files::before { -+ --icon: #{icon('files')}; -+} -+ -+.toolbar-button--icon--help::before { -+ --icon: #{icon('help')}; -+} -+ -+.toolbar-button--icon--navigation-media::before { -+ --icon: #{icon('media')}; -+} -+ -+.toolbar-button--icon--entity-user-collection::before { -+ --icon: #{icon('people-new')}; -+} -+ -+.toolbar-button--icon--system-admin-reports::before { -+ --icon: #{icon('reports')}; -+} -+ -+.toolbar-button--icon--system-admin-structure::before { -+ --icon: #{icon('structure')}; -+} -+ -+.toolbar-button--icon--announcements-feed-announcement::before { -+ --icon: #{icon('announcement')}; -+} -+ -+.toolbar-link--sidebar-toggle[aria-expanded=false]::before, -+.toolbar-link--sidebar-toggle[aria-expanded=true]::before { -+ --icon: #{icon('chevron-down')}; -+} -+ -+.toolbar-button--icon--user::before { -+ --icon: #{icon('user')}; -+} -+ -+.toolbar-button--icon--burger::before { -+ --icon: #{icon('hamburger')}; -+} -+ -+.toolbar-button--icon--back::before { -+ --icon: #{icon('nav-toggle-toleft')}; -+} -+ -+.toolbar-button--icon--cross::before { -+ --icon: #{icon('close')}; -+} -+ -+// -------------------------------------------------- -+// Create menu: Separate if first item -+// -------------------------------------------------- -+#navigation-link-navigationcreate:first-child { -+ margin-block-end: var(--admin-toolbar-space-8); -+ -+ &::after { -+ content: ""; -+ display: block; -+ margin-block-start: var(--admin-toolbar-space-12); -+ border-block-end: 1px solid var(--gin-border-color-secondary); -+ } -+} -+ -+// -------------------------------------------------- -+// Expand button -+// -------------------------------------------------- -+.admin-toolbar__expand-button { -+ margin-block-start: calc(var(--gin-spacing-xs) * -1); -+ -+ &, -+ &:hover { -+ color: var(--gin-color-text-light); -+ border-color: var(--gin-border-color); -+ } -+} -+ -+// -------------------------------------------------- -+// Popover -+// -------------------------------------------------- -+.toolbar-popover--expanded { -+ background-color: transparent; -+ -+ > .toolbar-button { -+ background-color: var(--gin-bg-item-hover); -+ } -+} -+ -+.gin--dark-mode .toolbar-popover__wrapper { -+ border-inline-start: 2px solid var(--gin-border-color-secondary); -+} -+ -+// -------------------------------------------------- -+// Tooltips -+// -------------------------------------------------- -+.admin-toolbar__tooltip { -+ font-size: var(--gin-font-size-xs); -+ color: #fff; -+ background-color: var(--gin-tooltip-bg); -+} -+ -+// -------------------------------------------------- -+// Hide links from Admin Toolbar Extra module -+// -------------------------------------------------- -+.toolbar-block__list-item[id*="navigation-link-admin-toolbar-toolsextra-links"] { -+ display: none; -+} -+ -+// -------------------------------------------------- -+// Hide Shortcuts & Announcements in primary nav -+// -------------------------------------------------- -+#navigation-block-shortcuts, -+#navigation-link-announcements-feedannouncement { -+ display: none; -+} -+ -+// -------------------------------------------------- -+// Hide duplicated Gin secondary items -+// -------------------------------------------------- -+.gin-secondary-toolbar { -+ .toolbar-id--toolbar-icon-user { -+ display: none !important; -+ } -+ -+ .toolbar-menu { -+ gap: 0; -+ } -+ -+ // Fixes quirks when the toolbar -+ // module is still active -+ .toolbar-menu__trigger { -+ display: none; -+ } -+ -+ .toolbar-tray .menu-item + .menu-item { -+ border-left: none; -+ } -+ -+ .toolbar-secondary .toolbar-bar .toolbar-id--toolbar-icon-user { -+ margin-right: -1em; -+ } -+} -+ -+// Fix weird shortcuts menu flickering. -+.toolbar-loading #toolbar-item-shortcuts-tray { -+ visibility: hidden; -+} -diff --git a/styles/layout/navigation.scss b/styles/layout/navigation.scss -index 3dfc011c6b952e44dc9c7d59e12630b714f2f703..fa0ecd3dd69cd93fd9af8ed3f9b134d7204377e5 100644 ---- a/styles/layout/navigation.scss -+++ b/styles/layout/navigation.scss -@@ -74,6 +74,11 @@ body.gin--navigation { - @include mq($to: medium) { - display: none; - } -+ -+ // Beta 1 -+ &::after { -+ display: none; -+ } - } - - .admin-toolbar__content { -@@ -107,7 +112,11 @@ body.gin--navigation { - - // Navigation header - .admin-toolbar__header { -- margin-bottom: -4px; -+ margin-bottom: -.5rem; -+ -+ &:has(img) { -+ margin-bottom: -1.25rem; -+ } - } - - // Content block -@@ -137,6 +146,16 @@ body.gin--navigation { - box-shadow: 0 0 0 1px var(--gin-color-focus-border), 0 0 0 4px var(--gin-color-focus); - } - } -+ -+ // Beta 1 -+ > .toolbar-link--create { -+ margin-top: var(--gin-spacing-l); -+ } -+ -+ // Beta 1 -+ > .toolbar-link--sidebar-toggle { -+ margin-bottom: calc(var(--gin-spacing-xxs) * -1); -+ } - } - - .navigation-menu-wrapper.menu--logo { -@@ -202,8 +221,10 @@ body.gin--navigation { - .toolbar-menu__item--level-1 > .toolbar-link--manage-display, - .toolbar-menu__item--level-1 > .toolbar-link--manage-permissions, - .toolbar-menu__item--level-1 > .toolbar-link--delete, -- .toolbar-menu__item--level-1 > .toolbar-link[class*="toolbar-link--admin-toolbar-tools-extra-linksentity"], -- .toolbar-menu__item--level-1 > .toolbar-link--announcements-feed-announcement -+ .toolbar-menu__item--level-1 > .toolbar-link--announcements-feed-announcement, -+ .toolbar-menu__item--level-1 > .toolbar-link[class*="toolbar-link--admin-toolbar-tools-extra-"], -+ // Beta 1: Hide Admin Toolbar Extra links from Level 1 -+ .toolbar-menu__item--level-1[data-url*="/media/add/"] - { - display: none; - } -@@ -235,6 +256,16 @@ body.gin--navigation { - .toolbar .toolbar-menu .toolbar-menu a { - color: var(--gin-color-text); - } -+ -+ // Beta 1 -+ .admin-toolbar__item { -+ flex-basis: unset; -+ flex-grow: unset; -+ } -+ -+ .admin-toolbar__item > .toolbar-menu { -+ margin-block-start: 0; -+ } - } - - // Submenu -@@ -321,7 +352,7 @@ button.toolbar-link--sidebar-toggle { - // -------------- - - .toolbar-link--has-icon { -- --icon: icon('fallback'); /* Default icon, aka --basic */ -+ --icon: #{icon('fallback')}; /* Default icon, aka --basic */ - - &::before { - width: var(--gin-icon-size-toolbar); -@@ -336,39 +367,64 @@ button.toolbar-link--sidebar-toggle { - } - } - --// Custom Gin Home logo --.toolbar-link--gin-home { -- color: var(--gin-bg-layer2); -- box-sizing: border-box; -- width: 40px; -- height: 40px; -- max-width: 40px; -- padding: 10px !important; // make the logo 32px -- margin: 6px 4px 0 !important; -+// Beta 1: Logo -+.admin-toolbar__logo { -+ margin: 0; -+ overflow: unset; - -- &, &:hover, &:focus { -- background: var(--gin-color-primary) !important; -+ img { -+ width: 40px; -+ height: auto; - } - -- &::before { -- --icon: #{icon('drupal')}; -- background: var(--gin-bg-layer2) !important; -+ .toolbar-link:not(.toolbar-link--gin-home) { -+ padding: var(--gin-spacing-xxs) !important; -+ margin-top: var(--gin-spacing-xxs); - } - -- span { -- display: none; -- } -+ // Custom Gin Home logo -+ .toolbar-link--gin-home { -+ color: var(--gin-bg-layer2); -+ box-sizing: border-box; -+ width: 40px; -+ height: 40px; -+ max-width: 40px; -+ padding: 10px !important; -+ margin: 6px var(--gin-spacing-xxs) 0 !important; - -- // Navigation expanded -- .admin-toolbar-expanded & { -- margin-inline-start: 0; -+ &, &:hover, &:focus { -+ background: var(--gin-color-primary) !important; -+ } - - &::before { -- margin-inline-end: 0; -+ --icon: #{icon('drupal')}; -+ background: var(--gin-bg-layer2) !important; -+ } -+ -+ span { -+ display: none; -+ } -+ -+ // Navigation expanded -+ .admin-toolbar-expanded & { -+ margin-inline-start: 0; -+ -+ &::before { -+ margin-inline-end: 0; -+ } - } - } - } - -+// Beta 1 -+.admin-toolbar__content .toolbar-block:nth-last-child(n+2)::after { -+ margin-top: calc(var(--gin-spacing-s) - var(--gin-spacing-xxs)); -+ padding-top: calc(var(--gin-spacing-xs) + var(--gin-spacing-xxs)); -+ content: ""; -+ display: block; -+ border-top: 1px solid var(--gin-border-color-table); -+} -+ - .toolbar-link--admin-toolbar-tools-help::before { - --icon: #{icon('tool')}; - } -diff --git a/styles/navigation/tooltip.scss b/styles/navigation/tooltip.scss -index 2af5870048e59cbd75dc873bf89ab1c100d548e5..932698339494644761b2fc8c326b7f3384942831 100644 ---- a/styles/navigation/tooltip.scss -+++ b/styles/navigation/tooltip.scss -@@ -8,7 +8,7 @@ - position: absolute; - top: var(--space-xs); - left: 100%; -- background-color: var(--color-gray-950); -+ background-color: var(--gin-tooltip-bg); - color: white; - padding: 0.25rem var(--space-xs); - border-radius: var(--space-xs); -diff --git a/styles/theme/variables.scss b/styles/theme/variables.scss -index d36e36a1ab5f8f863be87111af5098c2f896b4cf..37c76988104a25c86f6ee7213e0438988a880bec 100644 ---- a/styles/theme/variables.scss -+++ b/styles/theme/variables.scss -@@ -185,6 +185,9 @@ - --input-padding-horizontal: var(--gin-spacing-s); - --input-padding-vertical: var(--gin-spacing-xs); - -+ // Tooltips -+ --gin-tooltip-bg: #232429; -+ - // Claro - --jui-dialog-z-index: 1260; - } -diff --git a/templates/html.html.twig b/templates/html.html.twig -index 0afb6318e2e94880066070a47ae9aa258314062a..1a095770086cbfcde3cde417c44d58461d664071 100644 ---- a/templates/html.html.twig -+++ b/templates/html.html.twig -@@ -48,6 +48,7 @@ - <a href="#main-content" class="visually-hidden focusable skip-link"> - {{ 'Skip to main content'|t }} - </a> -+ - {{ page_top }} - {{ page }} - {{ page_bottom }} -diff --git a/templates/navigation--gin.html.twig b/templates/navigation--gin.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..0c42c531131b525757852510045302225338432e ---- /dev/null -+++ b/templates/navigation--gin.html.twig -@@ -0,0 +1,49 @@ -+{% include '@gin/navigation/fix-toolbar-js-error.html.twig' ignore missing with { -+ toolbar_variant, -+ core_navigation, -+} %} -+ -+<aside class="admin-toolbar" id="admin-toolbar" data-drupal-admin-styles> -+ {# This lil' div will get the Drupal.displace() attributes added to it via JS. #} -+ <div class="admin-toolbar__displace-placeholder"></div> -+ -+ {% set title_menu = 'admin-toolbar-title' %} -+ -+ {# @todo - We should get rid of this ID below. #} -+ <nav id="menu-builder" class="admin-toolbar__content" aria-labelledby="{{ title_menu }}"> -+ <h3 id="{{ title_menu }}" class="visually-hidden">{{ 'Administrative toolbar content'|t }}</h3> -+ -+ <div class="admin-toolbar__header"> -+ <div class="navigation-menu-wrapper menu--logo admin-toolbar__logo"> -+ <ul class="toolbar-menu" data-once="toolbar-menu" aria-labelledby="menu--logo"> -+ <li class="toolbar-menu__item toolbar-menu__item--level-1"> -+ {% if icon_path %} -+ <a class="toolbar-link" href="/"> -+ <img alt="{{ 'Home'|t }}" src="{{ file_url(icon_path) }}" loading="eager" /> -+ </a> -+ {% else %} -+ <a href="/" class="toolbar-link toolbar-link--has-icon toolbar-link--gin-home"> -+ <span>{{ 'Home'|t }}</span> -+ </a> -+ {% endif %} -+ </li> -+ </ul> -+ </div> -+ </div> -+ -+ {{ menu_top }} -+ {% for section in menu_middle %} -+ {{ section }} -+ {% endfor %} -+ </nav> -+ -+ {# @todo - This sticky menu should exist under the <nav> landmark element above. #} -+ <div class="admin-toolbar__sticky-section"> -+ {{ menu_bottom }} -+ </div> -+</aside> -+<script> -+ if (localStorage.getItem('Drupal.navigation.sidebarExpanded') !== 'false') { -+ document.documentElement.classList.add('admin-toolbar-expanded'); -+ } -+</script> -diff --git a/templates/navigation.html.twig b/templates/navigation.html.twig -index 87479048a76ad500a9f732914bc185c5a1598d9f..acc40d6b92e19b5eded48cb1706710eb0952a0ae 100644 ---- a/templates/navigation.html.twig -+++ b/templates/navigation.html.twig -@@ -1,48 +1,64 @@ --<aside class="admin-toolbar" id="admin-toolbar"> -+{% include '@gin/navigation/fix-toolbar-js-error.html.twig' ignore missing with { -+ toolbar_variant, -+ core_navigation, -+} %} -+ -+<aside -+ class="admin-toolbar" id="admin-toolbar" data-drupal-admin-styles> - {# This lil' div will get the Drupal.displace() attributes added to it via JS. #} - <div class="admin-toolbar__displace-placeholder"></div> -- {# ---- Start Gin Custom ---- #} -- {% if icon_path %} -- <div class="admin-toolbar__header"> -- <a class="admin-toolbar__logo" href="/"> -- <img alt="{{ 'Home'|t }}" src="{{ file_url(icon_path) }}" loading="eager" width=" 32" height=" 32"> -- </a> -- </div> -- {% endif %} -- {# ---- End Gin Custom ---- #} - -- {% set title_menu = 'admin-toolbar' %} -- <h2 id="{{ title_menu }}" class="visually-hidden">{{ 'Administrative toolbar'|t }}</h2> -+ <div class="admin-toolbar__scroll-wrapper"> -+ {% set title_menu = 'admin-toolbar-title'|clean_unique_id %} - -- {# @todo - We should get rid of this ID below. #} -- <nav id="menu-builder" class="admin-toolbar__content" aria-labelledby="{{ title_menu }}"> -- {# ---- Start Gin Custom ---- #} -- {% if not icon_path %} -- <div class="navigation-menu-wrapper menu--logo"> -- <h2 id="menu--logo" class="menu-title visually-hidden">{{ 'Home'|t }}</h2> -- <ul class="toolbar-menu" data-once="toolbar-menu" aria-labelledby="menu--logo"> -- <li class="toolbar-menu__item toolbar-menu__item--level-1"> -- <a href="/" class="toolbar-link toolbar-link--has-icon toolbar-link--gin-home"> -- <span>{{ 'Home'|t }}</span> -+ {# @todo - We should get rid of this ID below. #} -+ <nav id="menu-builder" class="admin-toolbar__content" aria-labelledby="{{ title_menu }}"> -+ <h3 id="{{ title_menu }}" class="visually-hidden">{{ 'Administrative toolbar content'|t }}</h3> -+ {# @todo - Needs to be placed here so we can have the header footer on mobile. #} -+ <div class="admin-toolbar__header"> -+ {% if not hide_logo %} -+ <a class="admin-toolbar__logo" href="{{ path('<front>') }}"> -+ {% if icon_path or logo_path %} -+ {% set logo = icon_path ? icon_path : logo_path %} -+ <img alt="{{ 'Home'|t }}" src="{{ file_url(logo) }}" loading="eager" /> -+ {% else %} -+ {% include '@navigation/logo.svg.twig' ignore missing with { -+ label: 'Navigation logo'|t -+ } only %} -+ {% endif %} - </a> -- </li> -- </ul> -- </div> -- {% endif %} -- {# ---- End Gin Custom ---- #} -- {{ menu_top }} -- {% for section in menu_middle %} -- {{ section }} -- {% endfor %} -- </nav> -+ {% endif %} -+ {% include '@navigation/toolbar-button.html.twig' ignore missing with { -+ attributes: create_attribute({ 'data-toolbar-back-control': true, 'tabindex': '-1' }), -+ extra_classes: 'admin-toolbar__back-button', -+ icon: 'back', -+ text: 'Back'|t, -+ } only %} -+ {% include '@navigation/toolbar-button.html.twig' ignore missing with { -+ attributes: create_attribute({ 'aria-controls': 'admin-toolbar', 'tabindex': '-1' }), -+ extra_classes: 'admin-toolbar__close-button', -+ icon: 'cross', -+ label_classes: 'visually-hidden', -+ text: 'Collapse sidebar'|t, -+ } only %} -+ </div> -+ -+ {{ menu_content }} -+ </nav> - -- {# @todo - This sticky menu should exist under the <nav> landmark element above. #} -- <div class="admin-toolbar__sticky-section"> -- {{ menu_bottom }} -+ {% set title_menu_footer = 'admin-toolbar-footer'|clean_unique_id %} -+ <nav id="menu-footer" class="admin-toolbar__footer" aria-labelledby="{{ title_menu_footer }}"> -+ <h3 id="{{ title_menu_footer }}" class="visually-hidden">{{ 'Administrative toolbar footer'|t }}</h3> -+ {{ menu_footer }} -+ <button aria-controls="admin-toolbar" class="admin-toolbar__expand-button"> -+ <span class="visually-hidden" data-text>{{ 'Collapse sidebar'|t }}</span> -+ </button> -+ </nav> - </div> - </aside> -+<div class="admin-toolbar-overlay" aria-controls="admin-toolbar"></div> - <script> -- if (localStorage.getItem('Drupal.navigation.sidebarExpanded') !== 'false') { -- document.documentElement.classList.add('admin-toolbar-expanded'); -+ if (localStorage.getItem('Drupal.navigation.sidebarExpanded') !== 'false' && (window.matchMedia('(min-width: 1024px)').matches)) { -+ document.documentElement.setAttribute('data-admin-toolbar', 'expanded'); - } - </script> -diff --git a/templates/navigation/breadcrumb--navigation.html.twig b/templates/navigation/breadcrumb--navigation.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..1e34077825ef7aae3b710fa9dc9ae903b314d76b ---- /dev/null -+++ b/templates/navigation/breadcrumb--navigation.html.twig -@@ -0,0 +1 @@ -+{% extends '@gin/navigation/breadcrumb.html.twig' %} -diff --git a/templates/navigation/fix-toolbar-js-error.html.twig b/templates/navigation/fix-toolbar-js-error.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..b5b82a59f576c037593994c42c122331330e5dc6 ---- /dev/null -+++ b/templates/navigation/fix-toolbar-js-error.html.twig -@@ -0,0 +1,20 @@ -+{# @todo - Temporarily fix toolbar JS errors as toolbar is not present anymore. #} -+{% if toolbar_variant == 'new' or core_navigation %} -+ <div id="toolbar-administration" class="toolbar" height="0px" hidden> -+ <div class="toolbar-tab--toolbar-item-shortcuts" hidden> -+ <a id="toolbar-item-shortcuts" class="toolbar-item" data-toolbar-tray="toolbar-item-shortcuts-tray" hidden></a> -+ <div id="toolbar-item-shortcuts-tray" class="toolbar-tray" data-toolbar-tray="toolbar-item-shortcuts-tray" hidden></div> -+ </div> -+ <div class="toolbar-tab--toolbar-item-devel" hidden> -+ <a id="toolbar-item-devel" class="toolbar-item" data-toolbar-tray="toolbar-item-devel-tray" hidden></a> -+ <div id="toolbar-item-devel-tray" class="toolbar-tray" data-toolbar-tray="toolbar-item-devel-tray" hidden></div> -+ </div> -+ <div class="toolbar-tab--toolbar-item-user" hidden> -+ <a id="toolbar-item-user" class="toolbar-item" data-toolbar-tray="toolbar-item-user-tray" hidden></a> -+ <div id="toolbar-item-user-tray" class="toolbar-tray" data-toolbar-tray="toolbar-item-user-tray" hidden></div> -+ </div> -+ <div class="toolbar-toggle-orientation" hidden> -+ <button hidden></button> -+ </div> -+ </div> -+{% endif %} -diff --git a/templates/navigation/toolbar--gin.html.twig b/templates/navigation/toolbar--gin.html.twig -index 908d3eb15a69cbfb53b0e0825105ada840133681..90242928a980f35fea91cf60f40ffa7260c1783e 100644 ---- a/templates/navigation/toolbar--gin.html.twig -+++ b/templates/navigation/toolbar--gin.html.twig -@@ -20,7 +20,7 @@ - * @see template_preprocess_toolbar() - */ - #} --{% set gin_toolbar_id = toolbar_variant != 'classic' ? 'gin-toolbar-bar' : 'toolbar-bar' %} -+{% set gin_toolbar_id = toolbar_variant != 'classic' or core_navigation ? 'gin-toolbar-bar' : 'toolbar-bar' %} - <div{{ attributes.addClass('toolbar') }}> - <nav{{ toolbar_attributes.addClass('toolbar-bar', 'clearfix').setAttribute('id', gin_toolbar_id) }}> - <h2 class="visually-hidden">{{ toolbar_heading }}</h2> -diff --git a/webpack.config.js b/webpack.config.js -index fec8ffd4c6e1078c5ee6f0bab1aa7e20c38daf81..bdeb6fbbbad3ef15fc7eb913106e45bc7fcd03a5 100644 ---- a/webpack.config.js -+++ b/webpack.config.js -@@ -67,6 +67,7 @@ module.exports = { - 'components/workbench': ['./styles/components/workbench.scss'], - 'components/workspaces': ['./styles/components/workspaces.scss'], - // Layout -+ 'layout/core_navigation': ['./styles/layout/core_navigation.scss'], - 'layout/navigation': ['./styles/layout/navigation.scss'], - 'layout/toolbar': ['./styles/layout/toolbar.scss'], - 'layout/horizontal_toolbar': ['./styles/layout/horizontal_toolbar.scss'], diff --git a/patches/d10/3442127.diff b/patches/d10/3442127.diff deleted file mode 100644 index 2017d169f479e039e2c9f9070ff7a997b4b98835..0000000000000000000000000000000000000000 --- a/patches/d10/3442127.diff +++ /dev/null @@ -1,94 +0,0 @@ -diff --git a/js/linkit.filter_html.admin.js b/js/linkit.filter_html.admin.js -deleted file mode 100644 -index 8295286fa87320b623a6624739e2b1b518efddf6..0000000000000000000000000000000000000000 ---- a/js/linkit.filter_html.admin.js -+++ /dev/null -@@ -1,54 +0,0 @@ --/** -- * @file -- * Send events to add or remove a tags to the filter_html allowed tags. -- */ -- --(function ($, Drupal, document, once) { -- -- 'use strict'; -- -- /** -- * When enabling the linkit filter, also add linkit rules to filter_html. -- * -- * @type {Drupal~behavior} -- * -- * @prop {Drupal~behaviorAttach} attach -- * Attaches linkitFilterHtml behavior. -- */ -- Drupal.behaviors.linkitFilterHtml = { -- attach: function (context) { -- var selector = '[data-drupal-selector="edit-filters-linkit-status"]'; -- var feature = editorFeature(); -- -- once('filters-linkit-status', selector, context) -- .forEach((checkbox) => { -- const $checkbox = $(checkbox); -- $checkbox.on('click', function () { -- var eventName = $(this).is(':checked') ? 'drupalEditorFeatureAdded' : 'drupalEditorFeatureRemoved'; -- $(document).trigger(eventName, feature); -- }); -- }); -- } -- }; -- -- /** -- * Returns a editor feature. -- * -- * @return {Drupal.EditorFeature} -- * A editor feature with linkit specific tags and attributes. -- */ -- function editorFeature() { -- var linkitFeature = new Drupal.EditorFeature('linkit'); -- var rule = new Drupal.EditorFeatureHTMLRule(); -- // Tags. -- rule.required.tags = ['a']; -- rule.allowed.tags = ['a']; -- // Attributes. -- rule.required.attributes = ['data-entity-substitution', 'data-entity-type', 'data-entity-uuid', 'title']; -- rule.allowed.attributes = ['data-entity-substitution', 'data-entity-type', 'data-entity-uuid', 'title']; -- -- linkitFeature.addHTMLRule(rule); -- return linkitFeature; -- } -- --})(jQuery, Drupal, document, once); -diff --git a/linkit.libraries.yml b/linkit.libraries.yml -index bb04aedbdebad1b16dfc1159190afdc1ca6bf728..963c0222c530b2804113febe0a7971c6bea3598b 100644 ---- a/linkit.libraries.yml -+++ b/linkit.libraries.yml -@@ -26,15 +26,6 @@ linkit.autocomplete: - - core/drupal.autocomplete - - core/once - --linkit.filter_html.admin: -- version: VERSION -- js: -- js/linkit.filter_html.admin.js: {} -- dependencies: -- - core/jquery -- - core/drupal -- - core/once -- - ckeditor5: - version: VERSION - js: -diff --git a/src/Plugin/Filter/LinkitFilter.php b/src/Plugin/Filter/LinkitFilter.php -index 8dfd2063a8c7ab32dc198682e837fc8eb1937f09..6bc75bb2f28c043412b595608036a1cc7e5ff323 100644 ---- a/src/Plugin/Filter/LinkitFilter.php -+++ b/src/Plugin/Filter/LinkitFilter.php -@@ -193,9 +193,6 @@ class LinkitFilter extends FilterBase implements ContainerFactoryPluginInterface - '#type' => 'checkbox', - '#title' => $this->t('Automatically set the <code>title</code> attribute to that of the (translated) referenced content'), - '#default_value' => $this->settings['title'], -- '#attached' => [ -- 'library' => ['linkit/linkit.filter_html.admin'], -- ], - ]; - return $form; - } diff --git a/patches/d10/3442173.diff b/patches/d10/3442173.diff deleted file mode 100644 index f1c526cb3cc5c437f912687629f0ac306bd35690..0000000000000000000000000000000000000000 --- a/patches/d10/3442173.diff +++ /dev/null @@ -1,380 +0,0 @@ -diff --git a/gin_toolbar.module b/gin_toolbar.module -index 3fb44b40c1c3c977775637bad7d9fda8beeb90c3..fafd09fbdb0d2175e925b45e62922d8a4de7a0fc 100644 ---- a/gin_toolbar.module -+++ b/gin_toolbar.module -@@ -43,10 +43,17 @@ function gin_toolbar_preprocess_html(&$variables) { - return; - } - -+ // Check if Navigation module is active. -+ if (_gin_toolbar_module_is_active('navigation')) { -+ $variables['attributes']['class'][] = 'gin--core-navigation'; -+ return; -+ } -+ - // Check for new Drupal navigation. - if ($toolbar === 'new') { - /** @var \Drupal\gin\GinNavigaton $navigation */ - $navigation = \Drupal::classResolver(GinNavigation::class); -+ - // Get new navigation. - $variables['page_top']['navigation'] = $navigation->getNavigationStructure(); - // Get active trail. -@@ -60,6 +67,29 @@ function gin_toolbar_preprocess_html(&$variables) { - } - } - -+/** -+ * Implements hook_page_top(). -+ */ -+function gin_toolbar_page_top(array &$page_top) { -+ // Check if Navigation module is active. -+ if (!_gin_toolbar_module_is_active('navigation')) { -+ return; -+ } -+ -+ $page_top['gin_secondary_toolbar'] = [ -+ '#type' => 'toolbar', -+ '#access' => \Drupal::currentUser()->hasPermission('access toolbar'), -+ '#cache' => [ -+ 'keys' => ['toolbar_secondary'], -+ 'contexts' => ['user.permissions'], -+ ], -+ '#attributes' => [ -+ 'id' => 'toolbar-administration-secondary', -+ ], -+ ]; -+ unset($page_top['toolbar']); -+} -+ - /** - * Implements hook_preprocess_HOOK() for page_attachments. - */ -@@ -69,34 +99,12 @@ function gin_toolbar_page_attachments_alter(&$page) { - return; - } - -- // Get theme settings. -- /** @var \Drupal\gin\GinSettings $settings */ -- $settings = \Drupal::classResolver(GinSettings::class); -- $toolbar = $settings->get('classic_toolbar'); -- - // Attach the base library. - $page['#attached']['library'][] = 'gin/gin_base'; - - // Attach accent overrides CSS. - $page['#attached']['library'][] = 'gin/gin_accent'; - -- if ($toolbar === 'classic') { -- // Attach the classic toolbar styles. -- $page['#attached']['library'][] = 'gin/gin_classic_toolbar'; -- } -- elseif ($toolbar === 'horizontal') { -- // Attach the horizontal toolbar styles. -- $page['#attached']['library'][] = 'gin/gin_horizontal_toolbar'; -- } -- elseif ($toolbar === 'new') { -- // Attach the new drupal navigation styles. -- $page['#attached']['library'][] = 'gin/navigation'; -- } -- else { -- // Attach toolbar styles. -- $page['#attached']['library'][] = 'gin/gin_toolbar'; -- } -- - // Attach the init script. - $page['#attached']['library'][] = 'gin/gin_init'; - -@@ -105,6 +113,10 @@ function gin_toolbar_page_attachments_alter(&$page) { - $page['#attached']['library'][] = 'gin/gin_custom_css'; - } - -+ // Get theme settings. -+ /** @var \Drupal\gin\GinSettings $settings */ -+ $settings = \Drupal::classResolver(GinSettings::class); -+ - // Expose settings to JS. - $page['#attached']['drupalSettings']['gin']['darkmode'] = $settings->get('enable_darkmode'); - $page['#attached']['drupalSettings']['gin']['darkmode_class'] = 'gin--dark-mode'; -@@ -115,6 +127,35 @@ function gin_toolbar_page_attachments_alter(&$page) { - $page['#attached']['drupalSettings']['gin']['highcontrastmode'] = $settings->get('high_contrast_mode'); - $page['#attached']['drupalSettings']['gin']['highcontrastmode_class'] = 'gin--high-contrast-mode'; - $page['#attached']['drupalSettings']['gin']['toolbar_variant'] = $settings->get('classic_toolbar'); -+ -+ // Check if Navigation module is active. -+ if (_gin_toolbar_module_is_active('navigation')) { -+ // Attach the new drupal navigation styles. -+ $page['#attached']['library'][] = 'gin/core_navigation'; -+ return; -+ } -+ -+ switch ($settings->get('classic_toolbar')) { -+ case 'classic': -+ // Attach the classic toolbar styles. -+ $page['#attached']['library'][] = 'gin/gin_classic_toolbar'; -+ break; -+ -+ case 'horizontal': -+ // Attach the horizontal toolbar styles. -+ $page['#attached']['library'][] = 'gin/gin_horizontal_toolbar'; -+ break; -+ -+ case 'new': -+ // Attach the experimental drupal navigation styles. -+ $page['#attached']['library'][] = 'gin/navigation'; -+ break; -+ -+ default: -+ // Attach toolbar styles. -+ $page['#attached']['library'][] = 'gin/gin_toolbar'; -+ break; -+ } - } - - /** -@@ -157,12 +198,16 @@ function gin_toolbar_library_info_alter(&$libraries, $extension) { - } - - /** -- * Toolbar alter(). -+ * Registry alter(). - */ - function gin_toolbar_theme_registry_alter(&$theme_registry) { - $templates_path = \Drupal::service('extension.list.module')->getPath('gin_toolbar') . '/templates'; -+ - $theme_registry['toolbar']['path'] = $templates_path; -+ $theme_registry['toolbar__gin']['path'] = $templates_path; - $theme_registry['menu__toolbar']['path'] = $templates_path; -+ $theme_registry['navigation']['path'] = $templates_path; -+ $theme_registry['navigation__gin']['path'] = $templates_path; - } - - /** -@@ -189,8 +234,9 @@ function gin_toolbar_preprocess_toolbar(&$variables) { - $settings = \Drupal::classResolver(GinSettings::class); - $variables['toolbar_variant'] = $settings->get('classic_toolbar'); - $variables['secondary_toolbar_frontend'] = $settings->get('secondary_toolbar_frontend'); -+ $variables['core_navigation'] = _gin_toolbar_module_is_active('navigation'); - -- if ($variables['toolbar_variant'] !== 'classic') { -+ if ($variables['toolbar_variant'] !== 'classic' || $variables['core_navigation']) { - // Move Admin Toolbar Search to start. - $toolbar_search = array_search('administration_search', array_keys($variables['tabs'])); - if (is_numeric($toolbar_search)) { -@@ -201,6 +247,18 @@ function gin_toolbar_preprocess_toolbar(&$variables) { - } - } - } -+ -+ // Move user tab to end. -+ $toolbar_user = array_search('user', array_keys($variables['tabs'])); -+ if (is_numeric($toolbar_user)) { -+ foreach ($variables['tabs'] as $key => $item) { -+ if ($key === 'user') { -+ $user_tab = $variables['tabs'][$key]; -+ unset($variables['tabs'][$key]); -+ $variables['tabs'][$key] = $user_tab; -+ } -+ } -+ } - } - - // Expose Route name. -@@ -272,7 +330,7 @@ function gin_toolbar_preprocess_navigation(&$variables) { - /** @var \Drupal\gin\GinSettings $settings */ - $settings = \Drupal::classResolver(GinSettings::class); - $logo_default = $settings->getDefault('logo.use_default'); -- $variables['icon_default'] = $logo_default; -+ $variables['icon_path'] = !$logo_default ? $settings->getDefault('logo.path') : ''; - $variables['toolbar_variant'] = $settings->get('classic_toolbar'); - $variables['secondary_toolbar_frontend'] = $settings->get('secondary_toolbar_frontend'); - -@@ -377,54 +435,66 @@ function gin_toolbar_help($route_name, RouteMatchInterface $route_match) { - * Implements hook_theme(). - */ - function gin_toolbar_theme() { -- // Check if help is enabled. -- $help_enabled = FALSE; -- $module_handler = \Drupal::service('module_handler'); -- if ($module_handler->moduleExists('help')) { -- $help_enabled = TRUE; -+ // Check if Navigation module is active. -+ if (_gin_toolbar_module_is_active('navigation')) { -+ $items['navigation'] = [ -+ 'template' => 'navigation', -+ 'preprocess functions' => ['gin_toolbar_preprocess_navigation'], -+ 'variables' => [ -+ 'icon_path' => NULL, -+ 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -+ ], -+ ]; -+ } -+ else { -+ $items['navigation'] = [ -+ 'template' => 'navigation--gin', -+ 'preprocess functions' => ['gin_toolbar_preprocess_navigation'], -+ 'variables' => [ -+ 'icon_path' => NULL, -+ 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -+ 'menu_middle' => [], -+ 'menu_top' => [], -+ 'menu_bottom' => [], -+ ], -+ ]; -+ -+ $items['menu_region__top'] = [ -+ 'variables' => [ -+ 'links' => [], -+ 'title' => NULL, -+ 'menu_name' => NULL, -+ ], -+ ]; -+ -+ $items['menu_region__middle'] = [ -+ 'base hook' => 'menu', -+ 'variables' => [ -+ 'menu_name' => NULL, -+ 'items' => [], -+ 'attributes' => [], -+ 'title' => NULL, -+ ], -+ ]; -+ -+ // Check if help is enabled. -+ $help_enabled = _gin_toolbar_module_is_active('help'); -+ -+ $items['menu_region__bottom'] = [ -+ 'variables' => [ -+ 'help_enabled' => $help_enabled, -+ 'items' => [], -+ 'title' => NULL, -+ 'menu_name' => NULL, -+ 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -+ ], -+ ]; -+ -+ $items['container__administration_menu'] = [ -+ 'base hook' => 'container', -+ 'render element' => 'container', -+ ]; - } -- -- $items['navigation'] = [ -- 'variables' => [ -- 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -- 'menu_middle' => [], -- 'menu_top' => [], -- 'menu_bottom' => [], -- ], -- ]; -- -- $items['menu_region__top'] = [ -- 'variables' => [ -- 'links' => [], -- 'title' => NULL, -- 'menu_name' => NULL, -- ], -- ]; -- -- $items['menu_region__middle'] = [ -- 'base hook' => 'menu', -- 'variables' => [ -- 'menu_name' => NULL, -- 'items' => [], -- 'attributes' => [], -- 'title' => NULL, -- ], -- ]; -- -- $items['menu_region__bottom'] = [ -- 'variables' => [ -- 'help_enabled' => $help_enabled, -- 'items' => [], -- 'title' => NULL, -- 'menu_name' => NULL, -- 'path' => \Drupal::service('extension.list.theme')->getPath('gin'), -- ], -- ]; -- -- $items['container__administration_menu'] = [ -- 'base hook' => 'container', -- 'render element' => 'container', -- ]; - - return $items; - } -@@ -432,7 +502,7 @@ function gin_toolbar_theme() { - /** - * Implements hook_theme_suggestions_HOOK_alter(). - */ --function gin_toolbar_theme_suggestions_container_alter(&$suggestions, $variables) { -+function gin_toolbar_theme_suggestions_container_alter(&$suggestions, array $variables) { - if (isset($variables['element']['administration_menu'])) { - $suggestions[] = 'container__administration_menu'; - } -@@ -487,3 +557,19 @@ function _gin_toolbar_gin_is_active() { - - return $theme_activated; - } -+ -+/** -+ * Helper function for check if a module is active. -+ */ -+function _gin_toolbar_module_is_active($module) { -+ $module_handler = \Drupal::service('module_handler'); -+ -+ // Check if Navigation module is active. -+ $check_module = $module_handler->moduleExists($module); -+ -+ if ($check_module) { -+ return TRUE; -+ } -+ -+ return FALSE; -+} -diff --git a/templates/navigation--gin.html.twig b/templates/navigation--gin.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..43cc4e99ebfeb3b43b9bd6eeab48235dc24fa75f ---- /dev/null -+++ b/templates/navigation--gin.html.twig -@@ -0,0 +1,7 @@ -+{# -+/** -+ * @file -+ * Include navigation--gin.html.twig template from Gin. -+ */ -+#} -+{% include '@gin/navigation--gin.html.twig' ignore missing %} -diff --git a/templates/toolbar--gin.html.twig b/templates/toolbar--gin.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..d5a660aea1b48bc93033701ef49a9c8e97e99a4d ---- /dev/null -+++ b/templates/toolbar--gin.html.twig -@@ -0,0 +1,9 @@ -+{# -+/** -+ * @file -+ * Include toolbar--gin.html.twig template from Gin. -+ */ -+#} -+{% if toolbar_variant != 'new' and not core_navigation %} -+ {% include '@gin/navigation/toolbar--gin.html.twig' ignore missing %} -+{% endif %} -diff --git a/templates/toolbar.html.twig b/templates/toolbar.html.twig -index f050f4d1c354fef8a9726c8164d0ae45579b1cc5..3e99e34e024786af0ed902bea73e77d0a9e27de4 100644 ---- a/templates/toolbar.html.twig -+++ b/templates/toolbar.html.twig -@@ -4,8 +4,11 @@ - * Include toolbar.html.twig template from Gin. - */ - #} --{% include '@gin/navigation/toolbar--gin.html.twig' ignore missing %} --{% if secondary_toolbar_frontend and toolbar_variant != 'classic' %} -+{% if not core_navigation %} -+ {% include '@gin/navigation/toolbar--gin.html.twig' ignore missing %} -+{% endif %} -+ -+{% if secondary_toolbar_frontend and (toolbar_variant != 'classic' or core_navigation) %} - <div class="gin-secondary-toolbar gin-secondary-toolbar--frontend"{{ toolbar_variant == 'vertical' or toolbar_variant == 'new' ? ' data-offset-top' : '' }}> - <div class="gin-secondary-toolbar__layout-container"> - <div class="gin-breadcrumb-wrapper"> diff --git a/patches/d10/3443956.diff b/patches/d10/3443956.diff deleted file mode 100644 index ba8949436386388e9bda49bdb9e69b6f28e601aa..0000000000000000000000000000000000000000 --- a/patches/d10/3443956.diff +++ /dev/null @@ -1,43 +0,0 @@ -diff --git a/modules/smart_date_recur/src/Plugin/QueueWorker/RecurRuleUpdate.php b/modules/smart_date_recur/src/Plugin/QueueWorker/RecurRuleUpdate.php -index d45fce43ccf6b6f3521b6123f0f4acecf2197da8..1d9f6fbaa57af60650113d121f3f1bbaac5bfb7a 100644 ---- a/modules/smart_date_recur/src/Plugin/QueueWorker/RecurRuleUpdate.php -+++ b/modules/smart_date_recur/src/Plugin/QueueWorker/RecurRuleUpdate.php -@@ -3,6 +3,7 @@ - namespace Drupal\smart_date_recur\Plugin\QueueWorker; - - use Drupal\Core\Entity\EntityTypeManagerInterface; -+use Drupal\Core\Plugin\ContainerFactoryPluginInterface; - use Drupal\Core\Queue\QueueWorkerBase; - use Drupal\smart_date_recur\Entity\SmartDateRule; - use Symfony\Component\DependencyInjection\ContainerInterface; -@@ -16,7 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; - * cron = {"time" = 60} - * ) - */ --class RecurRuleUpdate extends QueueWorkerBase { -+class RecurRuleUpdate extends QueueWorkerBase implements ContainerFactoryPluginInterface { - - /** - * Definition of form entity type manager service. -@@ -31,15 +32,19 @@ class RecurRuleUpdate extends QueueWorkerBase { - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager - * The entity type manager service. - */ -- public function __construct(EntityTypeManagerInterface $entityTypeManager) { -+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entityTypeManager) { -+ parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->entityTypeManager = $entityTypeManager; - } - - /** - * {@inheritdoc} - */ -- public static function create(ContainerInterface $container) { -+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( -+ $configuration, -+ $plugin_id, -+ $plugin_definition, - $container->get('entity_type.manager'), - ); - } diff --git a/patches/d10/3449733.diff b/patches/d10/3449733.diff deleted file mode 100644 index 87619b3e4200681c07a3f80a9584f3fe1bda9a7d..0000000000000000000000000000000000000000 --- a/patches/d10/3449733.diff +++ /dev/null @@ -1,48 +0,0 @@ -diff --git a/src/Plugin/Action/PromptSetFieldValue.php b/src/Plugin/Action/PromptSetFieldValue.php -index 86155094b87b7da45120e03107982b170fe0973b..c6841a61a1da651917c51e04412eb8a2d94b3a67 100644 ---- a/src/Plugin/Action/PromptSetFieldValue.php -+++ b/src/Plugin/Action/PromptSetFieldValue.php -@@ -62,7 +62,7 @@ class PromptSetFieldValue extends SetFieldValue { - * {@inheritdoc} - */ - protected function getFieldsToUpdate() { -- $name = $this->tokenServices->replace($this->configuration['field_name']); -+ $name = $this->tokenService->replace($this->configuration['field_name']); - return [$name => '']; - } - -@@ -70,7 +70,7 @@ class PromptSetFieldValue extends SetFieldValue { - * {@inheritdoc} - */ - protected function getValueToUpdate($entity) { -- $name = $this->tokenServices->replace($this->configuration['field_name']); -+ $name = $this->tokenService->replace($this->configuration['field_name']); - $prompt_id = $this->configuration['prompt_id']; - - $values = \Drupal::service('prompt.service')->prompt_request($prompt_id, $entity, TRUE); -@@ -78,14 +78,14 @@ class PromptSetFieldValue extends SetFieldValue { - // Process the field values. - $use_token_replace = TRUE; - // Check whether the input wants to directly use defined data. -- if ((mb_substr($values, 0, 1) === '[') && (mb_substr($values, -1, 1) === ']') && (mb_strlen($values) <= 255) && ($data = $this->tokenServices->getTokenData($values))) { -+ if ((mb_substr($values, 0, 1) === '[') && (mb_substr($values, -1, 1) === ']') && (mb_strlen($values) <= 255) && ($data = $this->tokenService->getTokenData($values))) { - if (!($data instanceof TypedDataInterface) || !empty($data->getValue())) { - $use_token_replace = FALSE; - $values = $data; - } - } - if ($use_token_replace) { -- $values = $this->tokenServices->replaceClear($values); -+ $values = $this->tokenService->replaceClear($values); - } - - return [$name => $values]; -@@ -96,7 +96,7 @@ class PromptSetFieldValue extends SetFieldValue { - * - * @throws \Drupal\Core\Entity\EntityStorageException - */ -- public function execute($entity = NULL) { -+ public function execute(mixed $entity = NULL): void { - if (!($entity instanceof FieldableEntityInterface)) { - return; - } diff --git a/patches/d10/3454977.diff b/patches/d10/3454977.diff deleted file mode 100644 index 1c26a37dc8871c49db09acd485c705ae13cae8af..0000000000000000000000000000000000000000 --- a/patches/d10/3454977.diff +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/src/Plugin/search_api/backend/SearchApiElasticsearchBackend.php b/src/Plugin/search_api/backend/SearchApiElasticsearchBackend.php -index 9e47f52e823ad9907e5e66d86f96080407bc9919..2fdd6d710e8743f6a21f26cb6bd550fdb985547c 100644 ---- a/src/Plugin/search_api/backend/SearchApiElasticsearchBackend.php -+++ b/src/Plugin/search_api/backend/SearchApiElasticsearchBackend.php -@@ -1233,8 +1233,8 @@ class SearchApiElasticsearchBackend extends BackendPluginBase implements PluginFormInterf - * - * Prevents closure serialization error on search_api server add form. - */ -- public function __sleep() { -+ public function __sleep(): array { - return []; - } - - } diff --git a/patches/d10/3456176.patch b/patches/d10/3456176.patch deleted file mode 100644 index 060d9a3afdbbe224b2073eabc0c94eb346b51cfe..0000000000000000000000000000000000000000 --- a/patches/d10/3456176.patch +++ /dev/null @@ -1,23 +0,0 @@ -diff --git a/core/modules/big_pipe/src/Render/BigPipe.php b/core/modules/big_pipe/src/Render/BigPipe.php -index d7de08e29e..8e39116cbe 100644 ---- a/core/modules/big_pipe/src/Render/BigPipe.php -+++ b/core/modules/big_pipe/src/Render/BigPipe.php -@@ -561,12 +561,12 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde - - // Delete all messages that were generated during the rendering of this - // placeholder, to render them in a BigPipe-optimized way. -- $messages = $this->messenger->deleteAll(); -- foreach ($messages as $type => $type_messages) { -- foreach ($type_messages as $message) { -- $ajax_response->addCommand(new MessageCommand($message, NULL, ['type' => $type], FALSE)); -- } -- } -+ // $messages = $this->messenger->deleteAll(); -+ // foreach ($messages as $type => $type_messages) { -+ // foreach ($type_messages as $message) { -+ // $ajax_response->addCommand(new MessageCommand($message, NULL, ['type' => $type], FALSE)); -+ // } -+ //} - - // Push a fake request with the asset libraries loaded so far and - // dispatch KernelEvents::RESPONSE event. This results in the diff --git a/patches/d10/3460390.diff b/patches/d10/3460390.diff deleted file mode 100644 index b3978c111d1fa03b8f002c81f15ebc310e7479e9..0000000000000000000000000000000000000000 --- a/patches/d10/3460390.diff +++ /dev/null @@ -1,56 +0,0 @@ -diff --git a/dist/js/sidebar.js b/dist/js/sidebar.js -index f46fc7547850d36cf833c504e857a02c1abd1dee..ee9b7c3eb7ddb7dda105ac1a2fe94ac76badf610 100644 ---- a/dist/js/sidebar.js -+++ b/dist/js/sidebar.js -@@ -33,9 +33,10 @@ - const chooseStorage = window.innerWidth < 1024 ? "Drupal.gin.sidebarExpanded.mobile" : storageDesktop, hideLabel = Drupal.t("Hide sidebar panel"), sidebarTrigger = document.querySelector(".meta-sidebar__trigger"); - var _Drupal$ginCoreNaviga; - if (sidebarTrigger.querySelector("span").innerHTML = hideLabel, sidebarTrigger.setAttribute("title", hideLabel), -- sidebarTrigger.nextSibling.innerHTML = hideLabel, sidebarTrigger.setAttribute("aria-expanded", "true"), -- sidebarTrigger.classList.add("is-active"), document.body.setAttribute("data-meta-sidebar", "open"), -- localStorage.setItem(chooseStorage, "true"), window.innerWidth < 1280) if (null === (_Drupal$ginCoreNaviga = Drupal.ginCoreNavigation) || void 0 === _Drupal$ginCoreNaviga || _Drupal$ginCoreNaviga.collapseToolbar(), -+ sidebarTrigger.nextSibling && (sidebarTrigger.nextSibling.innerHTML = hideLabel), -+ sidebarTrigger.setAttribute("aria-expanded", "true"), sidebarTrigger.classList.add("is-active"), -+ document.body.setAttribute("data-meta-sidebar", "open"), localStorage.setItem(chooseStorage, "true"), -+ window.innerWidth < 1280) if (null === (_Drupal$ginCoreNaviga = Drupal.ginCoreNavigation) || void 0 === _Drupal$ginCoreNaviga || _Drupal$ginCoreNaviga.collapseToolbar(), - "vertical" === toolbarVariant) Drupal.ginToolbar.collapseToolbar(); else if ("new" === toolbarVariant) { - var _Drupal$behaviors$gin; - null === (_Drupal$behaviors$gin = Drupal.behaviors.ginNavigation) || void 0 === _Drupal$behaviors$gin || _Drupal$behaviors$gin.collapseSidebar(); -@@ -44,9 +45,9 @@ - collapseSidebar: () => { - const chooseStorage = window.innerWidth < 1024 ? "Drupal.gin.sidebarExpanded.mobile" : storageDesktop, showLabel = Drupal.t("Show sidebar panel"), sidebarTrigger = document.querySelector(".meta-sidebar__trigger"); - sidebarTrigger.querySelector("span").innerHTML = showLabel, sidebarTrigger.setAttribute("title", showLabel), -- sidebarTrigger.nextSibling.innerHTML = showLabel, sidebarTrigger.setAttribute("aria-expanded", "false"), -- sidebarTrigger.classList.remove("is-active"), document.body.setAttribute("data-meta-sidebar", "closed"), -- localStorage.setItem(chooseStorage, "false"); -+ sidebarTrigger.nextSibling && (sidebarTrigger.nextSibling.innerHTML = showLabel), -+ sidebarTrigger.setAttribute("aria-expanded", "false"), sidebarTrigger.classList.remove("is-active"), -+ document.body.setAttribute("data-meta-sidebar", "closed"), localStorage.setItem(chooseStorage, "false"); - }, - handleResize: function() { - let windowSize = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : window; -diff --git a/js/sidebar.js b/js/sidebar.js -index 25e104725f8e2f42e8b6b059a90a20f7db996e52..9fd9411c68037ec7032c5e0f6d69cba80b5935b9 100644 ---- a/js/sidebar.js -+++ b/js/sidebar.js -@@ -90,7 +90,9 @@ - - sidebarTrigger.querySelector('span').innerHTML = hideLabel; - sidebarTrigger.setAttribute('title', hideLabel); -- sidebarTrigger.nextSibling.innerHTML = hideLabel; -+ if (sidebarTrigger.nextSibling) { -+ sidebarTrigger.nextSibling.innerHTML = hideLabel; -+ } - sidebarTrigger.setAttribute('aria-expanded', 'true'); - sidebarTrigger.classList.add('is-active'); - -@@ -118,7 +120,9 @@ - - sidebarTrigger.querySelector('span').innerHTML = showLabel; - sidebarTrigger.setAttribute('title', showLabel); -- sidebarTrigger.nextSibling.innerHTML = showLabel; -+ if (sidebarTrigger.nextSibling) { -+ sidebarTrigger.nextSibling.innerHTML = showLabel; -+ } - sidebarTrigger.setAttribute('aria-expanded', 'false'); - sidebarTrigger.classList.remove('is-active'); diff --git a/patches/d10/3461808.diff b/patches/d10/3461808.diff deleted file mode 100644 index 01e39193eff9bad6e437a6e436063a754b119bdd..0000000000000000000000000000000000000000 --- a/patches/d10/3461808.diff +++ /dev/null @@ -1,81 +0,0 @@ -diff --git a/modules/log/src/Event/LogMessageEvent.php b/modules/log/src/Event/LogMessageEvent.php -index 485a8f9f..875590cc 100644 ---- a/modules/log/src/Event/LogMessageEvent.php -+++ b/modules/log/src/Event/LogMessageEvent.php -@@ -2,7 +2,6 @@ - - namespace Drupal\eca_log\Event; - --use Drupal\eca\Plugin\DataType\DataTransferObject; - use Symfony\Contracts\EventDispatcher\Event; - - /** -@@ -37,13 +36,6 @@ class LogMessageEvent extends Event { - */ - protected array $context; - -- /** -- * An instance holding log data accessible as token. -- * -- * @var \Drupal\eca\Plugin\DataType\DataTransferObject|null -- */ -- protected ?DataTransferObject $logData = NULL; -- - /** - * Construct a LogMessageEvent. - * -@@ -90,14 +82,4 @@ class LogMessageEvent extends Event { - return $this->context; - } - -- /** -- * Get the log data. -- * -- * @return \Drupal\eca\Plugin\DataType\DataTransferObject|null -- * The log data or NULL. -- */ -- public function getLogData(): ?DataTransferObject { -- return $this->logData; -- } -- - } -diff --git a/modules/log/src/Plugin/ECA/Event/LogEvent.php b/modules/log/src/Plugin/ECA/Event/LogEvent.php -index 5f9cf751..e23ecb72 100644 ---- a/modules/log/src/Plugin/ECA/Event/LogEvent.php -+++ b/modules/log/src/Plugin/ECA/Event/LogEvent.php -@@ -28,6 +28,13 @@ use Symfony\Contracts\EventDispatcher\Event; - */ - class LogEvent extends EventBase { - -+ /** -+ * An instance holding log data accessible as token. -+ * -+ * @var \Drupal\eca\Plugin\DataType\DataTransferObject|null -+ */ -+ protected ?DataTransferObject $logData = NULL; -+ - /** - * {@inheritdoc} - */ -@@ -142,10 +149,10 @@ class LogEvent extends EventBase { - public function getData(string $key): mixed { - $event = $this->event; - if ($key === 'log' && $event instanceof LogMessageEvent) { -- if ($logData = $event->getLogData()) { -+ if ($this->logData === NULL) { - $message = str_replace('@backtrace_string', '', $event->getMessage()); - $context = $this->cleanupIterableForDto($event->getContext()); -- $logData = DataTransferObject::create([ -+ $this->logData = DataTransferObject::create([ - 'severity' => DataTransferObject::create($event->getSeverity()), - 'message' => [ - 'raw' => DataTransferObject::create($event->getMessage()), -@@ -154,7 +161,7 @@ class LogEvent extends EventBase { - 'context' => DataTransferObject::create($context), - ]); - } -- return $logData; -+ return $this->logData; - } - return parent::getData($key); - } diff --git a/patches/d10/3465300.diff b/patches/d10/3465300.diff deleted file mode 100644 index c45e6b1f5751c61fdf413ea0be0af4e8fefe4dff..0000000000000000000000000000000000000000 --- a/patches/d10/3465300.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/MenuItemRoleAccessLinkTreeManipulator.php b/src/MenuItemRoleAccessLinkTreeManipulator.php -index 38e316b09e384f6f92b900dd3cb93430828c7116..0021497bcf49bcf65970e743d1a2c1f95efff3f6 100755 ---- a/src/MenuItemRoleAccessLinkTreeManipulator.php -+++ b/src/MenuItemRoleAccessLinkTreeManipulator.php -@@ -79,7 +79,7 @@ class MenuItemRoleAccessLinkTreeManipulator extends DefaultMenuLinkTreeManipulat - // When no route name is specified, this must be an external link. - $config = $this->configManager->get('menu_item_role_access.config'); - // If we want to check this URL or not. -- if (!$this->checkUrl($config, $url) && !isset($metadata['entity_id'])) { -+ if (!$this->checkUrl($config, $url) || !isset($metadata['entity_id'])) { - return $access_result->cachePerPermissions(); - } - diff --git a/patches/d10/3466609.diff b/patches/d10/3466609.diff deleted file mode 100644 index ccf3116ed54e0b08999ff06c46f2530ed5b7b720..0000000000000000000000000000000000000000 --- a/patches/d10/3466609.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/Plugin/EmbedType/Entity.php b/src/Plugin/EmbedType/Entity.php -index 745c4e4ab654b95d987accbefc0f7bef1b5d95ac..56091963d42f501235ae047f87df3b2e11249788 100644 ---- a/src/Plugin/EmbedType/Entity.php -+++ b/src/Plugin/EmbedType/Entity.php -@@ -53,7 +53,7 @@ class Entity extends EmbedTypeBase implements ContainerFactoryPluginInterface { - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { -- $instance = new static($configuration, $plugin_id, $plugin_definition); -+ $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); - $instance->entityTypeManager = $container->get('entity_type.manager'); - $instance->entityTypeRepository = $container->get('entity_type.repository'); - $instance->entityTypeBundleInfo = $container->get('entity_type.bundle.info'); diff --git a/patches/d10/3467754.diff b/patches/d10/3467754.diff deleted file mode 100644 index 0538b82d8a26bfcb3d14d60f276bb8a0d27587db..0000000000000000000000000000000000000000 --- a/patches/d10/3467754.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/Entity/GroupRole.php b/src/Entity/GroupRole.php -index d71622a05cacbb931557d5164526a3e51ad1d1d4..2d707ce887a0280bba27bf7d500afd41a4e0aaa2 100644 ---- a/src/Entity/GroupRole.php -+++ b/src/Entity/GroupRole.php -@@ -312,7 +312,7 @@ class GroupRole extends ConfigEntityBase implements GroupRoleInterface { - parent::postLoad($storage, $entities); - // Sort the queried roles by their weight. - // See \Drupal\Core\Config\Entity\ConfigEntityBase::sort(). -- uasort($entities, 'static::sort'); -+ uasort($entities, [ConfigEntityBase::class, 'sort']); - } - - /** diff --git a/patches/d10/3469697.diff b/patches/d10/3469697.diff deleted file mode 100644 index c89fcec5e7c73f33f40c1c34d2903b641ebc2845..0000000000000000000000000000000000000000 --- a/patches/d10/3469697.diff +++ /dev/null @@ -1,42 +0,0 @@ -diff --git a/modules/form/src/HookHandler.php b/modules/form/src/HookHandler.php -index b67d9394e839c6ea0eada0b0abd237ae2c627392..3373e4e20b776580d9143352df9b576a61e7532c 100644 ---- a/modules/form/src/HookHandler.php -+++ b/modules/form/src/HookHandler.php -@@ -202,16 +202,18 @@ class HookHandler extends BaseHookHandler { - - try { - $form['#entity'] = clone $entity; -- $original_triggering_element = $form_state->getTriggeringElement(); -- $form_state->setTriggeringElement([ -- '#limit_validation_errors' => [], -- ]); -- /** @var \Drupal\Core\Form\FormValidatorInterface $validator */ -- $validator = \Drupal::service('form_validator'); -- $form_state->setValidationEnforced(TRUE); -- $validator->validateForm(\Drupal::formBuilder()->getFormId($form_state->getFormObject(), $form_state), $form, $form_state); -- $form_state->setTriggeringElement($original_triggering_element); -- $form_state->setValidationEnforced(TRUE); -+ if ($form['#eca_ief_info']['widget_plugin_id'] !== 'inline_entity_form_complex') { -+ $original_triggering_element = $form_state->getTriggeringElement(); -+ $form_state->setTriggeringElement([ -+ '#limit_validation_errors' => [], -+ ]); -+ /** @var \Drupal\Core\Form\FormValidatorInterface $validator */ -+ $validator = \Drupal::service('form_validator'); -+ $form_state->setValidationEnforced(TRUE); -+ $validator->validateForm(\Drupal::formBuilder()->getFormId($form_state->getFormObject(), $form_state), $form, $form_state); -+ $form_state->setTriggeringElement($original_triggering_element); -+ $form_state->setValidationEnforced(TRUE); -+ } - - /** @var \Drupal\inline_entity_form\InlineFormInterface $handler */ - $handler = \Drupal::entityTypeManager()->getHandler($form['#entity']->getEntityTypeId(), 'inline_form'); -@@ -226,7 +228,7 @@ class HookHandler extends BaseHookHandler { - ], $form['#entity']); - } - catch (\Throwable $t) { -- \Drupal::logger('eca_form')->error("An error occurred while trying to build an inline entity from submitted form values. This might be a problem in case you use ECA to extend inline entity forms. Please report this to the ECA issue queue to help us improving it."); -+ \Drupal::logger('eca_form')->warning("An error occurred while trying to build an inline entity from submitted form values. This might be a problem in case you use ECA to extend inline entity forms. Please report this to the ECA issue queue to help us improving it."); - } - - // Info is not needed anymore, remove it to prevent unnecessary bloat. diff --git a/patches/d10/3470506.diff b/patches/d10/3470506.diff deleted file mode 100644 index 96f60f16e4292418102ef6c68809edf36e705be3..0000000000000000000000000000000000000000 --- a/patches/d10/3470506.diff +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/config/schema/easy_email_type.schema.yml b/config/schema/easy_email_type.schema.yml -index a260019cc4465c26db51c2b59e675108550c0ee0..c5314081a45db174a927e6c575a48099f04f22e7 100644 ---- a/config/schema/easy_email_type.schema.yml -+++ b/config/schema/easy_email_type.schema.yml -@@ -54,15 +54,8 @@ easy_email.easy_email_type.*: - type: text - label: 'Inbox Preview' - bodyHtml: -- type: mapping -+ type: text_format - label: 'HTML Body' -- mapping: -- value: -- type: text -- label: 'Text Value' -- format: -- type: string -- label: 'Text Format' - bodyPlain: - type: text - label: 'Plain Text Body' diff --git a/patches/d10/3472268.diff b/patches/d10/3472268.diff deleted file mode 100644 index 680e807eeecd212aa0d9778c81707dc491d5824d..0000000000000000000000000000000000000000 --- a/patches/d10/3472268.diff +++ /dev/null @@ -1,27 +0,0 @@ -diff --git a/src/Plugin/Mail/SymfonyMailer.php b/src/Plugin/Mail/SymfonyMailer.php -index de3ab4334b2fa7d696ae5ee247f39f71676848af..c6a6f0ca47c851f5033b2c3322d8d160a3d5502f 100644 ---- a/src/Plugin/Mail/SymfonyMailer.php -+++ b/src/Plugin/Mail/SymfonyMailer.php -@@ -480,11 +480,6 @@ class SymfonyMailer implements MailInterface, ContainerFactoryPluginInterface { - continue; - } - -- // Convert markup to string. -- if($a['filecontent'] instanceof MarkupInterface) { -- $a['filecontent'] = (string) $a['filecontent']; -- } -- - // Attach file (either using a static file or provided content). - if (!empty($a['filepath'])) { - $file = new stdClass(); -@@ -494,6 +489,10 @@ class SymfonyMailer implements MailInterface, ContainerFactoryPluginInterface { - $this->attachFiles($email, [$file]); - } - else { -+ // Convert markup to string. -+ if ($a['filecontent'] instanceof MarkupInterface) { -+ $a['filecontent'] = (string) $a['filecontent']; -+ } - $email->addPart(new DataPart($a['filecontent'], $a['filename'], $a['filemime'])); - } - } diff --git a/patches/d10/3473763.diff b/patches/d10/3473763.diff deleted file mode 100644 index ad0411aab71e9598a3687ac9b1673b1db8a42289..0000000000000000000000000000000000000000 --- a/patches/d10/3473763.diff +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module -index 88fbdfe04d5b4266aee34a92db106c35d7d18f11..102844baef8eefaa3b54e2c3201f04738dd71f3e 100644 ---- a/core/modules/locale/locale.module -+++ b/core/modules/locale/locale.module -@@ -1012,6 +1012,9 @@ function locale_string_is_safe($string) { - // \Drupal\Component\Utility\Xss::filter() to not incorrectly alter the - // string. https://www.drupal.org/node/2372127 - $string = preg_replace('/\[[a-z0-9_-]+(:[a-z0-9_-]+)+\]/i', '', $string); -+ // Remove the "<front>" placeholder if it exists, before testing with the -+ // XSS filter, as this looks like an invalid HTML tag while it's a Drupalism. -+ $string = str_replace('<front>', '', $string); - - return Html::decodeEntities($string) == Html::decodeEntities(Xss::filter($string, ['a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var'])); - } diff --git a/patches/d10/3475773.diff b/patches/d10/3475773.diff deleted file mode 100644 index 174724e6317797c997d4f091c75c0a2d6ea582e4..0000000000000000000000000000000000000000 --- a/patches/d10/3475773.diff +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/dist/js/init.js b/dist/js/init.js -index 3a8a290b384132d12ca9b2720322e6ec817516bf..5a88ff2ea090f7f470e29908f37a6ff074940710 100644 ---- a/dist/js/init.js -+++ b/dist/js/init.js -@@ -2,9 +2,11 @@ - if (localStorage.getItem("GinDarkMode") && localStorage.removeItem("GinDarkMode"), - localStorage.getItem("Drupal.gin.darkmode") && localStorage.removeItem("Drupal.gin.darkmode"), - localStorage.getItem("GinSidebarOpen") && (localStorage.setItem("Drupal.gin.toolbarExpanded", localStorage.getItem("GinSidebarOpen")), -- localStorage.removeItem("GinSidebarOpen")), window.ginDarkmode = JSON.parse(document.getElementById("gin-setting-darkmode").textContent).ginDarkmode, -- 1 == window.ginDarkmode || "auto" === window.ginDarkmode && window.matchMedia("(prefers-color-scheme: dark)").matches ? document.documentElement.classList.add("gin--dark-mode") : !0 === document.documentElement.classList.contains("gin--dark-mode") && document.documentElement.classList.remove("gin--dark-mode"), -- localStorage.getItem("Drupal.gin.toolbarExpanded")) { -+ localStorage.removeItem("GinSidebarOpen")), function() { -+ const darkmodeSetting = document.getElementById("gin-setting-darkmode")?.textContent; -+ window.ginDarkmode = darkmodeSetting ? JSON.parse(darkmodeSetting)?.ginDarkmode : "auto", -+ 1 == window.ginDarkmode || "auto" === window.ginDarkmode && window.matchMedia("(prefers-color-scheme: dark)").matches ? document.documentElement.classList.add("gin--dark-mode") : !0 === document.documentElement.classList.contains("gin--dark-mode") && document.documentElement.classList.remove("gin--dark-mode"); -+ }(), localStorage.getItem("Drupal.gin.toolbarExpanded")) { - const style = document.createElement("style"), className = "gin-toolbar-inline-styles"; - if (style.className = className, "true" === localStorage.getItem("Drupal.gin.toolbarExpanded")) { - style.innerHTML = "\n @media (min-width: 976px) {\n /* Small CSS hack to make sure this has the highest priority */\n body.gin--vertical-toolbar.gin--vertical-toolbar.gin--vertical-toolbar {\n padding-inline-start: 256px !important;\n transition: none !important;\n }\n\n .gin--vertical-toolbar .toolbar-menu-administration {\n min-width: var(--gin-toolbar-width, 256px);\n transition: none;\n }\n\n .gin--vertical-toolbar .toolbar-menu-administration > .toolbar-menu > .menu-item > .toolbar-icon,\n .gin--vertical-toolbar .toolbar-menu-administration > .toolbar-menu > .menu-item > .toolbar-box > .toolbar-icon {\n min-width: calc(var(--gin-toolbar-width, 256px) - 16px);\n }\n }\n "; -diff --git a/js/init.js b/js/init.js -index b5167343640982546d23c5926a90e00e41cd0e45..560eafea4ee26f8146bf9d80784cff55ba0b0209 100644 ---- a/js/init.js -+++ b/js/init.js -@@ -22,7 +22,9 @@ checkLegacy(); - function ginInitDarkmode() { - const darkModeClass = 'gin--dark-mode'; - -- window.ginDarkmode = JSON.parse(document.getElementById('gin-setting-darkmode').textContent).ginDarkmode; -+ const darkmodeSetting = document.getElementById('gin-setting-darkmode')?.textContent; -+ // Set window variable. -+ window.ginDarkmode = darkmodeSetting ? JSON.parse(darkmodeSetting)?.ginDarkmode : 'auto'; - - if ( - window.ginDarkmode == 1 || diff --git a/patches/d10/3478471.diff b/patches/d10/3478471.diff deleted file mode 100644 index fc2b7a4f15cf669187f44b4bde52191f7ed9c71b..0000000000000000000000000000000000000000 --- a/patches/d10/3478471.diff +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/src/Plugin/views/filter/Date.php b/src/Plugin/views/filter/Date.php -index 63ee9a22883f65523b0cf20fead66e65ed1e250c..891c4fee97864715c89b53c24e5d96203aef054f 100644 ---- a/src/Plugin/views/filter/Date.php -+++ b/src/Plugin/views/filter/Date.php -@@ -106,6 +106,12 @@ class Date extends CoreDate implements ContainerFactoryPluginInterface { - 'short' => $this->t('contains'), - 'values' => 1, - ], -+ 'daterange_contains_partly' => [ -+ 'title' => $this->t('Contains partly'), -+ 'method' => 'opContains', -+ 'short' => $this->t('contains partly'), -+ 'values' => 1, -+ ], - 'daterange_not_contains' => [ - 'title' => $this->t('Does not contain'), - 'method' => 'opContains', -@@ -245,6 +251,10 @@ class Date extends CoreDate implements ContainerFactoryPluginInterface { - $this->query->addWhereExpression($this->options['group'], "$field <= $min_value AND $field_end >= $max_value"); - break; - -+ case 'daterange_contains_partly': -+ $this->query->addWhereExpression($this->options['group'], "$field <= $max_value AND $field_end >= $min_value"); -+ break; -+ - case 'daterange_not_contains': - $this->query->addWhereExpression($this->options['group'], "$field >= $max_value OR $field_end <= $min_value"); - break; diff --git a/patches/d10/3480137.diff b/patches/d10/3480137.diff deleted file mode 100644 index 49b71b4e7abc05e2cb2548ffa42d997496a76aa0..0000000000000000000000000000000000000000 --- a/patches/d10/3480137.diff +++ /dev/null @@ -1,164 +0,0 @@ -diff --git a/modules/content/src/Plugin/Action/GetBundleList.php b/modules/content/src/Plugin/Action/GetBundleList.php -new file mode 100644 -index 0000000000000000000000000000000000000000..4f715e5b4bdb7d621b3188acf697f602c6e5ce62 ---- /dev/null -+++ b/modules/content/src/Plugin/Action/GetBundleList.php -@@ -0,0 +1,116 @@ -+<?php -+ -+namespace Drupal\eca_content\Plugin\Action; -+ -+use Drupal\Core\Form\FormStateInterface; -+use Drupal\eca\Plugin\Action\ConfigurableActionBase; -+use Drupal\eca\Plugin\ECA\PluginFormTrait; -+use Drupal\eca\Service\ContentEntityTypes; -+use Symfony\Component\DependencyInjection\ContainerInterface; -+ -+/** -+ * Get a list of bundles. -+ * -+ * @Action( -+ * id = "eca_get_bundle_list", -+ * label = @Translation("Entity: get list of bundles"), -+ * description = @Translation("Gets the list of bundles for a given entity type."), -+ * eca_version_introduced = "2.1.0" -+ * ) -+ */ -+class GetBundleList extends ConfigurableActionBase { -+ -+ use PluginFormTrait; -+ -+ /** -+ * The entity type service. -+ * -+ * @var \Drupal\eca\Service\ContentEntityTypes -+ */ -+ protected ContentEntityTypes $entityTypes; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { -+ $plugin = parent::create($container, $configuration, $plugin_id, $plugin_definition); -+ $plugin->entityTypes = $container->get('eca.service.content_entity_types'); -+ return $plugin; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function defaultConfiguration(): array { -+ return [ -+ 'token_name' => '', -+ 'type' => '', -+ 'mode' => 'ids', -+ ] + parent::defaultConfiguration(); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { -+ $form['token_name'] = [ -+ '#type' => 'textfield', -+ '#title' => $this->t('Name of token'), -+ '#default_value' => $this->configuration['token_name'], -+ '#description' => $this->t('Provide the name of a token that holds the bundle list.'), -+ '#weight' => -60, -+ '#required' => TRUE, -+ '#eca_token_reference' => TRUE, -+ ]; -+ $options = $this->entityTypes->getTypes(); -+ $form['type'] = [ -+ '#type' => 'select', -+ '#title' => $this->t('Type'), -+ '#options' => $options, -+ '#default_value' => $this->configuration['type'], -+ '#description' => $this->t('The entity type for which to receive the list of bundles.'), -+ '#weight' => -50, -+ '#eca_token_select_option' => TRUE, -+ ]; -+ $form['mode'] = [ -+ '#type' => 'select', -+ '#title' => $this->t('Mode'), -+ '#options' => [ -+ 'ids' => $this->t('IDs'), -+ 'labels' => $this->t('ILabels'), -+ ], -+ '#default_value' => $this->configuration['mode'], -+ '#description' => $this->t('This either returns a list of bundle IDs or of their labels.'), -+ '#weight' => -40, -+ ]; -+ return parent::buildConfigurationForm($form, $form_state); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { -+ $this->configuration['token_name'] = $form_state->getValue('token_name'); -+ $this->configuration['type'] = $form_state->getValue('type'); -+ $this->configuration['mode'] = $form_state->getValue('mode'); -+ parent::submitConfigurationForm($form, $form_state); -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function execute(): void { -+ if ($this->configuration['type'] === '_eca_token') { -+ $type = $this->getTokenValue('type', ''); -+ } -+ else { -+ $type = $this->configuration['type']; -+ } -+ $bundles = $this->entityTypes->getBundles($type); -+ if ($this->configuration['mode'] === 'ids') { -+ $bundles = array_keys($bundles); -+ } -+ $this->tokenService->addTokenData($this->configuration['token_name'], $bundles); -+ } -+ -+} -diff --git a/src/Service/ContentEntityTypes.php b/src/Service/ContentEntityTypes.php -index e8889ea735346d39c6441c17c635e0653c37f750..75e2aa51d7297946e0f6f93f07570ad53791c26e 100644 ---- a/src/Service/ContentEntityTypes.php -+++ b/src/Service/ContentEntityTypes.php -@@ -119,6 +119,37 @@ class ContentEntityTypes { - return $this->typesAndBundles[$idx1][$idx2]; - } - -+ /** -+ * Gets the entity types. -+ * -+ * @return array -+ * The entity types. -+ */ -+ public function getTypes(): array { -+ $result = []; -+ foreach ($this->entityTypeManager->getDefinitions() as $definition) { -+ if ($definition instanceof ContentEntityTypeInterface) { -+ $result[$definition->id()] = $definition->getLabel(); -+ } -+ } -+ return $result; -+ } -+ -+ /** -+ * Gets the bundles of an entity type. -+ * -+ * @return array -+ * The bundles of the entity type. -+ */ -+ public function getBundles(string $entity_type_id): array { -+ $result = []; -+ $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id); -+ foreach ($bundles as $bundle => $bundleDef) { -+ $result[$bundle] = $bundleDef['label']; -+ } -+ return $result; -+ } -+ - /** - * Gets the type and bundles. - * diff --git a/patches/d10/3480140.diff b/patches/d10/3480140.diff deleted file mode 100644 index 807217c422cd87c9de64c00ae3b4bbd9fb64de49..0000000000000000000000000000000000000000 --- a/patches/d10/3480140.diff +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/src/Plugin/Tamper/Encode.php b/src/Plugin/Tamper/Encode.php -index a72b71fb7eda4275305e3b4a59ac46fdc26d74ac..a259df27a5096c0c9aa61d77006ed9ae9bd15e0a 100644 ---- a/src/Plugin/Tamper/Encode.php -+++ b/src/Plugin/Tamper/Encode.php -@@ -5,6 +5,7 @@ namespace Drupal\tamper\Plugin\Tamper; - use Drupal\Core\Form\FormStateInterface; - use Drupal\tamper\TamperableItemInterface; - use Drupal\tamper\TamperBase; -+use Symfony\Component\Yaml\Yaml; - - /** - * Plugin implementation for encoding / decoding. -@@ -66,6 +67,8 @@ class Encode extends TamperBase { - 'json_decode' => $this->t('Json Decode'), - 'base64_encode' => $this->t('Base64 Encode'), - 'base64_decode' => $this->t('Base64 Decode'), -+ 'yaml_encode' => $this->t('YAML Encode'), -+ 'yaml_decode' => $this->t('YAML Decode'), - ]; - } - -@@ -78,6 +81,12 @@ class Encode extends TamperBase { - if (function_exists($function)) { - $data = call_user_func($function, $data); - } -+ elseif ($function === 'yaml_encode') { -+ $data = Yaml::dump($data); -+ } -+ elseif ($function === 'yaml_decode') { -+ $data = Yaml::parse($data); -+ } - - return $data; - } diff --git a/patches/d10/3481142.diff b/patches/d10/3481142.diff deleted file mode 100644 index 6acb3c3c6612995d39c979a491abeab79867f4a4..0000000000000000000000000000000000000000 --- a/patches/d10/3481142.diff +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/src/Controller/RouteAccessController.php b/src/Controller/RouteAccessController.php -index d572a3acd686049425fcdaf53c9b7f41a8f2f0b4..e4da1b054c876ad79973b68ee0826c0d3b19c0d1 100644 ---- a/src/Controller/RouteAccessController.php -+++ b/src/Controller/RouteAccessController.php -@@ -36,7 +36,13 @@ class RouteAccessController extends ControllerBase implements ContainerInjection - protected BookManagerInterface $bookManager, - protected RouteMatchInterface $route_match, - ) { -- $this->node = $route_match->getParameter('node'); -+ $node = $route_match->getParameter('node'); -+ if (!$node instanceof NodeInterface) { -+ $node = $this->entityTypeManager() -+ ->getStorage('node') -+ ->load($node); -+ } -+ $this->node = $node; - } - - /** diff --git a/patches/d10/3482226.patch b/patches/d10/3482226.patch deleted file mode 100644 index f56bb30080ad963701aa79fff8d7af2fee7382f8..0000000000000000000000000000000000000000 --- a/patches/d10/3482226.patch +++ /dev/null @@ -1,23 +0,0 @@ -diff --git a/src/Form/ConfigureAction.php b/src/Form/ConfigureAction.php -index 4cea420..fe88590 100644 ---- a/src/Form/ConfigureAction.php -+++ b/src/Form/ConfigureAction.php -@@ -2,6 +2,7 @@ - - namespace Drupal\views_bulk_operations\Form; - -+use Drupal\Core\DependencyInjection\DependencySerializationTrait; - use Drupal\Core\Form\FormBase; - use Drupal\Core\Form\FormStateInterface; - use Drupal\Core\TempStore\PrivateTempStoreFactory; -@@ -16,6 +17,10 @@ use Symfony\Component\DependencyInjection\ContainerInterface; - class ConfigureAction extends FormBase { - - use ViewsBulkOperationsFormTrait; -+ // Use the serialization trait to ensure the read only params get instantiated -+ // given the fact that their read only status prevents the parent class from -+ // doing so. -+ use DependencySerializationTrait; - - /** - * Constructor. diff --git a/patches/d10/3484892.diff b/patches/d10/3484892.diff deleted file mode 100644 index e1c094ae4574eeaaa5223f640d688616073f2caf..0000000000000000000000000000000000000000 --- a/patches/d10/3484892.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/Entity/WebformContentCreatorEntity.php b/src/Entity/WebformContentCreatorEntity.php -index 0934ac064f98e8b06e3907182c9a2dfb29c1dfc4..1c752f74a25ead1b426469f082008d0e9f822844 100644 ---- a/src/Entity/WebformContentCreatorEntity.php -+++ b/src/Entity/WebformContentCreatorEntity.php -@@ -32,7 +32,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse; - * } - * }, - * config_prefix = "webform_content_creator", -- * admin_permission = "administer site configuration", -+ * admin_permission = "access webform content creator configuration", - * entity_keys = { - * "id" = "id", - * "title" = "title", diff --git a/patches/d10/3487530.diff b/patches/d10/3487530.diff deleted file mode 100644 index 227fff6a04826ae4a029a5fbee09a65419b2d727..0000000000000000000000000000000000000000 --- a/patches/d10/3487530.diff +++ /dev/null @@ -1,65 +0,0 @@ -diff --git a/src/WebformEntityListBuilder.php b/src/WebformEntityListBuilder.php -index 827110be1d819f7e7090c415498d0a747ada0765..c27958aa5b7e3b3dfa31f4f4a23722fb4942ba56 100644 ---- a/src/WebformEntityListBuilder.php -+++ b/src/WebformEntityListBuilder.php -@@ -173,8 +173,11 @@ class WebformEntityListBuilder extends ConfigEntityListBuilder { - $build['table']['#attributes']['class'][] = 'webform-forms'; - - // Bulk operations. -- if ($this->bulkOperations && $this->currentUser->hasPermission('administer webform')) { -+ if ($this->bulkOperations && ($this->currentUser->hasPermission('administer webform') || $this->currentUser->hasPermission('administer webform overview'))) { - $build['table'] = \Drupal::formBuilder()->getForm('\Drupal\webform\Form\WebformEntityBulkForm', $build['table']); -+ if (!$this->currentUser->hasPermission('delete any webform')) { -+ unset($build['table']['operations']['action']['#options']['webform_delete_action']); -+ } - } - - // Attachments. -@@ -194,7 +197,7 @@ class WebformEntityListBuilder extends ConfigEntityListBuilder { - */ - protected function buildFilterForm() { - // Add the filter by key(word) and/or state. -- if ($this->currentUser->hasPermission('administer webform')) { -+ if ($this->currentUser->hasPermission('administer webform') || $this->currentUser->hasPermission('administer webform overview')) { - $state_options = [ - (string) $this->t('Active') => [ - '' => $this->t('All [@total]', ['@total' => $this->getTotal(NULL, NULL)]), -@@ -231,7 +234,7 @@ class WebformEntityListBuilder extends ConfigEntityListBuilder { - */ - protected function buildInfo() { - // Display info. -- if ($this->currentUser->hasPermission('administer webform') && ($total = $this->getTotal($this->keys, $this->category, $this->state))) { -+ if (($this->currentUser->hasPermission('administer webform') || $this->currentUser->hasPermission('administer webform overview')) && ($total = $this->getTotal($this->keys, $this->category, $this->state))) { - return [ - '#markup' => $this->formatPlural($total, '@count webform', '@count webforms'), - '#prefix' => '<div>', -@@ -672,12 +675,12 @@ class WebformEntityListBuilder extends ConfigEntityListBuilder { - * Is the current user a webform administrator. - * - * @return bool -- * TRUE if the current user has 'administer webform' or 'edit any webform' -- * permission. -+ * TRUE if the current user has 'administer webform' or 'administer webform overview' -+ * or 'edit any webform' permission. - */ - protected function isAdmin() { - $account = $this->currentUser; -- return ($account->hasPermission('administer webform') || $account->hasPermission('edit any webform') || $account->hasPermission('view any webform submission')); -+ return ($account->hasPermission('administer webform') || $account->hasPermission('administer webform overview') || $account->hasPermission('edit any webform') || $account->hasPermission('view any webform submission')); - } - - /** -diff --git a/webform.permissions.yml b/webform.permissions.yml -index 96570ace1413b3232e8ffda50c466860dc0bce18..2c24ee565c5905adcf0ade43b069bbf04be1539e 100644 ---- a/webform.permissions.yml -+++ b/webform.permissions.yml -@@ -21,6 +21,9 @@ - 'administer webform element access': - title: 'Administer webform element access' - description: 'Restrict webform element access to certain roles and users.' -+'administer webform overview': -+ title: 'Administer webform overview' -+ description: 'Filter webforms and perform bulk actions on overview page.' - - 'edit webform source': - title: 'Edit webform source code' diff --git a/patches/d10/3488084.diff b/patches/d10/3488084.diff deleted file mode 100644 index 0e201dada3c8586f9dd46744b83d61af60a23de0..0000000000000000000000000000000000000000 --- a/patches/d10/3488084.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/Plugin/SecurityCheck/NamePasswords.php b/src/Plugin/SecurityCheck/NamePasswords.php -index 439017ed957d8f1e53210210b9bcbc8fcfd32bd8..7a992799b4c270253e9753beaff3810b3b4a3fa0 100644 ---- a/src/Plugin/SecurityCheck/NamePasswords.php -+++ b/src/Plugin/SecurityCheck/NamePasswords.php -@@ -96,7 +96,7 @@ class NamePasswords extends SecurityCheckBase { - $users = User::loadMultiple($ids); - $findings = []; - foreach ($users as $user) { -- if ($this->userAuth->authenticateAccount($this->userAuth->lookupAccount($user->getDisplayName()), $user->getDisplayName())) { -+ if (($account = $this->userAuth->lookupAccount($user->getAccountName())) && $this->userAuth->authenticateAccount($account, $user->getAccountName())) { - $findings[] = $user->getDisplayName(); - } - diff --git a/patches/d10/3488128.diff b/patches/d10/3488128.diff deleted file mode 100644 index 2d2207f76a5f786c20d38e3710ac4abc436a1eee..0000000000000000000000000000000000000000 --- a/patches/d10/3488128.diff +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/config/install/zoom_video.settings.yml b/config/install/zoom_video.settings.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..7cafa14cce5f6842a2a1f84029af077e89bf36a0 ---- /dev/null -+++ b/config/install/zoom_video.settings.yml -@@ -0,0 +1,2 @@ -+sdk_key: '' -+sdk_secret: '' -diff --git a/config/schema/zoom.video.schema.yml b/config/schema/zoom.video.schema.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..75eb23fa90173623fd2c59c16f438bef32bca085 ---- /dev/null -+++ b/config/schema/zoom.video.schema.yml -@@ -0,0 +1,10 @@ -+zoom_video.settings: -+ type: config_object -+ label: 'Zoom Video settings' -+ mapping: -+ sdk_key: -+ type: string -+ label: 'Client ID' -+ sdk_secret: -+ type: string -+ label: 'Client Secret' diff --git a/patches/d10/3488135.diff b/patches/d10/3488135.diff deleted file mode 100644 index 32afd0ce18c1be4bb08b319438a4dcb208718ae0..0000000000000000000000000000000000000000 --- a/patches/d10/3488135.diff +++ /dev/null @@ -1,64 +0,0 @@ -diff --git a/src/Plugin/Field/FieldFormatter/ZoomVideoFormatter.php b/src/Plugin/Field/FieldFormatter/ZoomVideoFormatter.php -index ac430088c1e0a938e81afe3d37c13cd93bd667e6..f9c76fd1478a432d9877e0bd4c291ae9d5c84605 100644 ---- a/src/Plugin/Field/FieldFormatter/ZoomVideoFormatter.php -+++ b/src/Plugin/Field/FieldFormatter/ZoomVideoFormatter.php -@@ -92,6 +92,9 @@ class ZoomVideoFormatter extends FormatterBase { - foreach ($items as $delta => $item) { - $meeting_number = $item->get('meeting_number')->getValue(); - $meeting_password = $item->get('password')->getValue(); -+ if ($meeting_number === NULL || $meeting_password === NULL) { -+ continue; -+ } - - $elements[$delta] = [ - '#theme' => 'zoom_video_formatter_template', -@@ -103,20 +106,22 @@ class ZoomVideoFormatter extends FormatterBase { - ]; - } - -- // Attach the zoom_video settings to drupalSettings. -- $elements['#attached']['drupalSettings']['zoom_video'] = [ -- 'sdk_key' => $config->get('sdk_key'), -- // @todo We need to improve related JS code to handle multiple values. -- 'meetingNumber' => $meeting_number ?? '', -- 'password' => $meeting_password ?? '', -- ]; -+ if (count($elements) > 0) { -+ // Attach the zoom_video settings to drupalSettings. -+ $elements['#attached']['drupalSettings']['zoom_video'] = [ -+ 'sdk_key' => $config->get('sdk_key'), -+ // @todo We need to improve related JS code to handle multiple values. -+ 'meetingNumber' => $meeting_number ?? '', -+ 'password' => $meeting_password ?? '', -+ ]; - -- // Attach the current user settings to drupalSettings. -- $elements['#attached']['drupalSettings']['user'] = [ -- 'userName' => $this->currentUser->getAccountName(), -- 'userEmail' => $this->currentUser->getEmail(), -- 'isAuthenticated' => $this->currentUser->isAuthenticated(), -- ]; -+ // Attach the current user settings to drupalSettings. -+ $elements['#attached']['drupalSettings']['user'] = [ -+ 'userName' => $this->currentUser->getAccountName(), -+ 'userEmail' => $this->currentUser->getEmail(), -+ 'isAuthenticated' => $this->currentUser->isAuthenticated(), -+ ]; -+ } - - return $elements; - } -diff --git a/src/Plugin/Field/FieldType/ZoomVideoField.php b/src/Plugin/Field/FieldType/ZoomVideoField.php -index 3f7976a96f28a443ea28f905bf9465476fd5a34c..a78b1184dad7b59fce9fb8dd183d2e91d79670fc 100644 ---- a/src/Plugin/Field/FieldType/ZoomVideoField.php -+++ b/src/Plugin/Field/FieldType/ZoomVideoField.php -@@ -47,6 +47,9 @@ class ZoomVideoField extends FieldItemBase { - public function preSave(): void { - $value = $this->getValue(); - preg_match('/\/j\/(\d+)\?pwd=([a-zA-Z0-9]+)/', $value['meeting_url'], $matches); -+ if (count($matches) !== 3) { -+ return; -+ } - $value['meeting_number'] = $matches[1]; - $value['password'] = $matches[2]; - $this->setValue($value); diff --git a/patches/d10/3490176.diff b/patches/d10/3490176.diff deleted file mode 100644 index d09d60b87bceff68d011058a789dabdc8b2ed317..0000000000000000000000000000000000000000 --- a/patches/d10/3490176.diff +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/src/Plugin/ProjectBrowserSource/Recipes.php b/src/Plugin/ProjectBrowserSource/Recipes.php -index 40d8410e51ab8006cd88975d79dff01ebdea6f4b..8269158ebd44b8452ecd9a033d8c158cd6b04bf4 100644 ---- a/src/Plugin/ProjectBrowserSource/Recipes.php -+++ b/src/Plugin/ProjectBrowserSource/Recipes.php -@@ -28,12 +28,12 @@ use Symfony\Component\Finder\Finder; - class Recipes extends ProjectBrowserSourceBase { - - public function __construct( -- private readonly FileSystemInterface $fileSystem, -- private readonly CacheBackendInterface $cacheBin, -- private readonly ModuleExtensionList $moduleList, -- private readonly FileUrlGeneratorInterface $fileUrlGenerator, -- private readonly ConfigFactoryInterface $configFactory, -- private readonly string $appRoot, -+ protected readonly FileSystemInterface $fileSystem, -+ protected readonly CacheBackendInterface $cacheBin, -+ protected readonly ModuleExtensionList $moduleList, -+ protected readonly FileUrlGeneratorInterface $fileUrlGenerator, -+ protected readonly ConfigFactoryInterface $configFactory, -+ protected readonly string $appRoot, - mixed ...$arguments, - ) { - parent::__construct(...$arguments); -@@ -165,7 +165,7 @@ class Recipes extends ProjectBrowserSourceBase { - * @return \Symfony\Component\Finder\Finder - * A Symfony Finder object, configured to find locally installed recipes. - */ -- private function getFinder(): Finder { -+ protected function getFinder(): Finder { - $search_in = [$this->appRoot . '/core/recipes']; - - // If any recipes have been installed by Composer, also search there. The diff --git a/patches/d10/3492453.diff b/patches/d10/3492453.diff deleted file mode 100644 index 1aa3b1bbd1299600b6af8036373935ab288afe28..0000000000000000000000000000000000000000 --- a/patches/d10/3492453.diff +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Installer/InstallerKernel.php b/core/lib/Drupal/Core/Installer/InstallerKernel.php -index 8f4f1c760dee0882ad0cff4f29dd8a5c3fe5d9c8..23f329eff551b67d86f8e2c69ebe7d26bd86e5ad 100644 ---- a/core/lib/Drupal/Core/Installer/InstallerKernel.php -+++ b/core/lib/Drupal/Core/Installer/InstallerKernel.php -@@ -3,6 +3,7 @@ - namespace Drupal\Core\Installer; - - use Drupal\Core\DrupalKernel; -+use Symfony\Component\DependencyInjection\ContainerInterface; - - /** - * Extend DrupalKernel to handle force some kernel behaviors. -@@ -83,4 +84,18 @@ public static function installationAttempted() { - return isset($GLOBALS['install_state']) && empty($GLOBALS['install_state']['installation_finished']); - } - -+ /** -+ * {@inheritdoc} -+ */ -+ protected function attachSynthetic(ContainerInterface $container): void { -+ parent::attachSynthetic($container); -+ -+ // Reset any existing container in order to avoid holding on to old object -+ // references, otherwise memory usage grows exponentially with each rebuild -+ // when multiple modules are being installed. -+ // @todo Move this to the parent class after https://www.drupal.org/i/2066993 -+ $this->container?->reset(); -+ $container->set('kernel', $this); -+ } -+ - } diff --git a/patches/d10/5758-1.diff b/patches/d10/5758-1.diff deleted file mode 100644 index b44a29ebdb8a6ad060f0992f47d8c8d6e7048628..0000000000000000000000000000000000000000 --- a/patches/d10/5758-1.diff +++ /dev/null @@ -1,1790 +0,0 @@ -diff --git a/core/modules/ckeditor5/ckeditor5.ckeditor5.yml b/core/modules/ckeditor5/ckeditor5.ckeditor5.yml -index 2a2c3fd165877ef2d3790dfd63779104c425d13e..3c2f61d05bd7575d4e8b68c6c5e01c1eb37ca82c 100644 ---- a/core/modules/ckeditor5/ckeditor5.ckeditor5.yml -+++ b/core/modules/ckeditor5/ckeditor5.ckeditor5.yml -@@ -742,6 +742,9 @@ media_media: - - <drupal-media> - - <drupal-media data-entity-type data-entity-uuid alt> - - <drupal-media data-view-mode> -+ - <drupal-media-inline> -+ - <drupal-media-inline data-entity-type data-entity-uuid alt> -+ - <drupal-media-inline data-view-mode> - conditions: - filter: media_embed - -@@ -756,6 +759,7 @@ ckeditor5_drupalMediaCaption: - label: Media caption - elements: - - <drupal-media data-caption> -+ - <drupal-media-inline data-caption> - conditions: - filter: filter_caption - plugins: -@@ -805,6 +809,7 @@ media_mediaAlign: - library: ckeditor5/internal.drupal.ckeditor5.mediaAlign - elements: - - <drupal-media data-align> -+ - <drupal-media-inline data-align> - conditions: - filter: filter_align - plugins: [media_media] -diff --git a/core/modules/ckeditor5/ckeditor5.module b/core/modules/ckeditor5/ckeditor5.module -index 561795fa958175d6f562f814840ab6e3de005d79..ce745000affb58f286aae24fb9162860fe26e323 100644 ---- a/core/modules/ckeditor5/ckeditor5.module -+++ b/core/modules/ckeditor5/ckeditor5.module -@@ -91,9 +91,37 @@ function ckeditor5_theme() { - 'ckeditor5_settings_toolbar' => [ - 'render element' => 'form', - ], -+ 'field__ckeditor_inline' => [ -+ 'base hook' => 'field', -+ ], -+ 'media__ckeditor_inline_embed' => [ -+ 'base hook' => 'media', -+ ], - ]; - } - -+/** -+ * Implements hook_theme_suggestions_HOOK_alter(). -+ */ -+function ckeditor5_theme_suggestions_media_alter(array &$suggestions, array $variables) { -+ if ($variables['elements']['#view_mode'] == 'ckeditor_inline') { -+ $suggestions[] = 'media__ckeditor_inline_embed'; -+ } -+} -+ -+/** -+ * Implements hook_preprocess_HOOK(). -+ */ -+function ckeditor5_preprocess_media(&$variables) { -+ if ($variables['view_mode'] == 'ckeditor_inline') { -+ foreach ($variables['content'] as &$element) { -+ if (!empty($element['#theme']) && $element['#theme'] == 'field') { -+ $element['#theme'] = 'field__ckeditor_inline'; -+ } -+ } -+ } -+} -+ - /** - * Implements hook_module_implements_alter(). - */ -diff --git a/core/modules/ckeditor5/ckeditor5.post_update.php b/core/modules/ckeditor5/ckeditor5.post_update.php -index f9b4e846c16d064d8ba9b36b64e0322ade4a779e..e2e925e63eaecfd9cf9c65b90ed21db3ef02ada3 100644 ---- a/core/modules/ckeditor5/ckeditor5.post_update.php -+++ b/core/modules/ckeditor5/ckeditor5.post_update.php -@@ -5,6 +5,8 @@ - * Post update functions for CKEditor 5. - */ - -+use Drupal\Core\Config\FileStorage; -+ - // cspell:ignore multiblock - - /** -@@ -20,3 +22,39 @@ function ckeditor5_removed_post_updates() { - 'ckeditor5_post_update_list_start_reversed' => '11.0.0', - ]; - } -+ -+/** -+ * Creates the ckeditor_inline view mode for media entities. -+ */ -+function ckeditor5_post_update_create_ckeditor_inline_view_mode() { -+ $config_path = \Drupal::service('extension.list.module')->getPath('ckeditor5') . '/config/optional'; -+ $source = new FileStorage($config_path); -+ $entity_type_manager = \Drupal::entityTypeManager(); -+ $module_handler = \Drupal::moduleHandler(); -+ /** @var \Drupal\Core\Config\StorageInterface $config_storage */ -+ $config_storage = \Drupal::service('config.storage'); -+ -+ if ($module_handler->moduleExists('media') && !$config_storage->exists('core.entity_view_mode.media.ckeditor_inline')) { -+ $entity_type_manager->getStorage('entity_view_mode') -+ ->create($source->read('core.entity_view_mode.media.ckeditor_inline')) -+ ->save(); -+ } -+} -+ -+/** -+ * Amends the filter format settings to allow the drupal-media-inline tag. -+ */ -+function ckeditor5_post_update_allow_inline_media_element() { -+ $formats = filter_formats(); -+ foreach ($formats as $format_id => $format) { -+ $config = \Drupal::configFactory()->getEditable('filter.format.' . $format_id); -+ $filters = $config->get('filters'); -+ if (\array_key_exists('media_embed', $filters) -+ && \array_key_exists('filter_html', $filters) -+ && !\str_contains($filters['filter_html']['settings']['allowed_html'], 'drupal-media-inline')) { -+ $filters['filter_html']['settings']['allowed_html'] .= ' <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode data-caption data-align>'; -+ $config->set('filters', $filters); -+ $config->save(); -+ } -+ } -+} -diff --git a/core/modules/ckeditor5/config/optional/core.entity_view_mode.media.ckeditor_inline.yml b/core/modules/ckeditor5/config/optional/core.entity_view_mode.media.ckeditor_inline.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..e659b44337d5e3f03c4720bbe63f7446d6536bbf ---- /dev/null -+++ b/core/modules/ckeditor5/config/optional/core.entity_view_mode.media.ckeditor_inline.yml -@@ -0,0 +1,10 @@ -+langcode: en -+status: true -+dependencies: -+ module: -+ - media -+id: media.ckeditor_inline -+label: 'Ckeditor Inline' -+description: 'View mode used for media embedded inline' -+targetEntityType: media -+cache: true -diff --git a/core/modules/ckeditor5/css/drupalmedia.css b/core/modules/ckeditor5/css/drupalmedia.css -index e69e9c7ed6fa6f183ae267317599e687e7c8917a..7fd2b0b188fca6a36f67cf9c96ae0c1a1ac50123 100644 ---- a/core/modules/ckeditor5/css/drupalmedia.css -+++ b/core/modules/ckeditor5/css/drupalmedia.css -@@ -13,6 +13,12 @@ - min-width: 50px; - } - -+.ck .drupal-media.drupal-media-inline { -+ display: inline-block; -+ clear: initial; -+ margin: 0; -+} -+ - .ck .drupal-media [data-drupal-media-preview] { - pointer-events: none; - } -diff --git a/core/modules/ckeditor5/js/build/drupalMedia.js b/core/modules/ckeditor5/js/build/drupalMedia.js -index 766eb373081549d9e4aad090332877178bbd30a8..bd81d5269440adfea48f5a404b138267d43d670d 100644 ---- a/core/modules/ckeditor5/js/build/drupalMedia.js -+++ b/core/modules/ckeditor5/js/build/drupalMedia.js -@@ -1 +1 @@ --!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalMedia=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/engine.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/engine.js")},"ckeditor5/src/ui.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/ui.js")},"ckeditor5/src/utils.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/utils.js")},"ckeditor5/src/widget.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/widget.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function i(n){var a=t[n];if(void 0!==a)return a.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,i),r.exports}i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{"use strict";i.d(n,{default:()=>ne});var e=i("ckeditor5/src/core.js"),t=i("ckeditor5/src/widget.js");function a(e){return!!e&&e.is("element","drupalMedia")}function r(e){return(0,t.isWidget)(e)&&!!e.getCustomProperty("drupalMedia")}function o(e){const t=e.getSelectedElement();return a(t)?t:e.getFirstPosition().findAncestor("drupalMedia")}function s(e){const t=e.getSelectedElement();if(t&&r(t))return t;if(null===e.getFirstPosition())return null;let i=e.getFirstPosition().parent;for(;i;){if(i.is("element")&&r(i))return i;i=i.parent}return null}function l(e){const t=typeof e;return null!=e&&("object"===t||"function"===t)}function d(e){for(const t of e){if(t.hasAttribute("data-drupal-media-preview"))return t;if(t.childCount){const e=d(t.getChildren());if(e)return e}}return null}function u(e){return`drupalElementStyle${e[0].toUpperCase()+e.substring(1)}`}class c extends e.Command{execute(e){const t=this.editor.plugins.get("DrupalMediaEditing"),i=Object.entries(t.attrs).reduce(((e,[t,i])=>(e[i]=t,e)),{}),n=Object.keys(e).reduce(((t,n)=>(i[n]&&(t[i[n]]=e[n]),t)),{});if(this.editor.plugins.has("DrupalElementStyleEditing")){const t=this.editor.plugins.get("DrupalElementStyleEditing"),{normalizedStyles:i}=t;for(const a of Object.keys(i))for(const i of t.normalizedStyles[a])if(e[i.attributeName]&&i.attributeValue===e[i.attributeName]){const e=u(a);n[e]=i.name}}this.editor.model.change((e=>{this.editor.model.insertObject(function(e,t){return e.createElement("drupalMedia",t)}(e,n))}))}refresh(){const e=this.editor.model,t=e.document.selection,i=e.schema.findAllowedParent(t.getFirstPosition(),"drupalMedia");this.isEnabled=null!==i}}const m="METADATA_ERROR";class p extends e.Plugin{static get requires(){return[t.Widget]}constructor(e){super(e),this.attrs={drupalMediaAlt:"alt",drupalMediaEntityType:"data-entity-type",drupalMediaEntityUuid:"data-entity-uuid"},this.converterAttributes=["drupalMediaEntityUuid","drupalElementStyleViewMode","drupalMediaEntityType","drupalMediaAlt"]}init(){const e=this.editor.config.get("drupalMedia");if(!e)return;const{previewURL:t,themeError:i}=e;this.previewUrl=t,this.labelError=Drupal.t("Preview failed"),this.themeError=i||`\n <p>${Drupal.t("An error occurred while trying to preview the media. Save your work and reload this page.")}<p>\n `,this._defineSchema(),this._defineConverters(),this._defineListeners(),this.editor.commands.add("insertDrupalMedia",new c(this.editor))}upcastDrupalMediaIsImage(e){const{model:t,plugins:i}=this.editor;i.get("DrupalMediaMetadataRepository").getMetadata(e).then((i=>{e&&t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",!!i.imageSourceMetadata,e)}))})).catch((i=>{e&&(console.warn(i.toString()),t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",m,e)})))}))}upcastDrupalMediaType(e){this.editor.plugins.get("DrupalMediaMetadataRepository").getMetadata(e).then((t=>{e&&this.editor.model.enqueueChange({isUndoable:!1},(i=>{i.setAttribute("drupalMediaType",t.type,e)}))})).catch((t=>{e&&(console.warn(t.toString()),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",m,e)})))}))}async _fetchPreview(e){const t={text:this._renderElement(e),uuid:e.getAttribute("drupalMediaEntityUuid")},i=await fetch(`${this.previewUrl}?${new URLSearchParams(t)}`,{headers:{"X-Drupal-MediaPreview-CSRF-Token":this.editor.config.get("drupalMedia").previewCsrfToken}});if(i.ok){return{label:i.headers.get("drupal-media-label"),preview:await i.text()}}return{label:this.labelError,preview:this.themeError}}_defineSchema(){this.editor.model.schema.register("drupalMedia",{inheritAllFrom:"$blockObject",allowAttributes:Object.keys(this.attrs)}),this.editor.editing.view.domConverter.blockElements.push("drupal-media")}_defineConverters(){const e=this.editor.conversion,i=this.editor.plugins.get("DrupalMediaMetadataRepository");e.for("upcast").elementToElement({view:{name:"drupal-media"},model:"drupalMedia"}).add((e=>{e.on("element:drupal-media",((e,t)=>{const[n]=t.modelRange.getItems();i.getMetadata(n).then((e=>{n&&(this.upcastDrupalMediaIsImage(n),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",e.type,n)})))})).catch((e=>{console.warn(e.toString())}))}),{priority:"lowest"})})),e.for("dataDowncast").elementToElement({model:"drupalMedia",view:{name:"drupal-media"}}),e.for("editingDowncast").elementToElement({model:"drupalMedia",view:(e,{writer:i})=>{const n=i.createContainerElement("figure",{class:"drupal-media"});if(!this.previewUrl){const e=i.createRawElement("div",{"data-drupal-media-preview":"unavailable"});i.insert(i.createPositionAt(n,0),e)}return i.setCustomProperty("drupalMedia",!0,n),(0,t.toWidget)(n,i,{label:Drupal.t("Media widget")})}}).add((e=>{const t=(e,t,i)=>{const n=i.writer,a=t.item,r=i.mapper.toViewElement(t.item);let o=d(r.getChildren());if(o){if("ready"!==o.getAttribute("data-drupal-media-preview"))return;n.setAttribute("data-drupal-media-preview","loading",o)}else o=n.createRawElement("div",{"data-drupal-media-preview":"loading"}),n.insert(n.createPositionAt(r,0),o);this._fetchPreview(a).then((({label:e,preview:t})=>{o&&this.editor.editing.view.change((i=>{const n=i.createRawElement("div",{"data-drupal-media-preview":"ready","aria-label":e},(e=>{e.innerHTML=t}));i.insert(i.createPositionBefore(o),n),i.remove(o)}))}))};return this.converterAttributes.forEach((i=>{e.on(`attribute:${i}:drupalMedia`,t)})),e})),e.for("editingDowncast").add((e=>{e.on("attribute:drupalElementStyleAlign:drupalMedia",((e,t,i)=>{const n={left:"drupal-media-style-align-left",right:"drupal-media-style-align-right",center:"drupal-media-style-align-center"},a=i.mapper.toViewElement(t.item),r=i.writer;n[t.attributeOldValue]&&r.removeClass(n[t.attributeOldValue],a),n[t.attributeNewValue]&&i.consumable.consume(t.item,e.name)&&r.addClass(n[t.attributeNewValue],a)}))})),Object.keys(this.attrs).forEach((t=>{const i={model:{key:t,name:"drupalMedia"},view:{name:"drupal-media",key:this.attrs[t]}};e.for("dataDowncast").attributeToAttribute(i),e.for("upcast").attributeToAttribute(i)}))}_defineListeners(){this.editor.model.on("insertContent",((e,[t])=>{a(t)&&(this.upcastDrupalMediaIsImage(t),this.upcastDrupalMediaType(t))}))}_renderElement(e){const t=this.editor.model.change((t=>{const i=t.createDocumentFragment(),n=t.cloneElement(e,!1);return["linkHref"].forEach((e=>{t.removeAttribute(e,n)})),t.append(n,i),i}));return this.editor.data.stringify(t)}static get pluginName(){return"DrupalMediaEditing"}}var g=i("ckeditor5/src/ui.js");class h extends e.Plugin{init(){const e=this.editor,t=this.editor.config.get("drupalMedia");if(!t)return;const{libraryURL:i,openDialog:n,dialogSettings:a={}}=t;i&&"function"==typeof n&&e.ui.componentFactory.add("drupalMedia",(t=>{const r=e.commands.get("insertDrupalMedia"),o=new g.ButtonView(t);return o.set({label:Drupal.t("Insert Media"),icon:'<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M19.1873 4.86414L10.2509 6.86414V7.02335H10.2499V15.5091C9.70972 15.1961 9.01793 15.1048 8.34069 15.3136C7.12086 15.6896 6.41013 16.8967 6.75322 18.0096C7.09631 19.1226 8.3633 19.72 9.58313 19.344C10.6666 19.01 11.3484 18.0203 11.2469 17.0234H11.2499V9.80173L18.1803 8.25067V14.3868C17.6401 14.0739 16.9483 13.9825 16.2711 14.1913C15.0513 14.5674 14.3406 15.7744 14.6836 16.8875C15.0267 18.0004 16.2937 18.5978 17.5136 18.2218C18.597 17.8877 19.2788 16.8982 19.1773 15.9011H19.1803V8.02687L19.1873 8.0253V4.86414Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M13.5039 0.743652H0.386932V12.1603H13.5039V0.743652ZM12.3379 1.75842H1.55289V11.1454H1.65715L4.00622 8.86353L6.06254 10.861L9.24985 5.91309L11.3812 9.22179L11.7761 8.6676L12.3379 9.45621V1.75842ZM6.22048 4.50869C6.22048 5.58193 5.35045 6.45196 4.27722 6.45196C3.20398 6.45196 2.33395 5.58193 2.33395 4.50869C2.33395 3.43546 3.20398 2.56543 4.27722 2.56543C5.35045 2.56543 6.22048 3.43546 6.22048 4.50869Z"/></svg>\n',tooltip:!0}),o.bind("isOn","isEnabled").to(r,"value","isEnabled"),this.listenTo(o,"execute",(()=>{n(i,(({attributes:t})=>{e.execute("insertDrupalMedia",t)}),a)})),o}))}}class f extends e.Plugin{static get requires(){return[t.WidgetToolbarRepository]}static get pluginName(){return"DrupalMediaToolbar"}afterInit(){const{editor:e}=this;var i;e.plugins.get(t.WidgetToolbarRepository).register("drupalMedia",{ariaLabel:Drupal.t("Drupal Media toolbar"),items:(i=e.config.get("drupalMedia.toolbar"),i.map((e=>l(e)?e.name:e))||[]),getRelatedElement:e=>s(e)})}}class b extends e.Command{refresh(){const e=o(this.editor.model.document.selection);this.isEnabled=!!e&&e.getAttribute("drupalMediaIsImage")&&e.getAttribute("drupalMediaIsImage")!==m,this.isEnabled?this.value=e.getAttribute("drupalMediaAlt"):this.value=!1}execute(e){const{model:t}=this.editor,i=o(t.document.selection);e.newValue=e.newValue.trim(),t.change((t=>{e.newValue.length>0?t.setAttribute("drupalMediaAlt",e.newValue,i):t.removeAttribute("drupalMediaAlt",i)}))}}class w extends e.Plugin{init(){this._data=new WeakMap}getMetadata(e){if(this._data.get(e))return new Promise((t=>{t(this._data.get(e))}));const t=this.editor.config.get("drupalMedia");if(!t)return new Promise(((e,t)=>{t(new Error("drupalMedia configuration is required for parsing metadata."))}));if(!e.hasAttribute("drupalMediaEntityUuid"))return new Promise(((e,t)=>{t(new Error("drupalMedia element must have drupalMediaEntityUuid attribute to retrieve metadata."))}));const{metadataUrl:i}=t;return(async e=>{const t=await fetch(e);if(t.ok)return JSON.parse(await t.text());throw new Error("Fetching media embed metadata from the server failed.")})(`${i}&${new URLSearchParams({uuid:e.getAttribute("drupalMediaEntityUuid")})}`).then((t=>(this._data.set(e,t),t)))}static get pluginName(){return"DrupalMediaMetadataRepository"}}class y extends e.Plugin{static get requires(){return[w]}static get pluginName(){return"MediaImageTextAlternativeEditing"}init(){const{editor:e,editor:{model:t,conversion:i}}=this;t.schema.extend("drupalMedia",{allowAttributes:["drupalMediaIsImage"]}),i.for("editingDowncast").add((e=>{e.on("attribute:drupalMediaIsImage",((e,t,i)=>{const{writer:n,mapper:a}=i,r=a.toViewElement(t.item);if(t.attributeNewValue!==m){const e=Array.from(r.getChildren()).find((e=>e.getCustomProperty("drupalMediaMetadataError")));return void(e&&(n.setCustomProperty("widgetLabel",e.getCustomProperty("drupalMediaOriginalWidgetLabel"),e),n.removeElement(e)))}const o=Drupal.t("Not all functionality may be available because some information could not be retrieved."),s=new g.Template({tag:"span",children:[{tag:"span",attributes:{class:"drupal-media__metadata-error-icon","data-cke-tooltip-text":o}}]}).render(),l=n.createRawElement("div",{class:"drupal-media__metadata-error"},((e,t)=>{t.setContentOf(e,s.outerHTML)}));n.setCustomProperty("drupalMediaMetadataError",!0,l);const d=r.getCustomProperty("widgetLabel");n.setCustomProperty("drupalMediaOriginalWidgetLabel",d,l),n.setCustomProperty("widgetLabel",`${d} (${o})`,r),n.insert(n.createPositionAt(r,0),l)}),{priority:"low"})})),e.commands.add("mediaImageTextAlternative",new b(this.editor))}}function v(e){const t=e.editing.view,i=g.BalloonPanelView.defaultPositions;return{target:t.domConverter.viewToDom(t.document.selection.getSelectedElement()),positions:[i.northArrowSouth,i.northArrowSouthWest,i.northArrowSouthEast,i.southArrowNorth,i.southArrowNorthWest,i.southArrowNorthEast]}}var E=i("ckeditor5/src/utils.js");class M extends g.View{constructor(t){super(t),this.focusTracker=new E.FocusTracker,this.keystrokes=new E.KeystrokeHandler,this.decorativeToggle=this._decorativeToggleView(),this.labeledInput=this._createLabeledInputView(),this.saveButtonView=this._createButton(Drupal.t("Save"),e.icons.check,"ck-button-save"),this.saveButtonView.type="submit",this.cancelButtonView=this._createButton(Drupal.t("Cancel"),e.icons.cancel,"ck-button-cancel","cancel"),this._focusables=new g.ViewCollection,this._focusCycler=new g.FocusCycler({focusables:this._focusables,focusTracker:this.focusTracker,keystrokeHandler:this.keystrokes,actions:{focusPrevious:"shift + tab",focusNext:"tab"}}),this.setTemplate({tag:"form",attributes:{class:["ck","ck-media-alternative-text-form","ck-vertical-form"],tabindex:"-1"},children:[{tag:"div",children:[this.decorativeToggle]},this.labeledInput,this.saveButtonView,this.cancelButtonView]}),(0,g.injectCssTransitionDisabler)(this)}render(){super.render(),this.keystrokes.listenTo(this.element),(0,g.submitHandler)({view:this}),[this.decorativeToggle,this.labeledInput,this.saveButtonView,this.cancelButtonView].forEach((e=>{this._focusables.add(e),this.focusTracker.add(e.element)}))}_createButton(e,t,i,n){const a=new g.ButtonView(this.locale);return a.set({label:e,icon:t,tooltip:!0}),a.extendTemplate({attributes:{class:i}}),n&&a.delegate("execute").to(this,n),a}_createLabeledInputView(){const e=new g.LabeledFieldView(this.locale,g.createLabeledInputText);return e.bind("class").to(this.decorativeToggle,"isOn",(e=>e?"ck-hidden":"")),e.label=Drupal.t("Alternative text override"),e}_decorativeToggleView(){const e=new g.SwitchButtonView(this.locale);return e.set({withText:!0,label:Drupal.t("Decorative image")}),e.on("execute",(()=>{e.isOn&&(this.labeledInput.fieldView.element.value=""),e.set("isOn",!e.isOn)})),e}}class k extends e.Plugin{static get requires(){return[g.ContextualBalloon]}static get pluginName(){return"MediaImageTextAlternativeUi"}init(){this._createButton(),this._createForm()}destroy(){super.destroy(),this._form.destroy()}_createButton(){const t=this.editor;t.ui.componentFactory.add("mediaImageTextAlternative",(i=>{const n=t.commands.get("mediaImageTextAlternative"),a=new g.ButtonView(i);return a.set({label:Drupal.t("Override media image alternative text"),icon:e.icons.lowVision,tooltip:!0}),a.bind("isVisible").to(n,"isEnabled"),this.listenTo(a,"execute",(()=>{this._showForm()})),a}))}_createForm(){const e=this.editor,t=e.editing.view.document;this._balloon=this.editor.plugins.get("ContextualBalloon"),this._form=new M(e.locale),this._form.render(),this.listenTo(this._form,"submit",(()=>{e.execute("mediaImageTextAlternative",{newValue:this._form.decorativeToggle.isOn?'""':this._form.labeledInput.fieldView.element.value}),this._hideForm(!0)})),this.listenTo(this._form,"cancel",(()=>{this._hideForm(!0)})),this._form.keystrokes.set("Esc",((e,t)=>{this._hideForm(!0),t()})),this.listenTo(e.ui,"update",(()=>{s(t.selection)?this._isVisible&&function(e){const t=e.plugins.get("ContextualBalloon");if(s(e.editing.view.document.selection)){const i=v(e);t.updatePosition(i)}}(e):this._hideForm(!0)})),(0,g.clickOutsideHandler)({emitter:this._form,activator:()=>this._isVisible,contextElements:[this._balloon.view.element],callback:()=>this._hideForm()})}_showForm(){if(this._isVisible)return;const e=this.editor,t=e.commands.get("mediaImageTextAlternative"),i=this._form.decorativeToggle,n=e.plugins.get("DrupalMediaMetadataRepository"),r=this._form.labeledInput;this._form.disableCssTransitions(),this._isInBalloon||this._balloon.add({view:this._form,position:v(e)}),i.isOn='""'===t.value,r.fieldView.element.value=t.value||"",r.fieldView.value=r.fieldView.element.value,this._form.defaultAltText="";const o=e.model.document.selection.getSelectedElement();a(o)&&n.getMetadata(o).then((e=>{this._form.defaultAltText=e.imageSourceMetadata?e.imageSourceMetadata.alt:"",r.infoText=Drupal.t(`Leave blank to use the default alternative text: "${this._form.defaultAltText}".`)})).catch((e=>{console.warn(e.toString())})),this._form.enableCssTransitions()}_hideForm(e){this._isInBalloon&&(this._form.focusTracker.isFocused&&this._form.saveButtonView.focus(),this._balloon.remove(this._form),e&&this.editor.editing.view.focus())}get _isVisible(){return this._balloon.visibleView===this._form}get _isInBalloon(){return this._balloon.hasView(this._form)}}class D extends e.Plugin{static get requires(){return[y,k]}static get pluginName(){return"MediaImageTextAlternative"}}function C(e,t,i){if(t.attributes)for(const[n,a]of Object.entries(t.attributes))e.setAttribute(n,a,i);t.styles&&e.setStyle(t.styles,i),t.classes&&e.addClass(t.classes,i)}function A(e,t,i){if(!i.consumable.consume(t.item,e.name))return;const n=i.mapper.toViewElement(t.item);C(i.writer,t.attributeNewValue,n)}class _ extends e.Plugin{constructor(e){if(super(e),!e.plugins.has("GeneralHtmlSupport"))return;e.plugins.has("DataFilter")&&e.plugins.has("DataSchema")||console.error("DataFilter and DataSchema plugins are required for Drupal Media to integrate with General HTML Support plugin.");const{schema:t}=e.model,{conversion:i}=e,n=this.editor.plugins.get("DataFilter");this.editor.plugins.get("DataSchema").registerBlockElement({model:"drupalMedia",view:"drupal-media"}),n.on("register:drupal-media",((e,a)=>{"drupalMedia"===a.model&&(t.extend("drupalMedia",{allowAttributes:["htmlLinkAttributes","htmlAttributes"]}),i.for("upcast").add(function(e){return t=>{t.on("element:drupal-media",((t,i,n)=>{function a(t,a){const r=e.processViewAttributes(t,n);r&&n.writer.setAttribute(a,r,i.modelRange)}const r=i.viewItem,o=r.parent;a(r,"htmlAttributes"),o.is("element","a")&&a(o,"htmlLinkAttributes")}),{priority:"low"})}}(n)),i.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{if(!i.consumable.consume(t.item,"attribute:htmlLinkAttributes:drupalMedia"))return;const n=i.mapper.toViewElement(t.item),a=function(e,t,i){const n=e.createRangeOn(t);for(const{item:e}of n.getWalker())if(e.is("element",i))return e}(i.writer,n,"a");C(i.writer,t.item.getAttribute("htmlLinkAttributes"),a)}),{priority:"low"})})),i.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{if(!i.consumable.consume(t.item,"attribute:htmlLinkAttributes:drupalMedia"))return;const n=i.mapper.toViewElement(t.item).parent;C(i.writer,t.item.getAttribute("htmlLinkAttributes"),n)}),{priority:"low"}),e.on("attribute:htmlAttributes:drupalMedia",A,{priority:"low"})})),e.stop())}))}static get pluginName(){return"DrupalMediaGeneralHtmlSupport"}}class x extends e.Plugin{static get requires(){return[p,_,h,f,D]}static get pluginName(){return"DrupalMedia"}}var V=i("ckeditor5/src/engine.js");function S(e){return Array.from(e.getChildren()).find((e=>"drupal-media"===e.name))}function T(e){return t=>{t.on(`attribute:${e.id}:drupalMedia`,((t,i,n)=>{const a=n.mapper.toViewElement(i.item);let r=Array.from(a.getChildren()).find((e=>"a"===e.name));if(r=!r&&a.is("element","a")?a:Array.from(a.getAncestors()).find((e=>"a"===e.name)),r){for(const[t,i]of(0,E.toMap)(e.attributes))n.writer.setAttribute(t,i,r);e.classes&&n.writer.addClass(e.classes,r);for(const t in e.styles)Object.prototype.hasOwnProperty.call(e.styles,t)&&n.writer.setStyle(t,e.styles[t],r)}}))}}function I(e,t){return e=>{e.on("element:a",((e,i,n)=>{const a=i.viewItem;if(!S(a))return;const r=new V.Matcher(t._createPattern()).match(a);if(!r)return;if(!n.consumable.consume(a,r.match))return;const o=i.modelCursor.nodeBefore;n.writer.setAttribute(t.id,!0,o)}),{priority:"high"})}}class L extends e.Plugin{static get requires(){return["LinkEditing","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaEditing"}init(){const{editor:e}=this;e.model.schema.extend("drupalMedia",{allowAttributes:["linkHref"]}),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const n=t.viewItem,a=S(n);if(!a)return;if(!i.consumable.consume(n,{attributes:["href"],name:!0}))return;const r=n.getAttribute("href");if(null===r)return;const o=i.convertItem(a,t.modelCursor);t.modelRange=o.modelRange,t.modelCursor=o.modelCursor;const s=t.modelCursor.nodeBefore;s&&s.is("element","drupalMedia")&&i.writer.setAttribute("linkHref",r,s)}),{priority:"high"})})),e.conversion.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=Array.from(a.getChildren()).find((e=>"a"===e.name));if(r)t.attributeNewValue?n.setAttribute("href",t.attributeNewValue,r):(n.move(n.createRangeIn(r),n.createPositionAt(a,0)),n.remove(r));else{const e=Array.from(a.getChildren()).find((e=>e.getAttribute("data-drupal-media-preview"))),i=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionAt(a,0),i),n.move(n.createRangeOn(e),n.createPositionAt(i,0))}}),{priority:"high"})})),e.conversion.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionBefore(a),r),n.move(n.createRangeOn(a),n.createPositionAt(r,0))}),{priority:"high"})})),this._enableManualDecorators();if(e.commands.get("link").automaticDecorators.length>0)throw new Error("The Drupal Media plugin is not compatible with automatic link decorators. To use Drupal Media, disable any plugins providing automatic link decorators.")}_enableManualDecorators(){const e=this.editor,t=e.commands.get("link");for(const i of t.manualDecorators)e.model.schema.extend("drupalMedia",{allowAttributes:i.id}),e.conversion.for("downcast").add(T(i)),e.conversion.for("upcast").add(I(0,i))}}class O extends e.Plugin{static get requires(){return["LinkEditing","LinkUI","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaUi"}init(){const{editor:e}=this,t=e.editing.view.document;this.listenTo(t,"click",((t,i)=>{this._isSelectedLinkedMedia(e.model.document.selection)&&(i.preventDefault(),t.stop())}),{priority:"high"}),this._createToolbarLinkMediaButton()}_createToolbarLinkMediaButton(){const{editor:e}=this;e.ui.componentFactory.add("drupalLinkMedia",(t=>{const i=new g.ButtonView(t),n=e.plugins.get("LinkUI"),a=e.commands.get("link");return i.set({isEnabled:!0,label:Drupal.t("Link media"),icon:'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184z"/></svg>\n',keystroke:"Ctrl+K",tooltip:!0,isToggleable:!0}),i.bind("isEnabled").to(a,"isEnabled"),i.bind("isOn").to(a,"value",(e=>!!e)),this.listenTo(i,"execute",(()=>{this._isSelectedLinkedMedia(e.model.document.selection)?n._addActionsView():n._showUI(!0)})),i}))}_isSelectedLinkedMedia(e){const t=e.getSelectedElement();return!!t&&t.is("element","drupalMedia")&&t.hasAttribute("linkHref")}}class P extends e.Plugin{static get requires(){return[L,O]}static get pluginName(){return"DrupalLinkMedia"}}const B={get inline(){return{name:"inline",title:"In line",icon:e.icons.objectInline,modelElements:["imageInline"],isDefault:!0}},get alignLeft(){return{name:"alignLeft",title:"Left aligned image",icon:e.icons.objectLeft,modelElements:["imageBlock","imageInline"],className:"image-style-align-left"}},get alignBlockLeft(){return{name:"alignBlockLeft",title:"Left aligned image",icon:e.icons.objectBlockLeft,modelElements:["imageBlock"],className:"image-style-block-align-left"}},get alignCenter(){return{name:"alignCenter",title:"Centered image",icon:e.icons.objectCenter,modelElements:["imageBlock"],className:"image-style-align-center"}},get alignRight(){return{name:"alignRight",title:"Right aligned image",icon:e.icons.objectRight,modelElements:["imageBlock","imageInline"],className:"image-style-align-right"}},get alignBlockRight(){return{name:"alignBlockRight",title:"Right aligned image",icon:e.icons.objectBlockRight,modelElements:["imageBlock"],className:"image-style-block-align-right"}},get block(){return{name:"block",title:"Centered image",icon:e.icons.objectCenter,modelElements:["imageBlock"],isDefault:!0}},get side(){return{name:"side",title:"Side image",icon:e.icons.objectRight,modelElements:["imageBlock"],className:"image-style-side"}}},N=(()=>({full:e.icons.objectFullWidth,left:e.icons.objectBlockLeft,right:e.icons.objectBlockRight,center:e.icons.objectCenter,inlineLeft:e.icons.objectLeft,inlineRight:e.icons.objectRight,inline:e.icons.objectInline}))(),j=[{name:"imageStyle:wrapText",title:"Wrap text",defaultItem:"imageStyle:alignLeft",items:["imageStyle:alignLeft","imageStyle:alignRight"]},{name:"imageStyle:breakText",title:"Break text",defaultItem:"imageStyle:block",items:["imageStyle:alignBlockLeft","imageStyle:block","imageStyle:alignBlockRight"]}];function R(e){(0,E.logWarning)("image-style-configuration-definition-invalid",e)}const F={normalizeStyles:function(e){return(e.configuredStyles.options||[]).map((e=>function(e){e="string"==typeof e?B[e]?{...B[e]}:{name:e}:function(e,t){const i={...t};for(const n in e)Object.prototype.hasOwnProperty.call(t,n)||(i[n]=e[n]);return i}(B[e.name],e);"string"==typeof e.icon&&(e.icon=N[e.icon]||e.icon);return e}(e))).filter((t=>function(e,{isBlockPluginLoaded:t,isInlinePluginLoaded:i}){const{modelElements:n,name:a}=e;if(!(n&&n.length&&a))return R({style:e}),!1;{const a=[t?"imageBlock":null,i?"imageInline":null];if(!n.some((e=>a.includes(e))))return(0,E.logWarning)("image-style-missing-dependency",{style:e,missingPlugins:n.map((e=>"imageBlock"===e?"ImageBlockEditing":"ImageInlineEditing"))}),!1}return!0}(t,e)))},getDefaultStylesConfiguration:function(e,t){return e&&t?{options:["inline","alignLeft","alignRight","alignCenter","alignBlockLeft","alignBlockRight","block","side"]}:e?{options:["block","side"]}:t?{options:["inline","alignLeft","alignRight"]}:{}},getDefaultDropdownDefinitions:function(e){return e.has("ImageBlockEditing")&&e.has("ImageInlineEditing")?[...j]:[]},warnInvalidStyle:R,DEFAULT_OPTIONS:B,DEFAULT_ICONS:N,DEFAULT_DROPDOWN_DEFINITIONS:j};function U(e,t,i){for(const n of t)if(i.checkAttribute(e,n))return!0;return!1}function H(e,t,i){const n=e.getSelectedElement();if(n&&U(n,i,t))return n;let{parent:a}=e.getFirstPosition();for(;a;){if(a.is("element")&&U(a,i,t))return a;a=a.parent}return null}class $ extends e.Command{constructor(e,t){super(e),this.styles={},Object.keys(t).forEach((e=>{this.styles[e]=new Map(t[e].map((e=>[e.name,e])))})),this.modelAttributes=[];for(const e of Object.keys(t)){const t=u(e);this.modelAttributes.push(t)}}refresh(){const{editor:e}=this,t=H(e.model.document.selection,e.model.schema,this.modelAttributes);this.isEnabled=!!t,this.isEnabled?this.value=this.getValue(t):this.value=!1}getValue(e){const t={};return Object.keys(this.styles).forEach((i=>{const n=u(i);if(e.hasAttribute(n))t[i]=e.getAttribute(n);else for(const[,e]of this.styles[i])e.isDefault&&(t[i]=e.name)})),t}execute(e={}){const{editor:{model:t}}=this,{value:i,group:n}=e,a=u(n);t.change((e=>{const r=H(t.document.selection,t.schema,this.modelAttributes);!i||this.styles[n].get(i).isDefault?e.removeAttribute(a,r):e.setAttribute(a,i,r)}))}}function q(e,t){for(const i of t)if(i.name===e)return i}class W extends e.Plugin{init(){const{editor:t}=this,i=t.config.get("drupalElementStyles");this.normalizedStyles={},Object.keys(i).forEach((t=>{this.normalizedStyles[t]=i[t].map((t=>("string"==typeof t.icon&&e.icons[t.icon]&&(t.icon=e.icons[t.icon]),t.name&&(t.name=`${t.name}`),t))).filter((e=>e.isDefault||e.attributeName&&e.attributeValue?e.modelElements&&Array.isArray(e.modelElements)?!!e.name||(console.warn("drupalElementStyles options must include a name."),!1):(console.warn("drupalElementStyles options must include an array of supported modelElements."),!1):(console.warn(`${e.attributeValue} drupalElementStyles options must include attributeName and attributeValue.`),!1)))})),this._setupConversion(),t.commands.add("drupalElementStyle",new $(t,this.normalizedStyles))}_setupConversion(){const{editor:e}=this,{schema:t}=e.model;Object.keys(this.normalizedStyles).forEach((i=>{const n=u(i),a=(r=this.normalizedStyles[i],(e,t,i)=>{if(!i.consumable.consume(t.item,e.name))return;const n=q(t.attributeNewValue,r),a=q(t.attributeOldValue,r),o=i.mapper.toViewElement(t.item),s=i.writer;a&&("class"===a.attributeName?s.removeClass(a.attributeValue,o):s.removeAttribute(a.attributeName,o)),n&&("class"===n.attributeName?s.addClass(n.attributeValue,o):n.isDefault||s.setAttribute(n.attributeName,n.attributeValue,o))});var r;const o=function(e,t){const i=e.filter((e=>!e.isDefault));return(e,n,a)=>{if(!n.modelRange)return;const r=n.viewItem,o=(0,E.first)(n.modelRange.getItems());if(o&&a.schema.checkAttribute(o,t))for(const e of i)if("class"===e.attributeName)a.consumable.consume(r,{classes:e.attributeValue})&&a.writer.setAttribute(t,e.name,o);else if(a.consumable.consume(r,{attributes:[e.attributeName]}))for(const e of i)e.attributeValue===r.getAttribute(e.attributeName)&&a.writer.setAttribute(t,e.name,o)}}(this.normalizedStyles[i],n);e.editing.downcastDispatcher.on(`attribute:${n}`,a),e.data.downcastDispatcher.on(`attribute:${n}`,a);[...new Set(this.normalizedStyles[i].map((e=>e.modelElements)).flat())].forEach((e=>{t.extend(e,{allowAttributes:n})})),e.data.upcastDispatcher.on("element",o,{priority:"low"})}))}static get pluginName(){return"DrupalElementStyleEditing"}}const K=e=>e,z=(e,t)=>(e?`${e}: `:"")+t;function Z(e,t){return`drupalElementStyle:${t}:${e}`}class G extends e.Plugin{static get requires(){return[W]}init(){const{plugins:e}=this.editor,t=this.editor.config.get("drupalMedia.toolbar")||[],i=e.get("DrupalElementStyleEditing").normalizedStyles;Object.keys(i).forEach((e=>{i[e].forEach((t=>{this._createButton(t,e,i[e])}))}));t.filter(l).filter((e=>{const t=[];if(!e.display)return console.warn("dropdown configuration must include a display key specifying either listDropdown or splitButton."),!1;e.items.includes(e.defaultItem)||console.warn("defaultItem must be part of items in the dropdown configuration.");for(const i of e.items){const e=i.split(":")[1];t.push(e)}return!!t.every((e=>e===t[0]))||(console.warn("dropdown configuration should only contain buttons from one group."),!1)})).forEach((e=>{if(e.items.length>=2){const t=e.name.split(":")[1];switch(e.display){case"splitButton":this._createDropdown(e,i[t]);break;case"listDropdown":this._createListDropdown(e,i[t])}}}))}updateOptionVisibility(e,t,i,n){const{selection:a}=this.editor.model.document,r={};r[n]=e;const o=a?a.getSelectedElement():H(a,this.editor.model.schema,r),s=e.filter((function(e){for(const[t,i]of(0,E.toMap)(e.modelAttributes))if(o&&o.hasAttribute(t))return i.includes(o.getAttribute(t));return!0}));i.hasOwnProperty("model")?s.includes(t)?i.model.set({class:""}):i.model.set({class:"ck-hidden"}):s.includes(t)?i.set({class:""}):i.set({class:"ck-hidden"})}_createDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s}=e,l=o.filter((e=>{const i=e.split(":")[1];return t.find((({name:t})=>Z(t,i)===e))})).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==l.length&&F.warnInvalidStyle({dropdown:e});const d=(0,g.createDropdown)(n,g.SplitButtonView),u=d.buttonView;return(0,g.addToolbarToDropdown)(d,l),u.set({label:z(s,a.label),class:null,tooltip:!0}),u.bind("icon").toMany(l,"isOn",((...e)=>{const t=e.findIndex(K);return t<0?a.icon:l[t].icon})),u.bind("label").toMany(l,"isOn",((...e)=>{const t=e.findIndex(K);return z(s,t<0?a.label:l[t].label)})),u.bind("isOn").toMany(l,"isOn",((...e)=>e.some(K))),u.bind("class").toMany(l,"isOn",((...e)=>e.some(K)?"ck-splitbutton_flatten":null)),u.on("execute",(()=>{l.some((({isOn:e})=>e))?d.isOpen=!d.isOpen:a.fire("execute")})),d.bind("isEnabled").toMany(l,"isEnabled",((...e)=>e.some(K))),d}))}_createButton(e,t,i){const n=e.name;this.editor.ui.componentFactory.add(Z(n,t),(a=>{const r=this.editor.commands.get("drupalElementStyle"),o=new g.ButtonView(a);return o.set({label:e.title,icon:e.icon,tooltip:!0,isToggleable:!0}),o.bind("isEnabled").to(r,"isEnabled"),o.bind("isOn").to(r,"value",(e=>e&&e[t]===n)),o.on("execute",this._executeCommand.bind(this,n,t)),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(i,e,o,t)})),o}))}getDropdownListItemDefinitions(e,t,i){const n=new E.Collection;return e.forEach((t=>{const a={type:"button",model:new g.ViewModel({group:i,commandValue:t.name,label:t.title,withText:!0,class:""})};n.add(a),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(e,t,a,i)}))})),n}_createListDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s,defaultText:l}=e,d=e.name.split(":")[1],u=o.filter((e=>t.find((({name:t})=>Z(t,d)===e)))).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==u.length&&F.warnInvalidStyle({dropdown:e});const c=(0,g.createDropdown)(n,g.DropdownButtonView),m=c.buttonView;m.set({label:z(s,a.label),class:null,tooltip:l,withText:!0});const p=this.editor.commands.get("drupalElementStyle");return m.bind("label").to(p,"value",(e=>{if(e&&e[d])for(const i of t)if(i.name===e[d])return i.title;return l})),c.bind("isOn").to(p),c.bind("isEnabled").to(this),(0,g.addListToDropdown)(c,this.getDropdownListItemDefinitions(t,p,d)),this.listenTo(c,"execute",(e=>{this._executeCommand(e.source.commandValue,e.source.group)})),c}))}_executeCommand(e,t){this.editor.execute("drupalElementStyle",{value:e,group:t}),this.editor.editing.view.focus()}static get pluginName(){return"DrupalElementStyleUi"}}class J extends e.Plugin{static get requires(){return[W,G]}static get pluginName(){return"DrupalElementStyle"}}function X(e){const t=e.getFirstPosition().findAncestor("caption");return t&&a(t.parent)?t:null}function Q(e){for(const t of e.getChildren())if(t&&t.is("element","caption"))return t;return null}class Y extends e.Command{refresh(){const e=this.editor.model.document.selection,t=e.getSelectedElement();if(!t)return this.isEnabled=!!o(e),void(this.value=!!X(e));this.isEnabled=a(t),this.isEnabled?this.value=!!Q(t):this.value=!1}execute(e={}){const{focusCaptionOnShow:t}=e;this.editor.model.change((e=>{this.value?this._hideDrupalMediaCaption(e):this._showDrupalMediaCaption(e,t)}))}_showDrupalMediaCaption(e,t){const i=this.editor.model.document.selection,n=this.editor.plugins.get("DrupalMediaCaptionEditing"),a=o(i),r=n._getSavedCaption(a)||e.createElement("caption");e.append(r,a),t&&e.setSelection(r,"in")}_hideDrupalMediaCaption(e){const t=this.editor,i=t.model.document.selection,n=t.plugins.get("DrupalMediaCaptionEditing");let a,r=i.getSelectedElement();r?a=Q(r):(a=X(i),r=o(i)),n._saveCaption(r,a),e.setSelection(r,"on"),e.remove(a)}}class ee extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionEditing"}constructor(e){super(e),this._savedCaptionsMap=new WeakMap}init(){const e=this.editor,t=e.model.schema;t.isRegistered("caption")?t.extend("caption",{allowIn:"drupalMedia"}):t.register("caption",{allowIn:"drupalMedia",allowContentOf:"$block",isLimit:!0}),e.commands.add("toggleMediaCaption",new Y(e)),this._setupConversion()}_setupConversion(){const e=this.editor,i=e.editing.view;var n;e.conversion.for("upcast").add(function(e){const t=(t,i,n)=>{const{viewItem:a}=i,{writer:r,consumable:o}=n;if(!i.modelRange||!o.consume(a,{attributes:["data-caption"]}))return;const s=r.createElement("caption"),l=i.modelRange.start.nodeAfter,d=e.data.processor.toView(a.getAttribute("data-caption"));n.consumable.constructor.createFrom(d,n.consumable),n.convertChildren(d,s),r.append(s,l)};return e=>{e.on("element:drupal-media",t,{priority:"low"})}}(e)),e.conversion.for("editingDowncast").elementToElement({model:"caption",view:(e,{writer:n})=>{if(!a(e.parent))return null;const r=n.createEditableElement("figcaption");return r.placeholder=Drupal.t("Enter media caption"),(0,V.enablePlaceholder)({view:i,element:r,keepOnFocus:!0}),(0,t.toWidgetEditable)(r,n)}}),e.editing.mapper.on("modelToViewPosition",(n=i,(e,t)=>{const i=t.modelPosition,r=i.parent;if(!a(r))return;const o=t.mapper.toViewElement(r);t.viewPosition=n.createPositionAt(o,i.offset+1)})),e.conversion.for("dataDowncast").add(function(e){return t=>{t.on("insert:caption",((t,i,n)=>{const{consumable:r,writer:o,mapper:s}=n;if(!a(i.item.parent)||!r.consume(i.item,"insert"))return;const l=e.model.createRangeIn(i.item),d=o.createDocumentFragment();s.bindElements(i.item,d);for(const{item:t}of Array.from(l)){const i={item:t,range:e.model.createRangeOn(t)},a=`insert:${t.name||"$text"}`;e.data.downcastDispatcher.fire(a,i,n);for(const a of t.getAttributeKeys())Object.assign(i,{attributeKey:a,attributeOldValue:null,attributeNewValue:i.item.getAttribute(a)}),e.data.downcastDispatcher.fire(`attribute:${a}`,i,n)}for(const e of o.createRangeIn(d).getItems())s.unbindViewElement(e);s.unbindViewElement(d);const u=e.data.processor.toData(d);if(u){const e=s.toViewElement(i.item.parent);o.setAttribute("data-caption",u,e)}}))}}(e))}_getSavedCaption(e){const t=this._savedCaptionsMap.get(e);return t?V.Element.fromJSON(t):null}_saveCaption(e,t){this._savedCaptionsMap.set(e,t.toJSON())}}class te extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionUI"}init(){const{editor:t}=this,i=t.editing.view;t.ui.componentFactory.add("toggleDrupalMediaCaption",(n=>{const a=new g.ButtonView(n),r=t.commands.get("toggleMediaCaption");return a.set({label:Drupal.t("Caption media"),icon:e.icons.caption,tooltip:!0,isToggleable:!0}),a.bind("isOn","isEnabled").to(r,"value","isEnabled"),a.bind("label").to(r,"value",(e=>e?Drupal.t("Toggle caption off"):Drupal.t("Toggle caption on"))),this.listenTo(a,"execute",(()=>{t.execute("toggleMediaCaption",{focusCaptionOnShow:!0});const e=X(t.model.document.selection);if(e){const n=t.editing.mapper.toViewElement(e);i.scrollToTheSelection(),i.change((e=>{e.addClass("drupal-media__caption_highlighted",n)}))}t.editing.view.focus()})),a}))}}class ie extends e.Plugin{static get requires(){return[ee,te]}static get pluginName(){return"DrupalMediaCaption"}}const ne={DrupalMedia:x,MediaImageTextAlternative:D,MediaImageTextAlternativeEditing:y,MediaImageTextAlternativeUi:k,DrupalLinkMedia:P,DrupalMediaCaption:ie,DrupalElementStyle:J}})(),n=n.default})())); -\ No newline at end of file -+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalMedia=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/engine.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/engine.js")},"ckeditor5/src/ui.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/ui.js")},"ckeditor5/src/utils.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/utils.js")},"ckeditor5/src/widget.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/widget.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function i(n){var a=t[n];if(void 0!==a)return a.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,i),r.exports}i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{"use strict";i.d(n,{default:()=>ce});var e=i("ckeditor5/src/core.js"),t=i("ckeditor5/src/widget.js"),a=i("ckeditor5/src/utils.js");function r(e,t){return void 0===t&&(t=!0),t?!!e&&(e.is("element","drupalMedia")||e.is("element","drupalMediaInline")):!!e&&e.is("element","drupalMedia")}function o(e,i){return void 0===i&&(i=!0),i?(0,t.isWidget)(e)&&(!!e.getCustomProperty("drupalMedia")||!!e.getCustomProperty("drupalMediaInline")):(0,t.isWidget)(e)&&!!e.getCustomProperty("drupalMedia")}function s(e,t){void 0===t&&(t=!0);const i=e.getSelectedElement();return r(i,t)?i:e.getFirstPosition().findAncestor(t?/drupalMedia(Inline)?/:/drupalMedia/)}function l(e,t){void 0===t&&(t=!0);const i=e.getSelectedElement();if(i&&o(i,t))return i;if(null===e.getFirstPosition())return null;let n=e.getFirstPosition().parent;for(;n;){if(n.is("element")&&o(n,t))return n;n=n.parent}return null}function d(e){const t=typeof e;return null!=e&&("object"===t||"function"===t)}function u(e){for(const t of e){if(t.hasAttribute("data-drupal-media-preview"))return t;if(t.childCount){const e=u(t.getChildren());if(e)return e}}return null}function c(e){return`drupalElementStyle${e[0].toUpperCase()+e.substring(1)}`}function m(e,t){const i=e.model.schema;return t.is("selection")?function(e,t){const i=(0,a.first)(t.getSelectedBlocks());return!i||e.isObject(i)||i.isEmpty&&"listItem"!==i.name?"drupalMedia":"drupalMediaInline"}(i,t):i.checkChild(t,"drupalMediaInline")?"drupalMediaInline":"drupalMedia"}class p extends e.Command{execute(e){const t=this.editor.plugins.get("DrupalMediaEditing"),i=Object.entries(t.attrs).reduce(((e,[t,i])=>(e[i]=t,e)),{}),n=Object.keys(e).reduce(((t,n)=>(i[n]&&(t[i[n]]=e[n]),t)),{});if(this.editor.plugins.has("DrupalElementStyleEditing")){const t=this.editor.plugins.get("DrupalElementStyleEditing"),{normalizedStyles:i}=t;for(const a of Object.keys(i))for(const i of t.normalizedStyles[a])if(e[i.attributeName]&&i.attributeValue===e[i.attributeName]){const e=c(a);n[e]=i.name}}const a=m(this.editor,this.editor.model.document.selection);this.editor.model.change((e=>{this.editor.model.insertObject(function(e,t,i){return e.createElement(i,t)}(e,n,a))}))}refresh(){const e=this.editor.model,t=e.document.selection,i=m(this.editor,t),n=e.schema.findAllowedParent(t.getFirstPosition(),i);this.isEnabled=null!==n}}const g="METADATA_ERROR";class h extends e.Plugin{static get requires(){return[t.Widget]}constructor(e){super(e),this.attrs={drupalMediaAlt:"alt",drupalMediaEntityType:"data-entity-type",drupalMediaEntityUuid:"data-entity-uuid"},this.converterAttributes=["drupalMediaEntityUuid","drupalElementStyleViewMode","drupalMediaEntityType","drupalMediaAlt"]}init(){const e=this.editor.config.get("drupalMedia");if(!e)return;const{previewURL:t,themeError:i}=e;this.previewUrl=t,this.labelError=Drupal.t("Preview failed"),this.themeError=i||`\n <p>${Drupal.t("An error occurred while trying to preview the media. Save your work and reload this page.")}<p>\n `,this._defineSchema(),this._defineConverters(),this._defineListeners(),this.editor.commands.add("insertDrupalMedia",new p(this.editor))}upcastDrupalMediaIsImage(e){const{model:t,plugins:i}=this.editor;i.get("DrupalMediaMetadataRepository").getMetadata(e).then((i=>{e&&t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",!!i.imageSourceMetadata,e)}))})).catch((i=>{e&&(console.warn(i.toString()),t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",g,e)})))}))}upcastDrupalMediaType(e){this.editor.plugins.get("DrupalMediaMetadataRepository").getMetadata(e).then((t=>{e&&this.editor.model.enqueueChange({isUndoable:!1},(i=>{i.setAttribute("drupalMediaType",t.type,e)}))})).catch((t=>{e&&(console.warn(t.toString()),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",g,e)})))}))}async _fetchPreview(e){const t={text:this._renderElement(e),uuid:e.getAttribute("drupalMediaEntityUuid")},i=await fetch(`${this.previewUrl}?${new URLSearchParams(t)}`,{headers:{"X-Drupal-MediaPreview-CSRF-Token":this.editor.config.get("drupalMedia").previewCsrfToken}});if(i.ok){return{label:i.headers.get("drupal-media-label"),preview:await i.text()}}return{label:this.labelError,preview:this.themeError}}_defineSchema(){const e=this.editor.model.schema;e.register("drupalMedia",{inheritAllFrom:"$blockObject",allowAttributes:Object.keys(this.attrs)}),e.register("drupalMediaInline",{inheritAllFrom:"$inlineObject",allowIn:["$block","$container","$text"],allowAttributes:Object.keys(this.attrs)}),this.editor.editing.view.domConverter.blockElements.push("drupal-media")}_defineConverters(){const e=this.editor.conversion,i=this.editor.plugins.get("DrupalMediaMetadataRepository"),n={"drupal-media":"drupalMedia","drupal-media-inline":"drupalMediaInline"};Object.keys(n).forEach((a=>{const r=n[a];e.for("upcast").elementToElement({view:{name:a},model:r}).add((e=>{e.on(`element:${a}`,((e,t)=>{const[n]=t.modelRange.getItems();i.getMetadata(n).then((e=>{n&&(this.upcastDrupalMediaIsImage(n),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",e.type,n)})))})).catch((e=>{console.warn(e.toString())}))}),{priority:"lowest"})})),e.for("dataDowncast").elementToElement({model:r,view:{name:a}}),e.for("editingDowncast").elementToElement({model:r,view:(e,{writer:i})=>{const n=i.createContainerElement("figure",{class:"drupal-media-inline"===a?`drupal-media ${a}`:a});if(!this.previewUrl){const e=i.createRawElement("div",{"data-drupal-media-preview":"unavailable"});i.insert(i.createPositionAt(n,0),e)}return i.setCustomProperty(r,!0,n),(0,t.toWidget)(n,i,{label:Drupal.t("Media widget")})}}).add((e=>{const t=(e,t,i)=>{const n=i.writer,a=t.item,r=i.mapper.toViewElement(t.item);let o=u(r.getChildren());if(o){if("ready"!==o.getAttribute("data-drupal-media-preview"))return;n.setAttribute("data-drupal-media-preview","loading",o)}else o=n.createRawElement("div",{"data-drupal-media-preview":"loading"}),n.insert(n.createPositionAt(r,0),o);this._fetchPreview(a).then((({label:e,preview:t})=>{o&&this.editor.editing.view.change((i=>{const n=i.createRawElement("div",{"data-drupal-media-preview":"ready","aria-label":e},(e=>{e.innerHTML=t}));i.insert(i.createPositionBefore(o),n),i.remove(o)}))}))};return this.converterAttributes.forEach((i=>{e.on(`attribute:${i}:${r}`,t)})),e})),e.for("editingDowncast").add((e=>{e.on(`attribute:drupalElementStyleAlign:${r}`,((e,t,i)=>{const n={left:"drupal-media-style-align-left",right:"drupal-media-style-align-right",center:"drupal-media-style-align-center"},a=i.mapper.toViewElement(t.item),r=i.writer;n[t.attributeOldValue]&&r.removeClass(n[t.attributeOldValue],a),n[t.attributeNewValue]&&i.consumable.consume(t.item,e.name)&&r.addClass(n[t.attributeNewValue],a)}))})),Object.keys(this.attrs).forEach((t=>{const i={model:{key:t,name:r},view:{name:a,key:this.attrs[t]}};e.for("dataDowncast").attributeToAttribute(i),e.for("upcast").attributeToAttribute(i)}))}))}_defineListeners(){this.editor.model.on("insertContent",((e,[t])=>{r(t)&&(this.upcastDrupalMediaIsImage(t),this.upcastDrupalMediaType(t))}))}_renderElement(e){const t=this.editor.model.change((t=>{const i=t.createDocumentFragment(),n=t.cloneElement(e,!1);return["linkHref"].forEach((e=>{t.removeAttribute(e,n)})),t.append(n,i),i}));return this.editor.data.stringify(t)}static get pluginName(){return"DrupalMediaEditing"}}var f=i("ckeditor5/src/ui.js");class b extends e.Plugin{init(){const e=this.editor,t=this.editor.config.get("drupalMedia");if(!t)return;const{libraryURL:i,openDialog:n,dialogSettings:a={}}=t;i&&"function"==typeof n&&e.ui.componentFactory.add("drupalMedia",(t=>{const r=e.commands.get("insertDrupalMedia"),o=new f.ButtonView(t);return o.set({label:Drupal.t("Insert Media"),icon:'<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M19.1873 4.86414L10.2509 6.86414V7.02335H10.2499V15.5091C9.70972 15.1961 9.01793 15.1048 8.34069 15.3136C7.12086 15.6896 6.41013 16.8967 6.75322 18.0096C7.09631 19.1226 8.3633 19.72 9.58313 19.344C10.6666 19.01 11.3484 18.0203 11.2469 17.0234H11.2499V9.80173L18.1803 8.25067V14.3868C17.6401 14.0739 16.9483 13.9825 16.2711 14.1913C15.0513 14.5674 14.3406 15.7744 14.6836 16.8875C15.0267 18.0004 16.2937 18.5978 17.5136 18.2218C18.597 17.8877 19.2788 16.8982 19.1773 15.9011H19.1803V8.02687L19.1873 8.0253V4.86414Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M13.5039 0.743652H0.386932V12.1603H13.5039V0.743652ZM12.3379 1.75842H1.55289V11.1454H1.65715L4.00622 8.86353L6.06254 10.861L9.24985 5.91309L11.3812 9.22179L11.7761 8.6676L12.3379 9.45621V1.75842ZM6.22048 4.50869C6.22048 5.58193 5.35045 6.45196 4.27722 6.45196C3.20398 6.45196 2.33395 5.58193 2.33395 4.50869C2.33395 3.43546 3.20398 2.56543 4.27722 2.56543C5.35045 2.56543 6.22048 3.43546 6.22048 4.50869Z"/></svg>\n',tooltip:!0}),o.bind("isOn","isEnabled").to(r,"value","isEnabled"),this.listenTo(o,"execute",(()=>{n(i,(({attributes:t})=>{e.execute("insertDrupalMedia",t)}),a)})),o}))}}class w extends e.Plugin{static get requires(){return[t.WidgetToolbarRepository]}static get pluginName(){return"DrupalMediaToolbar"}afterInit(){const{editor:e}=this;var i;e.plugins.get(t.WidgetToolbarRepository).register("drupalMedia",{ariaLabel:Drupal.t("Drupal Media toolbar"),items:(i=e.config.get("drupalMedia.toolbar"),i.map((e=>d(e)?e.name:e))||[]),getRelatedElement:e=>l(e)})}}class y extends e.Command{refresh(){const e=s(this.editor.model.document.selection);this.isEnabled=!!e&&e.getAttribute("drupalMediaIsImage")&&e.getAttribute("drupalMediaIsImage")!==g,this.isEnabled?this.value=e.getAttribute("drupalMediaAlt"):this.value=!1}execute(e){const{model:t}=this.editor,i=s(t.document.selection);e.newValue=e.newValue.trim(),t.change((t=>{e.newValue.length>0?t.setAttribute("drupalMediaAlt",e.newValue,i):t.removeAttribute("drupalMediaAlt",i)}))}}class v extends e.Plugin{init(){this._data=new WeakMap}getMetadata(e){if(this._data.get(e))return new Promise((t=>{t(this._data.get(e))}));const t=this.editor.config.get("drupalMedia");if(!t)return new Promise(((e,t)=>{t(new Error("drupalMedia configuration is required for parsing metadata."))}));if(!e.hasAttribute("drupalMediaEntityUuid"))return new Promise(((e,t)=>{t(new Error("drupalMedia element must have drupalMediaEntityUuid attribute to retrieve metadata."))}));const{metadataUrl:i}=t;return(async e=>{const t=await fetch(e);if(t.ok)return JSON.parse(await t.text());throw new Error("Fetching media embed metadata from the server failed.")})(`${i}&${new URLSearchParams({uuid:e.getAttribute("drupalMediaEntityUuid")})}`).then((t=>(this._data.set(e,t),t)))}static get pluginName(){return"DrupalMediaMetadataRepository"}}class E extends e.Plugin{static get requires(){return[v]}static get pluginName(){return"MediaImageTextAlternativeEditing"}init(){const{editor:e,editor:{model:t,conversion:i}}=this;t.schema.extend("drupalMedia",{allowAttributes:["drupalMediaIsImage"]}),i.for("editingDowncast").add((e=>{e.on("attribute:drupalMediaIsImage",((e,t,i)=>{const{writer:n,mapper:a}=i,r=a.toViewElement(t.item);if(t.attributeNewValue!==g){const e=Array.from(r.getChildren()).find((e=>e.getCustomProperty("drupalMediaMetadataError")));return void(e&&(n.setCustomProperty("widgetLabel",e.getCustomProperty("drupalMediaOriginalWidgetLabel"),e),n.removeElement(e)))}const o=Drupal.t("Not all functionality may be available because some information could not be retrieved."),s=new f.Template({tag:"span",children:[{tag:"span",attributes:{class:"drupal-media__metadata-error-icon","data-cke-tooltip-text":o}}]}).render(),l=n.createRawElement("div",{class:"drupal-media__metadata-error"},((e,t)=>{t.setContentOf(e,s.outerHTML)}));n.setCustomProperty("drupalMediaMetadataError",!0,l);const d=r.getCustomProperty("widgetLabel");n.setCustomProperty("drupalMediaOriginalWidgetLabel",d,l),n.setCustomProperty("widgetLabel",`${d} (${o})`,r),n.insert(n.createPositionAt(r,0),l)}),{priority:"low"})})),e.commands.add("mediaImageTextAlternative",new y(this.editor))}}function M(e){const t=e.editing.view,i=f.BalloonPanelView.defaultPositions;return{target:t.domConverter.viewToDom(t.document.selection.getSelectedElement()),positions:[i.northArrowSouth,i.northArrowSouthWest,i.northArrowSouthEast,i.southArrowNorth,i.southArrowNorthWest,i.southArrowNorthEast]}}class k extends f.View{constructor(t){super(t),this.focusTracker=new a.FocusTracker,this.keystrokes=new a.KeystrokeHandler,this.labeledInput=this._createLabeledInputView(),this.set("defaultAltText",void 0),this.defaultAltTextView=this._createDefaultAltTextView(),this.saveButtonView=this._createButton(Drupal.t("Save"),e.icons.check,"ck-button-save"),this.saveButtonView.type="submit",this.cancelButtonView=this._createButton(Drupal.t("Cancel"),e.icons.cancel,"ck-button-cancel","cancel"),this._focusables=new f.ViewCollection,this._focusCycler=new f.FocusCycler({focusables:this._focusables,focusTracker:this.focusTracker,keystrokeHandler:this.keystrokes,actions:{focusPrevious:"shift + tab",focusNext:"tab"}}),this.setTemplate({tag:"form",attributes:{class:["ck","ck-media-alternative-text-form","ck-vertical-form"],tabindex:"-1"},children:[this.defaultAltTextView,this.labeledInput,this.saveButtonView,this.cancelButtonView]}),(0,f.injectCssTransitionDisabler)(this)}render(){super.render(),this.keystrokes.listenTo(this.element),(0,f.submitHandler)({view:this}),[this.labeledInput,this.saveButtonView,this.cancelButtonView].forEach((e=>{this._focusables.add(e),this.focusTracker.add(e.element)}))}_createButton(e,t,i,n){const a=new f.ButtonView(this.locale);return a.set({label:e,icon:t,tooltip:!0}),a.extendTemplate({attributes:{class:i}}),n&&a.delegate("execute").to(this,n),a}_createLabeledInputView(){const e=new f.LabeledFieldView(this.locale,f.createLabeledInputText);return e.label=Drupal.t("Alternative text override"),e}_createDefaultAltTextView(){const e=f.Template.bind(this,this);return new f.Template({tag:"div",attributes:{class:["ck-media-alternative-text-form__default-alt-text",e.if("defaultAltText","ck-hidden",(e=>!e))]},children:[{tag:"strong",attributes:{class:"ck-media-alternative-text-form__default-alt-text-label"},children:[Drupal.t("Default alternative text:")]}," ",{tag:"span",attributes:{class:"ck-media-alternative-text-form__default-alt-text-value"},children:[{text:[e.to("defaultAltText")]}]}]})}}class A extends e.Plugin{static get requires(){return[f.ContextualBalloon]}static get pluginName(){return"MediaImageTextAlternativeUi"}init(){this._createButton(),this._createForm()}destroy(){super.destroy(),this._form.destroy()}_createButton(){const t=this.editor;t.ui.componentFactory.add("mediaImageTextAlternative",(i=>{const n=t.commands.get("mediaImageTextAlternative"),a=new f.ButtonView(i);return a.set({label:Drupal.t("Override media image alternative text"),icon:e.icons.lowVision,tooltip:!0}),a.bind("isVisible").to(n,"isEnabled"),this.listenTo(a,"execute",(()=>{this._showForm()})),a}))}_createForm(){const e=this.editor,t=e.editing.view.document;this._balloon=this.editor.plugins.get("ContextualBalloon"),this._form=new k(e.locale),this._form.render(),this.listenTo(this._form,"submit",(()=>{e.execute("mediaImageTextAlternative",{newValue:this._form.labeledInput.fieldView.element.value}),this._hideForm(!0)})),this.listenTo(this._form,"cancel",(()=>{this._hideForm(!0)})),this._form.keystrokes.set("Esc",((e,t)=>{this._hideForm(!0),t()})),this.listenTo(e.ui,"update",(()=>{l(t.selection)?this._isVisible&&function(e){const t=e.plugins.get("ContextualBalloon");if(l(e.editing.view.document.selection)){const i=M(e);t.updatePosition(i)}}(e):this._hideForm(!0)})),(0,f.clickOutsideHandler)({emitter:this._form,activator:()=>this._isVisible,contextElements:[this._balloon.view.element],callback:()=>this._hideForm()})}_showForm(){if(this._isVisible)return;const e=this.editor,t=e.commands.get("mediaImageTextAlternative"),i=e.plugins.get("DrupalMediaMetadataRepository"),n=this._form.labeledInput;this._form.disableCssTransitions(),this._isInBalloon||this._balloon.add({view:this._form,position:M(e)}),n.fieldView.element.value=t.value||"",n.fieldView.value=n.fieldView.element.value,this._form.defaultAltText="";const a=e.model.document.selection.getSelectedElement();r(a)&&i.getMetadata(a).then((e=>{this._form.defaultAltText=e.imageSourceMetadata?e.imageSourceMetadata.alt:""})).catch((e=>{console.warn(e.toString())})),this._form.labeledInput.fieldView.select(),this._form.enableCssTransitions()}_hideForm(e){this._isInBalloon&&(this._form.focusTracker.isFocused&&this._form.saveButtonView.focus(),this._balloon.remove(this._form),e&&this.editor.editing.view.focus())}get _isVisible(){return this._balloon.visibleView===this._form}get _isInBalloon(){return this._balloon.hasView(this._form)}}class D extends e.Plugin{static get requires(){return[E,A]}static get pluginName(){return"MediaImageTextAlternative"}}function C(e,t,i){if(t.attributes)for(const[n,a]of Object.entries(t.attributes))e.setAttribute(n,a,i);t.styles&&e.setStyle(t.styles,i),t.classes&&e.addClass(t.classes,i)}function _(e,t,i){if(!i.consumable.consume(t.item,e.name))return;const n=i.mapper.toViewElement(t.item);C(i.writer,t.attributeNewValue,n)}class x extends e.Plugin{constructor(e){if(super(e),!e.plugins.has("GeneralHtmlSupport"))return;e.plugins.has("DataFilter")&&e.plugins.has("DataSchema")||console.error("DataFilter and DataSchema plugins are required for Drupal Media to integrate with General HTML Support plugin.");const{schema:t}=e.model,{conversion:i}=e,n=this.editor.plugins.get("DataFilter"),a=this.editor.plugins.get("DataSchema"),r={"drupal-media":"drupalMedia","drupal-media-inline":"drupalMediaInline"};Object.keys(r).forEach((e=>{const o=r[e];a.registerBlockElement({model:o,view:e}),n.on(`register:${e}`,((a,r)=>{r.model===o&&(t.extend(o,{allowAttributes:["htmlLinkAttributes","htmlAttributes"]}),i.for("upcast").add(function(e,t,i){return t=>{t.on(`element:${i}`,((t,i,n)=>{function a(t,a){const r=e.processViewAttributes(t,n);r&&n.writer.setAttribute(a,r,i.modelRange)}const r=i.viewItem,o=r.parent;a(r,"htmlAttributes"),o.is("element","a")&&a(o,"htmlLinkAttributes")}),{priority:"low"})}}(n,0,e)),i.for("editingDowncast").add(function(e){return t=>{t.on(`attribute:linkHref:${e}`,((t,i,n)=>{if(!n.consumable.consume(i.item,`attribute:htmlLinkAttributes:${e}`))return;const a=n.mapper.toViewElement(i.item),r=function(e,t,i){const n=e.createRangeOn(t);for(const{item:e}of n.getWalker())if(e.is("element",i))return e}(n.writer,a,"a");C(n.writer,i.item.getAttribute("htmlLinkAttributes"),r)}),{priority:"low"})}}(o)),i.for("dataDowncast").add(function(e){return t=>{t.on(`attribute:linkHref:${e}`,((t,i,n)=>{if(!n.consumable.consume(i.item,`attribute:htmlLinkAttributes:${e}`))return;const a=n.mapper.toViewElement(i.item).parent;C(n.writer,i.item.getAttribute("htmlLinkAttributes"),a)}),{priority:"low"}),t.on(`attribute:htmlAttributes:${e}`,_,{priority:"low"})}}(o)),a.stop())}))}))}static get pluginName(){return"DrupalMediaGeneralHtmlSupport"}}class V extends e.Plugin{static get requires(){return[h,x,b,w,D]}static get pluginName(){return"DrupalMedia"}}var I=i("ckeditor5/src/engine.js");function S(e){return Array.from(e.getChildren()).find((e=>"drupal-media"===e.name))}function T(e){return t=>{t.on(`attribute:${e.id}:drupalMedia`,((t,i,n)=>{const r=n.mapper.toViewElement(i.item);let o=Array.from(r.getChildren()).find((e=>"a"===e.name));if(o=!o&&r.is("element","a")?r:Array.from(r.getAncestors()).find((e=>"a"===e.name)),o){for(const[t,i]of(0,a.toMap)(e.attributes))n.writer.setAttribute(t,i,o);e.classes&&n.writer.addClass(e.classes,o);for(const t in e.styles)Object.prototype.hasOwnProperty.call(e.styles,t)&&n.writer.setStyle(t,e.styles[t],o)}}))}}function L(e,t){return e=>{e.on("element:a",((e,i,n)=>{const a=i.viewItem;if(!S(a))return;const r=new I.Matcher(t._createPattern()).match(a);if(!r)return;if(!n.consumable.consume(a,r.match))return;const o=i.modelCursor.nodeBefore;n.writer.setAttribute(t.id,!0,o)}),{priority:"high"})}}class P extends e.Plugin{static get requires(){return["LinkEditing","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaEditing"}init(){const{editor:e}=this;e.model.schema.extend("drupalMedia",{allowAttributes:["linkHref"]}),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const n=t.viewItem,a=S(n);if(!a)return;if(!i.consumable.consume(n,{attributes:["href"],name:!0}))return;const r=n.getAttribute("href");if(null===r)return;const o=i.convertItem(a,t.modelCursor);t.modelRange=o.modelRange,t.modelCursor=o.modelCursor;const s=t.modelCursor.nodeBefore;s&&s.is("element","drupalMedia")&&i.writer.setAttribute("linkHref",r,s)}),{priority:"high"})})),e.conversion.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=Array.from(a.getChildren()).find((e=>"a"===e.name));if(r)t.attributeNewValue?n.setAttribute("href",t.attributeNewValue,r):(n.move(n.createRangeIn(r),n.createPositionAt(a,0)),n.remove(r));else{const e=Array.from(a.getChildren()).find((e=>e.getAttribute("data-drupal-media-preview"))),i=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionAt(a,0),i),n.move(n.createRangeOn(e),n.createPositionAt(i,0))}}),{priority:"high"})})),e.conversion.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionBefore(a),r),n.move(n.createRangeOn(a),n.createPositionAt(r,0))}),{priority:"high"})})),this._enableManualDecorators();if(e.commands.get("link").automaticDecorators.length>0)throw new Error("The Drupal Media plugin is not compatible with automatic link decorators. To use Drupal Media, disable any plugins providing automatic link decorators.")}_enableManualDecorators(){const e=this.editor,t=e.commands.get("link");for(const i of t.manualDecorators)e.model.schema.extend("drupalMedia",{allowAttributes:i.id}),e.conversion.for("downcast").add(T(i)),e.conversion.for("upcast").add(L(0,i))}}class O extends e.Plugin{static get requires(){return["LinkEditing","LinkUI","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaUi"}init(){const{editor:e}=this,t=e.editing.view.document;this.listenTo(t,"click",((t,i)=>{this._isSelectedLinkedMedia(e.model.document.selection)&&(i.preventDefault(),t.stop())}),{priority:"high"}),this._createToolbarLinkMediaButton()}_createToolbarLinkMediaButton(){const{editor:e}=this;e.ui.componentFactory.add("drupalLinkMedia",(t=>{const i=new f.ButtonView(t),n=e.plugins.get("LinkUI"),a=e.commands.get("link");return i.set({isEnabled:!0,label:Drupal.t("Link media"),icon:'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184z"/></svg>\n',keystroke:"Ctrl+K",tooltip:!0,isToggleable:!0}),i.bind("isEnabled").to(a,"isEnabled"),i.bind("isOn").to(a,"value",(e=>!!e)),this.listenTo(i,"execute",(()=>{this._isSelectedLinkedMedia(e.model.document.selection)?n._addActionsView():n._showUI(!0)})),i}))}_isSelectedLinkedMedia(e){const t=e.getSelectedElement();return!!t&&t.is("element","drupalMedia")&&t.hasAttribute("linkHref")}}class B extends e.Plugin{static get requires(){return[P,O]}static get pluginName(){return"DrupalLinkMedia"}}const{objectFullWidth:N,objectInline:j,objectLeft:R,objectRight:F,objectCenter:$,objectBlockLeft:U,objectBlockRight:H}=e.icons,q={get inline(){return{name:"inline",title:"In line",icon:j,modelElements:["imageInline"],isDefault:!0}},get alignLeft(){return{name:"alignLeft",title:"Left aligned image",icon:R,modelElements:["imageBlock","imageInline"],className:"image-style-align-left"}},get alignBlockLeft(){return{name:"alignBlockLeft",title:"Left aligned image",icon:U,modelElements:["imageBlock"],className:"image-style-block-align-left"}},get alignCenter(){return{name:"alignCenter",title:"Centered image",icon:$,modelElements:["imageBlock"],className:"image-style-align-center"}},get alignRight(){return{name:"alignRight",title:"Right aligned image",icon:F,modelElements:["imageBlock","imageInline"],className:"image-style-align-right"}},get alignBlockRight(){return{name:"alignBlockRight",title:"Right aligned image",icon:H,modelElements:["imageBlock"],className:"image-style-block-align-right"}},get block(){return{name:"block",title:"Centered image",icon:$,modelElements:["imageBlock"],isDefault:!0}},get side(){return{name:"side",title:"Side image",icon:F,modelElements:["imageBlock"],className:"image-style-side"}}},W={full:N,left:U,right:H,center:$,inlineLeft:R,inlineRight:F,inline:j},K=[{name:"imageStyle:wrapText",title:"Wrap text",defaultItem:"imageStyle:alignLeft",items:["imageStyle:alignLeft","imageStyle:alignRight"]},{name:"imageStyle:breakText",title:"Break text",defaultItem:"imageStyle:block",items:["imageStyle:alignBlockLeft","imageStyle:block","imageStyle:alignBlockRight"]}];function z(e){(0,a.logWarning)("image-style-configuration-definition-invalid",e)}const Z={normalizeStyles:function(e){return(e.configuredStyles.options||[]).map((e=>function(e){e="string"==typeof e?q[e]?{...q[e]}:{name:e}:function(e,t){const i={...t};for(const n in e)Object.prototype.hasOwnProperty.call(t,n)||(i[n]=e[n]);return i}(q[e.name],e);"string"==typeof e.icon&&(e.icon=W[e.icon]||e.icon);return e}(e))).filter((t=>function(e,{isBlockPluginLoaded:t,isInlinePluginLoaded:i}){const{modelElements:n,name:r}=e;if(!(n&&n.length&&r))return z({style:e}),!1;{const r=[t?"imageBlock":null,i?"imageInline":null];if(!n.some((e=>r.includes(e))))return(0,a.logWarning)("image-style-missing-dependency",{style:e,missingPlugins:n.map((e=>"imageBlock"===e?"ImageBlockEditing":"ImageInlineEditing"))}),!1}return!0}(t,e)))},getDefaultStylesConfiguration:function(e,t){return e&&t?{options:["inline","alignLeft","alignRight","alignCenter","alignBlockLeft","alignBlockRight","block","side"]}:e?{options:["block","side"]}:t?{options:["inline","alignLeft","alignRight"]}:{}},getDefaultDropdownDefinitions:function(e){return e.has("ImageBlockEditing")&&e.has("ImageInlineEditing")?[...K]:[]},warnInvalidStyle:z,DEFAULT_OPTIONS:q,DEFAULT_ICONS:W,DEFAULT_DROPDOWN_DEFINITIONS:K};function G(e,t,i){for(const n of t)if(i.checkAttribute(e,n))return!0;return!1}function J(e,t,i){const n=e.getSelectedElement();if(n&&G(n,i,t))return n;let{parent:a}=e.getFirstPosition();for(;a;){if(a.is("element")&&G(a,i,t))return a;a=a.parent}return null}class X extends e.Command{constructor(e,t){super(e),this.styles={},Object.keys(t).forEach((e=>{this.styles[e]=new Map(t[e].map((e=>[e.name,e])))})),this.modelAttributes=[];for(const e of Object.keys(t)){const t=c(e);this.modelAttributes.push(t)}}refresh(){const{editor:e}=this,t=J(e.model.document.selection,e.model.schema,this.modelAttributes);this.isEnabled=!!t,this.isEnabled?this.value=this.getValue(t):this.value=!1}getValue(e){const t={};return Object.keys(this.styles).forEach((i=>{const n=c(i);if(e.hasAttribute(n))t[i]=e.getAttribute(n);else for(const[,e]of this.styles[i])e.isDefault&&(t[i]=e.name)})),t}execute(e={}){const{editor:{model:t}}=this,{value:i,group:n}=e,a=c(n);t.change((e=>{const r=J(t.document.selection,t.schema,this.modelAttributes);!i||this.styles[n].get(i).isDefault?e.removeAttribute(a,r):e.setAttribute(a,i,r)}))}}function Q(e,t){for(const i of t)if(i.name===e)return i}class Y extends e.Plugin{init(){const{editor:t}=this,i=t.config.get("drupalElementStyles");this.normalizedStyles={},Object.keys(i).forEach((t=>{this.normalizedStyles[t]=i[t].map((t=>("string"==typeof t.icon&&e.icons[t.icon]&&(t.icon=e.icons[t.icon]),t.name&&(t.name=`${t.name}`),t))).filter((e=>e.isDefault||e.attributeName&&e.attributeValue?e.modelElements&&Array.isArray(e.modelElements)?!!e.name||(console.warn("drupalElementStyles options must include a name."),!1):(console.warn("drupalElementStyles options must include an array of supported modelElements."),!1):(console.warn(`${e.attributeValue} drupalElementStyles options must include attributeName and attributeValue.`),!1)))})),this._setupConversion(),t.commands.add("drupalElementStyle",new X(t,this.normalizedStyles))}_setupConversion(){const{editor:e}=this,{schema:t}=e.model;Object.keys(this.normalizedStyles).forEach((i=>{const n=c(i),r=(o=this.normalizedStyles[i],(e,t,i)=>{if(!i.consumable.consume(t.item,e.name))return;const n=Q(t.attributeNewValue,o),a=Q(t.attributeOldValue,o),r=i.mapper.toViewElement(t.item),s=i.writer;a&&("class"===a.attributeName?s.removeClass(a.attributeValue,r):s.removeAttribute(a.attributeName,r)),n&&("class"===n.attributeName?s.addClass(n.attributeValue,r):n.isDefault||s.setAttribute(n.attributeName,n.attributeValue,r))});var o;const s=function(e,t){const i=e.filter((e=>!e.isDefault));return(e,n,r)=>{if(!n.modelRange)return;const o=n.viewItem,s=(0,a.first)(n.modelRange.getItems());if(s&&r.schema.checkAttribute(s,t))for(const e of i)if("class"===e.attributeName)r.consumable.consume(o,{classes:e.attributeValue})&&r.writer.setAttribute(t,e.name,s);else if(r.consumable.consume(o,{attributes:[e.attributeName]}))for(const e of i)e.attributeValue===o.getAttribute(e.attributeName)&&r.writer.setAttribute(t,e.name,s)}}(this.normalizedStyles[i],n);e.editing.downcastDispatcher.on(`attribute:${n}`,r),e.data.downcastDispatcher.on(`attribute:${n}`,r);[...new Set(this.normalizedStyles[i].map((e=>e.modelElements)).flat())].forEach((e=>{t.extend(e,{allowAttributes:n})})),e.data.upcastDispatcher.on("element",s,{priority:"low"})}))}static get pluginName(){return"DrupalElementStyleEditing"}}const ee=e=>e,te=(e,t)=>(e?`${e}: `:"")+t;function ie(e,t){return`drupalElementStyle:${t}:${e}`}class ne extends e.Plugin{static get requires(){return[Y]}init(){const{plugins:e}=this.editor,t=this.editor.config.get("drupalMedia.toolbar")||[],i=e.get("DrupalElementStyleEditing").normalizedStyles;Object.keys(i).forEach((e=>{i[e].forEach((t=>{this._createButton(t,e,i[e])}))}));t.filter(d).filter((e=>{const t=[];if(!e.display)return console.warn("dropdown configuration must include a display key specifying either listDropdown or splitButton."),!1;e.items.includes(e.defaultItem)||console.warn("defaultItem must be part of items in the dropdown configuration.");for(const i of e.items){const e=i.split(":")[1];t.push(e)}return!!t.every((e=>e===t[0]))||(console.warn("dropdown configuration should only contain buttons from one group."),!1)})).forEach((e=>{if(e.items.length>=2){const t=e.name.split(":")[1];switch(e.display){case"splitButton":this._createDropdown(e,i[t]);break;case"listDropdown":this._createListDropdown(e,i[t])}}}))}updateOptionVisibility(e,t,i,n){const{selection:r}=this.editor.model.document,o={};o[n]=e;const s=r?r.getSelectedElement():J(r,this.editor.model.schema,o),l=e.filter((function(e){for(const[t,i]of(0,a.toMap)(e.modelAttributes))if(s&&s.hasAttribute(t))return i.includes(s.getAttribute(t));return!0}));i.hasOwnProperty("model")?l.includes(t)?i.model.set({class:""}):i.model.set({class:"ck-hidden"}):l.includes(t)?i.set({class:""}):i.set({class:"ck-hidden"})}_createDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s}=e,l=o.filter((e=>{const i=e.split(":")[1];return t.find((({name:t})=>ie(t,i)===e))})).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==l.length&&Z.warnInvalidStyle({dropdown:e});const d=(0,f.createDropdown)(n,f.SplitButtonView),u=d.buttonView;return(0,f.addToolbarToDropdown)(d,l),u.set({label:te(s,a.label),class:null,tooltip:!0}),u.bind("icon").toMany(l,"isOn",((...e)=>{const t=e.findIndex(ee);return t<0?a.icon:l[t].icon})),u.bind("label").toMany(l,"isOn",((...e)=>{const t=e.findIndex(ee);return te(s,t<0?a.label:l[t].label)})),u.bind("isOn").toMany(l,"isOn",((...e)=>e.some(ee))),u.bind("class").toMany(l,"isOn",((...e)=>e.some(ee)?"ck-splitbutton_flatten":null)),u.on("execute",(()=>{l.some((({isOn:e})=>e))?d.isOpen=!d.isOpen:a.fire("execute")})),d.bind("isEnabled").toMany(l,"isEnabled",((...e)=>e.some(ee))),d}))}_createButton(e,t,i){const n=e.name;this.editor.ui.componentFactory.add(ie(n,t),(a=>{const r=this.editor.commands.get("drupalElementStyle"),o=new f.ButtonView(a);return o.set({label:e.title,icon:e.icon,tooltip:!0,isToggleable:!0}),o.bind("isEnabled").to(r,"isEnabled"),o.bind("isOn").to(r,"value",(e=>e&&e[t]===n)),o.on("execute",this._executeCommand.bind(this,n,t)),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(i,e,o,t)})),o}))}getDropdownListItemDefinitions(e,t,i){const n=new a.Collection;return e.forEach((t=>{const a={type:"button",model:new f.ViewModel({group:i,commandValue:t.name,label:t.title,withText:!0,class:""})};n.add(a),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(e,t,a,i)}))})),n}_createListDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s,defaultText:l}=e,d=e.name.split(":")[1],u=o.filter((e=>t.find((({name:t})=>ie(t,d)===e)))).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==u.length&&Z.warnInvalidStyle({dropdown:e});const c=(0,f.createDropdown)(n,f.DropdownButtonView),m=c.buttonView;m.set({label:te(s,a.label),class:null,tooltip:l,withText:!0});const p=this.editor.commands.get("drupalElementStyle");return m.bind("label").to(p,"value",(e=>{if(e&&e[d])for(const i of t)if(i.name===e[d])return i.title;return l})),c.bind("isOn").to(p),c.bind("isEnabled").to(this),(0,f.addListToDropdown)(c,this.getDropdownListItemDefinitions(t,p,d)),this.listenTo(c,"execute",(e=>{this._executeCommand(e.source.commandValue,e.source.group)})),c}))}_executeCommand(e,t){this.editor.execute("drupalElementStyle",{value:e,group:t}),this.editor.editing.view.focus()}static get pluginName(){return"DrupalElementStyleUi"}}class ae extends e.Plugin{static get requires(){return[Y,ne]}static get pluginName(){return"DrupalElementStyle"}}function re(e){const t=e.getFirstPosition().findAncestor("caption");return t&&r(t.parent)?t:null}function oe(e){for(const t of e.getChildren())if(t&&t.is("element","caption"))return t;return null}class se extends e.Command{refresh(){const e=this.editor.model.document.selection,t=e.getSelectedElement();if(!t)return this.isEnabled=!!s(e,!1),void(this.value=!!re(e));this.isEnabled=r(t,!1),this.isEnabled?this.value=!!oe(t):this.value=!1}execute(e={}){const{focusCaptionOnShow:t}=e;this.editor.model.change((e=>{this.value?this._hideDrupalMediaCaption(e):this._showDrupalMediaCaption(e,t)}))}_showDrupalMediaCaption(e,t){const i=this.editor.model.document.selection,n=this.editor.plugins.get("DrupalMediaCaptionEditing"),a=s(i),r=n._getSavedCaption(a)||e.createElement("caption");e.append(r,a),t&&e.setSelection(r,"in")}_hideDrupalMediaCaption(e){const t=this.editor,i=t.model.document.selection,n=t.plugins.get("DrupalMediaCaptionEditing");let a,r=i.getSelectedElement();r?a=oe(r):(a=re(i),r=s(i)),n._saveCaption(r,a),e.setSelection(r,"on"),e.remove(a)}}class le extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionEditing"}constructor(e){super(e),this._savedCaptionsMap=new WeakMap}init(){const e=this.editor,t=e.model.schema;t.isRegistered("caption")?t.extend("caption",{allowIn:"drupalMedia"}):t.register("caption",{allowIn:"drupalMedia",allowContentOf:"$block",isLimit:!0}),e.commands.add("toggleMediaCaption",new se(e)),this._setupConversion()}_setupConversion(){const e=this.editor,i=e.editing.view;var n;e.conversion.for("upcast").add(function(e){const t=(t,i,n)=>{const{viewItem:a}=i,{writer:r,consumable:o}=n;if(!i.modelRange||!o.consume(a,{attributes:["data-caption"]}))return;const s=r.createElement("caption"),l=i.modelRange.start.nodeAfter,d=e.data.processor.toView(a.getAttribute("data-caption"));n.consumable.constructor.createFrom(d,n.consumable),n.convertChildren(d,s),r.append(s,l)};return e=>{e.on("element:drupal-media",t,{priority:"low"})}}(e)),e.conversion.for("editingDowncast").elementToElement({model:"caption",view:(e,{writer:n})=>{if(!r(e.parent))return null;const a=n.createEditableElement("figcaption");return a.placeholder=Drupal.t("Enter media caption"),(0,I.enablePlaceholder)({view:i,element:a,keepOnFocus:!0}),(0,t.toWidgetEditable)(a,n)}}),e.editing.mapper.on("modelToViewPosition",(n=i,(e,t)=>{const i=t.modelPosition,a=i.parent;if(!r(a))return;const o=t.mapper.toViewElement(a);t.viewPosition=n.createPositionAt(o,i.offset+1)})),e.conversion.for("dataDowncast").add(function(e){return t=>{t.on("insert:caption",((t,i,n)=>{const{consumable:a,writer:o,mapper:s}=n;if(!r(i.item.parent)||!a.consume(i.item,"insert"))return;const l=e.model.createRangeIn(i.item),d=o.createDocumentFragment();s.bindElements(i.item,d);for(const{item:t}of Array.from(l)){const i={item:t,range:e.model.createRangeOn(t)},a=`insert:${t.name||"$text"}`;e.data.downcastDispatcher.fire(a,i,n);for(const a of t.getAttributeKeys())Object.assign(i,{attributeKey:a,attributeOldValue:null,attributeNewValue:i.item.getAttribute(a)}),e.data.downcastDispatcher.fire(`attribute:${a}`,i,n)}for(const e of o.createRangeIn(d).getItems())s.unbindViewElement(e);s.unbindViewElement(d);const u=e.data.processor.toData(d);if(u){const e=s.toViewElement(i.item.parent);o.setAttribute("data-caption",u,e)}}))}}(e))}_getSavedCaption(e){const t=this._savedCaptionsMap.get(e);return t?I.Element.fromJSON(t):null}_saveCaption(e,t){this._savedCaptionsMap.set(e,t.toJSON())}}class de extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionUI"}init(){const{editor:t}=this,i=t.editing.view;t.ui.componentFactory.add("toggleDrupalMediaCaption",(n=>{const a=new f.ButtonView(n),r=t.commands.get("toggleMediaCaption");return a.set({label:Drupal.t("Caption media"),icon:e.icons.caption,tooltip:!0,isToggleable:!0}),a.bind("isOn","isEnabled").to(r,"value","isEnabled"),a.bind("label").to(r,"value",(e=>e?Drupal.t("Toggle caption off"):Drupal.t("Toggle caption on"))),this.listenTo(a,"execute",(()=>{t.execute("toggleMediaCaption",{focusCaptionOnShow:!0});const e=re(t.model.document.selection);if(e){const n=t.editing.mapper.toViewElement(e);i.scrollToTheSelection(),i.change((e=>{e.addClass("drupal-media__caption_highlighted",n)}))}t.editing.view.focus()})),a}))}}class ue extends e.Plugin{static get requires(){return[le,de]}static get pluginName(){return"DrupalMediaCaption"}}const ce={DrupalMedia:V,MediaImageTextAlternative:D,MediaImageTextAlternativeEditing:E,MediaImageTextAlternativeUi:A,DrupalLinkMedia:B,DrupalMediaCaption:ue,DrupalElementStyle:ae}})(),n=n.default})())); -\ No newline at end of file -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js -index f1268afdab03696d93b1c76fc450f0699a8f8b13..a04133fba7731b36fb0585c3f9ea9ad5ba032883 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js -@@ -51,7 +51,7 @@ export default class ToggleDrupalMediaCaptionCommand extends Command { - if (!selectedElement) { - // Command should be enabled if `<drupalMedia>` element is part of the - // selection. -- this.isEnabled = !!getClosestSelectedDrupalMediaElement(selection); -+ this.isEnabled = !!getClosestSelectedDrupalMediaElement(selection, false); - // Check if the selection descends from a `<drupalMedia>` element that - // also includes a `<caption>`. - this.value = !!getMediaCaptionFromModelSelection(selection); -@@ -60,7 +60,7 @@ export default class ToggleDrupalMediaCaptionCommand extends Command { - } - - // If single element is selected, check if it's a `<drupalMedia>` element. -- this.isEnabled = isDrupalMedia(selectedElement); -+ this.isEnabled = isDrupalMedia(selectedElement, false); - - if (!this.isEnabled) { - this.value = false; -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js -index 9b9b582482a7f90b25e75d3d32e535a9651e00ba..34aef3fa935b29f02fab048609fd0a83e789dc59 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js -@@ -210,6 +210,13 @@ export default class DrupalMediaEditing extends Plugin { - inheritAllFrom: '$blockObject', - allowAttributes: Object.keys(this.attrs), - }); -+ -+ schema.register('drupalMediaInline', { -+ inheritAllFrom: '$inlineObject', -+ allowIn: ['$block', '$container', '$text'], -+ allowAttributes: Object.keys(this.attrs), -+ }); -+ - // Register `<drupal-media>` as a block element in the DOM converter. This - // ensures that the DOM converter knows to handle the `<drupal-media>` as a - // block element. -@@ -227,212 +234,228 @@ export default class DrupalMediaEditing extends Plugin { - 'DrupalMediaMetadataRepository', - ); - -- conversion -- .for('upcast') -- .elementToElement({ -+ const viewToModelMap = { -+ 'drupal-media': 'drupalMedia', -+ 'drupal-media-inline': 'drupalMediaInline', -+ }; -+ -+ Object.keys(viewToModelMap).forEach((view) => { -+ const model = viewToModelMap[view]; -+ -+ conversion -+ .for('upcast') -+ .elementToElement({ -+ view: { -+ name: view, -+ }, -+ model, -+ }) -+ .add((dispatcher) => { -+ dispatcher.on( -+ `element:${view}`, -+ (evt, data) => { -+ const [modelElement] = data.modelRange.getItems(); -+ metadataRepository -+ .getMetadata(modelElement) -+ .then((metadata) => { -+ if (!modelElement) { -+ return; -+ } -+ // On upcast, get `drupalMediaIsImage` attribute value from media metadata -+ // repository. -+ this.upcastDrupalMediaIsImage(modelElement); -+ // Enqueue a model change after getting modelElement. -+ this.editor.model.enqueueChange( -+ { isUndoable: false }, -+ (writer) => { -+ writer.setAttribute( -+ 'drupalMediaType', -+ metadata.type, -+ modelElement, -+ ); -+ }, -+ ); -+ }) -+ .catch((e) => { -+ // There isn't any UI indication for errors because this should be -+ // always called after the Drupal Media has been upcast, which would -+ // already display an error in the UI. -+ console.warn(e.toString()); -+ }); -+ }, -+ // This converter needs to have the lowest priority to ensure that the -+ // model element and its attributes have already been converted. It is only used -+ // to gather metadata to make the UI tailored to the specific media entity that -+ // is being dealt with. -+ { priority: 'lowest' }, -+ ); -+ }); -+ -+ conversion.for('dataDowncast').elementToElement({ -+ model, - view: { -- name: 'drupal-media', -+ name: view, - }, -- model: 'drupalMedia', -- }) -- .add((dispatcher) => { -- dispatcher.on( -- 'element:drupal-media', -- (evt, data) => { -- const [modelElement] = data.modelRange.getItems(); -- metadataRepository -- .getMetadata(modelElement) -- .then((metadata) => { -- if (!modelElement) { -- return; -- } -- // On upcast, get `drupalMediaIsImage` attribute value from media metadata -- // repository. -- this.upcastDrupalMediaIsImage(modelElement); -- // Enqueue a model change after getting modelElement. -- this.editor.model.enqueueChange( -- { isUndoable: false }, -- (writer) => { -- writer.setAttribute( -- 'drupalMediaType', -- metadata.type, -- modelElement, -- ); -- }, -- ); -- }) -- .catch((e) => { -- // There isn't any UI indication for errors because this should be -- // always called after the Drupal Media has been upcast, which would -- // already display an error in the UI. -- console.warn(e.toString()); -- }); -- }, -- // This converter needs to have the lowest priority to ensure that the -- // model element and its attributes have already been converted. It is only used -- // to gather metadata to make the UI tailored to the specific media entity that -- // is being dealt with. -- { priority: 'lowest' }, -- ); - }); -- -- conversion.for('dataDowncast').elementToElement({ -- model: 'drupalMedia', -- view: { -- name: 'drupal-media', -- }, -- }); -- conversion -- .for('editingDowncast') -- .elementToElement({ -- model: 'drupalMedia', -- view: (modelElement, { writer }) => { -- const container = writer.createContainerElement('figure', { -- class: 'drupal-media', -- }); -- if (!this.previewUrl) { -- // If preview URL isn't available, insert empty preview element -- // which indicates that preview couldn't be loaded. -- const mediaPreview = writer.createRawElement('div', { -- 'data-drupal-media-preview': 'unavailable', -+ conversion -+ .for('editingDowncast') -+ .elementToElement({ -+ model, -+ view: (modelElement, { writer }) => { -+ const container = writer.createContainerElement('figure', { -+ class: -+ view === 'drupal-media-inline' ? `drupal-media ${view}` : view, - }); -- writer.insert(writer.createPositionAt(container, 0), mediaPreview); -- } -- writer.setCustomProperty('drupalMedia', true, container); -- -- return toWidget(container, writer, { -- label: Drupal.t('Media widget'), -- }); -- }, -- }) -- .add((dispatcher) => { -- const converter = (event, data, conversionApi) => { -- const viewWriter = conversionApi.writer; -- const modelElement = data.item; -- const container = conversionApi.mapper.toViewElement(data.item); -- -- // Search for preview container recursively from its children because -- // the preview container could be wrapped with an element such as -- // `<a>`. -- let media = getPreviewContainer(container.getChildren()); -- -- // Use pre-existing media preview container if one exists. If the -- // preview element doesn't exist, create a new element. -- if (media) { -- // Stop processing if media preview is unavailable or a preview is -- // already loading. -- if (media.getAttribute('data-drupal-media-preview') !== 'ready') { -- return; -+ if (!this.previewUrl) { -+ // If preview URL isn't available, insert empty preview element -+ // which indicates that preview couldn't be loaded. -+ const mediaPreview = writer.createRawElement('div', { -+ 'data-drupal-media-preview': 'unavailable', -+ }); -+ writer.insert( -+ writer.createPositionAt(container, 0), -+ mediaPreview, -+ ); - } -+ writer.setCustomProperty(model, true, container); - -- // Preview was ready meaning that a new preview can be loaded. -- // "Change the attribute to loading to prepare for the loading of -- // the updated preview. Preview is kept intact so that it remains -- // interactable in the UI until the new preview has been rendered. -- viewWriter.setAttribute( -- 'data-drupal-media-preview', -- 'loading', -- media, -- ); -- } else { -- media = viewWriter.createRawElement('div', { -- 'data-drupal-media-preview': 'loading', -+ return toWidget(container, writer, { -+ label: Drupal.t('Media widget'), - }); -- viewWriter.insert(viewWriter.createPositionAt(container, 0), media); -- } -- -- this._fetchPreview(modelElement).then(({ label, preview }) => { -- if (!media) { -- // Nothing to do if associated preview wrapped no longer exist. -- return; -- } -- // CKEditor 5 doesn't support async view conversion. Therefore, once -- // the promise is fulfilled, the editing view needs to be modified -- // manually. -- this.editor.editing.view.change((writer) => { -- const mediaPreview = writer.createRawElement( -- 'div', -- { 'data-drupal-media-preview': 'ready', 'aria-label': label }, -- (domElement) => { -- domElement.innerHTML = preview; -- }, -+ }, -+ }) -+ .add((dispatcher) => { -+ const converter = (event, data, conversionApi) => { -+ const viewWriter = conversionApi.writer; -+ const modelElement = data.item; -+ const container = conversionApi.mapper.toViewElement(data.item); -+ -+ // Search for preview container recursively from its children because -+ // the preview container could be wrapped with an element such as -+ // `<a>`. -+ let media = getPreviewContainer(container.getChildren()); -+ -+ // Use pre-existing media preview container if one exists. If the -+ // preview element doesn't exist, create a new element. -+ if (media) { -+ // Stop processing if media preview is unavailable or a preview is -+ // already loading. -+ if (media.getAttribute('data-drupal-media-preview') !== 'ready') { -+ return; -+ } -+ -+ // Preview was ready meaning that a new preview can be loaded. -+ // "Change the attribute to loading to prepare for the loading of -+ // the updated preview. Preview is kept intact so that it remains -+ // interactable in the UI until the new preview has been rendered. -+ viewWriter.setAttribute( -+ 'data-drupal-media-preview', -+ 'loading', -+ media, - ); -- // Insert the new preview before the previous preview element to -- // ensure that the location remains same even if it is wrapped -- // with another element. -- writer.insert(writer.createPositionBefore(media), mediaPreview); -- writer.remove(media); -+ } else { -+ media = viewWriter.createRawElement('div', { -+ 'data-drupal-media-preview': 'loading', -+ }); -+ viewWriter.insert( -+ viewWriter.createPositionAt(container, 0), -+ media, -+ ); -+ } -+ -+ this._fetchPreview(modelElement).then(({ label, preview }) => { -+ if (!media) { -+ // Nothing to do if associated preview wrapped no longer exist. -+ return; -+ } -+ // CKEditor 5 doesn't support async view conversion. Therefore, once -+ // the promise is fulfilled, the editing view needs to be modified -+ // manually. -+ this.editor.editing.view.change((writer) => { -+ const mediaPreview = writer.createRawElement( -+ 'div', -+ { 'data-drupal-media-preview': 'ready', 'aria-label': label }, -+ (domElement) => { -+ domElement.innerHTML = preview; -+ }, -+ ); -+ // Insert the new preview before the previous preview element to -+ // ensure that the location remains same even if it is wrapped -+ // with another element. -+ writer.insert(writer.createPositionBefore(media), mediaPreview); -+ writer.remove(media); -+ }); - }); -+ }; -+ -+ // List all attributes that should trigger re-rendering of the -+ // preview. -+ this.converterAttributes.forEach((attribute) => { -+ dispatcher.on(`attribute:${attribute}:${model}`, converter); - }); -- }; - -- // List all attributes that should trigger re-rendering of the -- // preview. -- this.converterAttributes.forEach((attribute) => { -- dispatcher.on(`attribute:${attribute}:drupalMedia`, converter); -+ return dispatcher; - }); - -- return dispatcher; -- }); -+ conversion.for('editingDowncast').add((dispatcher) => { -+ dispatcher.on( -+ `attribute:drupalElementStyleAlign:${model}`, -+ (evt, data, conversionApi) => { -+ const alignMapping = { -+ // This is a map of CSS classes representing Drupal element styles for alignments. -+ left: 'drupal-media-style-align-left', -+ right: 'drupal-media-style-align-right', -+ center: 'drupal-media-style-align-center', -+ }; -+ const viewElement = conversionApi.mapper.toViewElement(data.item); -+ const viewWriter = conversionApi.writer; -+ -+ // If the prior value is alignment related, it should be removed -+ // whether or not the module property is consumed. -+ if (alignMapping[data.attributeOldValue]) { -+ viewWriter.removeClass( -+ alignMapping[data.attributeOldValue], -+ viewElement, -+ ); -+ } - -- conversion.for('editingDowncast').add((dispatcher) => { -- dispatcher.on( -- 'attribute:drupalElementStyleAlign:drupalMedia', -- (evt, data, conversionApi) => { -- const alignMapping = { -- // This is a map of CSS classes representing Drupal element styles for alignments. -- left: 'drupal-media-style-align-left', -- right: 'drupal-media-style-align-right', -- center: 'drupal-media-style-align-center', -- }; -- const viewElement = conversionApi.mapper.toViewElement(data.item); -- const viewWriter = conversionApi.writer; -- -- // If the prior value is alignment related, it should be removed -- // whether or not the module property is consumed. -- if (alignMapping[data.attributeOldValue]) { -- viewWriter.removeClass( -- alignMapping[data.attributeOldValue], -+ // If the new value is not alignment related, do not proceed. -+ if (!alignMapping[data.attributeNewValue]) { -+ return; -+ } -+ -+ // The model property is already consumed, do not proceed. -+ if (!conversionApi.consumable.consume(data.item, evt.name)) { -+ return; -+ } -+ -+ // Add the alignment class in the view that corresponds to the value -+ // of the model's drupalElementStyle property. -+ viewWriter.addClass( -+ alignMapping[data.attributeNewValue], - viewElement, - ); -- } -- -- // If the new value is not alignment related, do not proceed. -- if (!alignMapping[data.attributeNewValue]) { -- return; -- } -- -- // The model property is already consumed, do not proceed. -- if (!conversionApi.consumable.consume(data.item, evt.name)) { -- return; -- } -- -- // Add the alignment class in the view that corresponds to the value -- // of the model's drupalElementStyle property. -- viewWriter.addClass( -- alignMapping[data.attributeNewValue], -- viewElement, -- ); -- }, -- ); -- }); -+ }, -+ ); -+ }); - -- // Set attributeToAttribute conversion for all supported attributes. -- Object.keys(this.attrs).forEach((modelKey) => { -- const attributeMapping = { -- model: { -- key: modelKey, -- name: 'drupalMedia', -- }, -- view: { -- name: 'drupal-media', -- key: this.attrs[modelKey], -- }, -- }; -- // Attributes should be rendered only in dataDowncast to avoid having -- // unfiltered data-attributes on the Drupal Media widget. -- conversion.for('dataDowncast').attributeToAttribute(attributeMapping); -- conversion.for('upcast').attributeToAttribute(attributeMapping); -+ // Set attributeToAttribute conversion for all supported attributes. -+ Object.keys(this.attrs).forEach((modelKey) => { -+ const attributeMapping = { -+ model: { -+ key: modelKey, -+ name: model, -+ }, -+ view: { -+ name: view, -+ key: this.attrs[modelKey], -+ }, -+ }; -+ // Attributes should be rendered only in dataDowncast to avoid having -+ // unfiltered data-attributes on the Drupal Media widget. -+ conversion.for('dataDowncast').attributeToAttribute(attributeMapping); -+ conversion.for('upcast').attributeToAttribute(attributeMapping); -+ }); - }); - } - -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js -index 66ca7dbee62ee9bb97f9bed0f95a2ec718903f18..d65914e909bb479bf859a0bec9f0c1c049f6fdfc 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js -@@ -5,18 +5,22 @@ import { setViewAttributes } from '@ckeditor/ckeditor5-html-support/src/utils'; - - /** - * View-to-model conversion helper for Drupal Media. -- * Used for preserving allowed attributes on the Drupal Media model. -+ * Used for preserving allowed attributes on the Drupal Media models. - * -+ * @param {string} model -+ * The model name (DrupalMedia or DrupalMediaInline). -+ * @param {string} view -+ * The view name (drupal-media or drupal-media-inline). - * @param {module:html-support/datafilter~DataFilter} dataFilter - * The General HTML support data filter. - * - * @return {function} - * Function that adds an event listener to upcastDispatcher. - */ --function viewToModelDrupalMediaAttributeConverter(dataFilter) { -+function viewToModelDrupalMediaAttributeConverter(dataFilter, model, view) { - return (dispatcher) => { - dispatcher.on( -- 'element:drupal-media', -+ `element:${view}`, - (evt, data, conversionApi) => { - function preserveElementAttributes(viewElement, attributeName) { - const viewAttributes = dataFilter.processViewAttributes( -@@ -96,18 +100,21 @@ function modelToDataAttributeConverter(evt, data, conversionApi) { - /** - * Model to editing view attribute converter. - * -+ * @param {string} model -+ * The model name (DrupalMedia or DrupalMediaInline). -+ * - * @return {function} - * A function that adds an event listener to downcastDispatcher. - */ --function modelToEditingViewAttributeConverter() { -+function modelToEditingViewAttributeConverter(model) { - return (dispatcher) => { - dispatcher.on( -- 'attribute:linkHref:drupalMedia', -+ `attribute:linkHref:${model}`, - (evt, data, conversionApi) => { - if ( - !conversionApi.consumable.consume( - data.item, -- 'attribute:htmlLinkAttributes:drupalMedia', -+ `attribute:htmlLinkAttributes:${model}`, - ) - ) { - return; -@@ -134,18 +141,21 @@ function modelToEditingViewAttributeConverter() { - /** - * Model to data view attribute converter. - * -+ * @param {string} model -+ * The model name (DrupalMedia or DrupalMediaInline). -+ * - * @return {function} - * Function that adds an event listener to downcastDispatcher. - */ --function modelToDataViewAttributeConverter() { -+function modelToDataViewAttributeConverter(model) { - return (dispatcher) => { - dispatcher.on( -- 'attribute:linkHref:drupalMedia', -+ `attribute:linkHref:${model}`, - (evt, data, conversionApi) => { - if ( - !conversionApi.consumable.consume( - data.item, -- 'attribute:htmlLinkAttributes:drupalMedia', -+ `attribute:htmlLinkAttributes:${model}`, - ) - ) { - return; -@@ -163,7 +173,7 @@ function modelToDataViewAttributeConverter() { - ); - - dispatcher.on( -- 'attribute:htmlAttributes:drupalMedia', -+ `attribute:htmlAttributes:${model}`, - modelToDataAttributeConverter, - { priority: 'low' }, - ); -@@ -204,32 +214,45 @@ export default class DrupalMediaGeneralHtmlSupport extends Plugin { - const dataFilter = this.editor.plugins.get('DataFilter'); - const dataSchema = this.editor.plugins.get('DataSchema'); - -- // This needs to be initialized in ::constructor() to ensure this runs -- // before the General HTML Support has been initialized. -- // @see module:html-support/generalhtmlsupport~GeneralHtmlSupport -- dataSchema.registerBlockElement({ -- model: 'drupalMedia', -- view: 'drupal-media', -- }); -+ const viewToModelMap = { -+ 'drupal-media': 'drupalMedia', -+ 'drupal-media-inline': 'drupalMediaInline', -+ }; - -- dataFilter.on('register:drupal-media', (evt, definition) => { -- if (definition.model !== 'drupalMedia') { -- return; -- } -+ Object.keys(viewToModelMap).forEach((view) => { -+ const model = viewToModelMap[view]; - -- schema.extend('drupalMedia', { -- allowAttributes: ['htmlLinkAttributes', 'htmlAttributes'], -+ // This needs to be initialized in ::constructor() to ensure this runs -+ // before the General HTML Support has been initialized. -+ // @see module:html-support/generalhtmlsupport~GeneralHtmlSupport -+ dataSchema.registerBlockElement({ -+ model, -+ view, - }); - -- conversion -- .for('upcast') -- .add(viewToModelDrupalMediaAttributeConverter(dataFilter)); -- conversion -- .for('editingDowncast') -- .add(modelToEditingViewAttributeConverter()); -- conversion.for('dataDowncast').add(modelToDataViewAttributeConverter()); -+ dataFilter.on(`register:${view}`, (evt, definition) => { -+ if (definition.model !== model) { -+ return; -+ } - -- evt.stop(); -+ schema.extend(model, { -+ allowAttributes: ['htmlLinkAttributes', 'htmlAttributes'], -+ }); -+ -+ conversion -+ .for('upcast') -+ .add( -+ viewToModelDrupalMediaAttributeConverter(dataFilter, model, view), -+ ); -+ conversion -+ .for('editingDowncast') -+ .add(modelToEditingViewAttributeConverter(model)); -+ conversion -+ .for('dataDowncast') -+ .add(modelToDataViewAttributeConverter(model)); -+ -+ evt.stop(); -+ }); - }); - } - -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js -index bf9c6451e5330880b689017e3fcf8ade05f0f9f7..5cd6337426eb1cd499b7893dba0ccd7a9ddb921c 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js -@@ -1,15 +1,42 @@ - /* eslint-disable import/no-extraneous-dependencies */ - // cspell:ignore insertdrupalmediacommand - import { Command } from 'ckeditor5/src/core'; -+import { first } from 'ckeditor5/src/utils'; - import { groupNameToModelAttributeKey } from './utils'; - - /** - * @module drupalMedia/insertdrupalmediacommand - */ - --function createDrupalMedia(writer, attributes) { -- const drupalMedia = writer.createElement('drupalMedia', attributes); -- return drupalMedia; -+function createDrupalMedia(writer, attributes, model) { -+ return writer.createElement(model, attributes); -+} -+ -+function determineImageTypeForInsertionAtSelection(schema, selection) { -+ const firstBlock = first(selection.getSelectedBlocks()); -+ // Insert a block media if the selection is not in/on block elements or it's -+ // on a block widget. -+ if (!firstBlock || schema.isObject(firstBlock)) { -+ return 'drupalMedia'; -+ } -+ // A block image should also be inserted into an empty block element -+ // (that is not an empty list item so the list won't get split). -+ if (firstBlock.isEmpty && firstBlock.name !== 'listItem') { -+ return 'drupalMedia'; -+ } -+ // Otherwise insert an inline media. -+ return 'drupalMediaInline'; -+} -+ -+function determineImageTypeForInsertion(editor, selectable) { -+ const schema = editor.model.schema; -+ // Try to replace the selected widget (e.g. another image). -+ if (selectable.is('selection')) { -+ return determineImageTypeForInsertionAtSelection(schema, selectable); -+ } -+ return schema.checkChild(selectable, 'drupalMediaInline') -+ ? 'drupalMediaInline' -+ : 'drupalMedia'; - } - - /** -@@ -83,9 +110,14 @@ export default class InsertDrupalMediaCommand extends Command { - } - } - -+ const insertedModel = determineImageTypeForInsertion( -+ this.editor, -+ this.editor.model.document.selection, -+ ); -+ - this.editor.model.change((writer) => { - this.editor.model.insertObject( -- createDrupalMedia(writer, modelAttributes), -+ createDrupalMedia(writer, modelAttributes, insertedModel), - ); - }); - } -@@ -93,9 +125,10 @@ export default class InsertDrupalMediaCommand extends Command { - refresh() { - const model = this.editor.model; - const selection = model.document.selection; -+ const mediaModel = determineImageTypeForInsertion(this.editor, selection); - const allowedIn = model.schema.findAllowedParent( - selection.getFirstPosition(), -- 'drupalMedia', -+ mediaModel, - ); - this.isEnabled = allowedIn !== null; - } -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js -index afd77f4cd9828c77c226ddda92437775f3cd8686..180fdc3f3b2b01944f749c7788a9fed36f4e14d6 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js -@@ -7,12 +7,25 @@ import { isWidget } from 'ckeditor5/src/widget'; - * - * @param {module:engine/model/element~Element} modelElement - * The model element to be checked. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {boolean} - * A boolean indicating if the element is a drupalMedia element. - * - * @private - */ --export function isDrupalMedia(modelElement) { -+export function isDrupalMedia(modelElement, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } -+ if (includeInline) { -+ return ( -+ !!modelElement && -+ (modelElement.is('element', 'drupalMedia') || -+ modelElement.is('element', 'drupalMediaInline')) -+ ); -+ } - return !!modelElement && modelElement.is('element', 'drupalMedia'); - } - -@@ -21,12 +34,25 @@ export function isDrupalMedia(modelElement) { - * - * @param {module:engine/view/element~Element} viewElement - * The view element. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {boolean} - * A boolean indicating if the element is a <drupal-media> element. - * - * @private - */ --export function isDrupalMediaWidget(viewElement) { -+export function isDrupalMediaWidget(viewElement, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } -+ if (includeInline) { -+ return ( -+ isWidget(viewElement) && -+ (!!viewElement.getCustomProperty('drupalMedia') || -+ !!viewElement.getCustomProperty('drupalMediaInline')) -+ ); -+ } - return ( - isWidget(viewElement) && !!viewElement.getCustomProperty('drupalMedia') - ); -@@ -37,6 +63,9 @@ export function isDrupalMediaWidget(viewElement) { - * - * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection - * The current selection. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {module:engine/model/element~Element|null} - * The `drupalMedia` element which could be either the current selected an - * ancestor of the selection. Returns null if the selection has no Drupal -@@ -44,12 +73,17 @@ export function isDrupalMediaWidget(viewElement) { - * - * @private - */ --export function getClosestSelectedDrupalMediaElement(selection) { -+export function getClosestSelectedDrupalMediaElement(selection, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } - const selectedElement = selection.getSelectedElement(); - -- return isDrupalMedia(selectedElement) -+ return isDrupalMedia(selectedElement, includeInline) - ? selectedElement -- : selection.getFirstPosition().findAncestor('drupalMedia'); -+ : selection -+ .getFirstPosition() -+ .findAncestor(includeInline ? /drupalMedia(Inline)?/ : /drupalMedia/); - } - - /** -@@ -57,14 +91,20 @@ export function getClosestSelectedDrupalMediaElement(selection) { - * - * @param {module:engine/model/selection~Selection} selection - * The current selection. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {module:engine/view/element~Element|null} - * The currently selected Drupal Media widget or null. - * - * @private - */ --export function getClosestSelectedDrupalMediaWidget(selection) { -+export function getClosestSelectedDrupalMediaWidget(selection, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } - const viewElement = selection.getSelectedElement(); -- if (viewElement && isDrupalMediaWidget(viewElement)) { -+ if (viewElement && isDrupalMediaWidget(viewElement, includeInline)) { - return viewElement; - } - -@@ -76,7 +116,7 @@ export function getClosestSelectedDrupalMediaWidget(selection) { - let parent = selection.getFirstPosition().parent; - - while (parent) { -- if (parent.is('element') && isDrupalMediaWidget(parent)) { -+ if (parent.is('element') && isDrupalMediaWidget(parent, includeInline)) { - return parent; - } - -diff --git a/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php b/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php -index c6b9f345a98ab15a08e76a86bcf5d40cda93788a..dbaadb59a293caa2088cc32a101f67425420a366 100644 ---- a/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php -+++ b/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php -@@ -121,7 +121,7 @@ private function configureViewModes(EditorInterface $editor) { - 'title' => $all_view_modes[$view_mode], - 'attributeName' => 'data-view-mode', - 'attributeValue' => $view_mode, -- 'modelElements' => ['drupalMedia'], -+ 'modelElements' => ['drupalMedia', 'drupalMediaInline'], - 'modelAttributes' => [ - 'drupalMediaType' => array_keys($media_bundles), - ], -@@ -133,7 +133,7 @@ private function configureViewModes(EditorInterface $editor) { - 'title' => $all_view_modes[$view_mode], - 'attributeName' => 'data-view-mode', - 'attributeValue' => $view_mode, -- 'modelElements' => ['drupalMedia'], -+ 'modelElements' => ['drupalMedia', 'drupalMediaInline'], - 'modelAttributes' => [ - 'drupalMediaType' => $specific_bundles, - ], -@@ -196,7 +196,7 @@ public function getElementsSubset(): array { - $subset = $this->getPluginDefinition()->getElements(); - $view_mode_override_enabled = $this->getConfiguration()['allow_view_mode_override']; - if (!$view_mode_override_enabled) { -- $subset = array_diff($subset, ['<drupal-media data-view-mode>']); -+ $subset = array_diff($subset, ['<drupal-media data-view-mode>', '<drupal-media-inline data-view-mode>']); - } - return $subset; - } -diff --git a/core/modules/ckeditor5/templates/field--ckeditor-inline.html.twig b/core/modules/ckeditor5/templates/field--ckeditor-inline.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..7f0ae06f8369db15ea4b70771cacfc9a61eff771 ---- /dev/null -+++ b/core/modules/ckeditor5/templates/field--ckeditor-inline.html.twig -@@ -0,0 +1,72 @@ -+{# -+/** -+ * @file -+ * Default theme implementation for a field. -+ * -+ * To override output, copy the "field.html.twig" from the templates directory -+ * to your theme's directory and customize it, just like customizing other -+ * Drupal templates such as page.html.twig or node.html.twig. -+ * -+ * Instead of overriding the theming for all fields, you can also just override -+ * theming for a subset of fields using -+ * @link themeable Theme hook suggestions. @endlink For example, -+ * here are some theme hook suggestions that can be used for a field_foo field -+ * on an article node type: -+ * - field--node--field-foo--article.html.twig -+ * - field--node--field-foo.html.twig -+ * - field--node--article.html.twig -+ * - field--field-foo.html.twig -+ * - field--text-with-summary.html.twig -+ * - field.html.twig -+ * -+ * Available variables: -+ * - attributes: HTML attributes for the containing element. -+ * - label_hidden: Whether to show the field label or not. -+ * - title_attributes: HTML attributes for the title. -+ * - label: The label for the field. -+ * - multiple: TRUE if a field can contain multiple items. -+ * - items: List of all the field items. Each item contains: -+ * - attributes: List of HTML attributes for each item. -+ * - content: The field item's content. -+ * - entity_type: The entity type to which the field belongs. -+ * - field_name: The name of the field. -+ * - field_type: The type of the field. -+ * - label_display: The display settings for the label. -+ * -+ * @see template_preprocess_field() -+ * -+ * @ingroup themeable -+ */ -+#} -+{% -+ set title_classes = [ -+ label_display == 'visually_hidden' ? 'visually-hidden', -+ ] -+%} -+ -+{% if label_hidden %} -+ {% if multiple %} -+ <span{{ attributes }}> -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ </span> -+ {% else %} -+ {% for item in items %} -+ <span{{ attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% endif %} -+{% else %} -+ <span{{ attributes }}> -+ <span{{ title_attributes.addClass(title_classes) }}>{{ label }}</span> -+ {% if multiple %} -+ <span> -+ {% endif %} -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% if multiple %} -+ </span> -+ {% endif %} -+ </span> -+{% endif %} -diff --git a/core/modules/ckeditor5/templates/media--ckeditor-inline-embed.html.twig b/core/modules/ckeditor5/templates/media--ckeditor-inline-embed.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..18a903f24544a90cae12c4a7d2008dd6b340fba5 ---- /dev/null -+++ b/core/modules/ckeditor5/templates/media--ckeditor-inline-embed.html.twig -@@ -0,0 +1,36 @@ -+{# -+/** -+ * @file -+ * Default theme implementation to present a media item. -+ * -+ * Available variables: -+ * - media: The media item, with limited access to object properties and -+ * methods. Only method names starting with "get", "has", or "is" and -+ * a few common methods such as "id", "label", and "bundle" are available. -+ * For example: -+ * - entity.getEntityTypeId() will return the entity type ID. -+ * - entity.hasField('field_example') returns TRUE if the entity includes -+ * field_example. (This does not indicate the presence of a value in this -+ * field.) -+ * Calling other methods, such as entity.delete(), will result in -+ * an exception. -+ * See \Drupal\Core\Entity\EntityInterface for a full list of methods. -+ * - name: Name of the media item. -+ * - content: Media content. -+ * - title_prefix: Additional output populated by modules, intended to be -+ * displayed in front of the main title tag that appears in the template. -+ * - title_suffix: Additional output populated by modules, intended to be -+ * displayed after the main title tag that appears in the template. -+ * - view_mode: View mode; for example, "teaser" or "full". -+ * - attributes: HTML attributes for the containing element. -+ * - title_attributes: Same as attributes, except applied to the main title -+ * tag that appears in the template. -+ * -+ * @see template_preprocess_media() -+ * -+ * @ingroup themeable -+ */ -+#} -+<span{{ attributes }}> -+ {{ content }} -+</span> -diff --git a/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php b/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php -index 8df587a4373f2e54f6e0f54f13652330f59773e1..c9ae714e1910ce4d41c91159120ab15116989fea 100644 ---- a/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php -+++ b/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php -@@ -115,7 +115,7 @@ protected function setUp(): void { - 'status' => TRUE, - 'weight' => -10, - 'settings' => [ -- 'allowed_html' => "<p> <br> <drupal-media data-entity-type data-entity-uuid data-view-mode alt>", -+ 'allowed_html' => "<p> <br> <drupal-media data-entity-type data-entity-uuid data-view-mode alt> <drupal-media-inline data-entity-type data-entity-uuid data-view-mode alt>", - 'filter_html_help' => TRUE, - 'filter_html_nofollow' => TRUE, - ], -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php -index 65db1989a6497068867f2b4c9e3494c73c2351ba..40ed1bead31bc854920bd02a5ff206efe695bb58 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php -@@ -378,8 +378,8 @@ public function testMediaElementAllowedTags() { - $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_1]'); - $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_2]'); - -- $allowed_with_media = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode>'; -- $allowed_with_media_without_view_mode = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt>'; -+ $allowed_with_media = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode> <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode>'; -+ $allowed_with_media_without_view_mode = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt> <drupal-media-inline data-entity-type data-entity-uuid alt>'; - $page->clickLink('Media'); - $this->assertTrue($page->hasUncheckedField('editor[settings][plugins][media_media][allow_view_mode_override]')); - $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', $allowed_with_media_without_view_mode); -@@ -403,7 +403,7 @@ public function testMediaElementAllowedTags() { - // filter_align is enabled. - $page->checkField('filters[filter_align][status]'); - $assert_session->assertExpectedAjaxRequest(1); -- $this->assertEquals($this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode data-align>', $allowed_html_field->getValue()); -+ $this->assertEquals($this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode data-align> <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode data-align>', $allowed_html_field->getValue()); - - // Disable media embed. - $this->assertTrue($page->hasCheckedField('filters[media_embed][status]')); -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php -index 209b1de962c1b7f071a8cb707c6ffee04a949f81..4427a53661d33cca9a3357d1f83a267b75b5f34e 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php -@@ -41,7 +41,7 @@ public function testLinkedMediaArbitraryHtml(bool $unrestricted): void { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href data-foo> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <div data-bar>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href data-foo> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <div data-bar> <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode data-caption data-align>', - ], - ]); - } -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php -index c9aca401b65cb2cc8de02b87dde466b7d4dcd7a4..6e71a6113f0fd9fcdecfa4aa971a3f22ae388b83 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php -@@ -98,7 +98,7 @@ public function testMediaSplitList() { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <ol> <ul> <li>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <drupal-media-inline data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <ol> <ul> <li>', - ], - ]); - $filter_format->save(); -@@ -153,7 +153,7 @@ public function testMediaArbitraryHtml() { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-foo data-view-mode> <div data-bar>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-foo data-view-mode> <drupal-media-inline data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <div data-bar>', - ], - ]); - $filter_format->save(); -@@ -688,7 +688,7 @@ public function testDrupalMediaStyleWithClass() { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <h1 class> <div class> <section class> <drupal-media data-entity-type data-entity-uuid data-align data-caption data-view-mode alt class="layercake-side">', -+ 'allowed_html' => '<p> <br> <h1 class> <div class> <section class> <drupal-media data-entity-type data-entity-uuid data-align data-caption data-view-mode alt class="layercake-side"> <drupal-media-inline data-entity-type data-entity-uuid data-align data-caption data-view-mode alt>', - ], - ]); - $filter_format->save(); -@@ -1004,6 +1004,56 @@ public function testViewMode(bool $with_alignment) { - $this->assertNotEmpty($this->getBalloonButton('View Mode 4')); - } - -+ /** -+ * Tests inline embedding. -+ */ -+ public function testInlineMedia() { -+ // Reconfigure the text format to suit our needs. -+ /** @var \Drupal\filter\FilterFormatInterface $format */ -+ $format = FilterFormat::load($this->host->body->format); -+ $filter_html = $format->get('filters')['filter_html']; -+ $filter_html['settings']['allowed_html'] = $filter_html['settings']['allowed_html'] . '<ul> <li>'; -+ $format->setFilterConfig('filter_html', $filter_html); -+ $format->save(); -+ -+ // Setup the test content containing inline and normal media in the editor. -+ $assert_session = $this->assertSession(); -+ $original_value = $this->host->body->value; -+ $inline_value = \str_replace('drupal-media', 'drupal-media-inline', $original_value); -+ // @todo Captions on inline media are not supported yet. -+ $inline_value = \str_replace('data-caption="baz"', '', $inline_value); -+ $this->host->body->value = <<<END -+ <div> -+ $original_value -+ <p>This is a paragraph $inline_value with an inline media</p> -+ <ul> -+ <li>This is a list item $inline_value containing an inline media</li> -+ </ul> -+ </div> -+END; -+ $this->host->save(); -+ $this->drupalGet($this->host->toUrl('edit-form')); -+ -+ // Wait until media previews are fetched. -+ $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-widget.drupal-media-inline')); -+ $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-widget.drupal-media')); -+ -+ // Confirm that the inline media is contained as phrasing content inside -+ // flow content. -+ $assert_session->elementExists('css', 'p > .drupal-media-inline'); -+ $assert_session->elementExists('css', 'ul > li > .drupal-media-inline'); -+ // Also ensure that a media not embedded inside flow content is still -+ // rendered not using the inline templates. -+ $assert_session->elementExists('css', 'div > .drupal-media'); -+ -+ // Verify the rendered inline media is rendered with markup that is using -+ // the dedicated inline media and field templates. -+ $this->drupalGet($this->host->toUrl('canonical')); -+ $assert_session->elementExists('css', 'p > span.media-embedded-inline span img[src*="image-test.png"]'); -+ $assert_session->elementExists('css', 'ul > li > span.media-embedded-inline span img[src*="image-test.png"]'); -+ $assert_session->elementExists('css', 'div > figure.caption-drupal-media article.media img[src*="image-test.png"]'); -+ } -+ - /** - * For testing view modes in different scenarios. - */ -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php -index 3384ec7fa483a06b2b41e0209a1775c72327963d..0d8faf4931463d30bb8c760a76bc8f19bfdb7c45 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php -@@ -101,7 +101,7 @@ protected function setUp(): void { - 'filter_html' => [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-view-mode data-caption alt>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-view-mode data-caption alt> <drupal-media-inline data-entity-type data-entity-uuid data-align data-view-mode data-caption alt>', - ], - ], - 'filter_align' => ['status' => TRUE], -diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module -index 2ba66da8085e35686628703e882f4220a5f57f50..ef79b8e9614bcb9c73c3ca37b8d75392e09242a3 100644 ---- a/core/modules/filter/filter.module -+++ b/core/modules/filter/filter.module -@@ -705,7 +705,7 @@ function _filter_autop($text) { - // to avoid messing up code. We look for matched pairs and allow basic - // nesting. For example: - // "processed <pre> ignored <script> ignored </script> ignored </pre> processed" -- $chunks = preg_split('@(<!--.*?-->|</?(?:pre|script|style|object|iframe|drupal-media|svg|!--)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); -+ $chunks = preg_split('@(<!--.*?-->|</?(?:pre|script|style|object|iframe|drupal-media|drupal-media-inline|svg|!--)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - // Note: PHP ensures the array consists of alternating delimiters and literals - // and begins and ends with a literal (inserting NULL as required). - $ignore = FALSE; -diff --git a/core/modules/media/css/media.inline.css b/core/modules/media/css/media.inline.css -new file mode 100644 -index 0000000000000000000000000000000000000000..0ff40b92181fafb3edfb0d599d3e32376590f3ba ---- /dev/null -+++ b/core/modules/media/css/media.inline.css -@@ -0,0 +1,18 @@ -+/** -+ * @file -+ * Default styling for media that is embedded inline inside text, lists, etc. -+ */ -+ -+.media-embedded-inline { -+ display: inline-block; -+} -+ -+.drupal-media-inline .media-embedded-inline { -+ width: 100%; -+} -+ -+.media-embedded-inline video, -+.media-embedded-inline img, -+.media-embedded-inline audio { -+ display: inline-block; -+} -diff --git a/core/modules/media/media.libraries.yml b/core/modules/media/media.libraries.yml -index 41fc310fbb9a211b5a704715386379ef2444bc60..0e9527db1dbf30275718a68400b6c480aee5c8e4 100644 ---- a/core/modules/media/media.libraries.yml -+++ b/core/modules/media/media.libraries.yml -@@ -32,6 +32,12 @@ filter.caption: - dependencies: - - filter/caption - -+media.inline: -+ version: VERSION -+ css: -+ component: -+ css/media.inline.css: {} -+ - # Despite the name, this is actually not specific to CKEditor 4, and can be - # used by all text editor plugins. - media_embed_ckeditor_theme: -diff --git a/core/modules/media/src/Event/MediaBuildEmbedEvent.php b/core/modules/media/src/Event/MediaBuildEmbedEvent.php -new file mode 100644 -index 0000000000000000000000000000000000000000..98e5f4d5fb5e9044811810f5a013a1e838a0b6cb ---- /dev/null -+++ b/core/modules/media/src/Event/MediaBuildEmbedEvent.php -@@ -0,0 +1,95 @@ -+<?php -+ -+namespace Drupal\media\Event; -+ -+use Drupal\Component\EventDispatcher\Event; -+use Drupal\media\MediaInterface; -+ -+/** -+ * Event that gets triggered when a media is about to be embedded in ckeditor5. -+ */ -+class MediaBuildEmbedEvent extends Event { -+ -+ /** -+ * The view mode the embedded media will be rendered with. -+ * -+ * @var string -+ */ -+ protected string $viewMode; -+ -+ /** -+ * The media which will be embedded. -+ * -+ * @var \Drupal\media\MediaInterface -+ */ -+ protected MediaInterface $media; -+ -+ /** -+ * The build array of the media before it gets rendered for embedding. -+ * -+ * @var array -+ */ -+ protected array $build; -+ -+ /** -+ * The <drupal-media> DOM node that gets replaced by the embed. -+ * -+ * @var \DOMElement -+ */ -+ protected \DOMElement $node; -+ -+ /** -+ * Construct a MediaBuildEmbedEvent object. -+ * -+ * @param string $viewMode -+ * The view mode the embedded media will be rendered with. -+ * @param \Drupal\media\MediaInterface $media -+ * The media which will be embedded. -+ * @param array $build -+ * The build array of the media before it gets rendered for embedding. -+ * @param \DOMElement $node -+ * The <drupal-media> DOM node that gets replaced by the embed. -+ */ -+ public function __construct(string $viewMode, MediaInterface $media, array $build, \DOMElement $node) { -+ $this->viewMode = $viewMode; -+ $this->media = $media; -+ $this->build = $build; -+ $this->node = $node; -+ } -+ -+ /** -+ * Getter for the view mode. -+ */ -+ public function getViewMode(): string { -+ return $this->viewMode; -+ } -+ -+ /** -+ * Gets the media. -+ */ -+ public function getMedia(): MediaInterface { -+ return $this->media; -+ } -+ -+ /** -+ * Gets the build. -+ */ -+ public function getBuild(): array { -+ return $this->build; -+ } -+ -+ /** -+ * Sets the build. -+ */ -+ public function setBuild(array $build): void { -+ $this->build = $build; -+ } -+ -+ /** -+ * Gets the <drupal-media> DOM node. -+ */ -+ public function getNode(): \DOMElement { -+ return $this->node; -+ } -+ -+} -diff --git a/core/modules/media/src/Plugin/Filter/MediaEmbed.php b/core/modules/media/src/Plugin/Filter/MediaEmbed.php -index 118422bf2c28d594569aa58a806ef722e4e0c4e4..c138c2ea492cee4f1080d69afe76f007ea228091 100644 ---- a/core/modules/media/src/Plugin/Filter/MediaEmbed.php -+++ b/core/modules/media/src/Plugin/Filter/MediaEmbed.php -@@ -21,8 +21,10 @@ - use Drupal\filter\Plugin\FilterBase; - use Drupal\filter\Plugin\FilterInterface; - use Drupal\image\Plugin\Field\FieldType\ImageItem; -+use Drupal\media\Event\MediaBuildEmbedEvent; - use Drupal\media\MediaInterface; - use Symfony\Component\DependencyInjection\ContainerInterface; -+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - - /** - * Provides a filter to embed media items using a custom tag. -@@ -118,8 +120,10 @@ class MediaEmbed extends FilterBase implements ContainerFactoryPluginInterface, - * The renderer. - * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory - * The logger factory. -+ * @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $eventDispatcher -+ * The event dispatcher. - */ -- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityRepositoryInterface $entity_repository, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, EntityTypeBundleInfoInterface $bundle_info, RendererInterface $renderer, LoggerChannelFactoryInterface $logger_factory) { -+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityRepositoryInterface $entity_repository, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, EntityTypeBundleInfoInterface $bundle_info, RendererInterface $renderer, LoggerChannelFactoryInterface $logger_factory, protected EventDispatcherInterface $eventDispatcher) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->entityRepository = $entity_repository; - $this->entityTypeManager = $entity_type_manager; -@@ -142,7 +146,8 @@ public static function create(ContainerInterface $container, array $configuratio - $container->get('entity_display.repository'), - $container->get('entity_type.bundle.info'), - $container->get('renderer'), -- $container->get('logger.factory') -+ $container->get('logger.factory'), -+ $container->get('event_dispatcher'), - ); - } - -@@ -255,6 +260,7 @@ protected function renderMedia(MediaInterface $media, $view_mode, $langcode) { - // instead of only when #access allows this media to be viewed and hence - // only when media is actually rendered. - $build[':media_embed']['#attached']['library'][] = 'media/filter.caption'; -+ $build[':media_embed']['#attached']['library'][] = 'media/media.inline'; - - return $build; - } -@@ -278,18 +284,26 @@ protected function renderMissingMediaIndicator() { - public function process($text, $langcode) { - $result = new FilterProcessResult($text); - -- if (stristr($text, '<drupal-media') === FALSE) { -+ if (stristr($text, '<drupal-media') === FALSE && stristr($text, '<drupal-media-inline') === FALSE) { - return $result; - } - - $dom = Html::load($text); - $xpath = new \DOMXPath($dom); -+ $matched_attributes = '@data-entity-type="media" and normalize-space(@data-entity-uuid)!=""'; - -- foreach ($xpath->query('//drupal-media[@data-entity-type="media" and normalize-space(@data-entity-uuid)!=""]') as $node) { -+ foreach ($xpath->query('//drupal-media[' . $matched_attributes . ']|//drupal-media-inline[' . $matched_attributes . ']') as $node) { - /** @var \DOMElement $node */ - $uuid = $node->getAttribute('data-entity-uuid'); - $view_mode_id = $node->getAttribute('data-view-mode') ?: $this->settings['default_view_mode']; - -+ // Inline media needs a dedicated view mode in order to ensure rendering -+ // of valid (flow content) HTML when rendering inside flow content like -+ // <p> tags for example. -+ if ($node->tagName == 'drupal-media-inline') { -+ $view_mode_id = 'ckeditor_inline'; -+ } -+ - // Delete the consumed attributes. - $node->removeAttribute('data-entity-type'); - $node->removeAttribute('data-entity-uuid'); -@@ -314,9 +328,14 @@ public function process($text, $langcode) { - } - } - -- $build = $media && ($view_mode || $view_mode_id === EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE) -- ? $this->renderMedia($media, $view_mode_id, $langcode) -- : $this->renderMissingMediaIndicator(); -+ if ($media && ($view_mode || $view_mode_id === EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE)) { -+ $build = $this->renderMedia($media, $view_mode_id, $langcode); -+ $event = $this->eventDispatcher->dispatch(new MediaBuildEmbedEvent($view_mode_id, $media, $build, $node)); -+ $build = $event->getBuild(); -+ } -+ else { -+ $build = $this->renderMissingMediaIndicator(); -+ } - - if (empty($build['#attributes']['class'])) { - $build['#attributes']['class'] = []; -@@ -339,6 +358,10 @@ public function process($text, $langcode) { - } - } - -+ if ($node->tagName == 'drupal-media-inline') { -+ $build['#attributes']['class'][] = 'media-embedded-inline'; -+ } -+ - $this->renderIntoDomNode($build, $node, $result); - } - -diff --git a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php -index eb2d1d3bdcaa764666480b9567a2de63eb11071e..93da1306ee2b1223f8d49a83a1d6a1aa6c5b19fb 100644 ---- a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php -+++ b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php -@@ -40,7 +40,7 @@ public function testBasics(array $embed_attributes, $expected_view_mode, array $ - $this->assertEqualsCanonicalizing($expected_cacheability->getCacheContexts(), $result->getCacheContexts()); - $this->assertSame($expected_cacheability->getCacheMaxAge(), $result->getCacheMaxAge()); - $this->assertSame(['library'], array_keys($result->getAttachments())); -- $this->assertSame(['media/filter.caption'], $result->getAttachments()['library']); -+ $this->assertSame(['media/filter.caption', 'media/media.inline'], $result->getAttachments()['library']); - } - - /** -@@ -194,7 +194,7 @@ public static function providerAccessUnpublished() { - ]) - ->setCacheContexts(['timezone', 'user', 'user.permissions']) - ->setCacheMaxAge(Cache::PERMANENT), -- ['library' => ['media/filter.caption']], -+ ['library' => ['media/filter.caption', 'media/media.inline']], - ], - ]; - } -@@ -428,7 +428,7 @@ public function testFilterIntegration(array $filter_ids, array $additional_attri - * Data provider for testFilterIntegration(). - */ - public static function providerFilterIntegration() { -- $default_asset_libraries = ['media/filter.caption']; -+ $default_asset_libraries = ['media/filter.caption', 'media/media.inline']; - - $caption_additional_attributes = ['data-caption' => 'Yo.']; - $caption_verification_selector = 'figure > figcaption'; -@@ -445,14 +445,14 @@ public static function providerFilterIntegration() { - $caption_additional_attributes, - $caption_verification_selector, - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - ], - '`<a>` + `data-caption`; `filter_caption` + `media_embed` ⇒ caption present, link preserved' => [ - ['filter_caption', 'media_embed'], - $caption_additional_attributes, - 'figure > a[href="https://www.drupal.org"] + figcaption', - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - '<a href="https://www.drupal.org">', - '</a>', - ], -@@ -492,14 +492,14 @@ public static function providerFilterIntegration() { - $align_additional_attributes + $caption_additional_attributes, - 'figure.align-center > figcaption', - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - ], - '`<a>` + `data-caption` + `data-align`; `filter_align` + `filter_caption` + `media_embed` ⇒ aligned caption present, link preserved' => [ - ['filter_align', 'filter_caption', 'media_embed'], - $align_additional_attributes + $caption_additional_attributes, - 'figure.align-center > a[href="https://www.drupal.org"] + figcaption', - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - '<a href="https://www.drupal.org">', - '</a>', - ], -diff --git a/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml b/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml -index 60cd331cc875cdb4286b0be09ff99bb9c5a7db54..08518f40f78fa4f506ed54d06549cfd066db638c 100644 ---- a/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml -+++ b/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml -@@ -55,6 +55,7 @@ settings: - - '<h6 id>' - - '<img src alt data-entity-type data-entity-uuid data-align data-caption width height loading>' - - '<drupal-media title>' -+ - '<drupal-media-inline title>' - media_media: - allow_view_mode_override: true - image_upload: -diff --git a/core/profiles/demo_umami/config/install/filter.format.basic_html.yml b/core/profiles/demo_umami/config/install/filter.format.basic_html.yml -index b57e2a67a7950fe32b2a422be14c9154f510ae97..8ad1363564d2631f7265d87faaa4bff5ef00c9ba 100644 ---- a/core/profiles/demo_umami/config/install/filter.format.basic_html.yml -+++ b/core/profiles/demo_umami/config/install/filter.format.basic_html.yml -@@ -40,7 +40,7 @@ filters: - status: true - weight: -10 - settings: -- allowed_html: '<a href hreflang> <em> <strong> <cite> <blockquote cite> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <br> <img src alt loading height width data-entity-type data-entity-uuid data-align data-caption> <drupal-media data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title>' -+ allowed_html: '<a href hreflang> <em> <strong> <cite> <blockquote cite> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <br> <img src alt loading height width data-entity-type data-entity-uuid data-align data-caption> <drupal-media data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title> <drupal-media-inline data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title>' - filter_html_help: false - filter_html_nofollow: false - filter_html_image_secure: -diff --git a/core/themes/stable9/css/media/media.inline.css b/core/themes/stable9/css/media/media.inline.css -new file mode 100644 -index 0000000000000000000000000000000000000000..0ff40b92181fafb3edfb0d599d3e32376590f3ba ---- /dev/null -+++ b/core/themes/stable9/css/media/media.inline.css -@@ -0,0 +1,18 @@ -+/** -+ * @file -+ * Default styling for media that is embedded inline inside text, lists, etc. -+ */ -+ -+.media-embedded-inline { -+ display: inline-block; -+} -+ -+.drupal-media-inline .media-embedded-inline { -+ width: 100%; -+} -+ -+.media-embedded-inline video, -+.media-embedded-inline img, -+.media-embedded-inline audio { -+ display: inline-block; -+} -diff --git a/core/themes/stable9/stable9.info.yml b/core/themes/stable9/stable9.info.yml -index cace78bf8a8251b94b68a20f488824a9d9019f8b..4f79c0a60f5bb6ad85714643183a829a3aa02f04 100644 ---- a/core/themes/stable9/stable9.info.yml -+++ b/core/themes/stable9/stable9.info.yml -@@ -177,6 +177,11 @@ libraries-override: - component: - css/filter.caption.css: css/media/filter.caption.css - -+ media/media.inline: -+ css: -+ component: -+ css/media.inline.css: css/media/media.inline.css -+ - media/oembed.formatter: - css: - component: -diff --git a/core/themes/stable9/templates/content/media--ckeditor-inline-embed.html.twig b/core/themes/stable9/templates/content/media--ckeditor-inline-embed.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..18a903f24544a90cae12c4a7d2008dd6b340fba5 ---- /dev/null -+++ b/core/themes/stable9/templates/content/media--ckeditor-inline-embed.html.twig -@@ -0,0 +1,36 @@ -+{# -+/** -+ * @file -+ * Default theme implementation to present a media item. -+ * -+ * Available variables: -+ * - media: The media item, with limited access to object properties and -+ * methods. Only method names starting with "get", "has", or "is" and -+ * a few common methods such as "id", "label", and "bundle" are available. -+ * For example: -+ * - entity.getEntityTypeId() will return the entity type ID. -+ * - entity.hasField('field_example') returns TRUE if the entity includes -+ * field_example. (This does not indicate the presence of a value in this -+ * field.) -+ * Calling other methods, such as entity.delete(), will result in -+ * an exception. -+ * See \Drupal\Core\Entity\EntityInterface for a full list of methods. -+ * - name: Name of the media item. -+ * - content: Media content. -+ * - title_prefix: Additional output populated by modules, intended to be -+ * displayed in front of the main title tag that appears in the template. -+ * - title_suffix: Additional output populated by modules, intended to be -+ * displayed after the main title tag that appears in the template. -+ * - view_mode: View mode; for example, "teaser" or "full". -+ * - attributes: HTML attributes for the containing element. -+ * - title_attributes: Same as attributes, except applied to the main title -+ * tag that appears in the template. -+ * -+ * @see template_preprocess_media() -+ * -+ * @ingroup themeable -+ */ -+#} -+<span{{ attributes }}> -+ {{ content }} -+</span> -diff --git a/core/themes/stable9/templates/field/field--ckeditor-inline.html.twig b/core/themes/stable9/templates/field/field--ckeditor-inline.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..7f0ae06f8369db15ea4b70771cacfc9a61eff771 ---- /dev/null -+++ b/core/themes/stable9/templates/field/field--ckeditor-inline.html.twig -@@ -0,0 +1,72 @@ -+{# -+/** -+ * @file -+ * Default theme implementation for a field. -+ * -+ * To override output, copy the "field.html.twig" from the templates directory -+ * to your theme's directory and customize it, just like customizing other -+ * Drupal templates such as page.html.twig or node.html.twig. -+ * -+ * Instead of overriding the theming for all fields, you can also just override -+ * theming for a subset of fields using -+ * @link themeable Theme hook suggestions. @endlink For example, -+ * here are some theme hook suggestions that can be used for a field_foo field -+ * on an article node type: -+ * - field--node--field-foo--article.html.twig -+ * - field--node--field-foo.html.twig -+ * - field--node--article.html.twig -+ * - field--field-foo.html.twig -+ * - field--text-with-summary.html.twig -+ * - field.html.twig -+ * -+ * Available variables: -+ * - attributes: HTML attributes for the containing element. -+ * - label_hidden: Whether to show the field label or not. -+ * - title_attributes: HTML attributes for the title. -+ * - label: The label for the field. -+ * - multiple: TRUE if a field can contain multiple items. -+ * - items: List of all the field items. Each item contains: -+ * - attributes: List of HTML attributes for each item. -+ * - content: The field item's content. -+ * - entity_type: The entity type to which the field belongs. -+ * - field_name: The name of the field. -+ * - field_type: The type of the field. -+ * - label_display: The display settings for the label. -+ * -+ * @see template_preprocess_field() -+ * -+ * @ingroup themeable -+ */ -+#} -+{% -+ set title_classes = [ -+ label_display == 'visually_hidden' ? 'visually-hidden', -+ ] -+%} -+ -+{% if label_hidden %} -+ {% if multiple %} -+ <span{{ attributes }}> -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ </span> -+ {% else %} -+ {% for item in items %} -+ <span{{ attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% endif %} -+{% else %} -+ <span{{ attributes }}> -+ <span{{ title_attributes.addClass(title_classes) }}>{{ label }}</span> -+ {% if multiple %} -+ <span> -+ {% endif %} -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% if multiple %} -+ </span> -+ {% endif %} -+ </span> -+{% endif %} diff --git a/patches/d10/5758-2.diff b/patches/d10/5758-2.diff deleted file mode 100644 index 58d081e94a52c1323d10e0a08fbeb4d547243208..0000000000000000000000000000000000000000 --- a/patches/d10/5758-2.diff +++ /dev/null @@ -1,1794 +0,0 @@ -diff --git a/core/modules/ckeditor5/ckeditor5.ckeditor5.yml b/core/modules/ckeditor5/ckeditor5.ckeditor5.yml -index 2a2c3fd165877ef2d3790dfd63779104c425d13e..3c2f61d05bd7575d4e8b68c6c5e01c1eb37ca82c 100644 ---- a/core/modules/ckeditor5/ckeditor5.ckeditor5.yml -+++ b/core/modules/ckeditor5/ckeditor5.ckeditor5.yml -@@ -742,6 +742,9 @@ media_media: - - <drupal-media> - - <drupal-media data-entity-type data-entity-uuid alt> - - <drupal-media data-view-mode> -+ - <drupal-media-inline> -+ - <drupal-media-inline data-entity-type data-entity-uuid alt> -+ - <drupal-media-inline data-view-mode> - conditions: - filter: media_embed - -@@ -756,6 +759,7 @@ ckeditor5_drupalMediaCaption: - label: Media caption - elements: - - <drupal-media data-caption> -+ - <drupal-media-inline data-caption> - conditions: - filter: filter_caption - plugins: -@@ -805,6 +809,7 @@ media_mediaAlign: - library: ckeditor5/internal.drupal.ckeditor5.mediaAlign - elements: - - <drupal-media data-align> -+ - <drupal-media-inline data-align> - conditions: - filter: filter_align - plugins: [media_media] -diff --git a/core/modules/ckeditor5/ckeditor5.module b/core/modules/ckeditor5/ckeditor5.module -index caa6f3f50dabd92c2aa11087b2b29dd9f1720417..349beb67ac4a3c8a5d9050572674df7ed33686dc 100644 ---- a/core/modules/ckeditor5/ckeditor5.module -+++ b/core/modules/ckeditor5/ckeditor5.module -@@ -17,6 +17,42 @@ - use Drupal\Core\Ajax\RemoveCommand; - use Drupal\Core\Form\FormStateInterface; - -+/** -+ * Implements hook_theme(). -+ */ -+function ckeditor5_theme() { -+ return [ -+ 'field__ckeditor_inline' => [ -+ 'base hook' => 'field', -+ ], -+ 'media__ckeditor_inline_embed' => [ -+ 'base hook' => 'media', -+ ], -+ ]; -+} -+ -+/** -+ * Implements hook_theme_suggestions_HOOK_alter(). -+ */ -+function ckeditor5_theme_suggestions_media_alter(array &$suggestions, array $variables) { -+ if ($variables['elements']['#view_mode'] == 'ckeditor_inline') { -+ $suggestions[] = 'media__ckeditor_inline_embed'; -+ } -+} -+ -+/** -+ * Implements hook_preprocess_HOOK(). -+ */ -+function ckeditor5_preprocess_media(&$variables) { -+ if ($variables['view_mode'] == 'ckeditor_inline') { -+ foreach ($variables['content'] as &$element) { -+ if (!empty($element['#theme']) && $element['#theme'] == 'field') { -+ $element['#theme'] = 'field__ckeditor_inline'; -+ } -+ } -+ } -+} -+ - /** - * Implements hook_module_implements_alter(). - */ -diff --git a/core/modules/ckeditor5/ckeditor5.post_update.php b/core/modules/ckeditor5/ckeditor5.post_update.php -index db795a147d0982dfc640e013348a31b86f00d40f..265d0a65700ca8d32b55c84b41787fbb2ee7f37d 100644 ---- a/core/modules/ckeditor5/ckeditor5.post_update.php -+++ b/core/modules/ckeditor5/ckeditor5.post_update.php -@@ -5,6 +5,8 @@ - * Post update functions for CKEditor 5. - */ - -+use Drupal\Core\Config\FileStorage; -+ - // cspell:ignore multiblock - - /** -@@ -20,3 +22,39 @@ function ckeditor5_removed_post_updates(): array { - 'ckeditor5_post_update_list_start_reversed' => '11.0.0', - ]; - } -+ -+/** -+ * Creates the ckeditor_inline view mode for media entities. -+ */ -+function ckeditor5_post_update_create_ckeditor_inline_view_mode() { -+ $config_path = \Drupal::service('extension.list.module')->getPath('ckeditor5') . '/config/optional'; -+ $source = new FileStorage($config_path); -+ $entity_type_manager = \Drupal::entityTypeManager(); -+ $module_handler = \Drupal::moduleHandler(); -+ /** @var \Drupal\Core\Config\StorageInterface $config_storage */ -+ $config_storage = \Drupal::service('config.storage'); -+ -+ if ($module_handler->moduleExists('media') && !$config_storage->exists('core.entity_view_mode.media.ckeditor_inline')) { -+ $entity_type_manager->getStorage('entity_view_mode') -+ ->create($source->read('core.entity_view_mode.media.ckeditor_inline')) -+ ->save(); -+ } -+} -+ -+/** -+ * Amends the filter format settings to allow the drupal-media-inline tag. -+ */ -+function ckeditor5_post_update_allow_inline_media_element() { -+ $formats = filter_formats(); -+ foreach ($formats as $format_id => $format) { -+ $config = \Drupal::configFactory()->getEditable('filter.format.' . $format_id); -+ $filters = $config->get('filters'); -+ if (\array_key_exists('media_embed', $filters) -+ && \array_key_exists('filter_html', $filters) -+ && !\str_contains($filters['filter_html']['settings']['allowed_html'], 'drupal-media-inline')) { -+ $filters['filter_html']['settings']['allowed_html'] .= ' <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode data-caption data-align>'; -+ $config->set('filters', $filters); -+ $config->save(); -+ } -+ } -+} -diff --git a/core/modules/ckeditor5/config/optional/core.entity_view_mode.media.ckeditor_inline.yml b/core/modules/ckeditor5/config/optional/core.entity_view_mode.media.ckeditor_inline.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..e659b44337d5e3f03c4720bbe63f7446d6536bbf ---- /dev/null -+++ b/core/modules/ckeditor5/config/optional/core.entity_view_mode.media.ckeditor_inline.yml -@@ -0,0 +1,10 @@ -+langcode: en -+status: true -+dependencies: -+ module: -+ - media -+id: media.ckeditor_inline -+label: 'Ckeditor Inline' -+description: 'View mode used for media embedded inline' -+targetEntityType: media -+cache: true -diff --git a/core/modules/ckeditor5/css/drupalmedia.css b/core/modules/ckeditor5/css/drupalmedia.css -index 07ce32c8fedced313ceb59b23bc7895af290e604..03b7a3c0e6826c14a537e8aacb344b5a77a48a22 100644 ---- a/core/modules/ckeditor5/css/drupalmedia.css -+++ b/core/modules/ckeditor5/css/drupalmedia.css -@@ -13,6 +13,12 @@ - min-width: 50px; - } - -+.ck .drupal-media.drupal-media-inline { -+ display: inline-block; -+ clear: initial; -+ margin: 0; -+} -+ - .ck .drupal-media [data-drupal-media-preview] { - pointer-events: none; - } -diff --git a/core/modules/ckeditor5/js/build/drupalMedia.js b/core/modules/ckeditor5/js/build/drupalMedia.js -index 36a220c9368de0f13bf8a3a9fef17293885990f6..2d7a92b119af1169de1867f6f72418f9a04c90d0 100644 ---- a/core/modules/ckeditor5/js/build/drupalMedia.js -+++ b/core/modules/ckeditor5/js/build/drupalMedia.js -@@ -1 +1 @@ --!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalMedia=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/engine.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/engine.js")},"ckeditor5/src/ui.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/ui.js")},"ckeditor5/src/utils.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/utils.js")},"ckeditor5/src/widget.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/widget.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function i(n){var a=t[n];if(void 0!==a)return a.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,i),r.exports}i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{"use strict";i.d(n,{default:()=>ne});var e=i("ckeditor5/src/core.js"),t=i("ckeditor5/src/widget.js");function a(e){return!!e&&e.is("element","drupalMedia")}function r(e){return(0,t.isWidget)(e)&&!!e.getCustomProperty("drupalMedia")}function o(e){const t=e.getSelectedElement();return a(t)?t:e.getFirstPosition().findAncestor("drupalMedia")}function s(e){const t=e.getSelectedElement();if(t&&r(t))return t;if(null===e.getFirstPosition())return null;let i=e.getFirstPosition().parent;for(;i;){if(i.is("element")&&r(i))return i;i=i.parent}return null}function l(e){const t=typeof e;return null!=e&&("object"===t||"function"===t)}function d(e){for(const t of e){if(t.hasAttribute("data-drupal-media-preview"))return t;if(t.childCount){const e=d(t.getChildren());if(e)return e}}return null}function u(e){return`drupalElementStyle${e[0].toUpperCase()+e.substring(1)}`}class c extends e.Command{execute(e){const t=this.editor.plugins.get("DrupalMediaEditing"),i=Object.entries(t.attrs).reduce(((e,[t,i])=>(e[i]=t,e)),{}),n=Object.keys(e).reduce(((t,n)=>(i[n]&&(t[i[n]]=e[n]),t)),{});if(this.editor.plugins.has("DrupalElementStyleEditing")){const t=this.editor.plugins.get("DrupalElementStyleEditing"),{normalizedStyles:i}=t;for(const a of Object.keys(i))for(const i of t.normalizedStyles[a])if(e[i.attributeName]&&i.attributeValue===e[i.attributeName]){const e=u(a);n[e]=i.name}}this.editor.model.change((e=>{this.editor.model.insertObject(function(e,t){return e.createElement("drupalMedia",t)}(e,n))}))}refresh(){const e=this.editor.model,t=e.document.selection,i=e.schema.findAllowedParent(t.getFirstPosition(),"drupalMedia");this.isEnabled=null!==i}}const m="METADATA_ERROR";class p extends e.Plugin{static get requires(){return[t.Widget]}constructor(e){super(e),this.attrs={drupalMediaAlt:"alt",drupalMediaEntityType:"data-entity-type",drupalMediaEntityUuid:"data-entity-uuid"},this.converterAttributes=["drupalMediaEntityUuid","drupalElementStyleViewMode","drupalMediaEntityType","drupalMediaAlt"]}init(){const e=this.editor.config.get("drupalMedia");if(!e)return;const{previewURL:t,themeError:i}=e;this.previewUrl=t,this.labelError=Drupal.t("Preview failed"),this.themeError=i||`\n <p>${Drupal.t("An error occurred while trying to preview the media. Save your work and reload this page.")}<p>\n `,this._defineSchema(),this._defineConverters(),this._defineListeners(),this.editor.commands.add("insertDrupalMedia",new c(this.editor))}upcastDrupalMediaIsImage(e){const{model:t,plugins:i}=this.editor;i.get("DrupalMediaMetadataRepository").getMetadata(e).then((i=>{e&&t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",!!i.imageSourceMetadata,e)}))})).catch((i=>{e&&(console.warn(i.toString()),t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",m,e)})))}))}upcastDrupalMediaType(e){this.editor.plugins.get("DrupalMediaMetadataRepository").getMetadata(e).then((t=>{e&&this.editor.model.enqueueChange({isUndoable:!1},(i=>{i.setAttribute("drupalMediaType",t.type,e)}))})).catch((t=>{e&&(console.warn(t.toString()),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",m,e)})))}))}async _fetchPreview(e){const t={text:this._renderElement(e),uuid:e.getAttribute("drupalMediaEntityUuid")},i=await fetch(`${this.previewUrl}?${new URLSearchParams(t)}`,{headers:{"X-Drupal-MediaPreview-CSRF-Token":this.editor.config.get("drupalMedia").previewCsrfToken}});if(i.ok){return{label:i.headers.get("drupal-media-label"),preview:await i.text()}}return{label:this.labelError,preview:this.themeError}}_defineSchema(){this.editor.model.schema.register("drupalMedia",{inheritAllFrom:"$blockObject",allowAttributes:Object.keys(this.attrs)}),this.editor.editing.view.domConverter.blockElements.push("drupal-media")}_defineConverters(){const e=this.editor.conversion,i=this.editor.plugins.get("DrupalMediaMetadataRepository");e.for("upcast").elementToElement({view:{name:"drupal-media"},model:"drupalMedia"}).add((e=>{e.on("element:drupal-media",((e,t)=>{const[n]=t.modelRange.getItems();i.getMetadata(n).then((e=>{n&&(this.upcastDrupalMediaIsImage(n),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",e.type,n)})))})).catch((e=>{console.warn(e.toString())}))}),{priority:"lowest"})})),e.for("dataDowncast").elementToElement({model:"drupalMedia",view:{name:"drupal-media"}}),e.for("editingDowncast").elementToElement({model:"drupalMedia",view:(e,{writer:i})=>{const n=i.createContainerElement("figure",{class:"drupal-media"});if(!this.previewUrl){const e=i.createRawElement("div",{"data-drupal-media-preview":"unavailable"});i.insert(i.createPositionAt(n,0),e)}return i.setCustomProperty("drupalMedia",!0,n),(0,t.toWidget)(n,i,{label:Drupal.t("Media widget")})}}).add((e=>{const t=(e,t,i)=>{const n=i.writer,a=t.item,r=i.mapper.toViewElement(t.item);let o=d(r.getChildren());if(o){if("ready"!==o.getAttribute("data-drupal-media-preview"))return;n.setAttribute("data-drupal-media-preview","loading",o)}else o=n.createRawElement("div",{"data-drupal-media-preview":"loading"}),n.insert(n.createPositionAt(r,0),o);this._fetchPreview(a).then((({label:e,preview:t})=>{o&&this.editor.editing.view.change((i=>{const n=i.createRawElement("div",{"data-drupal-media-preview":"ready","aria-label":e},(e=>{e.innerHTML=t}));i.insert(i.createPositionBefore(o),n),i.remove(o)}))}))};return this.converterAttributes.forEach((i=>{e.on(`attribute:${i}:drupalMedia`,t)})),e})),e.for("editingDowncast").add((e=>{e.on("attribute:drupalElementStyleAlign:drupalMedia",((e,t,i)=>{const n={left:"drupal-media-style-align-left",right:"drupal-media-style-align-right",center:"drupal-media-style-align-center"},a=i.mapper.toViewElement(t.item),r=i.writer;n[t.attributeOldValue]&&r.removeClass(n[t.attributeOldValue],a),n[t.attributeNewValue]&&i.consumable.consume(t.item,e.name)&&r.addClass(n[t.attributeNewValue],a)}))})),Object.keys(this.attrs).forEach((t=>{const i={model:{key:t,name:"drupalMedia"},view:{name:"drupal-media",key:this.attrs[t]}};e.for("dataDowncast").attributeToAttribute(i),e.for("upcast").attributeToAttribute(i)}))}_defineListeners(){this.editor.model.on("insertContent",((e,[t])=>{a(t)&&(this.upcastDrupalMediaIsImage(t),this.upcastDrupalMediaType(t))}))}_renderElement(e){const t=this.editor.model.change((t=>{const i=t.createDocumentFragment(),n=t.cloneElement(e,!1);return["linkHref"].forEach((e=>{t.removeAttribute(e,n)})),t.append(n,i),i}));return this.editor.data.stringify(t)}static get pluginName(){return"DrupalMediaEditing"}}var g=i("ckeditor5/src/ui.js");class h extends e.Plugin{init(){const e=this.editor,t=this.editor.config.get("drupalMedia");if(!t)return;const{libraryURL:i,openDialog:n,dialogSettings:a={}}=t;i&&"function"==typeof n&&e.ui.componentFactory.add("drupalMedia",(t=>{const r=e.commands.get("insertDrupalMedia"),o=new g.ButtonView(t);return o.set({label:Drupal.t("Insert Media"),icon:'<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M19.1873 4.86414L10.2509 6.86414V7.02335H10.2499V15.5091C9.70972 15.1961 9.01793 15.1048 8.34069 15.3136C7.12086 15.6896 6.41013 16.8967 6.75322 18.0096C7.09631 19.1226 8.3633 19.72 9.58313 19.344C10.6666 19.01 11.3484 18.0203 11.2469 17.0234H11.2499V9.80173L18.1803 8.25067V14.3868C17.6401 14.0739 16.9483 13.9825 16.2711 14.1913C15.0513 14.5674 14.3406 15.7744 14.6836 16.8875C15.0267 18.0004 16.2937 18.5978 17.5136 18.2218C18.597 17.8877 19.2788 16.8982 19.1773 15.9011H19.1803V8.02687L19.1873 8.0253V4.86414Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M13.5039 0.743652H0.386932V12.1603H13.5039V0.743652ZM12.3379 1.75842H1.55289V11.1454H1.65715L4.00622 8.86353L6.06254 10.861L9.24985 5.91309L11.3812 9.22179L11.7761 8.6676L12.3379 9.45621V1.75842ZM6.22048 4.50869C6.22048 5.58193 5.35045 6.45196 4.27722 6.45196C3.20398 6.45196 2.33395 5.58193 2.33395 4.50869C2.33395 3.43546 3.20398 2.56543 4.27722 2.56543C5.35045 2.56543 6.22048 3.43546 6.22048 4.50869Z"/></svg>\n',tooltip:!0}),o.bind("isOn","isEnabled").to(r,"value","isEnabled"),this.listenTo(o,"execute",(()=>{n(i,(({attributes:t})=>{e.execute("insertDrupalMedia",t)}),a)})),o}))}}class f extends e.Plugin{static get requires(){return[t.WidgetToolbarRepository]}static get pluginName(){return"DrupalMediaToolbar"}afterInit(){const{editor:e}=this;var i;e.plugins.get(t.WidgetToolbarRepository).register("drupalMedia",{ariaLabel:Drupal.t("Drupal Media toolbar"),items:(i=e.config.get("drupalMedia.toolbar"),i.map((e=>l(e)?e.name:e))||[]),getRelatedElement:e=>s(e)})}}class b extends e.Command{refresh(){const e=o(this.editor.model.document.selection);this.isEnabled=!!e&&e.getAttribute("drupalMediaIsImage")&&e.getAttribute("drupalMediaIsImage")!==m,this.isEnabled?this.value=e.getAttribute("drupalMediaAlt"):this.value=!1}execute(e){const{model:t}=this.editor,i=o(t.document.selection);e.newValue=e.newValue.trim(),t.change((t=>{e.newValue.length>0?t.setAttribute("drupalMediaAlt",e.newValue,i):t.removeAttribute("drupalMediaAlt",i)}))}}class w extends e.Plugin{init(){this._data=new WeakMap}getMetadata(e){if(this._data.get(e))return new Promise((t=>{t(this._data.get(e))}));const t=this.editor.config.get("drupalMedia");if(!t)return new Promise(((e,t)=>{t(new Error("drupalMedia configuration is required for parsing metadata."))}));if(!e.hasAttribute("drupalMediaEntityUuid"))return new Promise(((e,t)=>{t(new Error("drupalMedia element must have drupalMediaEntityUuid attribute to retrieve metadata."))}));const{metadataUrl:i}=t;return(async e=>{const t=await fetch(e);if(t.ok)return JSON.parse(await t.text());throw new Error("Fetching media embed metadata from the server failed.")})(`${i}&${new URLSearchParams({uuid:e.getAttribute("drupalMediaEntityUuid")})}`).then((t=>(this._data.set(e,t),t)))}static get pluginName(){return"DrupalMediaMetadataRepository"}}class y extends e.Plugin{static get requires(){return[w]}static get pluginName(){return"MediaImageTextAlternativeEditing"}init(){const{editor:e,editor:{model:t,conversion:i}}=this;t.schema.extend("drupalMedia",{allowAttributes:["drupalMediaIsImage"]}),i.for("editingDowncast").add((e=>{e.on("attribute:drupalMediaIsImage",((e,t,i)=>{const{writer:n,mapper:a}=i,r=a.toViewElement(t.item);if(t.attributeNewValue!==m){const e=Array.from(r.getChildren()).find((e=>e.getCustomProperty("drupalMediaMetadataError")));return void(e&&(n.setCustomProperty("widgetLabel",e.getCustomProperty("drupalMediaOriginalWidgetLabel"),e),n.removeElement(e)))}const o=Drupal.t("Not all functionality may be available because some information could not be retrieved."),s=new g.Template({tag:"span",children:[{tag:"span",attributes:{class:"drupal-media__metadata-error-icon","data-cke-tooltip-text":o}}]}).render(),l=n.createRawElement("div",{class:"drupal-media__metadata-error"},((e,t)=>{t.setContentOf(e,s.outerHTML)}));n.setCustomProperty("drupalMediaMetadataError",!0,l);const d=r.getCustomProperty("widgetLabel");n.setCustomProperty("drupalMediaOriginalWidgetLabel",d,l),n.setCustomProperty("widgetLabel",`${d} (${o})`,r),n.insert(n.createPositionAt(r,0),l)}),{priority:"low"})})),e.commands.add("mediaImageTextAlternative",new b(this.editor))}}function v(e){const t=e.editing.view,i=g.BalloonPanelView.defaultPositions;return{target:t.domConverter.viewToDom(t.document.selection.getSelectedElement()),positions:[i.northArrowSouth,i.northArrowSouthWest,i.northArrowSouthEast,i.southArrowNorth,i.southArrowNorthWest,i.southArrowNorthEast]}}var E=i("ckeditor5/src/utils.js");class M extends g.View{constructor(t){super(t),this.focusTracker=new E.FocusTracker,this.keystrokes=new E.KeystrokeHandler,this.decorativeToggle=this._decorativeToggleView(),this.labeledInput=this._createLabeledInputView(),this.saveButtonView=this._createButton(Drupal.t("Save"),e.icons.check,"ck-button-save"),this.saveButtonView.type="submit",this.cancelButtonView=this._createButton(Drupal.t("Cancel"),e.icons.cancel,"ck-button-cancel","cancel"),this._focusables=new g.ViewCollection,this._focusCycler=new g.FocusCycler({focusables:this._focusables,focusTracker:this.focusTracker,keystrokeHandler:this.keystrokes,actions:{focusPrevious:"shift + tab",focusNext:"tab"}}),this.setTemplate({tag:"form",attributes:{class:["ck","ck-media-alternative-text-form","ck-vertical-form"],tabindex:"-1"},children:[{tag:"div",children:[this.decorativeToggle]},this.labeledInput,this.saveButtonView,this.cancelButtonView]}),(0,g.injectCssTransitionDisabler)(this)}render(){super.render(),this.keystrokes.listenTo(this.element),(0,g.submitHandler)({view:this}),[this.decorativeToggle,this.labeledInput,this.saveButtonView,this.cancelButtonView].forEach((e=>{this._focusables.add(e),this.focusTracker.add(e.element)}))}_createButton(e,t,i,n){const a=new g.ButtonView(this.locale);return a.set({label:e,icon:t,tooltip:!0}),a.extendTemplate({attributes:{class:i}}),n&&a.delegate("execute").to(this,n),a}_createLabeledInputView(){const e=new g.LabeledFieldView(this.locale,g.createLabeledInputText);return e.bind("class").to(this.decorativeToggle,"isOn",(e=>e?"ck-hidden":"")),e.label=Drupal.t("Alternative text override"),e}_decorativeToggleView(){const e=new g.SwitchButtonView(this.locale);return e.set({withText:!0,label:Drupal.t("Decorative image")}),e.on("execute",(()=>{e.isOn&&(this.labeledInput.fieldView.element.value=""),e.set("isOn",!e.isOn)})),e}}class k extends e.Plugin{static get requires(){return[g.ContextualBalloon]}static get pluginName(){return"MediaImageTextAlternativeUi"}init(){this._createButton(),this._createForm()}destroy(){super.destroy(),this._form.destroy()}_createButton(){const t=this.editor;t.ui.componentFactory.add("mediaImageTextAlternative",(i=>{const n=t.commands.get("mediaImageTextAlternative"),a=new g.ButtonView(i);return a.set({label:Drupal.t("Override media image alternative text"),icon:e.icons.lowVision,tooltip:!0}),a.bind("isVisible").to(n,"isEnabled"),this.listenTo(a,"execute",(()=>{this._showForm()})),a}))}_createForm(){const e=this.editor,t=e.editing.view.document;this._balloon=this.editor.plugins.get("ContextualBalloon"),this._form=new M(e.locale),this._form.render(),this.listenTo(this._form,"submit",(()=>{e.execute("mediaImageTextAlternative",{newValue:this._form.decorativeToggle.isOn?'""':this._form.labeledInput.fieldView.element.value}),this._hideForm(!0)})),this.listenTo(this._form,"cancel",(()=>{this._hideForm(!0)})),this._form.keystrokes.set("Esc",((e,t)=>{this._hideForm(!0),t()})),this.listenTo(e.ui,"update",(()=>{s(t.selection)?this._isVisible&&function(e){const t=e.plugins.get("ContextualBalloon");if(s(e.editing.view.document.selection)){const i=v(e);t.updatePosition(i)}}(e):this._hideForm(!0)})),(0,g.clickOutsideHandler)({emitter:this._form,activator:()=>this._isVisible,contextElements:[this._balloon.view.element],callback:()=>this._hideForm()})}_showForm(){if(this._isVisible)return;const e=this.editor,t=e.commands.get("mediaImageTextAlternative"),i=this._form.decorativeToggle,n=e.plugins.get("DrupalMediaMetadataRepository"),r=this._form.labeledInput;this._form.disableCssTransitions(),this._isInBalloon||this._balloon.add({view:this._form,position:v(e)}),i.isOn='""'===t.value,r.fieldView.element.value=t.value||"",r.fieldView.value=r.fieldView.element.value,this._form.defaultAltText="";const o=e.model.document.selection.getSelectedElement();a(o)&&n.getMetadata(o).then((e=>{this._form.defaultAltText=e.imageSourceMetadata?e.imageSourceMetadata.alt:"",r.infoText=Drupal.t(`Leave blank to use the default alternative text: "${this._form.defaultAltText}".`)})).catch((e=>{console.warn(e.toString())})),this._form.enableCssTransitions()}_hideForm(e){this._isInBalloon&&(this._form.focusTracker.isFocused&&this._form.saveButtonView.focus(),this._balloon.remove(this._form),e&&this.editor.editing.view.focus())}get _isVisible(){return this._balloon.visibleView===this._form}get _isInBalloon(){return this._balloon.hasView(this._form)}}class D extends e.Plugin{static get requires(){return[y,k]}static get pluginName(){return"MediaImageTextAlternative"}}function C(e,t,i){if(t.attributes)for(const[n,a]of Object.entries(t.attributes))e.setAttribute(n,a,i);t.styles&&e.setStyle(t.styles,i),t.classes&&e.addClass(t.classes,i)}function A(e,t,i){if(!i.consumable.consume(t.item,e.name))return;const n=i.mapper.toViewElement(t.item);C(i.writer,t.attributeNewValue,n)}class _ extends e.Plugin{constructor(e){if(super(e),!e.plugins.has("GeneralHtmlSupport"))return;e.plugins.has("DataFilter")&&e.plugins.has("DataSchema")||console.error("DataFilter and DataSchema plugins are required for Drupal Media to integrate with General HTML Support plugin.");const{schema:t}=e.model,{conversion:i}=e,n=this.editor.plugins.get("DataFilter");this.editor.plugins.get("DataSchema").registerBlockElement({model:"drupalMedia",view:"drupal-media"}),n.on("register:drupal-media",((e,a)=>{"drupalMedia"===a.model&&(t.extend("drupalMedia",{allowAttributes:["htmlLinkAttributes","htmlAttributes"]}),i.for("upcast").add(function(e){return t=>{t.on("element:drupal-media",((t,i,n)=>{function a(t,a){const r=e.processViewAttributes(t,n);r&&n.writer.setAttribute(a,r,i.modelRange)}const r=i.viewItem,o=r.parent;a(r,"htmlAttributes"),o.is("element","a")&&a(o,"htmlLinkAttributes")}),{priority:"low"})}}(n)),i.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{if(!i.consumable.consume(t.item,"attribute:htmlLinkAttributes:drupalMedia"))return;const n=i.mapper.toViewElement(t.item),a=function(e,t,i){const n=e.createRangeOn(t);for(const{item:e}of n.getWalker())if(e.is("element",i))return e}(i.writer,n,"a");C(i.writer,t.item.getAttribute("htmlLinkAttributes"),a)}),{priority:"low"})})),i.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{if(!i.consumable.consume(t.item,"attribute:htmlLinkAttributes:drupalMedia"))return;const n=i.mapper.toViewElement(t.item).parent;C(i.writer,t.item.getAttribute("htmlLinkAttributes"),n)}),{priority:"low"}),e.on("attribute:htmlAttributes:drupalMedia",A,{priority:"low"})})),e.stop())}))}static get pluginName(){return"DrupalMediaGeneralHtmlSupport"}}class x extends e.Plugin{static get requires(){return[p,_,h,f,D]}static get pluginName(){return"DrupalMedia"}}var V=i("ckeditor5/src/engine.js");function S(e){return Array.from(e.getChildren()).find((e=>"drupal-media"===e.name))}function T(e){return t=>{t.on(`attribute:${e.id}:drupalMedia`,((t,i,n)=>{const a=n.mapper.toViewElement(i.item);let r=Array.from(a.getChildren()).find((e=>"a"===e.name));if(r=!r&&a.is("element","a")?a:Array.from(a.getAncestors()).find((e=>"a"===e.name)),r){for(const[t,i]of(0,E.toMap)(e.attributes))n.writer.setAttribute(t,i,r);e.classes&&n.writer.addClass(e.classes,r);for(const t in e.styles)Object.prototype.hasOwnProperty.call(e.styles,t)&&n.writer.setStyle(t,e.styles[t],r)}}))}}function I(e,t){return e=>{e.on("element:a",((e,i,n)=>{const a=i.viewItem;if(!S(a))return;const r=new V.Matcher(t._createPattern()).match(a);if(!r)return;if(!n.consumable.consume(a,r.match))return;const o=i.modelCursor.nodeBefore;n.writer.setAttribute(t.id,!0,o)}),{priority:"high"})}}class L extends e.Plugin{static get requires(){return["LinkEditing","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaEditing"}init(){const{editor:e}=this;e.model.schema.extend("drupalMedia",{allowAttributes:["linkHref"]}),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const n=t.viewItem,a=S(n);if(!a)return;if(!i.consumable.consume(n,{attributes:["href"],name:!0}))return;const r=n.getAttribute("href");if(null===r)return;const o=i.convertItem(a,t.modelCursor);t.modelRange=o.modelRange,t.modelCursor=o.modelCursor;const s=t.modelCursor.nodeBefore;s&&s.is("element","drupalMedia")&&i.writer.setAttribute("linkHref",r,s)}),{priority:"high"})})),e.conversion.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=Array.from(a.getChildren()).find((e=>"a"===e.name));if(r)t.attributeNewValue?n.setAttribute("href",t.attributeNewValue,r):(n.move(n.createRangeIn(r),n.createPositionAt(a,0)),n.remove(r));else{const e=Array.from(a.getChildren()).find((e=>e.getAttribute("data-drupal-media-preview"))),i=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionAt(a,0),i),n.move(n.createRangeOn(e),n.createPositionAt(i,0))}}),{priority:"high"})})),e.conversion.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionBefore(a),r),n.move(n.createRangeOn(a),n.createPositionAt(r,0))}),{priority:"high"})})),this._enableManualDecorators();if(e.commands.get("link").automaticDecorators.length>0)throw new Error("The Drupal Media plugin is not compatible with automatic link decorators. To use Drupal Media, disable any plugins providing automatic link decorators.")}_enableManualDecorators(){const e=this.editor,t=e.commands.get("link");for(const i of t.manualDecorators)e.model.schema.extend("drupalMedia",{allowAttributes:i.id}),e.conversion.for("downcast").add(T(i)),e.conversion.for("upcast").add(I(0,i))}}class O extends e.Plugin{static get requires(){return["LinkEditing","LinkUI","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaUi"}init(){const{editor:e}=this,t=e.editing.view.document;this.listenTo(t,"click",((t,i)=>{this._isSelectedLinkedMedia(e.model.document.selection)&&(i.preventDefault(),t.stop())}),{priority:"high"}),this._createToolbarLinkMediaButton()}_createToolbarLinkMediaButton(){const{editor:e}=this;e.ui.componentFactory.add("drupalLinkMedia",(t=>{const i=new g.ButtonView(t),n=e.plugins.get("LinkUI"),a=e.commands.get("link");return i.set({isEnabled:!0,label:Drupal.t("Link media"),icon:'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184z"/></svg>\n',keystroke:"Ctrl+K",tooltip:!0,isToggleable:!0}),i.bind("isEnabled").to(a,"isEnabled"),i.bind("isOn").to(a,"value",(e=>!!e)),this.listenTo(i,"execute",(()=>{this._isSelectedLinkedMedia(e.model.document.selection)?n._addActionsView():n._showUI(!0)})),i}))}_isSelectedLinkedMedia(e){const t=e.getSelectedElement();return!!t&&t.is("element","drupalMedia")&&t.hasAttribute("linkHref")}}class P extends e.Plugin{static get requires(){return[L,O]}static get pluginName(){return"DrupalLinkMedia"}}const B={get inline(){return{name:"inline",title:"In line",icon:e.icons.objectInline,modelElements:["imageInline"],isDefault:!0}},get alignLeft(){return{name:"alignLeft",title:"Left aligned image",icon:e.icons.objectLeft,modelElements:["imageBlock","imageInline"],className:"image-style-align-left"}},get alignBlockLeft(){return{name:"alignBlockLeft",title:"Left aligned image",icon:e.icons.objectBlockLeft,modelElements:["imageBlock"],className:"image-style-block-align-left"}},get alignCenter(){return{name:"alignCenter",title:"Centered image",icon:e.icons.objectCenter,modelElements:["imageBlock"],className:"image-style-align-center"}},get alignRight(){return{name:"alignRight",title:"Right aligned image",icon:e.icons.objectRight,modelElements:["imageBlock","imageInline"],className:"image-style-align-right"}},get alignBlockRight(){return{name:"alignBlockRight",title:"Right aligned image",icon:e.icons.objectBlockRight,modelElements:["imageBlock"],className:"image-style-block-align-right"}},get block(){return{name:"block",title:"Centered image",icon:e.icons.objectCenter,modelElements:["imageBlock"],isDefault:!0}},get side(){return{name:"side",title:"Side image",icon:e.icons.objectRight,modelElements:["imageBlock"],className:"image-style-side"}}},N=(()=>({full:e.icons.objectFullWidth,left:e.icons.objectBlockLeft,right:e.icons.objectBlockRight,center:e.icons.objectCenter,inlineLeft:e.icons.objectLeft,inlineRight:e.icons.objectRight,inline:e.icons.objectInline}))(),j=[{name:"imageStyle:wrapText",title:"Wrap text",defaultItem:"imageStyle:alignLeft",items:["imageStyle:alignLeft","imageStyle:alignRight"]},{name:"imageStyle:breakText",title:"Break text",defaultItem:"imageStyle:block",items:["imageStyle:alignBlockLeft","imageStyle:block","imageStyle:alignBlockRight"]}];function R(e){(0,E.logWarning)("image-style-configuration-definition-invalid",e)}const F={normalizeStyles:function(e){return(e.configuredStyles.options||[]).map((e=>function(e){e="string"==typeof e?B[e]?{...B[e]}:{name:e}:function(e,t){const i={...t};for(const n in e)Object.prototype.hasOwnProperty.call(t,n)||(i[n]=e[n]);return i}(B[e.name],e);"string"==typeof e.icon&&(e.icon=N[e.icon]||e.icon);return e}(e))).filter((t=>function(e,{isBlockPluginLoaded:t,isInlinePluginLoaded:i}){const{modelElements:n,name:a}=e;if(!(n&&n.length&&a))return R({style:e}),!1;{const a=[t?"imageBlock":null,i?"imageInline":null];if(!n.some((e=>a.includes(e))))return(0,E.logWarning)("image-style-missing-dependency",{style:e,missingPlugins:n.map((e=>"imageBlock"===e?"ImageBlockEditing":"ImageInlineEditing"))}),!1}return!0}(t,e)))},getDefaultStylesConfiguration:function(e,t){return e&&t?{options:["inline","alignLeft","alignRight","alignCenter","alignBlockLeft","alignBlockRight","block","side"]}:e?{options:["block","side"]}:t?{options:["inline","alignLeft","alignRight"]}:{}},getDefaultDropdownDefinitions:function(e){return e.has("ImageBlockEditing")&&e.has("ImageInlineEditing")?[...j]:[]},warnInvalidStyle:R,DEFAULT_OPTIONS:B,DEFAULT_ICONS:N,DEFAULT_DROPDOWN_DEFINITIONS:j};function U(e,t,i){for(const n of t)if(i.checkAttribute(e,n))return!0;return!1}function H(e,t,i){const n=e.getSelectedElement();if(n&&U(n,i,t))return n;let{parent:a}=e.getFirstPosition();for(;a;){if(a.is("element")&&U(a,i,t))return a;a=a.parent}return null}class $ extends e.Command{constructor(e,t){super(e),this.styles={},Object.keys(t).forEach((e=>{this.styles[e]=new Map(t[e].map((e=>[e.name,e])))})),this.modelAttributes=[];for(const e of Object.keys(t)){const t=u(e);this.modelAttributes.push(t)}}refresh(){const{editor:e}=this,t=H(e.model.document.selection,e.model.schema,this.modelAttributes);this.isEnabled=!!t,this.isEnabled?this.value=this.getValue(t):this.value=!1}getValue(e){const t={};return Object.keys(this.styles).forEach((i=>{const n=u(i);if(e.hasAttribute(n))t[i]=e.getAttribute(n);else for(const[,e]of this.styles[i])e.isDefault&&(t[i]=e.name)})),t}execute(e={}){const{editor:{model:t}}=this,{value:i,group:n}=e,a=u(n);t.change((e=>{const r=H(t.document.selection,t.schema,this.modelAttributes);!i||this.styles[n].get(i).isDefault?e.removeAttribute(a,r):e.setAttribute(a,i,r)}))}}function q(e,t){for(const i of t)if(i.name===e)return i}class W extends e.Plugin{init(){const{editor:t}=this,i=t.config.get("drupalElementStyles");this.normalizedStyles={},Object.keys(i).forEach((t=>{this.normalizedStyles[t]=i[t].map((t=>("string"==typeof t.icon&&e.icons[t.icon]&&(t.icon=e.icons[t.icon]),t.name&&(t.name=`${t.name}`),t))).filter((e=>e.isDefault||e.attributeName&&e.attributeValue?e.modelElements&&Array.isArray(e.modelElements)?!!e.name||(console.warn("drupalElementStyles options must include a name."),!1):(console.warn("drupalElementStyles options must include an array of supported modelElements."),!1):(console.warn(`${e.attributeValue} drupalElementStyles options must include attributeName and attributeValue.`),!1)))})),this._setupConversion(),t.commands.add("drupalElementStyle",new $(t,this.normalizedStyles))}_setupConversion(){const{editor:e}=this,{schema:t}=e.model;Object.keys(this.normalizedStyles).forEach((i=>{const n=u(i),a=(r=this.normalizedStyles[i],(e,t,i)=>{if(!i.consumable.consume(t.item,e.name))return;const n=q(t.attributeNewValue,r),a=q(t.attributeOldValue,r),o=i.mapper.toViewElement(t.item),s=i.writer;a&&("class"===a.attributeName?s.removeClass(a.attributeValue,o):s.removeAttribute(a.attributeName,o)),n&&("class"===n.attributeName?s.addClass(n.attributeValue,o):n.isDefault||s.setAttribute(n.attributeName,n.attributeValue,o))});var r;const o=function(e,t){const i=e.filter((e=>!e.isDefault));return(e,n,a)=>{if(!n.modelRange)return;const r=n.viewItem,o=(0,E.first)(n.modelRange.getItems());if(o&&a.schema.checkAttribute(o,t))for(const e of i)if("class"===e.attributeName)a.consumable.consume(r,{classes:e.attributeValue})&&a.writer.setAttribute(t,e.name,o);else if(a.consumable.consume(r,{attributes:[e.attributeName]}))for(const e of i)e.attributeValue===r.getAttribute(e.attributeName)&&a.writer.setAttribute(t,e.name,o)}}(this.normalizedStyles[i],n);e.editing.downcastDispatcher.on(`attribute:${n}`,a),e.data.downcastDispatcher.on(`attribute:${n}`,a);[...new Set(this.normalizedStyles[i].map((e=>e.modelElements)).flat())].forEach((e=>{t.extend(e,{allowAttributes:n})})),e.data.upcastDispatcher.on("element",o,{priority:"low"})}))}static get pluginName(){return"DrupalElementStyleEditing"}}const K=e=>e,z=(e,t)=>(e?`${e}: `:"")+t;function Z(e,t){return`drupalElementStyle:${t}:${e}`}class G extends e.Plugin{static get requires(){return[W]}init(){const{plugins:e}=this.editor,t=this.editor.config.get("drupalMedia.toolbar")||[],i=e.get("DrupalElementStyleEditing").normalizedStyles;Object.keys(i).forEach((e=>{i[e].forEach((t=>{this._createButton(t,e,i[e])}))}));t.filter(l).filter((e=>{const t=[];if(!e.display)return console.warn("dropdown configuration must include a display key specifying either listDropdown or splitButton."),!1;e.items.includes(e.defaultItem)||console.warn("defaultItem must be part of items in the dropdown configuration.");for(const i of e.items){const e=i.split(":")[1];t.push(e)}return!!t.every((e=>e===t[0]))||(console.warn("dropdown configuration should only contain buttons from one group."),!1)})).forEach((e=>{if(e.items.length>=2){const t=e.name.split(":")[1];switch(e.display){case"splitButton":this._createDropdown(e,i[t]);break;case"listDropdown":this._createListDropdown(e,i[t])}}}))}updateOptionVisibility(e,t,i,n){const{selection:a}=this.editor.model.document,r={};r[n]=e;const o=a?a.getSelectedElement():H(a,this.editor.model.schema,r),s=e.filter((function(e){for(const[t,i]of(0,E.toMap)(e.modelAttributes))if(o&&o.hasAttribute(t))return i.includes(o.getAttribute(t));return!0}));i.hasOwnProperty("model")?s.includes(t)?i.model.set({class:""}):i.model.set({class:"ck-hidden"}):s.includes(t)?i.set({class:""}):i.set({class:"ck-hidden"})}_createDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s}=e,l=o.filter((e=>{const i=e.split(":")[1];return t.find((({name:t})=>Z(t,i)===e))})).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==l.length&&F.warnInvalidStyle({dropdown:e});const d=(0,g.createDropdown)(n,g.SplitButtonView),u=d.buttonView;return(0,g.addToolbarToDropdown)(d,l),u.set({label:z(s,a.label),class:null,tooltip:!0}),u.bind("icon").toMany(l,"isOn",((...e)=>{const t=e.findIndex(K);return t<0?a.icon:l[t].icon})),u.bind("label").toMany(l,"isOn",((...e)=>{const t=e.findIndex(K);return z(s,t<0?a.label:l[t].label)})),u.bind("isOn").toMany(l,"isOn",((...e)=>e.some(K))),u.bind("class").toMany(l,"isOn",((...e)=>e.some(K)?"ck-splitbutton_flatten":null)),u.on("execute",(()=>{l.some((({isOn:e})=>e))?d.isOpen=!d.isOpen:a.fire("execute")})),d.bind("isEnabled").toMany(l,"isEnabled",((...e)=>e.some(K))),d}))}_createButton(e,t,i){const n=e.name;this.editor.ui.componentFactory.add(Z(n,t),(a=>{const r=this.editor.commands.get("drupalElementStyle"),o=new g.ButtonView(a);return o.set({label:e.title,icon:e.icon,tooltip:!0,isToggleable:!0}),o.bind("isEnabled").to(r,"isEnabled"),o.bind("isOn").to(r,"value",(e=>e&&e[t]===n)),o.on("execute",this._executeCommand.bind(this,n,t)),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(i,e,o,t)})),o}))}getDropdownListItemDefinitions(e,t,i){const n=new E.Collection;return e.forEach((t=>{const a={type:"button",model:new g.ViewModel({group:i,commandValue:t.name,label:t.title,withText:!0,class:""})};n.add(a),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(e,t,a,i)}))})),n}_createListDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s,defaultText:l}=e,d=e.name.split(":")[1],u=o.filter((e=>t.find((({name:t})=>Z(t,d)===e)))).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==u.length&&F.warnInvalidStyle({dropdown:e});const c=(0,g.createDropdown)(n,g.DropdownButtonView),m=c.buttonView;m.set({label:z(s,a.label),class:null,tooltip:l,withText:!0});const p=this.editor.commands.get("drupalElementStyle");return m.bind("label").to(p,"value",(e=>{if(e&&e[d])for(const i of t)if(i.name===e[d])return i.title;return l})),c.bind("isOn").to(p),c.bind("isEnabled").to(this),(0,g.addListToDropdown)(c,this.getDropdownListItemDefinitions(t,p,d)),this.listenTo(c,"execute",(e=>{this._executeCommand(e.source.commandValue,e.source.group)})),c}))}_executeCommand(e,t){this.editor.execute("drupalElementStyle",{value:e,group:t}),this.editor.editing.view.focus()}static get pluginName(){return"DrupalElementStyleUi"}}class J extends e.Plugin{static get requires(){return[W,G]}static get pluginName(){return"DrupalElementStyle"}}function X(e){const t=e.getFirstPosition().findAncestor("caption");return t&&a(t.parent)?t:null}function Q(e){for(const t of e.getChildren())if(t&&t.is("element","caption"))return t;return null}class Y extends e.Command{refresh(){const e=this.editor.model.document.selection,t=e.getSelectedElement();if(!t)return this.isEnabled=!!o(e),void(this.value=!!X(e));this.isEnabled=a(t),this.isEnabled?this.value=!!Q(t):this.value=!1}execute(e={}){const{focusCaptionOnShow:t}=e;this.editor.model.change((e=>{this.value?this._hideDrupalMediaCaption(e):this._showDrupalMediaCaption(e,t)}))}_showDrupalMediaCaption(e,t){const i=this.editor.model.document.selection,n=this.editor.plugins.get("DrupalMediaCaptionEditing"),a=o(i),r=n._getSavedCaption(a)||e.createElement("caption");e.append(r,a),t&&e.setSelection(r,"in")}_hideDrupalMediaCaption(e){const t=this.editor,i=t.model.document.selection,n=t.plugins.get("DrupalMediaCaptionEditing");let a,r=i.getSelectedElement();r?a=Q(r):(a=X(i),r=o(i)),n._saveCaption(r,a),e.setSelection(r,"on"),e.remove(a)}}class ee extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionEditing"}constructor(e){super(e),this._savedCaptionsMap=new WeakMap}init(){const e=this.editor,t=e.model.schema;t.isRegistered("caption")?t.extend("caption",{allowIn:"drupalMedia"}):t.register("caption",{allowIn:"drupalMedia",allowContentOf:"$block",isLimit:!0}),e.commands.add("toggleMediaCaption",new Y(e)),this._setupConversion()}_setupConversion(){const e=this.editor,i=e.editing.view;var n;e.conversion.for("upcast").add(function(e){const t=(t,i,n)=>{const{viewItem:a}=i,{writer:r,consumable:o}=n;if(!i.modelRange||!o.consume(a,{attributes:["data-caption"]}))return;const s=r.createElement("caption"),l=i.modelRange.start.nodeAfter,d=e.data.processor.toView(a.getAttribute("data-caption"));n.consumable.constructor.createFrom(d,n.consumable),n.convertChildren(d,s),r.append(s,l)};return e=>{e.on("element:drupal-media",t,{priority:"low"})}}(e)),e.conversion.for("editingDowncast").elementToElement({model:"caption",view:(e,{writer:n})=>{if(!a(e.parent))return null;const r=n.createEditableElement("figcaption");return r.placeholder=Drupal.t("Enter media caption"),(0,V.enablePlaceholder)({view:i,element:r,keepOnFocus:!0}),(0,t.toWidgetEditable)(r,n)}}),e.editing.mapper.on("modelToViewPosition",(n=i,(e,t)=>{const i=t.modelPosition,r=i.parent;if(!a(r))return;const o=t.mapper.toViewElement(r);t.viewPosition=n.createPositionAt(o,i.offset+1)})),e.conversion.for("dataDowncast").add(function(e){return t=>{t.on("insert:caption",((t,i,n)=>{const{consumable:r,writer:o,mapper:s}=n;if(!a(i.item.parent)||!r.consume(i.item,"insert"))return;const l=e.model.createRangeIn(i.item),d=o.createDocumentFragment();s.bindElements(i.item,d);for(const{item:t}of Array.from(l)){const i={item:t,range:e.model.createRangeOn(t)},a=`insert:${t.name||"$text"}`;e.data.downcastDispatcher.fire(a,i,n);for(const a of t.getAttributeKeys())Object.assign(i,{attributeKey:a,attributeOldValue:null,attributeNewValue:i.item.getAttribute(a)}),e.data.downcastDispatcher.fire(`attribute:${a}`,i,n)}for(const e of o.createRangeIn(d).getItems())s.unbindViewElement(e);s.unbindViewElement(d);const u=e.data.processor.toData(d);if(u){const e=s.toViewElement(i.item.parent);o.setAttribute("data-caption",u,e)}}))}}(e))}_getSavedCaption(e){const t=this._savedCaptionsMap.get(e);return t?V.Element.fromJSON(t):null}_saveCaption(e,t){this._savedCaptionsMap.set(e,t.toJSON())}}class te extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionUI"}init(){const{editor:t}=this,i=t.editing.view;t.ui.componentFactory.add("toggleDrupalMediaCaption",(n=>{const a=new g.ButtonView(n),r=t.commands.get("toggleMediaCaption");return a.set({label:Drupal.t("Caption media"),icon:e.icons.caption,tooltip:!0,isToggleable:!0}),a.bind("isOn","isEnabled").to(r,"value","isEnabled"),a.bind("label").to(r,"value",(e=>e?Drupal.t("Toggle caption off"):Drupal.t("Toggle caption on"))),this.listenTo(a,"execute",(()=>{t.execute("toggleMediaCaption",{focusCaptionOnShow:!0});const e=X(t.model.document.selection);if(e){const n=t.editing.mapper.toViewElement(e);i.scrollToTheSelection(),i.change((e=>{e.addClass("drupal-media__caption_highlighted",n)}))}t.editing.view.focus()})),a}))}}class ie extends e.Plugin{static get requires(){return[ee,te]}static get pluginName(){return"DrupalMediaCaption"}}const ne={DrupalMedia:x,MediaImageTextAlternative:D,MediaImageTextAlternativeEditing:y,MediaImageTextAlternativeUi:k,DrupalLinkMedia:P,DrupalMediaCaption:ie,DrupalElementStyle:J}})(),n=n.default})())); -\ No newline at end of file -+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalMedia=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/engine.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/engine.js")},"ckeditor5/src/ui.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/ui.js")},"ckeditor5/src/utils.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/utils.js")},"ckeditor5/src/widget.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/widget.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function i(n){var a=t[n];if(void 0!==a)return a.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,i),r.exports}i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{"use strict";i.d(n,{default:()=>ce});var e=i("ckeditor5/src/core.js"),t=i("ckeditor5/src/widget.js"),a=i("ckeditor5/src/utils.js");function r(e,t){return void 0===t&&(t=!0),t?!!e&&(e.is("element","drupalMedia")||e.is("element","drupalMediaInline")):!!e&&e.is("element","drupalMedia")}function o(e,i){return void 0===i&&(i=!0),i?(0,t.isWidget)(e)&&(!!e.getCustomProperty("drupalMedia")||!!e.getCustomProperty("drupalMediaInline")):(0,t.isWidget)(e)&&!!e.getCustomProperty("drupalMedia")}function s(e,t){void 0===t&&(t=!0);const i=e.getSelectedElement();return r(i,t)?i:e.getFirstPosition().findAncestor(t?/drupalMedia(Inline)?/:/drupalMedia/)}function l(e,t){void 0===t&&(t=!0);const i=e.getSelectedElement();if(i&&o(i,t))return i;if(null===e.getFirstPosition())return null;let n=e.getFirstPosition().parent;for(;n;){if(n.is("element")&&o(n,t))return n;n=n.parent}return null}function d(e){const t=typeof e;return null!=e&&("object"===t||"function"===t)}function u(e){for(const t of e){if(t.hasAttribute("data-drupal-media-preview"))return t;if(t.childCount){const e=u(t.getChildren());if(e)return e}}return null}function c(e){return`drupalElementStyle${e[0].toUpperCase()+e.substring(1)}`}function m(e,t){const i=e.model.schema;return t.is("selection")?function(e,t){const i=(0,a.first)(t.getSelectedBlocks());return!i||e.isObject(i)||i.isEmpty&&"listItem"!==i.name?"drupalMedia":"drupalMediaInline"}(i,t):i.checkChild(t,"drupalMediaInline")?"drupalMediaInline":"drupalMedia"}class p extends e.Command{execute(e){const t=this.editor.plugins.get("DrupalMediaEditing"),i=Object.entries(t.attrs).reduce(((e,[t,i])=>(e[i]=t,e)),{}),n=Object.keys(e).reduce(((t,n)=>(i[n]&&(t[i[n]]=e[n]),t)),{});if(this.editor.plugins.has("DrupalElementStyleEditing")){const t=this.editor.plugins.get("DrupalElementStyleEditing"),{normalizedStyles:i}=t;for(const a of Object.keys(i))for(const i of t.normalizedStyles[a])if(e[i.attributeName]&&i.attributeValue===e[i.attributeName]){const e=c(a);n[e]=i.name}}const a=m(this.editor,this.editor.model.document.selection);this.editor.model.change((e=>{this.editor.model.insertObject(function(e,t,i){return e.createElement(i,t)}(e,n,a))}))}refresh(){const e=this.editor.model,t=e.document.selection,i=m(this.editor,t),n=e.schema.findAllowedParent(t.getFirstPosition(),i);this.isEnabled=null!==n}}const g="METADATA_ERROR";class h extends e.Plugin{static get requires(){return[t.Widget]}constructor(e){super(e),this.attrs={drupalMediaAlt:"alt",drupalMediaEntityType:"data-entity-type",drupalMediaEntityUuid:"data-entity-uuid"},this.converterAttributes=["drupalMediaEntityUuid","drupalElementStyleViewMode","drupalMediaEntityType","drupalMediaAlt"]}init(){const e=this.editor.config.get("drupalMedia");if(!e)return;const{previewURL:t,themeError:i}=e;this.previewUrl=t,this.labelError=Drupal.t("Preview failed"),this.themeError=i||`\n <p>${Drupal.t("An error occurred while trying to preview the media. Save your work and reload this page.")}<p>\n `,this._defineSchema(),this._defineConverters(),this._defineListeners(),this.editor.commands.add("insertDrupalMedia",new p(this.editor))}upcastDrupalMediaIsImage(e){const{model:t,plugins:i}=this.editor;i.get("DrupalMediaMetadataRepository").getMetadata(e).then((i=>{e&&t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",!!i.imageSourceMetadata,e)}))})).catch((i=>{e&&(console.warn(i.toString()),t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",g,e)})))}))}upcastDrupalMediaType(e){this.editor.plugins.get("DrupalMediaMetadataRepository").getMetadata(e).then((t=>{e&&this.editor.model.enqueueChange({isUndoable:!1},(i=>{i.setAttribute("drupalMediaType",t.type,e)}))})).catch((t=>{e&&(console.warn(t.toString()),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",g,e)})))}))}async _fetchPreview(e){const t={text:this._renderElement(e),uuid:e.getAttribute("drupalMediaEntityUuid")},i=await fetch(`${this.previewUrl}?${new URLSearchParams(t)}`,{headers:{"X-Drupal-MediaPreview-CSRF-Token":this.editor.config.get("drupalMedia").previewCsrfToken}});if(i.ok){return{label:i.headers.get("drupal-media-label"),preview:await i.text()}}return{label:this.labelError,preview:this.themeError}}_defineSchema(){const e=this.editor.model.schema;e.register("drupalMedia",{inheritAllFrom:"$blockObject",allowAttributes:Object.keys(this.attrs)}),e.register("drupalMediaInline",{inheritAllFrom:"$inlineObject",allowIn:["$block","$container","$text"],allowAttributes:Object.keys(this.attrs)}),this.editor.editing.view.domConverter.blockElements.push("drupal-media")}_defineConverters(){const e=this.editor.conversion,i=this.editor.plugins.get("DrupalMediaMetadataRepository"),n={"drupal-media":"drupalMedia","drupal-media-inline":"drupalMediaInline"};Object.keys(n).forEach((a=>{const r=n[a];e.for("upcast").elementToElement({view:{name:a},model:r}).add((e=>{e.on(`element:${a}`,((e,t)=>{const[n]=t.modelRange.getItems();i.getMetadata(n).then((e=>{n&&(this.upcastDrupalMediaIsImage(n),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",e.type,n)})))})).catch((e=>{console.warn(e.toString())}))}),{priority:"lowest"})})),e.for("dataDowncast").elementToElement({model:r,view:{name:a}}),e.for("editingDowncast").elementToElement({model:r,view:(e,{writer:i})=>{const n=i.createContainerElement("figure",{class:"drupal-media-inline"===a?`drupal-media ${a}`:a});if(!this.previewUrl){const e=i.createRawElement("div",{"data-drupal-media-preview":"unavailable"});i.insert(i.createPositionAt(n,0),e)}return i.setCustomProperty(r,!0,n),(0,t.toWidget)(n,i,{label:Drupal.t("Media widget")})}}).add((e=>{const t=(e,t,i)=>{const n=i.writer,a=t.item,r=i.mapper.toViewElement(t.item);let o=u(r.getChildren());if(o){if("ready"!==o.getAttribute("data-drupal-media-preview"))return;n.setAttribute("data-drupal-media-preview","loading",o)}else o=n.createRawElement("div",{"data-drupal-media-preview":"loading"}),n.insert(n.createPositionAt(r,0),o);this._fetchPreview(a).then((({label:e,preview:t})=>{o&&this.editor.editing.view.change((i=>{const n=i.createRawElement("div",{"data-drupal-media-preview":"ready","aria-label":e},(e=>{e.innerHTML=t}));i.insert(i.createPositionBefore(o),n),i.remove(o)}))}))};return this.converterAttributes.forEach((i=>{e.on(`attribute:${i}:${r}`,t)})),e})),e.for("editingDowncast").add((e=>{e.on(`attribute:drupalElementStyleAlign:${r}`,((e,t,i)=>{const n={left:"drupal-media-style-align-left",right:"drupal-media-style-align-right",center:"drupal-media-style-align-center"},a=i.mapper.toViewElement(t.item),r=i.writer;n[t.attributeOldValue]&&r.removeClass(n[t.attributeOldValue],a),n[t.attributeNewValue]&&i.consumable.consume(t.item,e.name)&&r.addClass(n[t.attributeNewValue],a)}))})),Object.keys(this.attrs).forEach((t=>{const i={model:{key:t,name:r},view:{name:a,key:this.attrs[t]}};e.for("dataDowncast").attributeToAttribute(i),e.for("upcast").attributeToAttribute(i)}))}))}_defineListeners(){this.editor.model.on("insertContent",((e,[t])=>{r(t)&&(this.upcastDrupalMediaIsImage(t),this.upcastDrupalMediaType(t))}))}_renderElement(e){const t=this.editor.model.change((t=>{const i=t.createDocumentFragment(),n=t.cloneElement(e,!1);return["linkHref"].forEach((e=>{t.removeAttribute(e,n)})),t.append(n,i),i}));return this.editor.data.stringify(t)}static get pluginName(){return"DrupalMediaEditing"}}var f=i("ckeditor5/src/ui.js");class b extends e.Plugin{init(){const e=this.editor,t=this.editor.config.get("drupalMedia");if(!t)return;const{libraryURL:i,openDialog:n,dialogSettings:a={}}=t;i&&"function"==typeof n&&e.ui.componentFactory.add("drupalMedia",(t=>{const r=e.commands.get("insertDrupalMedia"),o=new f.ButtonView(t);return o.set({label:Drupal.t("Insert Media"),icon:'<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M19.1873 4.86414L10.2509 6.86414V7.02335H10.2499V15.5091C9.70972 15.1961 9.01793 15.1048 8.34069 15.3136C7.12086 15.6896 6.41013 16.8967 6.75322 18.0096C7.09631 19.1226 8.3633 19.72 9.58313 19.344C10.6666 19.01 11.3484 18.0203 11.2469 17.0234H11.2499V9.80173L18.1803 8.25067V14.3868C17.6401 14.0739 16.9483 13.9825 16.2711 14.1913C15.0513 14.5674 14.3406 15.7744 14.6836 16.8875C15.0267 18.0004 16.2937 18.5978 17.5136 18.2218C18.597 17.8877 19.2788 16.8982 19.1773 15.9011H19.1803V8.02687L19.1873 8.0253V4.86414Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M13.5039 0.743652H0.386932V12.1603H13.5039V0.743652ZM12.3379 1.75842H1.55289V11.1454H1.65715L4.00622 8.86353L6.06254 10.861L9.24985 5.91309L11.3812 9.22179L11.7761 8.6676L12.3379 9.45621V1.75842ZM6.22048 4.50869C6.22048 5.58193 5.35045 6.45196 4.27722 6.45196C3.20398 6.45196 2.33395 5.58193 2.33395 4.50869C2.33395 3.43546 3.20398 2.56543 4.27722 2.56543C5.35045 2.56543 6.22048 3.43546 6.22048 4.50869Z"/></svg>\n',tooltip:!0}),o.bind("isOn","isEnabled").to(r,"value","isEnabled"),this.listenTo(o,"execute",(()=>{n(i,(({attributes:t})=>{e.execute("insertDrupalMedia",t)}),a)})),o}))}}class w extends e.Plugin{static get requires(){return[t.WidgetToolbarRepository]}static get pluginName(){return"DrupalMediaToolbar"}afterInit(){const{editor:e}=this;var i;e.plugins.get(t.WidgetToolbarRepository).register("drupalMedia",{ariaLabel:Drupal.t("Drupal Media toolbar"),items:(i=e.config.get("drupalMedia.toolbar"),i.map((e=>d(e)?e.name:e))||[]),getRelatedElement:e=>l(e)})}}class y extends e.Command{refresh(){const e=s(this.editor.model.document.selection);this.isEnabled=!!e&&e.getAttribute("drupalMediaIsImage")&&e.getAttribute("drupalMediaIsImage")!==g,this.isEnabled?this.value=e.getAttribute("drupalMediaAlt"):this.value=!1}execute(e){const{model:t}=this.editor,i=s(t.document.selection);e.newValue=e.newValue.trim(),t.change((t=>{e.newValue.length>0?t.setAttribute("drupalMediaAlt",e.newValue,i):t.removeAttribute("drupalMediaAlt",i)}))}}class v extends e.Plugin{init(){this._data=new WeakMap}getMetadata(e){if(this._data.get(e))return new Promise((t=>{t(this._data.get(e))}));const t=this.editor.config.get("drupalMedia");if(!t)return new Promise(((e,t)=>{t(new Error("drupalMedia configuration is required for parsing metadata."))}));if(!e.hasAttribute("drupalMediaEntityUuid"))return new Promise(((e,t)=>{t(new Error("drupalMedia element must have drupalMediaEntityUuid attribute to retrieve metadata."))}));const{metadataUrl:i}=t;return(async e=>{const t=await fetch(e);if(t.ok)return JSON.parse(await t.text());throw new Error("Fetching media embed metadata from the server failed.")})(`${i}&${new URLSearchParams({uuid:e.getAttribute("drupalMediaEntityUuid")})}`).then((t=>(this._data.set(e,t),t)))}static get pluginName(){return"DrupalMediaMetadataRepository"}}class E extends e.Plugin{static get requires(){return[v]}static get pluginName(){return"MediaImageTextAlternativeEditing"}init(){const{editor:e,editor:{model:t,conversion:i}}=this;t.schema.extend("drupalMedia",{allowAttributes:["drupalMediaIsImage"]}),i.for("editingDowncast").add((e=>{e.on("attribute:drupalMediaIsImage",((e,t,i)=>{const{writer:n,mapper:a}=i,r=a.toViewElement(t.item);if(t.attributeNewValue!==g){const e=Array.from(r.getChildren()).find((e=>e.getCustomProperty("drupalMediaMetadataError")));return void(e&&(n.setCustomProperty("widgetLabel",e.getCustomProperty("drupalMediaOriginalWidgetLabel"),e),n.removeElement(e)))}const o=Drupal.t("Not all functionality may be available because some information could not be retrieved."),s=new f.Template({tag:"span",children:[{tag:"span",attributes:{class:"drupal-media__metadata-error-icon","data-cke-tooltip-text":o}}]}).render(),l=n.createRawElement("div",{class:"drupal-media__metadata-error"},((e,t)=>{t.setContentOf(e,s.outerHTML)}));n.setCustomProperty("drupalMediaMetadataError",!0,l);const d=r.getCustomProperty("widgetLabel");n.setCustomProperty("drupalMediaOriginalWidgetLabel",d,l),n.setCustomProperty("widgetLabel",`${d} (${o})`,r),n.insert(n.createPositionAt(r,0),l)}),{priority:"low"})})),e.commands.add("mediaImageTextAlternative",new y(this.editor))}}function M(e){const t=e.editing.view,i=f.BalloonPanelView.defaultPositions;return{target:t.domConverter.viewToDom(t.document.selection.getSelectedElement()),positions:[i.northArrowSouth,i.northArrowSouthWest,i.northArrowSouthEast,i.southArrowNorth,i.southArrowNorthWest,i.southArrowNorthEast]}}class k extends f.View{constructor(t){super(t),this.focusTracker=new a.FocusTracker,this.keystrokes=new a.KeystrokeHandler,this.labeledInput=this._createLabeledInputView(),this.set("defaultAltText",void 0),this.defaultAltTextView=this._createDefaultAltTextView(),this.saveButtonView=this._createButton(Drupal.t("Save"),e.icons.check,"ck-button-save"),this.saveButtonView.type="submit",this.cancelButtonView=this._createButton(Drupal.t("Cancel"),e.icons.cancel,"ck-button-cancel","cancel"),this._focusables=new f.ViewCollection,this._focusCycler=new f.FocusCycler({focusables:this._focusables,focusTracker:this.focusTracker,keystrokeHandler:this.keystrokes,actions:{focusPrevious:"shift + tab",focusNext:"tab"}}),this.setTemplate({tag:"form",attributes:{class:["ck","ck-media-alternative-text-form","ck-vertical-form"],tabindex:"-1"},children:[this.defaultAltTextView,this.labeledInput,this.saveButtonView,this.cancelButtonView]}),(0,f.injectCssTransitionDisabler)(this)}render(){super.render(),this.keystrokes.listenTo(this.element),(0,f.submitHandler)({view:this}),[this.labeledInput,this.saveButtonView,this.cancelButtonView].forEach((e=>{this._focusables.add(e),this.focusTracker.add(e.element)}))}_createButton(e,t,i,n){const a=new f.ButtonView(this.locale);return a.set({label:e,icon:t,tooltip:!0}),a.extendTemplate({attributes:{class:i}}),n&&a.delegate("execute").to(this,n),a}_createLabeledInputView(){const e=new f.LabeledFieldView(this.locale,f.createLabeledInputText);return e.label=Drupal.t("Alternative text override"),e}_createDefaultAltTextView(){const e=f.Template.bind(this,this);return new f.Template({tag:"div",attributes:{class:["ck-media-alternative-text-form__default-alt-text",e.if("defaultAltText","ck-hidden",(e=>!e))]},children:[{tag:"strong",attributes:{class:"ck-media-alternative-text-form__default-alt-text-label"},children:[Drupal.t("Default alternative text:")]}," ",{tag:"span",attributes:{class:"ck-media-alternative-text-form__default-alt-text-value"},children:[{text:[e.to("defaultAltText")]}]}]})}}class A extends e.Plugin{static get requires(){return[f.ContextualBalloon]}static get pluginName(){return"MediaImageTextAlternativeUi"}init(){this._createButton(),this._createForm()}destroy(){super.destroy(),this._form.destroy()}_createButton(){const t=this.editor;t.ui.componentFactory.add("mediaImageTextAlternative",(i=>{const n=t.commands.get("mediaImageTextAlternative"),a=new f.ButtonView(i);return a.set({label:Drupal.t("Override media image alternative text"),icon:e.icons.lowVision,tooltip:!0}),a.bind("isVisible").to(n,"isEnabled"),this.listenTo(a,"execute",(()=>{this._showForm()})),a}))}_createForm(){const e=this.editor,t=e.editing.view.document;this._balloon=this.editor.plugins.get("ContextualBalloon"),this._form=new k(e.locale),this._form.render(),this.listenTo(this._form,"submit",(()=>{e.execute("mediaImageTextAlternative",{newValue:this._form.labeledInput.fieldView.element.value}),this._hideForm(!0)})),this.listenTo(this._form,"cancel",(()=>{this._hideForm(!0)})),this._form.keystrokes.set("Esc",((e,t)=>{this._hideForm(!0),t()})),this.listenTo(e.ui,"update",(()=>{l(t.selection)?this._isVisible&&function(e){const t=e.plugins.get("ContextualBalloon");if(l(e.editing.view.document.selection)){const i=M(e);t.updatePosition(i)}}(e):this._hideForm(!0)})),(0,f.clickOutsideHandler)({emitter:this._form,activator:()=>this._isVisible,contextElements:[this._balloon.view.element],callback:()=>this._hideForm()})}_showForm(){if(this._isVisible)return;const e=this.editor,t=e.commands.get("mediaImageTextAlternative"),i=e.plugins.get("DrupalMediaMetadataRepository"),n=this._form.labeledInput;this._form.disableCssTransitions(),this._isInBalloon||this._balloon.add({view:this._form,position:M(e)}),n.fieldView.element.value=t.value||"",n.fieldView.value=n.fieldView.element.value,this._form.defaultAltText="";const a=e.model.document.selection.getSelectedElement();r(a)&&i.getMetadata(a).then((e=>{this._form.defaultAltText=e.imageSourceMetadata?e.imageSourceMetadata.alt:""})).catch((e=>{console.warn(e.toString())})),this._form.labeledInput.fieldView.select(),this._form.enableCssTransitions()}_hideForm(e){this._isInBalloon&&(this._form.focusTracker.isFocused&&this._form.saveButtonView.focus(),this._balloon.remove(this._form),e&&this.editor.editing.view.focus())}get _isVisible(){return this._balloon.visibleView===this._form}get _isInBalloon(){return this._balloon.hasView(this._form)}}class D extends e.Plugin{static get requires(){return[E,A]}static get pluginName(){return"MediaImageTextAlternative"}}function C(e,t,i){if(t.attributes)for(const[n,a]of Object.entries(t.attributes))e.setAttribute(n,a,i);t.styles&&e.setStyle(t.styles,i),t.classes&&e.addClass(t.classes,i)}function _(e,t,i){if(!i.consumable.consume(t.item,e.name))return;const n=i.mapper.toViewElement(t.item);C(i.writer,t.attributeNewValue,n)}class x extends e.Plugin{constructor(e){if(super(e),!e.plugins.has("GeneralHtmlSupport"))return;e.plugins.has("DataFilter")&&e.plugins.has("DataSchema")||console.error("DataFilter and DataSchema plugins are required for Drupal Media to integrate with General HTML Support plugin.");const{schema:t}=e.model,{conversion:i}=e,n=this.editor.plugins.get("DataFilter"),a=this.editor.plugins.get("DataSchema"),r={"drupal-media":"drupalMedia","drupal-media-inline":"drupalMediaInline"};Object.keys(r).forEach((e=>{const o=r[e];a.registerBlockElement({model:o,view:e}),n.on(`register:${e}`,((a,r)=>{r.model===o&&(t.extend(o,{allowAttributes:["htmlLinkAttributes","htmlAttributes"]}),i.for("upcast").add(function(e,t,i){return t=>{t.on(`element:${i}`,((t,i,n)=>{function a(t,a){const r=e.processViewAttributes(t,n);r&&n.writer.setAttribute(a,r,i.modelRange)}const r=i.viewItem,o=r.parent;a(r,"htmlAttributes"),o.is("element","a")&&a(o,"htmlLinkAttributes")}),{priority:"low"})}}(n,0,e)),i.for("editingDowncast").add(function(e){return t=>{t.on(`attribute:linkHref:${e}`,((t,i,n)=>{if(!n.consumable.consume(i.item,`attribute:htmlLinkAttributes:${e}`))return;const a=n.mapper.toViewElement(i.item),r=function(e,t,i){const n=e.createRangeOn(t);for(const{item:e}of n.getWalker())if(e.is("element",i))return e}(n.writer,a,"a");C(n.writer,i.item.getAttribute("htmlLinkAttributes"),r)}),{priority:"low"})}}(o)),i.for("dataDowncast").add(function(e){return t=>{t.on(`attribute:linkHref:${e}`,((t,i,n)=>{if(!n.consumable.consume(i.item,`attribute:htmlLinkAttributes:${e}`))return;const a=n.mapper.toViewElement(i.item).parent;C(n.writer,i.item.getAttribute("htmlLinkAttributes"),a)}),{priority:"low"}),t.on(`attribute:htmlAttributes:${e}`,_,{priority:"low"})}}(o)),a.stop())}))}))}static get pluginName(){return"DrupalMediaGeneralHtmlSupport"}}class V extends e.Plugin{static get requires(){return[h,x,b,w,D]}static get pluginName(){return"DrupalMedia"}}var I=i("ckeditor5/src/engine.js");function S(e){return Array.from(e.getChildren()).find((e=>"drupal-media"===e.name))}function T(e){return t=>{t.on(`attribute:${e.id}:drupalMedia`,((t,i,n)=>{const r=n.mapper.toViewElement(i.item);let o=Array.from(r.getChildren()).find((e=>"a"===e.name));if(o=!o&&r.is("element","a")?r:Array.from(r.getAncestors()).find((e=>"a"===e.name)),o){for(const[t,i]of(0,a.toMap)(e.attributes))n.writer.setAttribute(t,i,o);e.classes&&n.writer.addClass(e.classes,o);for(const t in e.styles)Object.prototype.hasOwnProperty.call(e.styles,t)&&n.writer.setStyle(t,e.styles[t],o)}}))}}function L(e,t){return e=>{e.on("element:a",((e,i,n)=>{const a=i.viewItem;if(!S(a))return;const r=new I.Matcher(t._createPattern()).match(a);if(!r)return;if(!n.consumable.consume(a,r.match))return;const o=i.modelCursor.nodeBefore;n.writer.setAttribute(t.id,!0,o)}),{priority:"high"})}}class P extends e.Plugin{static get requires(){return["LinkEditing","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaEditing"}init(){const{editor:e}=this;e.model.schema.extend("drupalMedia",{allowAttributes:["linkHref"]}),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const n=t.viewItem,a=S(n);if(!a)return;if(!i.consumable.consume(n,{attributes:["href"],name:!0}))return;const r=n.getAttribute("href");if(null===r)return;const o=i.convertItem(a,t.modelCursor);t.modelRange=o.modelRange,t.modelCursor=o.modelCursor;const s=t.modelCursor.nodeBefore;s&&s.is("element","drupalMedia")&&i.writer.setAttribute("linkHref",r,s)}),{priority:"high"})})),e.conversion.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=Array.from(a.getChildren()).find((e=>"a"===e.name));if(r)t.attributeNewValue?n.setAttribute("href",t.attributeNewValue,r):(n.move(n.createRangeIn(r),n.createPositionAt(a,0)),n.remove(r));else{const e=Array.from(a.getChildren()).find((e=>e.getAttribute("data-drupal-media-preview"))),i=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionAt(a,0),i),n.move(n.createRangeOn(e),n.createPositionAt(i,0))}}),{priority:"high"})})),e.conversion.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionBefore(a),r),n.move(n.createRangeOn(a),n.createPositionAt(r,0))}),{priority:"high"})})),this._enableManualDecorators();if(e.commands.get("link").automaticDecorators.length>0)throw new Error("The Drupal Media plugin is not compatible with automatic link decorators. To use Drupal Media, disable any plugins providing automatic link decorators.")}_enableManualDecorators(){const e=this.editor,t=e.commands.get("link");for(const i of t.manualDecorators)e.model.schema.extend("drupalMedia",{allowAttributes:i.id}),e.conversion.for("downcast").add(T(i)),e.conversion.for("upcast").add(L(0,i))}}class O extends e.Plugin{static get requires(){return["LinkEditing","LinkUI","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaUi"}init(){const{editor:e}=this,t=e.editing.view.document;this.listenTo(t,"click",((t,i)=>{this._isSelectedLinkedMedia(e.model.document.selection)&&(i.preventDefault(),t.stop())}),{priority:"high"}),this._createToolbarLinkMediaButton()}_createToolbarLinkMediaButton(){const{editor:e}=this;e.ui.componentFactory.add("drupalLinkMedia",(t=>{const i=new f.ButtonView(t),n=e.plugins.get("LinkUI"),a=e.commands.get("link");return i.set({isEnabled:!0,label:Drupal.t("Link media"),icon:'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184z"/></svg>\n',keystroke:"Ctrl+K",tooltip:!0,isToggleable:!0}),i.bind("isEnabled").to(a,"isEnabled"),i.bind("isOn").to(a,"value",(e=>!!e)),this.listenTo(i,"execute",(()=>{this._isSelectedLinkedMedia(e.model.document.selection)?n._addActionsView():n._showUI(!0)})),i}))}_isSelectedLinkedMedia(e){const t=e.getSelectedElement();return!!t&&t.is("element","drupalMedia")&&t.hasAttribute("linkHref")}}class B extends e.Plugin{static get requires(){return[P,O]}static get pluginName(){return"DrupalLinkMedia"}}const{objectFullWidth:N,objectInline:j,objectLeft:R,objectRight:F,objectCenter:$,objectBlockLeft:U,objectBlockRight:H}=e.icons,q={get inline(){return{name:"inline",title:"In line",icon:j,modelElements:["imageInline"],isDefault:!0}},get alignLeft(){return{name:"alignLeft",title:"Left aligned image",icon:R,modelElements:["imageBlock","imageInline"],className:"image-style-align-left"}},get alignBlockLeft(){return{name:"alignBlockLeft",title:"Left aligned image",icon:U,modelElements:["imageBlock"],className:"image-style-block-align-left"}},get alignCenter(){return{name:"alignCenter",title:"Centered image",icon:$,modelElements:["imageBlock"],className:"image-style-align-center"}},get alignRight(){return{name:"alignRight",title:"Right aligned image",icon:F,modelElements:["imageBlock","imageInline"],className:"image-style-align-right"}},get alignBlockRight(){return{name:"alignBlockRight",title:"Right aligned image",icon:H,modelElements:["imageBlock"],className:"image-style-block-align-right"}},get block(){return{name:"block",title:"Centered image",icon:$,modelElements:["imageBlock"],isDefault:!0}},get side(){return{name:"side",title:"Side image",icon:F,modelElements:["imageBlock"],className:"image-style-side"}}},W={full:N,left:U,right:H,center:$,inlineLeft:R,inlineRight:F,inline:j},K=[{name:"imageStyle:wrapText",title:"Wrap text",defaultItem:"imageStyle:alignLeft",items:["imageStyle:alignLeft","imageStyle:alignRight"]},{name:"imageStyle:breakText",title:"Break text",defaultItem:"imageStyle:block",items:["imageStyle:alignBlockLeft","imageStyle:block","imageStyle:alignBlockRight"]}];function z(e){(0,a.logWarning)("image-style-configuration-definition-invalid",e)}const Z={normalizeStyles:function(e){return(e.configuredStyles.options||[]).map((e=>function(e){e="string"==typeof e?q[e]?{...q[e]}:{name:e}:function(e,t){const i={...t};for(const n in e)Object.prototype.hasOwnProperty.call(t,n)||(i[n]=e[n]);return i}(q[e.name],e);"string"==typeof e.icon&&(e.icon=W[e.icon]||e.icon);return e}(e))).filter((t=>function(e,{isBlockPluginLoaded:t,isInlinePluginLoaded:i}){const{modelElements:n,name:r}=e;if(!(n&&n.length&&r))return z({style:e}),!1;{const r=[t?"imageBlock":null,i?"imageInline":null];if(!n.some((e=>r.includes(e))))return(0,a.logWarning)("image-style-missing-dependency",{style:e,missingPlugins:n.map((e=>"imageBlock"===e?"ImageBlockEditing":"ImageInlineEditing"))}),!1}return!0}(t,e)))},getDefaultStylesConfiguration:function(e,t){return e&&t?{options:["inline","alignLeft","alignRight","alignCenter","alignBlockLeft","alignBlockRight","block","side"]}:e?{options:["block","side"]}:t?{options:["inline","alignLeft","alignRight"]}:{}},getDefaultDropdownDefinitions:function(e){return e.has("ImageBlockEditing")&&e.has("ImageInlineEditing")?[...K]:[]},warnInvalidStyle:z,DEFAULT_OPTIONS:q,DEFAULT_ICONS:W,DEFAULT_DROPDOWN_DEFINITIONS:K};function G(e,t,i){for(const n of t)if(i.checkAttribute(e,n))return!0;return!1}function J(e,t,i){const n=e.getSelectedElement();if(n&&G(n,i,t))return n;let{parent:a}=e.getFirstPosition();for(;a;){if(a.is("element")&&G(a,i,t))return a;a=a.parent}return null}class X extends e.Command{constructor(e,t){super(e),this.styles={},Object.keys(t).forEach((e=>{this.styles[e]=new Map(t[e].map((e=>[e.name,e])))})),this.modelAttributes=[];for(const e of Object.keys(t)){const t=c(e);this.modelAttributes.push(t)}}refresh(){const{editor:e}=this,t=J(e.model.document.selection,e.model.schema,this.modelAttributes);this.isEnabled=!!t,this.isEnabled?this.value=this.getValue(t):this.value=!1}getValue(e){const t={};return Object.keys(this.styles).forEach((i=>{const n=c(i);if(e.hasAttribute(n))t[i]=e.getAttribute(n);else for(const[,e]of this.styles[i])e.isDefault&&(t[i]=e.name)})),t}execute(e={}){const{editor:{model:t}}=this,{value:i,group:n}=e,a=c(n);t.change((e=>{const r=J(t.document.selection,t.schema,this.modelAttributes);!i||this.styles[n].get(i).isDefault?e.removeAttribute(a,r):e.setAttribute(a,i,r)}))}}function Q(e,t){for(const i of t)if(i.name===e)return i}class Y extends e.Plugin{init(){const{editor:t}=this,i=t.config.get("drupalElementStyles");this.normalizedStyles={},Object.keys(i).forEach((t=>{this.normalizedStyles[t]=i[t].map((t=>("string"==typeof t.icon&&e.icons[t.icon]&&(t.icon=e.icons[t.icon]),t.name&&(t.name=`${t.name}`),t))).filter((e=>e.isDefault||e.attributeName&&e.attributeValue?e.modelElements&&Array.isArray(e.modelElements)?!!e.name||(console.warn("drupalElementStyles options must include a name."),!1):(console.warn("drupalElementStyles options must include an array of supported modelElements."),!1):(console.warn(`${e.attributeValue} drupalElementStyles options must include attributeName and attributeValue.`),!1)))})),this._setupConversion(),t.commands.add("drupalElementStyle",new X(t,this.normalizedStyles))}_setupConversion(){const{editor:e}=this,{schema:t}=e.model;Object.keys(this.normalizedStyles).forEach((i=>{const n=c(i),r=(o=this.normalizedStyles[i],(e,t,i)=>{if(!i.consumable.consume(t.item,e.name))return;const n=Q(t.attributeNewValue,o),a=Q(t.attributeOldValue,o),r=i.mapper.toViewElement(t.item),s=i.writer;a&&("class"===a.attributeName?s.removeClass(a.attributeValue,r):s.removeAttribute(a.attributeName,r)),n&&("class"===n.attributeName?s.addClass(n.attributeValue,r):n.isDefault||s.setAttribute(n.attributeName,n.attributeValue,r))});var o;const s=function(e,t){const i=e.filter((e=>!e.isDefault));return(e,n,r)=>{if(!n.modelRange)return;const o=n.viewItem,s=(0,a.first)(n.modelRange.getItems());if(s&&r.schema.checkAttribute(s,t))for(const e of i)if("class"===e.attributeName)r.consumable.consume(o,{classes:e.attributeValue})&&r.writer.setAttribute(t,e.name,s);else if(r.consumable.consume(o,{attributes:[e.attributeName]}))for(const e of i)e.attributeValue===o.getAttribute(e.attributeName)&&r.writer.setAttribute(t,e.name,s)}}(this.normalizedStyles[i],n);e.editing.downcastDispatcher.on(`attribute:${n}`,r),e.data.downcastDispatcher.on(`attribute:${n}`,r);[...new Set(this.normalizedStyles[i].map((e=>e.modelElements)).flat())].forEach((e=>{t.extend(e,{allowAttributes:n})})),e.data.upcastDispatcher.on("element",s,{priority:"low"})}))}static get pluginName(){return"DrupalElementStyleEditing"}}const ee=e=>e,te=(e,t)=>(e?`${e}: `:"")+t;function ie(e,t){return`drupalElementStyle:${t}:${e}`}class ne extends e.Plugin{static get requires(){return[Y]}init(){const{plugins:e}=this.editor,t=this.editor.config.get("drupalMedia.toolbar")||[],i=e.get("DrupalElementStyleEditing").normalizedStyles;Object.keys(i).forEach((e=>{i[e].forEach((t=>{this._createButton(t,e,i[e])}))}));t.filter(d).filter((e=>{const t=[];if(!e.display)return console.warn("dropdown configuration must include a display key specifying either listDropdown or splitButton."),!1;e.items.includes(e.defaultItem)||console.warn("defaultItem must be part of items in the dropdown configuration.");for(const i of e.items){const e=i.split(":")[1];t.push(e)}return!!t.every((e=>e===t[0]))||(console.warn("dropdown configuration should only contain buttons from one group."),!1)})).forEach((e=>{if(e.items.length>=2){const t=e.name.split(":")[1];switch(e.display){case"splitButton":this._createDropdown(e,i[t]);break;case"listDropdown":this._createListDropdown(e,i[t])}}}))}updateOptionVisibility(e,t,i,n){const{selection:r}=this.editor.model.document,o={};o[n]=e;const s=r?r.getSelectedElement():J(r,this.editor.model.schema,o),l=e.filter((function(e){for(const[t,i]of(0,a.toMap)(e.modelAttributes))if(s&&s.hasAttribute(t))return i.includes(s.getAttribute(t));return!0}));i.hasOwnProperty("model")?l.includes(t)?i.model.set({class:""}):i.model.set({class:"ck-hidden"}):l.includes(t)?i.set({class:""}):i.set({class:"ck-hidden"})}_createDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s}=e,l=o.filter((e=>{const i=e.split(":")[1];return t.find((({name:t})=>ie(t,i)===e))})).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==l.length&&Z.warnInvalidStyle({dropdown:e});const d=(0,f.createDropdown)(n,f.SplitButtonView),u=d.buttonView;return(0,f.addToolbarToDropdown)(d,l),u.set({label:te(s,a.label),class:null,tooltip:!0}),u.bind("icon").toMany(l,"isOn",((...e)=>{const t=e.findIndex(ee);return t<0?a.icon:l[t].icon})),u.bind("label").toMany(l,"isOn",((...e)=>{const t=e.findIndex(ee);return te(s,t<0?a.label:l[t].label)})),u.bind("isOn").toMany(l,"isOn",((...e)=>e.some(ee))),u.bind("class").toMany(l,"isOn",((...e)=>e.some(ee)?"ck-splitbutton_flatten":null)),u.on("execute",(()=>{l.some((({isOn:e})=>e))?d.isOpen=!d.isOpen:a.fire("execute")})),d.bind("isEnabled").toMany(l,"isEnabled",((...e)=>e.some(ee))),d}))}_createButton(e,t,i){const n=e.name;this.editor.ui.componentFactory.add(ie(n,t),(a=>{const r=this.editor.commands.get("drupalElementStyle"),o=new f.ButtonView(a);return o.set({label:e.title,icon:e.icon,tooltip:!0,isToggleable:!0}),o.bind("isEnabled").to(r,"isEnabled"),o.bind("isOn").to(r,"value",(e=>e&&e[t]===n)),o.on("execute",this._executeCommand.bind(this,n,t)),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(i,e,o,t)})),o}))}getDropdownListItemDefinitions(e,t,i){const n=new a.Collection;return e.forEach((t=>{const a={type:"button",model:new f.ViewModel({group:i,commandValue:t.name,label:t.title,withText:!0,class:""})};n.add(a),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(e,t,a,i)}))})),n}_createListDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s,defaultText:l}=e,d=e.name.split(":")[1],u=o.filter((e=>t.find((({name:t})=>ie(t,d)===e)))).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==u.length&&Z.warnInvalidStyle({dropdown:e});const c=(0,f.createDropdown)(n,f.DropdownButtonView),m=c.buttonView;m.set({label:te(s,a.label),class:null,tooltip:l,withText:!0});const p=this.editor.commands.get("drupalElementStyle");return m.bind("label").to(p,"value",(e=>{if(e&&e[d])for(const i of t)if(i.name===e[d])return i.title;return l})),c.bind("isOn").to(p),c.bind("isEnabled").to(this),(0,f.addListToDropdown)(c,this.getDropdownListItemDefinitions(t,p,d)),this.listenTo(c,"execute",(e=>{this._executeCommand(e.source.commandValue,e.source.group)})),c}))}_executeCommand(e,t){this.editor.execute("drupalElementStyle",{value:e,group:t}),this.editor.editing.view.focus()}static get pluginName(){return"DrupalElementStyleUi"}}class ae extends e.Plugin{static get requires(){return[Y,ne]}static get pluginName(){return"DrupalElementStyle"}}function re(e){const t=e.getFirstPosition().findAncestor("caption");return t&&r(t.parent)?t:null}function oe(e){for(const t of e.getChildren())if(t&&t.is("element","caption"))return t;return null}class se extends e.Command{refresh(){const e=this.editor.model.document.selection,t=e.getSelectedElement();if(!t)return this.isEnabled=!!s(e,!1),void(this.value=!!re(e));this.isEnabled=r(t,!1),this.isEnabled?this.value=!!oe(t):this.value=!1}execute(e={}){const{focusCaptionOnShow:t}=e;this.editor.model.change((e=>{this.value?this._hideDrupalMediaCaption(e):this._showDrupalMediaCaption(e,t)}))}_showDrupalMediaCaption(e,t){const i=this.editor.model.document.selection,n=this.editor.plugins.get("DrupalMediaCaptionEditing"),a=s(i),r=n._getSavedCaption(a)||e.createElement("caption");e.append(r,a),t&&e.setSelection(r,"in")}_hideDrupalMediaCaption(e){const t=this.editor,i=t.model.document.selection,n=t.plugins.get("DrupalMediaCaptionEditing");let a,r=i.getSelectedElement();r?a=oe(r):(a=re(i),r=s(i)),n._saveCaption(r,a),e.setSelection(r,"on"),e.remove(a)}}class le extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionEditing"}constructor(e){super(e),this._savedCaptionsMap=new WeakMap}init(){const e=this.editor,t=e.model.schema;t.isRegistered("caption")?t.extend("caption",{allowIn:"drupalMedia"}):t.register("caption",{allowIn:"drupalMedia",allowContentOf:"$block",isLimit:!0}),e.commands.add("toggleMediaCaption",new se(e)),this._setupConversion()}_setupConversion(){const e=this.editor,i=e.editing.view;var n;e.conversion.for("upcast").add(function(e){const t=(t,i,n)=>{const{viewItem:a}=i,{writer:r,consumable:o}=n;if(!i.modelRange||!o.consume(a,{attributes:["data-caption"]}))return;const s=r.createElement("caption"),l=i.modelRange.start.nodeAfter,d=e.data.processor.toView(a.getAttribute("data-caption"));n.consumable.constructor.createFrom(d,n.consumable),n.convertChildren(d,s),r.append(s,l)};return e=>{e.on("element:drupal-media",t,{priority:"low"})}}(e)),e.conversion.for("editingDowncast").elementToElement({model:"caption",view:(e,{writer:n})=>{if(!r(e.parent))return null;const a=n.createEditableElement("figcaption");return a.placeholder=Drupal.t("Enter media caption"),(0,I.enablePlaceholder)({view:i,element:a,keepOnFocus:!0}),(0,t.toWidgetEditable)(a,n)}}),e.editing.mapper.on("modelToViewPosition",(n=i,(e,t)=>{const i=t.modelPosition,a=i.parent;if(!r(a))return;const o=t.mapper.toViewElement(a);t.viewPosition=n.createPositionAt(o,i.offset+1)})),e.conversion.for("dataDowncast").add(function(e){return t=>{t.on("insert:caption",((t,i,n)=>{const{consumable:a,writer:o,mapper:s}=n;if(!r(i.item.parent)||!a.consume(i.item,"insert"))return;const l=e.model.createRangeIn(i.item),d=o.createDocumentFragment();s.bindElements(i.item,d);for(const{item:t}of Array.from(l)){const i={item:t,range:e.model.createRangeOn(t)},a=`insert:${t.name||"$text"}`;e.data.downcastDispatcher.fire(a,i,n);for(const a of t.getAttributeKeys())Object.assign(i,{attributeKey:a,attributeOldValue:null,attributeNewValue:i.item.getAttribute(a)}),e.data.downcastDispatcher.fire(`attribute:${a}`,i,n)}for(const e of o.createRangeIn(d).getItems())s.unbindViewElement(e);s.unbindViewElement(d);const u=e.data.processor.toData(d);if(u){const e=s.toViewElement(i.item.parent);o.setAttribute("data-caption",u,e)}}))}}(e))}_getSavedCaption(e){const t=this._savedCaptionsMap.get(e);return t?I.Element.fromJSON(t):null}_saveCaption(e,t){this._savedCaptionsMap.set(e,t.toJSON())}}class de extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionUI"}init(){const{editor:t}=this,i=t.editing.view;t.ui.componentFactory.add("toggleDrupalMediaCaption",(n=>{const a=new f.ButtonView(n),r=t.commands.get("toggleMediaCaption");return a.set({label:Drupal.t("Caption media"),icon:e.icons.caption,tooltip:!0,isToggleable:!0}),a.bind("isOn","isEnabled").to(r,"value","isEnabled"),a.bind("label").to(r,"value",(e=>e?Drupal.t("Toggle caption off"):Drupal.t("Toggle caption on"))),this.listenTo(a,"execute",(()=>{t.execute("toggleMediaCaption",{focusCaptionOnShow:!0});const e=re(t.model.document.selection);if(e){const n=t.editing.mapper.toViewElement(e);i.scrollToTheSelection(),i.change((e=>{e.addClass("drupal-media__caption_highlighted",n)}))}t.editing.view.focus()})),a}))}}class ue extends e.Plugin{static get requires(){return[le,de]}static get pluginName(){return"DrupalMediaCaption"}}const ce={DrupalMedia:V,MediaImageTextAlternative:D,MediaImageTextAlternativeEditing:E,MediaImageTextAlternativeUi:A,DrupalLinkMedia:B,DrupalMediaCaption:ue,DrupalElementStyle:ae}})(),n=n.default})())); -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js -index f1268afdab03696d93b1c76fc450f0699a8f8b13..a04133fba7731b36fb0585c3f9ea9ad5ba032883 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js -@@ -51,7 +51,7 @@ export default class ToggleDrupalMediaCaptionCommand extends Command { - if (!selectedElement) { - // Command should be enabled if `<drupalMedia>` element is part of the - // selection. -- this.isEnabled = !!getClosestSelectedDrupalMediaElement(selection); -+ this.isEnabled = !!getClosestSelectedDrupalMediaElement(selection, false); - // Check if the selection descends from a `<drupalMedia>` element that - // also includes a `<caption>`. - this.value = !!getMediaCaptionFromModelSelection(selection); -@@ -60,7 +60,7 @@ export default class ToggleDrupalMediaCaptionCommand extends Command { - } - - // If single element is selected, check if it's a `<drupalMedia>` element. -- this.isEnabled = isDrupalMedia(selectedElement); -+ this.isEnabled = isDrupalMedia(selectedElement, false); - - if (!this.isEnabled) { - this.value = false; -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js -index 9b9b582482a7f90b25e75d3d32e535a9651e00ba..34aef3fa935b29f02fab048609fd0a83e789dc59 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js -@@ -210,6 +210,13 @@ export default class DrupalMediaEditing extends Plugin { - inheritAllFrom: '$blockObject', - allowAttributes: Object.keys(this.attrs), - }); -+ -+ schema.register('drupalMediaInline', { -+ inheritAllFrom: '$inlineObject', -+ allowIn: ['$block', '$container', '$text'], -+ allowAttributes: Object.keys(this.attrs), -+ }); -+ - // Register `<drupal-media>` as a block element in the DOM converter. This - // ensures that the DOM converter knows to handle the `<drupal-media>` as a - // block element. -@@ -227,212 +234,228 @@ export default class DrupalMediaEditing extends Plugin { - 'DrupalMediaMetadataRepository', - ); - -- conversion -- .for('upcast') -- .elementToElement({ -+ const viewToModelMap = { -+ 'drupal-media': 'drupalMedia', -+ 'drupal-media-inline': 'drupalMediaInline', -+ }; -+ -+ Object.keys(viewToModelMap).forEach((view) => { -+ const model = viewToModelMap[view]; -+ -+ conversion -+ .for('upcast') -+ .elementToElement({ -+ view: { -+ name: view, -+ }, -+ model, -+ }) -+ .add((dispatcher) => { -+ dispatcher.on( -+ `element:${view}`, -+ (evt, data) => { -+ const [modelElement] = data.modelRange.getItems(); -+ metadataRepository -+ .getMetadata(modelElement) -+ .then((metadata) => { -+ if (!modelElement) { -+ return; -+ } -+ // On upcast, get `drupalMediaIsImage` attribute value from media metadata -+ // repository. -+ this.upcastDrupalMediaIsImage(modelElement); -+ // Enqueue a model change after getting modelElement. -+ this.editor.model.enqueueChange( -+ { isUndoable: false }, -+ (writer) => { -+ writer.setAttribute( -+ 'drupalMediaType', -+ metadata.type, -+ modelElement, -+ ); -+ }, -+ ); -+ }) -+ .catch((e) => { -+ // There isn't any UI indication for errors because this should be -+ // always called after the Drupal Media has been upcast, which would -+ // already display an error in the UI. -+ console.warn(e.toString()); -+ }); -+ }, -+ // This converter needs to have the lowest priority to ensure that the -+ // model element and its attributes have already been converted. It is only used -+ // to gather metadata to make the UI tailored to the specific media entity that -+ // is being dealt with. -+ { priority: 'lowest' }, -+ ); -+ }); -+ -+ conversion.for('dataDowncast').elementToElement({ -+ model, - view: { -- name: 'drupal-media', -+ name: view, - }, -- model: 'drupalMedia', -- }) -- .add((dispatcher) => { -- dispatcher.on( -- 'element:drupal-media', -- (evt, data) => { -- const [modelElement] = data.modelRange.getItems(); -- metadataRepository -- .getMetadata(modelElement) -- .then((metadata) => { -- if (!modelElement) { -- return; -- } -- // On upcast, get `drupalMediaIsImage` attribute value from media metadata -- // repository. -- this.upcastDrupalMediaIsImage(modelElement); -- // Enqueue a model change after getting modelElement. -- this.editor.model.enqueueChange( -- { isUndoable: false }, -- (writer) => { -- writer.setAttribute( -- 'drupalMediaType', -- metadata.type, -- modelElement, -- ); -- }, -- ); -- }) -- .catch((e) => { -- // There isn't any UI indication for errors because this should be -- // always called after the Drupal Media has been upcast, which would -- // already display an error in the UI. -- console.warn(e.toString()); -- }); -- }, -- // This converter needs to have the lowest priority to ensure that the -- // model element and its attributes have already been converted. It is only used -- // to gather metadata to make the UI tailored to the specific media entity that -- // is being dealt with. -- { priority: 'lowest' }, -- ); - }); -- -- conversion.for('dataDowncast').elementToElement({ -- model: 'drupalMedia', -- view: { -- name: 'drupal-media', -- }, -- }); -- conversion -- .for('editingDowncast') -- .elementToElement({ -- model: 'drupalMedia', -- view: (modelElement, { writer }) => { -- const container = writer.createContainerElement('figure', { -- class: 'drupal-media', -- }); -- if (!this.previewUrl) { -- // If preview URL isn't available, insert empty preview element -- // which indicates that preview couldn't be loaded. -- const mediaPreview = writer.createRawElement('div', { -- 'data-drupal-media-preview': 'unavailable', -+ conversion -+ .for('editingDowncast') -+ .elementToElement({ -+ model, -+ view: (modelElement, { writer }) => { -+ const container = writer.createContainerElement('figure', { -+ class: -+ view === 'drupal-media-inline' ? `drupal-media ${view}` : view, - }); -- writer.insert(writer.createPositionAt(container, 0), mediaPreview); -- } -- writer.setCustomProperty('drupalMedia', true, container); -- -- return toWidget(container, writer, { -- label: Drupal.t('Media widget'), -- }); -- }, -- }) -- .add((dispatcher) => { -- const converter = (event, data, conversionApi) => { -- const viewWriter = conversionApi.writer; -- const modelElement = data.item; -- const container = conversionApi.mapper.toViewElement(data.item); -- -- // Search for preview container recursively from its children because -- // the preview container could be wrapped with an element such as -- // `<a>`. -- let media = getPreviewContainer(container.getChildren()); -- -- // Use pre-existing media preview container if one exists. If the -- // preview element doesn't exist, create a new element. -- if (media) { -- // Stop processing if media preview is unavailable or a preview is -- // already loading. -- if (media.getAttribute('data-drupal-media-preview') !== 'ready') { -- return; -+ if (!this.previewUrl) { -+ // If preview URL isn't available, insert empty preview element -+ // which indicates that preview couldn't be loaded. -+ const mediaPreview = writer.createRawElement('div', { -+ 'data-drupal-media-preview': 'unavailable', -+ }); -+ writer.insert( -+ writer.createPositionAt(container, 0), -+ mediaPreview, -+ ); - } -+ writer.setCustomProperty(model, true, container); - -- // Preview was ready meaning that a new preview can be loaded. -- // "Change the attribute to loading to prepare for the loading of -- // the updated preview. Preview is kept intact so that it remains -- // interactable in the UI until the new preview has been rendered. -- viewWriter.setAttribute( -- 'data-drupal-media-preview', -- 'loading', -- media, -- ); -- } else { -- media = viewWriter.createRawElement('div', { -- 'data-drupal-media-preview': 'loading', -+ return toWidget(container, writer, { -+ label: Drupal.t('Media widget'), - }); -- viewWriter.insert(viewWriter.createPositionAt(container, 0), media); -- } -- -- this._fetchPreview(modelElement).then(({ label, preview }) => { -- if (!media) { -- // Nothing to do if associated preview wrapped no longer exist. -- return; -- } -- // CKEditor 5 doesn't support async view conversion. Therefore, once -- // the promise is fulfilled, the editing view needs to be modified -- // manually. -- this.editor.editing.view.change((writer) => { -- const mediaPreview = writer.createRawElement( -- 'div', -- { 'data-drupal-media-preview': 'ready', 'aria-label': label }, -- (domElement) => { -- domElement.innerHTML = preview; -- }, -+ }, -+ }) -+ .add((dispatcher) => { -+ const converter = (event, data, conversionApi) => { -+ const viewWriter = conversionApi.writer; -+ const modelElement = data.item; -+ const container = conversionApi.mapper.toViewElement(data.item); -+ -+ // Search for preview container recursively from its children because -+ // the preview container could be wrapped with an element such as -+ // `<a>`. -+ let media = getPreviewContainer(container.getChildren()); -+ -+ // Use pre-existing media preview container if one exists. If the -+ // preview element doesn't exist, create a new element. -+ if (media) { -+ // Stop processing if media preview is unavailable or a preview is -+ // already loading. -+ if (media.getAttribute('data-drupal-media-preview') !== 'ready') { -+ return; -+ } -+ -+ // Preview was ready meaning that a new preview can be loaded. -+ // "Change the attribute to loading to prepare for the loading of -+ // the updated preview. Preview is kept intact so that it remains -+ // interactable in the UI until the new preview has been rendered. -+ viewWriter.setAttribute( -+ 'data-drupal-media-preview', -+ 'loading', -+ media, - ); -- // Insert the new preview before the previous preview element to -- // ensure that the location remains same even if it is wrapped -- // with another element. -- writer.insert(writer.createPositionBefore(media), mediaPreview); -- writer.remove(media); -+ } else { -+ media = viewWriter.createRawElement('div', { -+ 'data-drupal-media-preview': 'loading', -+ }); -+ viewWriter.insert( -+ viewWriter.createPositionAt(container, 0), -+ media, -+ ); -+ } -+ -+ this._fetchPreview(modelElement).then(({ label, preview }) => { -+ if (!media) { -+ // Nothing to do if associated preview wrapped no longer exist. -+ return; -+ } -+ // CKEditor 5 doesn't support async view conversion. Therefore, once -+ // the promise is fulfilled, the editing view needs to be modified -+ // manually. -+ this.editor.editing.view.change((writer) => { -+ const mediaPreview = writer.createRawElement( -+ 'div', -+ { 'data-drupal-media-preview': 'ready', 'aria-label': label }, -+ (domElement) => { -+ domElement.innerHTML = preview; -+ }, -+ ); -+ // Insert the new preview before the previous preview element to -+ // ensure that the location remains same even if it is wrapped -+ // with another element. -+ writer.insert(writer.createPositionBefore(media), mediaPreview); -+ writer.remove(media); -+ }); - }); -+ }; -+ -+ // List all attributes that should trigger re-rendering of the -+ // preview. -+ this.converterAttributes.forEach((attribute) => { -+ dispatcher.on(`attribute:${attribute}:${model}`, converter); - }); -- }; - -- // List all attributes that should trigger re-rendering of the -- // preview. -- this.converterAttributes.forEach((attribute) => { -- dispatcher.on(`attribute:${attribute}:drupalMedia`, converter); -+ return dispatcher; - }); - -- return dispatcher; -- }); -+ conversion.for('editingDowncast').add((dispatcher) => { -+ dispatcher.on( -+ `attribute:drupalElementStyleAlign:${model}`, -+ (evt, data, conversionApi) => { -+ const alignMapping = { -+ // This is a map of CSS classes representing Drupal element styles for alignments. -+ left: 'drupal-media-style-align-left', -+ right: 'drupal-media-style-align-right', -+ center: 'drupal-media-style-align-center', -+ }; -+ const viewElement = conversionApi.mapper.toViewElement(data.item); -+ const viewWriter = conversionApi.writer; -+ -+ // If the prior value is alignment related, it should be removed -+ // whether or not the module property is consumed. -+ if (alignMapping[data.attributeOldValue]) { -+ viewWriter.removeClass( -+ alignMapping[data.attributeOldValue], -+ viewElement, -+ ); -+ } - -- conversion.for('editingDowncast').add((dispatcher) => { -- dispatcher.on( -- 'attribute:drupalElementStyleAlign:drupalMedia', -- (evt, data, conversionApi) => { -- const alignMapping = { -- // This is a map of CSS classes representing Drupal element styles for alignments. -- left: 'drupal-media-style-align-left', -- right: 'drupal-media-style-align-right', -- center: 'drupal-media-style-align-center', -- }; -- const viewElement = conversionApi.mapper.toViewElement(data.item); -- const viewWriter = conversionApi.writer; -- -- // If the prior value is alignment related, it should be removed -- // whether or not the module property is consumed. -- if (alignMapping[data.attributeOldValue]) { -- viewWriter.removeClass( -- alignMapping[data.attributeOldValue], -+ // If the new value is not alignment related, do not proceed. -+ if (!alignMapping[data.attributeNewValue]) { -+ return; -+ } -+ -+ // The model property is already consumed, do not proceed. -+ if (!conversionApi.consumable.consume(data.item, evt.name)) { -+ return; -+ } -+ -+ // Add the alignment class in the view that corresponds to the value -+ // of the model's drupalElementStyle property. -+ viewWriter.addClass( -+ alignMapping[data.attributeNewValue], - viewElement, - ); -- } -- -- // If the new value is not alignment related, do not proceed. -- if (!alignMapping[data.attributeNewValue]) { -- return; -- } -- -- // The model property is already consumed, do not proceed. -- if (!conversionApi.consumable.consume(data.item, evt.name)) { -- return; -- } -- -- // Add the alignment class in the view that corresponds to the value -- // of the model's drupalElementStyle property. -- viewWriter.addClass( -- alignMapping[data.attributeNewValue], -- viewElement, -- ); -- }, -- ); -- }); -+ }, -+ ); -+ }); - -- // Set attributeToAttribute conversion for all supported attributes. -- Object.keys(this.attrs).forEach((modelKey) => { -- const attributeMapping = { -- model: { -- key: modelKey, -- name: 'drupalMedia', -- }, -- view: { -- name: 'drupal-media', -- key: this.attrs[modelKey], -- }, -- }; -- // Attributes should be rendered only in dataDowncast to avoid having -- // unfiltered data-attributes on the Drupal Media widget. -- conversion.for('dataDowncast').attributeToAttribute(attributeMapping); -- conversion.for('upcast').attributeToAttribute(attributeMapping); -+ // Set attributeToAttribute conversion for all supported attributes. -+ Object.keys(this.attrs).forEach((modelKey) => { -+ const attributeMapping = { -+ model: { -+ key: modelKey, -+ name: model, -+ }, -+ view: { -+ name: view, -+ key: this.attrs[modelKey], -+ }, -+ }; -+ // Attributes should be rendered only in dataDowncast to avoid having -+ // unfiltered data-attributes on the Drupal Media widget. -+ conversion.for('dataDowncast').attributeToAttribute(attributeMapping); -+ conversion.for('upcast').attributeToAttribute(attributeMapping); -+ }); - }); - } - -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js -index 66ca7dbee62ee9bb97f9bed0f95a2ec718903f18..d65914e909bb479bf859a0bec9f0c1c049f6fdfc 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js -@@ -5,18 +5,22 @@ import { setViewAttributes } from '@ckeditor/ckeditor5-html-support/src/utils'; - - /** - * View-to-model conversion helper for Drupal Media. -- * Used for preserving allowed attributes on the Drupal Media model. -+ * Used for preserving allowed attributes on the Drupal Media models. - * -+ * @param {string} model -+ * The model name (DrupalMedia or DrupalMediaInline). -+ * @param {string} view -+ * The view name (drupal-media or drupal-media-inline). - * @param {module:html-support/datafilter~DataFilter} dataFilter - * The General HTML support data filter. - * - * @return {function} - * Function that adds an event listener to upcastDispatcher. - */ --function viewToModelDrupalMediaAttributeConverter(dataFilter) { -+function viewToModelDrupalMediaAttributeConverter(dataFilter, model, view) { - return (dispatcher) => { - dispatcher.on( -- 'element:drupal-media', -+ `element:${view}`, - (evt, data, conversionApi) => { - function preserveElementAttributes(viewElement, attributeName) { - const viewAttributes = dataFilter.processViewAttributes( -@@ -96,18 +100,21 @@ function modelToDataAttributeConverter(evt, data, conversionApi) { - /** - * Model to editing view attribute converter. - * -+ * @param {string} model -+ * The model name (DrupalMedia or DrupalMediaInline). -+ * - * @return {function} - * A function that adds an event listener to downcastDispatcher. - */ --function modelToEditingViewAttributeConverter() { -+function modelToEditingViewAttributeConverter(model) { - return (dispatcher) => { - dispatcher.on( -- 'attribute:linkHref:drupalMedia', -+ `attribute:linkHref:${model}`, - (evt, data, conversionApi) => { - if ( - !conversionApi.consumable.consume( - data.item, -- 'attribute:htmlLinkAttributes:drupalMedia', -+ `attribute:htmlLinkAttributes:${model}`, - ) - ) { - return; -@@ -134,18 +141,21 @@ function modelToEditingViewAttributeConverter() { - /** - * Model to data view attribute converter. - * -+ * @param {string} model -+ * The model name (DrupalMedia or DrupalMediaInline). -+ * - * @return {function} - * Function that adds an event listener to downcastDispatcher. - */ --function modelToDataViewAttributeConverter() { -+function modelToDataViewAttributeConverter(model) { - return (dispatcher) => { - dispatcher.on( -- 'attribute:linkHref:drupalMedia', -+ `attribute:linkHref:${model}`, - (evt, data, conversionApi) => { - if ( - !conversionApi.consumable.consume( - data.item, -- 'attribute:htmlLinkAttributes:drupalMedia', -+ `attribute:htmlLinkAttributes:${model}`, - ) - ) { - return; -@@ -163,7 +173,7 @@ function modelToDataViewAttributeConverter() { - ); - - dispatcher.on( -- 'attribute:htmlAttributes:drupalMedia', -+ `attribute:htmlAttributes:${model}`, - modelToDataAttributeConverter, - { priority: 'low' }, - ); -@@ -204,32 +214,45 @@ export default class DrupalMediaGeneralHtmlSupport extends Plugin { - const dataFilter = this.editor.plugins.get('DataFilter'); - const dataSchema = this.editor.plugins.get('DataSchema'); - -- // This needs to be initialized in ::constructor() to ensure this runs -- // before the General HTML Support has been initialized. -- // @see module:html-support/generalhtmlsupport~GeneralHtmlSupport -- dataSchema.registerBlockElement({ -- model: 'drupalMedia', -- view: 'drupal-media', -- }); -+ const viewToModelMap = { -+ 'drupal-media': 'drupalMedia', -+ 'drupal-media-inline': 'drupalMediaInline', -+ }; - -- dataFilter.on('register:drupal-media', (evt, definition) => { -- if (definition.model !== 'drupalMedia') { -- return; -- } -+ Object.keys(viewToModelMap).forEach((view) => { -+ const model = viewToModelMap[view]; - -- schema.extend('drupalMedia', { -- allowAttributes: ['htmlLinkAttributes', 'htmlAttributes'], -+ // This needs to be initialized in ::constructor() to ensure this runs -+ // before the General HTML Support has been initialized. -+ // @see module:html-support/generalhtmlsupport~GeneralHtmlSupport -+ dataSchema.registerBlockElement({ -+ model, -+ view, - }); - -- conversion -- .for('upcast') -- .add(viewToModelDrupalMediaAttributeConverter(dataFilter)); -- conversion -- .for('editingDowncast') -- .add(modelToEditingViewAttributeConverter()); -- conversion.for('dataDowncast').add(modelToDataViewAttributeConverter()); -+ dataFilter.on(`register:${view}`, (evt, definition) => { -+ if (definition.model !== model) { -+ return; -+ } - -- evt.stop(); -+ schema.extend(model, { -+ allowAttributes: ['htmlLinkAttributes', 'htmlAttributes'], -+ }); -+ -+ conversion -+ .for('upcast') -+ .add( -+ viewToModelDrupalMediaAttributeConverter(dataFilter, model, view), -+ ); -+ conversion -+ .for('editingDowncast') -+ .add(modelToEditingViewAttributeConverter(model)); -+ conversion -+ .for('dataDowncast') -+ .add(modelToDataViewAttributeConverter(model)); -+ -+ evt.stop(); -+ }); - }); - } - -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js -index bf9c6451e5330880b689017e3fcf8ade05f0f9f7..5cd6337426eb1cd499b7893dba0ccd7a9ddb921c 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js -@@ -1,15 +1,42 @@ - /* eslint-disable import/no-extraneous-dependencies */ - // cspell:ignore insertdrupalmediacommand - import { Command } from 'ckeditor5/src/core'; -+import { first } from 'ckeditor5/src/utils'; - import { groupNameToModelAttributeKey } from './utils'; - - /** - * @module drupalMedia/insertdrupalmediacommand - */ - --function createDrupalMedia(writer, attributes) { -- const drupalMedia = writer.createElement('drupalMedia', attributes); -- return drupalMedia; -+function createDrupalMedia(writer, attributes, model) { -+ return writer.createElement(model, attributes); -+} -+ -+function determineImageTypeForInsertionAtSelection(schema, selection) { -+ const firstBlock = first(selection.getSelectedBlocks()); -+ // Insert a block media if the selection is not in/on block elements or it's -+ // on a block widget. -+ if (!firstBlock || schema.isObject(firstBlock)) { -+ return 'drupalMedia'; -+ } -+ // A block image should also be inserted into an empty block element -+ // (that is not an empty list item so the list won't get split). -+ if (firstBlock.isEmpty && firstBlock.name !== 'listItem') { -+ return 'drupalMedia'; -+ } -+ // Otherwise insert an inline media. -+ return 'drupalMediaInline'; -+} -+ -+function determineImageTypeForInsertion(editor, selectable) { -+ const schema = editor.model.schema; -+ // Try to replace the selected widget (e.g. another image). -+ if (selectable.is('selection')) { -+ return determineImageTypeForInsertionAtSelection(schema, selectable); -+ } -+ return schema.checkChild(selectable, 'drupalMediaInline') -+ ? 'drupalMediaInline' -+ : 'drupalMedia'; - } - - /** -@@ -83,9 +110,14 @@ export default class InsertDrupalMediaCommand extends Command { - } - } - -+ const insertedModel = determineImageTypeForInsertion( -+ this.editor, -+ this.editor.model.document.selection, -+ ); -+ - this.editor.model.change((writer) => { - this.editor.model.insertObject( -- createDrupalMedia(writer, modelAttributes), -+ createDrupalMedia(writer, modelAttributes, insertedModel), - ); - }); - } -@@ -93,9 +125,10 @@ export default class InsertDrupalMediaCommand extends Command { - refresh() { - const model = this.editor.model; - const selection = model.document.selection; -+ const mediaModel = determineImageTypeForInsertion(this.editor, selection); - const allowedIn = model.schema.findAllowedParent( - selection.getFirstPosition(), -- 'drupalMedia', -+ mediaModel, - ); - this.isEnabled = allowedIn !== null; - } -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js -index afd77f4cd9828c77c226ddda92437775f3cd8686..180fdc3f3b2b01944f749c7788a9fed36f4e14d6 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js -@@ -7,12 +7,25 @@ import { isWidget } from 'ckeditor5/src/widget'; - * - * @param {module:engine/model/element~Element} modelElement - * The model element to be checked. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {boolean} - * A boolean indicating if the element is a drupalMedia element. - * - * @private - */ --export function isDrupalMedia(modelElement) { -+export function isDrupalMedia(modelElement, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } -+ if (includeInline) { -+ return ( -+ !!modelElement && -+ (modelElement.is('element', 'drupalMedia') || -+ modelElement.is('element', 'drupalMediaInline')) -+ ); -+ } - return !!modelElement && modelElement.is('element', 'drupalMedia'); - } - -@@ -21,12 +34,25 @@ export function isDrupalMedia(modelElement) { - * - * @param {module:engine/view/element~Element} viewElement - * The view element. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {boolean} - * A boolean indicating if the element is a <drupal-media> element. - * - * @private - */ --export function isDrupalMediaWidget(viewElement) { -+export function isDrupalMediaWidget(viewElement, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } -+ if (includeInline) { -+ return ( -+ isWidget(viewElement) && -+ (!!viewElement.getCustomProperty('drupalMedia') || -+ !!viewElement.getCustomProperty('drupalMediaInline')) -+ ); -+ } - return ( - isWidget(viewElement) && !!viewElement.getCustomProperty('drupalMedia') - ); -@@ -37,6 +63,9 @@ export function isDrupalMediaWidget(viewElement) { - * - * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection - * The current selection. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {module:engine/model/element~Element|null} - * The `drupalMedia` element which could be either the current selected an - * ancestor of the selection. Returns null if the selection has no Drupal -@@ -44,12 +73,17 @@ export function isDrupalMediaWidget(viewElement) { - * - * @private - */ --export function getClosestSelectedDrupalMediaElement(selection) { -+export function getClosestSelectedDrupalMediaElement(selection, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } - const selectedElement = selection.getSelectedElement(); - -- return isDrupalMedia(selectedElement) -+ return isDrupalMedia(selectedElement, includeInline) - ? selectedElement -- : selection.getFirstPosition().findAncestor('drupalMedia'); -+ : selection -+ .getFirstPosition() -+ .findAncestor(includeInline ? /drupalMedia(Inline)?/ : /drupalMedia/); - } - - /** -@@ -57,14 +91,20 @@ export function getClosestSelectedDrupalMediaElement(selection) { - * - * @param {module:engine/model/selection~Selection} selection - * The current selection. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {module:engine/view/element~Element|null} - * The currently selected Drupal Media widget or null. - * - * @private - */ --export function getClosestSelectedDrupalMediaWidget(selection) { -+export function getClosestSelectedDrupalMediaWidget(selection, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } - const viewElement = selection.getSelectedElement(); -- if (viewElement && isDrupalMediaWidget(viewElement)) { -+ if (viewElement && isDrupalMediaWidget(viewElement, includeInline)) { - return viewElement; - } - -@@ -76,7 +116,7 @@ export function getClosestSelectedDrupalMediaWidget(selection) { - let parent = selection.getFirstPosition().parent; - - while (parent) { -- if (parent.is('element') && isDrupalMediaWidget(parent)) { -+ if (parent.is('element') && isDrupalMediaWidget(parent, includeInline)) { - return parent; - } - -diff --git a/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php b/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php -index e06b2422ada859fd14c523fea60e6077bb3acea3..cbbdca8e1d67b563aca2bf93dbefc5104aa1fd39 100644 ---- a/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php -+++ b/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php -@@ -121,7 +121,7 @@ private function configureViewModes(EditorInterface $editor) { - 'title' => $all_view_modes[$view_mode], - 'attributeName' => 'data-view-mode', - 'attributeValue' => $view_mode, -- 'modelElements' => ['drupalMedia'], -+ 'modelElements' => ['drupalMedia', 'drupalMediaInline'], - 'modelAttributes' => [ - 'drupalMediaType' => array_keys($media_bundles), - ], -@@ -133,7 +133,7 @@ private function configureViewModes(EditorInterface $editor) { - 'title' => $all_view_modes[$view_mode], - 'attributeName' => 'data-view-mode', - 'attributeValue' => $view_mode, -- 'modelElements' => ['drupalMedia'], -+ 'modelElements' => ['drupalMedia', 'drupalMediaInline'], - 'modelAttributes' => [ - 'drupalMediaType' => $specific_bundles, - ], -@@ -196,7 +196,7 @@ public function getElementsSubset(): array { - $subset = $this->getPluginDefinition()->getElements(); - $view_mode_override_enabled = $this->getConfiguration()['allow_view_mode_override']; - if (!$view_mode_override_enabled) { -- $subset = array_diff($subset, ['<drupal-media data-view-mode>']); -+ $subset = array_diff($subset, ['<drupal-media data-view-mode>', '<drupal-media-inline data-view-mode>']); - } - return $subset; - } -diff --git a/core/modules/ckeditor5/templates/field--ckeditor-inline.html.twig b/core/modules/ckeditor5/templates/field--ckeditor-inline.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..7f0ae06f8369db15ea4b70771cacfc9a61eff771 ---- /dev/null -+++ b/core/modules/ckeditor5/templates/field--ckeditor-inline.html.twig -@@ -0,0 +1,72 @@ -+{# -+/** -+ * @file -+ * Default theme implementation for a field. -+ * -+ * To override output, copy the "field.html.twig" from the templates directory -+ * to your theme's directory and customize it, just like customizing other -+ * Drupal templates such as page.html.twig or node.html.twig. -+ * -+ * Instead of overriding the theming for all fields, you can also just override -+ * theming for a subset of fields using -+ * @link themeable Theme hook suggestions. @endlink For example, -+ * here are some theme hook suggestions that can be used for a field_foo field -+ * on an article node type: -+ * - field--node--field-foo--article.html.twig -+ * - field--node--field-foo.html.twig -+ * - field--node--article.html.twig -+ * - field--field-foo.html.twig -+ * - field--text-with-summary.html.twig -+ * - field.html.twig -+ * -+ * Available variables: -+ * - attributes: HTML attributes for the containing element. -+ * - label_hidden: Whether to show the field label or not. -+ * - title_attributes: HTML attributes for the title. -+ * - label: The label for the field. -+ * - multiple: TRUE if a field can contain multiple items. -+ * - items: List of all the field items. Each item contains: -+ * - attributes: List of HTML attributes for each item. -+ * - content: The field item's content. -+ * - entity_type: The entity type to which the field belongs. -+ * - field_name: The name of the field. -+ * - field_type: The type of the field. -+ * - label_display: The display settings for the label. -+ * -+ * @see template_preprocess_field() -+ * -+ * @ingroup themeable -+ */ -+#} -+{% -+ set title_classes = [ -+ label_display == 'visually_hidden' ? 'visually-hidden', -+ ] -+%} -+ -+{% if label_hidden %} -+ {% if multiple %} -+ <span{{ attributes }}> -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ </span> -+ {% else %} -+ {% for item in items %} -+ <span{{ attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% endif %} -+{% else %} -+ <span{{ attributes }}> -+ <span{{ title_attributes.addClass(title_classes) }}>{{ label }}</span> -+ {% if multiple %} -+ <span> -+ {% endif %} -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% if multiple %} -+ </span> -+ {% endif %} -+ </span> -+{% endif %} -diff --git a/core/modules/ckeditor5/templates/media--ckeditor-inline-embed.html.twig b/core/modules/ckeditor5/templates/media--ckeditor-inline-embed.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..18a903f24544a90cae12c4a7d2008dd6b340fba5 ---- /dev/null -+++ b/core/modules/ckeditor5/templates/media--ckeditor-inline-embed.html.twig -@@ -0,0 +1,36 @@ -+{# -+/** -+ * @file -+ * Default theme implementation to present a media item. -+ * -+ * Available variables: -+ * - media: The media item, with limited access to object properties and -+ * methods. Only method names starting with "get", "has", or "is" and -+ * a few common methods such as "id", "label", and "bundle" are available. -+ * For example: -+ * - entity.getEntityTypeId() will return the entity type ID. -+ * - entity.hasField('field_example') returns TRUE if the entity includes -+ * field_example. (This does not indicate the presence of a value in this -+ * field.) -+ * Calling other methods, such as entity.delete(), will result in -+ * an exception. -+ * See \Drupal\Core\Entity\EntityInterface for a full list of methods. -+ * - name: Name of the media item. -+ * - content: Media content. -+ * - title_prefix: Additional output populated by modules, intended to be -+ * displayed in front of the main title tag that appears in the template. -+ * - title_suffix: Additional output populated by modules, intended to be -+ * displayed after the main title tag that appears in the template. -+ * - view_mode: View mode; for example, "teaser" or "full". -+ * - attributes: HTML attributes for the containing element. -+ * - title_attributes: Same as attributes, except applied to the main title -+ * tag that appears in the template. -+ * -+ * @see template_preprocess_media() -+ * -+ * @ingroup themeable -+ */ -+#} -+<span{{ attributes }}> -+ {{ content }} -+</span> -diff --git a/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php b/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php -index 2601e97e4902108b5ab24dd0a8c39ec0c6ab8c99..9f9ca043ee0cfa90a17c87a4ea1f8c5fd6806e79 100644 ---- a/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php -+++ b/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php -@@ -115,7 +115,7 @@ protected function setUp(): void { - 'status' => TRUE, - 'weight' => -10, - 'settings' => [ -- 'allowed_html' => "<p> <br> <drupal-media data-entity-type data-entity-uuid data-view-mode alt>", -+ 'allowed_html' => "<p> <br> <drupal-media data-entity-type data-entity-uuid data-view-mode alt> <drupal-media-inline data-entity-type data-entity-uuid data-view-mode alt>", - 'filter_html_help' => TRUE, - 'filter_html_nofollow' => TRUE, - ], -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php -index ca9cb17ca62bc9bb80b6fa207d88822402fcb77c..fcef2b9b2fa87a2748ddb1e14009da65b9118e5a 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php -@@ -378,8 +378,8 @@ public function testMediaElementAllowedTags(): void { - $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_1]'); - $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_2]'); - -- $allowed_with_media = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode>'; -- $allowed_with_media_without_view_mode = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt>'; -+ $allowed_with_media = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode> <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode>'; -+ $allowed_with_media_without_view_mode = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt> <drupal-media-inline data-entity-type data-entity-uuid alt>'; - $page->clickLink('Media'); - $this->assertTrue($page->hasUncheckedField('editor[settings][plugins][media_media][allow_view_mode_override]')); - $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', $allowed_with_media_without_view_mode); -@@ -403,7 +403,7 @@ public function testMediaElementAllowedTags(): void { - // filter_align is enabled. - $page->checkField('filters[filter_align][status]'); - $assert_session->assertExpectedAjaxRequest(1); -- $this->assertEquals($this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode data-align>', $allowed_html_field->getValue()); -+ $this->assertEquals($this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode data-align> <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode data-align>', $allowed_html_field->getValue()); - - // Disable media embed. - $this->assertTrue($page->hasCheckedField('filters[media_embed][status]')); -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php -index d22a3705798862f6484cd74ce9f38239b93a8823..0f10b1aa37782e06bbf96c206682dcafd6e25f5c 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php -@@ -40,7 +40,7 @@ public function testLinkedMediaArbitraryHtml(bool $unrestricted): void { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href data-foo> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <div data-bar>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href data-foo> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <div data-bar> <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode data-caption data-align>', - ], - ]); - } -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php -index 92a3c3d2feecaaf10771bbf903bb27cb6372d71b..c929bd58c72e46f2ccc59c593dab25a5a1fbbd0d 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php -@@ -110,7 +110,7 @@ protected function testMediaSplitList(): void { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <ol> <ul> <li>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <drupal-media-inline data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <ol> <ul> <li>', - ], - ]); - $filter_format->save(); -@@ -165,7 +165,7 @@ public function testMediaArbitraryHtml(): void { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-foo data-view-mode> <div data-bar>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-foo data-view-mode> <drupal-media-inline data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <div data-bar>', - ], - ]); - $filter_format->save(); -@@ -703,7 +703,7 @@ public function testDrupalMediaStyleWithClass(): void { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <h1 class> <div class> <section class> <drupal-media data-entity-type data-entity-uuid data-align data-caption data-view-mode alt class="layercake-side">', -+ 'allowed_html' => '<p> <br> <h1 class> <div class> <section class> <drupal-media data-entity-type data-entity-uuid data-align data-caption data-view-mode alt class="layercake-side"> <drupal-media-inline data-entity-type data-entity-uuid data-align data-caption data-view-mode alt>', - ], - ]); - $filter_format->save(); -@@ -1019,6 +1019,56 @@ public function testViewMode(bool $with_alignment): void { - $this->assertNotEmpty($this->getBalloonButton('View Mode 4')); - } - -+ /** -+ * Tests inline embedding. -+ */ -+ public function testInlineMedia() { -+ // Reconfigure the text format to suit our needs. -+ /** @var \Drupal\filter\FilterFormatInterface $format */ -+ $format = FilterFormat::load($this->host->body->format); -+ $filter_html = $format->get('filters')['filter_html']; -+ $filter_html['settings']['allowed_html'] = $filter_html['settings']['allowed_html'] . '<ul> <li>'; -+ $format->setFilterConfig('filter_html', $filter_html); -+ $format->save(); -+ -+ // Setup the test content containing inline and normal media in the editor. -+ $assert_session = $this->assertSession(); -+ $original_value = $this->host->body->value; -+ $inline_value = \str_replace('drupal-media', 'drupal-media-inline', $original_value); -+ // @todo Captions on inline media are not supported yet. -+ $inline_value = \str_replace('data-caption="baz"', '', $inline_value); -+ $this->host->body->value = <<<END -+ <div> -+ $original_value -+ <p>This is a paragraph $inline_value with an inline media</p> -+ <ul> -+ <li>This is a list item $inline_value containing an inline media</li> -+ </ul> -+ </div> -+END; -+ $this->host->save(); -+ $this->drupalGet($this->host->toUrl('edit-form')); -+ -+ // Wait until media previews are fetched. -+ $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-widget.drupal-media-inline')); -+ $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-widget.drupal-media')); -+ -+ // Confirm that the inline media is contained as phrasing content inside -+ // flow content. -+ $assert_session->elementExists('css', 'p > .drupal-media-inline'); -+ $assert_session->elementExists('css', 'ul > li > .drupal-media-inline'); -+ // Also ensure that a media not embedded inside flow content is still -+ // rendered not using the inline templates. -+ $assert_session->elementExists('css', 'div > .drupal-media'); -+ -+ // Verify the rendered inline media is rendered with markup that is using -+ // the dedicated inline media and field templates. -+ $this->drupalGet($this->host->toUrl('canonical')); -+ $assert_session->elementExists('css', 'p > span.media-embedded-inline span img[src*="image-test.png"]'); -+ $assert_session->elementExists('css', 'ul > li > span.media-embedded-inline span img[src*="image-test.png"]'); -+ $assert_session->elementExists('css', 'div > figure.caption-drupal-media article.media img[src*="image-test.png"]'); -+ } -+ - /** - * For testing view modes in different scenarios. - */ -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php -index ea8a8c83573f27df064a9567f5cfe9cb5fdac51e..1ab23473514d7011b95283fdb9cd9f839d05cecc 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php -@@ -101,7 +101,7 @@ protected function setUp(): void { - 'filter_html' => [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-view-mode data-caption alt>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-view-mode data-caption alt> <drupal-media-inline data-entity-type data-entity-uuid data-align data-view-mode data-caption alt>', - ], - ], - 'filter_align' => ['status' => TRUE], -diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module -index ecf0740003675ade86dae74e35dd87c6fe914d93..bcce95ca2505c645b22c04b3ab8f11ba21c33772 100644 ---- a/core/modules/filter/filter.module -+++ b/core/modules/filter/filter.module -@@ -637,7 +637,7 @@ function _filter_autop($text) { - // to avoid messing up code. We look for matched pairs and allow basic - // nesting. For example: - // "processed <pre> ignored <script> ignored </script> ignored </pre> processed" -- $chunks = preg_split('@(<!--.*?-->|</?(?:pre|script|style|object|iframe|drupal-media|svg|!--)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); -+ $chunks = preg_split('@(<!--.*?-->|</?(?:pre|script|style|object|iframe|drupal-media|drupal-media-inline|svg|!--)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - // Note: PHP ensures the array consists of alternating delimiters and literals - // and begins and ends with a literal (inserting NULL as required). - $ignore = FALSE; -diff --git a/core/modules/media/css/media.inline.css b/core/modules/media/css/media.inline.css -new file mode 100644 -index 0000000000000000000000000000000000000000..0ff40b92181fafb3edfb0d599d3e32376590f3ba ---- /dev/null -+++ b/core/modules/media/css/media.inline.css -@@ -0,0 +1,18 @@ -+/** -+ * @file -+ * Default styling for media that is embedded inline inside text, lists, etc. -+ */ -+ -+.media-embedded-inline { -+ display: inline-block; -+} -+ -+.drupal-media-inline .media-embedded-inline { -+ width: 100%; -+} -+ -+.media-embedded-inline video, -+.media-embedded-inline img, -+.media-embedded-inline audio { -+ display: inline-block; -+} -diff --git a/core/modules/media/media.libraries.yml b/core/modules/media/media.libraries.yml -index 41fc310fbb9a211b5a704715386379ef2444bc60..0e9527db1dbf30275718a68400b6c480aee5c8e4 100644 ---- a/core/modules/media/media.libraries.yml -+++ b/core/modules/media/media.libraries.yml -@@ -32,6 +32,12 @@ filter.caption: - dependencies: - - filter/caption - -+media.inline: -+ version: VERSION -+ css: -+ component: -+ css/media.inline.css: {} -+ - # Despite the name, this is actually not specific to CKEditor 4, and can be - # used by all text editor plugins. - media_embed_ckeditor_theme: -diff --git a/core/modules/media/src/Event/MediaBuildEmbedEvent.php b/core/modules/media/src/Event/MediaBuildEmbedEvent.php -new file mode 100644 -index 0000000000000000000000000000000000000000..98e5f4d5fb5e9044811810f5a013a1e838a0b6cb ---- /dev/null -+++ b/core/modules/media/src/Event/MediaBuildEmbedEvent.php -@@ -0,0 +1,95 @@ -+<?php -+ -+namespace Drupal\media\Event; -+ -+use Drupal\Component\EventDispatcher\Event; -+use Drupal\media\MediaInterface; -+ -+/** -+ * Event that gets triggered when a media is about to be embedded in ckeditor5. -+ */ -+class MediaBuildEmbedEvent extends Event { -+ -+ /** -+ * The view mode the embedded media will be rendered with. -+ * -+ * @var string -+ */ -+ protected string $viewMode; -+ -+ /** -+ * The media which will be embedded. -+ * -+ * @var \Drupal\media\MediaInterface -+ */ -+ protected MediaInterface $media; -+ -+ /** -+ * The build array of the media before it gets rendered for embedding. -+ * -+ * @var array -+ */ -+ protected array $build; -+ -+ /** -+ * The <drupal-media> DOM node that gets replaced by the embed. -+ * -+ * @var \DOMElement -+ */ -+ protected \DOMElement $node; -+ -+ /** -+ * Construct a MediaBuildEmbedEvent object. -+ * -+ * @param string $viewMode -+ * The view mode the embedded media will be rendered with. -+ * @param \Drupal\media\MediaInterface $media -+ * The media which will be embedded. -+ * @param array $build -+ * The build array of the media before it gets rendered for embedding. -+ * @param \DOMElement $node -+ * The <drupal-media> DOM node that gets replaced by the embed. -+ */ -+ public function __construct(string $viewMode, MediaInterface $media, array $build, \DOMElement $node) { -+ $this->viewMode = $viewMode; -+ $this->media = $media; -+ $this->build = $build; -+ $this->node = $node; -+ } -+ -+ /** -+ * Getter for the view mode. -+ */ -+ public function getViewMode(): string { -+ return $this->viewMode; -+ } -+ -+ /** -+ * Gets the media. -+ */ -+ public function getMedia(): MediaInterface { -+ return $this->media; -+ } -+ -+ /** -+ * Gets the build. -+ */ -+ public function getBuild(): array { -+ return $this->build; -+ } -+ -+ /** -+ * Sets the build. -+ */ -+ public function setBuild(array $build): void { -+ $this->build = $build; -+ } -+ -+ /** -+ * Gets the <drupal-media> DOM node. -+ */ -+ public function getNode(): \DOMElement { -+ return $this->node; -+ } -+ -+} -diff --git a/core/modules/media/src/Plugin/Filter/MediaEmbed.php b/core/modules/media/src/Plugin/Filter/MediaEmbed.php -index 118422bf2c28d594569aa58a806ef722e4e0c4e4..c138c2ea492cee4f1080d69afe76f007ea228091 100644 ---- a/core/modules/media/src/Plugin/Filter/MediaEmbed.php -+++ b/core/modules/media/src/Plugin/Filter/MediaEmbed.php -@@ -21,8 +21,10 @@ - use Drupal\filter\Plugin\FilterBase; - use Drupal\filter\Plugin\FilterInterface; - use Drupal\image\Plugin\Field\FieldType\ImageItem; -+use Drupal\media\Event\MediaBuildEmbedEvent; - use Drupal\media\MediaInterface; - use Symfony\Component\DependencyInjection\ContainerInterface; -+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - - /** - * Provides a filter to embed media items using a custom tag. -@@ -118,8 +120,10 @@ class MediaEmbed extends FilterBase implements ContainerFactoryPluginInterface, - * The renderer. - * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory - * The logger factory. -+ * @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $eventDispatcher -+ * The event dispatcher. - */ -- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityRepositoryInterface $entity_repository, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, EntityTypeBundleInfoInterface $bundle_info, RendererInterface $renderer, LoggerChannelFactoryInterface $logger_factory) { -+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityRepositoryInterface $entity_repository, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, EntityTypeBundleInfoInterface $bundle_info, RendererInterface $renderer, LoggerChannelFactoryInterface $logger_factory, protected EventDispatcherInterface $eventDispatcher) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->entityRepository = $entity_repository; - $this->entityTypeManager = $entity_type_manager; -@@ -142,7 +146,8 @@ public static function create(ContainerInterface $container, array $configuratio - $container->get('entity_display.repository'), - $container->get('entity_type.bundle.info'), - $container->get('renderer'), -- $container->get('logger.factory') -+ $container->get('logger.factory'), -+ $container->get('event_dispatcher'), - ); - } - -@@ -255,6 +260,7 @@ protected function renderMedia(MediaInterface $media, $view_mode, $langcode) { - // instead of only when #access allows this media to be viewed and hence - // only when media is actually rendered. - $build[':media_embed']['#attached']['library'][] = 'media/filter.caption'; -+ $build[':media_embed']['#attached']['library'][] = 'media/media.inline'; - - return $build; - } -@@ -278,18 +284,26 @@ protected function renderMissingMediaIndicator() { - public function process($text, $langcode) { - $result = new FilterProcessResult($text); - -- if (stristr($text, '<drupal-media') === FALSE) { -+ if (stristr($text, '<drupal-media') === FALSE && stristr($text, '<drupal-media-inline') === FALSE) { - return $result; - } - - $dom = Html::load($text); - $xpath = new \DOMXPath($dom); -+ $matched_attributes = '@data-entity-type="media" and normalize-space(@data-entity-uuid)!=""'; - -- foreach ($xpath->query('//drupal-media[@data-entity-type="media" and normalize-space(@data-entity-uuid)!=""]') as $node) { -+ foreach ($xpath->query('//drupal-media[' . $matched_attributes . ']|//drupal-media-inline[' . $matched_attributes . ']') as $node) { - /** @var \DOMElement $node */ - $uuid = $node->getAttribute('data-entity-uuid'); - $view_mode_id = $node->getAttribute('data-view-mode') ?: $this->settings['default_view_mode']; - -+ // Inline media needs a dedicated view mode in order to ensure rendering -+ // of valid (flow content) HTML when rendering inside flow content like -+ // <p> tags for example. -+ if ($node->tagName == 'drupal-media-inline') { -+ $view_mode_id = 'ckeditor_inline'; -+ } -+ - // Delete the consumed attributes. - $node->removeAttribute('data-entity-type'); - $node->removeAttribute('data-entity-uuid'); -@@ -314,9 +328,14 @@ public function process($text, $langcode) { - } - } - -- $build = $media && ($view_mode || $view_mode_id === EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE) -- ? $this->renderMedia($media, $view_mode_id, $langcode) -- : $this->renderMissingMediaIndicator(); -+ if ($media && ($view_mode || $view_mode_id === EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE)) { -+ $build = $this->renderMedia($media, $view_mode_id, $langcode); -+ $event = $this->eventDispatcher->dispatch(new MediaBuildEmbedEvent($view_mode_id, $media, $build, $node)); -+ $build = $event->getBuild(); -+ } -+ else { -+ $build = $this->renderMissingMediaIndicator(); -+ } - - if (empty($build['#attributes']['class'])) { - $build['#attributes']['class'] = []; -@@ -339,6 +358,10 @@ public function process($text, $langcode) { - } - } - -+ if ($node->tagName == 'drupal-media-inline') { -+ $build['#attributes']['class'][] = 'media-embedded-inline'; -+ } -+ - $this->renderIntoDomNode($build, $node, $result); - } - -diff --git a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php -index e283aae92bb1bb8eb2bda4d2f7631d59903cf984..24c00cfa3da7fc633874e23d37d7544f782f10bd 100644 ---- a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php -+++ b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php -@@ -40,7 +40,7 @@ public function testBasics(array $embed_attributes, $expected_view_mode, array $ - $this->assertEqualsCanonicalizing($expected_cacheability->getCacheContexts(), $result->getCacheContexts()); - $this->assertSame($expected_cacheability->getCacheMaxAge(), $result->getCacheMaxAge()); - $this->assertSame(['library'], array_keys($result->getAttachments())); -- $this->assertSame(['media/filter.caption'], $result->getAttachments()['library']); -+ $this->assertSame(['media/filter.caption', 'media/media.inline'], $result->getAttachments()['library']); - } - - /** -@@ -194,7 +194,7 @@ public static function providerAccessUnpublished() { - ]) - ->setCacheContexts(['timezone', 'user', 'user.permissions']) - ->setCacheMaxAge(Cache::PERMANENT), -- ['library' => ['media/filter.caption']], -+ ['library' => ['media/filter.caption', 'media/media.inline']], - ], - ]; - } -@@ -428,7 +428,7 @@ public function testFilterIntegration(array $filter_ids, array $additional_attri - * Data provider for testFilterIntegration(). - */ - public static function providerFilterIntegration() { -- $default_asset_libraries = ['media/filter.caption']; -+ $default_asset_libraries = ['media/filter.caption', 'media/media.inline']; - - $caption_additional_attributes = ['data-caption' => 'Yo.']; - $caption_verification_selector = 'figure > figcaption'; -@@ -445,14 +445,14 @@ public static function providerFilterIntegration() { - $caption_additional_attributes, - $caption_verification_selector, - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - ], - '`<a>` + `data-caption`; `filter_caption` + `media_embed` ⇒ caption present, link preserved' => [ - ['filter_caption', 'media_embed'], - $caption_additional_attributes, - 'figure > a[href="https://www.drupal.org"] + figcaption', - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - '<a href="https://www.drupal.org">', - '</a>', - ], -@@ -492,14 +492,14 @@ public static function providerFilterIntegration() { - $align_additional_attributes + $caption_additional_attributes, - 'figure.align-center > figcaption', - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - ], - '`<a>` + `data-caption` + `data-align`; `filter_align` + `filter_caption` + `media_embed` ⇒ aligned caption present, link preserved' => [ - ['filter_align', 'filter_caption', 'media_embed'], - $align_additional_attributes + $caption_additional_attributes, - 'figure.align-center > a[href="https://www.drupal.org"] + figcaption', - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - '<a href="https://www.drupal.org">', - '</a>', - ], -diff --git a/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml b/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml -index 60cd331cc875cdb4286b0be09ff99bb9c5a7db54..08518f40f78fa4f506ed54d06549cfd066db638c 100644 ---- a/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml -+++ b/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml -@@ -55,6 +55,7 @@ settings: - - '<h6 id>' - - '<img src alt data-entity-type data-entity-uuid data-align data-caption width height loading>' - - '<drupal-media title>' -+ - '<drupal-media-inline title>' - media_media: - allow_view_mode_override: true - image_upload: -diff --git a/core/profiles/demo_umami/config/install/filter.format.basic_html.yml b/core/profiles/demo_umami/config/install/filter.format.basic_html.yml -index b57e2a67a7950fe32b2a422be14c9154f510ae97..8ad1363564d2631f7265d87faaa4bff5ef00c9ba 100644 ---- a/core/profiles/demo_umami/config/install/filter.format.basic_html.yml -+++ b/core/profiles/demo_umami/config/install/filter.format.basic_html.yml -@@ -40,7 +40,7 @@ filters: - status: true - weight: -10 - settings: -- allowed_html: '<a href hreflang> <em> <strong> <cite> <blockquote cite> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <br> <img src alt loading height width data-entity-type data-entity-uuid data-align data-caption> <drupal-media data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title>' -+ allowed_html: '<a href hreflang> <em> <strong> <cite> <blockquote cite> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <br> <img src alt loading height width data-entity-type data-entity-uuid data-align data-caption> <drupal-media data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title> <drupal-media-inline data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title>' - filter_html_help: false - filter_html_nofollow: false - filter_html_image_secure: -diff --git a/core/themes/stable9/css/media/media.inline.css b/core/themes/stable9/css/media/media.inline.css -new file mode 100644 -index 0000000000000000000000000000000000000000..0ff40b92181fafb3edfb0d599d3e32376590f3ba ---- /dev/null -+++ b/core/themes/stable9/css/media/media.inline.css -@@ -0,0 +1,18 @@ -+/** -+ * @file -+ * Default styling for media that is embedded inline inside text, lists, etc. -+ */ -+ -+.media-embedded-inline { -+ display: inline-block; -+} -+ -+.drupal-media-inline .media-embedded-inline { -+ width: 100%; -+} -+ -+.media-embedded-inline video, -+.media-embedded-inline img, -+.media-embedded-inline audio { -+ display: inline-block; -+} -diff --git a/core/themes/stable9/stable9.info.yml b/core/themes/stable9/stable9.info.yml -index 0314380fbdfb5730cd79b7f1f0014a0b041f300c..59976e7c5c5eb396cf71c3190ac05c22393c41af 100644 ---- a/core/themes/stable9/stable9.info.yml -+++ b/core/themes/stable9/stable9.info.yml -@@ -165,6 +165,11 @@ libraries-override: - component: - css/filter.caption.css: css/media/filter.caption.css - -+ media/media.inline: -+ css: -+ component: -+ css/media.inline.css: css/media/media.inline.css -+ - media/oembed.formatter: - css: - component: -diff --git a/core/themes/stable9/templates/content/media--ckeditor-inline-embed.html.twig b/core/themes/stable9/templates/content/media--ckeditor-inline-embed.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..18a903f24544a90cae12c4a7d2008dd6b340fba5 ---- /dev/null -+++ b/core/themes/stable9/templates/content/media--ckeditor-inline-embed.html.twig -@@ -0,0 +1,36 @@ -+{# -+/** -+ * @file -+ * Default theme implementation to present a media item. -+ * -+ * Available variables: -+ * - media: The media item, with limited access to object properties and -+ * methods. Only method names starting with "get", "has", or "is" and -+ * a few common methods such as "id", "label", and "bundle" are available. -+ * For example: -+ * - entity.getEntityTypeId() will return the entity type ID. -+ * - entity.hasField('field_example') returns TRUE if the entity includes -+ * field_example. (This does not indicate the presence of a value in this -+ * field.) -+ * Calling other methods, such as entity.delete(), will result in -+ * an exception. -+ * See \Drupal\Core\Entity\EntityInterface for a full list of methods. -+ * - name: Name of the media item. -+ * - content: Media content. -+ * - title_prefix: Additional output populated by modules, intended to be -+ * displayed in front of the main title tag that appears in the template. -+ * - title_suffix: Additional output populated by modules, intended to be -+ * displayed after the main title tag that appears in the template. -+ * - view_mode: View mode; for example, "teaser" or "full". -+ * - attributes: HTML attributes for the containing element. -+ * - title_attributes: Same as attributes, except applied to the main title -+ * tag that appears in the template. -+ * -+ * @see template_preprocess_media() -+ * -+ * @ingroup themeable -+ */ -+#} -+<span{{ attributes }}> -+ {{ content }} -+</span> -diff --git a/core/themes/stable9/templates/field/field--ckeditor-inline.html.twig b/core/themes/stable9/templates/field/field--ckeditor-inline.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..7f0ae06f8369db15ea4b70771cacfc9a61eff771 ---- /dev/null -+++ b/core/themes/stable9/templates/field/field--ckeditor-inline.html.twig -@@ -0,0 +1,72 @@ -+{# -+/** -+ * @file -+ * Default theme implementation for a field. -+ * -+ * To override output, copy the "field.html.twig" from the templates directory -+ * to your theme's directory and customize it, just like customizing other -+ * Drupal templates such as page.html.twig or node.html.twig. -+ * -+ * Instead of overriding the theming for all fields, you can also just override -+ * theming for a subset of fields using -+ * @link themeable Theme hook suggestions. @endlink For example, -+ * here are some theme hook suggestions that can be used for a field_foo field -+ * on an article node type: -+ * - field--node--field-foo--article.html.twig -+ * - field--node--field-foo.html.twig -+ * - field--node--article.html.twig -+ * - field--field-foo.html.twig -+ * - field--text-with-summary.html.twig -+ * - field.html.twig -+ * -+ * Available variables: -+ * - attributes: HTML attributes for the containing element. -+ * - label_hidden: Whether to show the field label or not. -+ * - title_attributes: HTML attributes for the title. -+ * - label: The label for the field. -+ * - multiple: TRUE if a field can contain multiple items. -+ * - items: List of all the field items. Each item contains: -+ * - attributes: List of HTML attributes for each item. -+ * - content: The field item's content. -+ * - entity_type: The entity type to which the field belongs. -+ * - field_name: The name of the field. -+ * - field_type: The type of the field. -+ * - label_display: The display settings for the label. -+ * -+ * @see template_preprocess_field() -+ * -+ * @ingroup themeable -+ */ -+#} -+{% -+ set title_classes = [ -+ label_display == 'visually_hidden' ? 'visually-hidden', -+ ] -+%} -+ -+{% if label_hidden %} -+ {% if multiple %} -+ <span{{ attributes }}> -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ </span> -+ {% else %} -+ {% for item in items %} -+ <span{{ attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% endif %} -+{% else %} -+ <span{{ attributes }}> -+ <span{{ title_attributes.addClass(title_classes) }}>{{ label }}</span> -+ {% if multiple %} -+ <span> -+ {% endif %} -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% if multiple %} -+ </span> -+ {% endif %} -+ </span> -+{% endif %} diff --git a/patches/d10/5758.diff b/patches/d10/5758.diff deleted file mode 100644 index f5793a0c35f9825559a94e4e75fb63322aeefac6..0000000000000000000000000000000000000000 --- a/patches/d10/5758.diff +++ /dev/null @@ -1,1790 +0,0 @@ -diff --git a/core/modules/ckeditor5/ckeditor5.ckeditor5.yml b/core/modules/ckeditor5/ckeditor5.ckeditor5.yml -index 2a2c3fd165877ef2d3790dfd63779104c425d13e..3c2f61d05bd7575d4e8b68c6c5e01c1eb37ca82c 100644 ---- a/core/modules/ckeditor5/ckeditor5.ckeditor5.yml -+++ b/core/modules/ckeditor5/ckeditor5.ckeditor5.yml -@@ -742,6 +742,9 @@ media_media: - - <drupal-media> - - <drupal-media data-entity-type data-entity-uuid alt> - - <drupal-media data-view-mode> -+ - <drupal-media-inline> -+ - <drupal-media-inline data-entity-type data-entity-uuid alt> -+ - <drupal-media-inline data-view-mode> - conditions: - filter: media_embed - -@@ -756,6 +759,7 @@ ckeditor5_drupalMediaCaption: - label: Media caption - elements: - - <drupal-media data-caption> -+ - <drupal-media-inline data-caption> - conditions: - filter: filter_caption - plugins: -@@ -805,6 +809,7 @@ media_mediaAlign: - library: ckeditor5/internal.drupal.ckeditor5.mediaAlign - elements: - - <drupal-media data-align> -+ - <drupal-media-inline data-align> - conditions: - filter: filter_align - plugins: [media_media] -diff --git a/core/modules/ckeditor5/ckeditor5.module b/core/modules/ckeditor5/ckeditor5.module -index 561795fa958175d6f562f814840ab6e3de005d79..ce745000affb58f286aae24fb9162860fe26e323 100644 ---- a/core/modules/ckeditor5/ckeditor5.module -+++ b/core/modules/ckeditor5/ckeditor5.module -@@ -91,9 +91,37 @@ function ckeditor5_theme() { - 'ckeditor5_settings_toolbar' => [ - 'render element' => 'form', - ], -+ 'field__ckeditor_inline' => [ -+ 'base hook' => 'field', -+ ], -+ 'media__ckeditor_inline_embed' => [ -+ 'base hook' => 'media', -+ ], - ]; - } - -+/** -+ * Implements hook_theme_suggestions_HOOK_alter(). -+ */ -+function ckeditor5_theme_suggestions_media_alter(array &$suggestions, array $variables) { -+ if ($variables['elements']['#view_mode'] == 'ckeditor_inline') { -+ $suggestions[] = 'media__ckeditor_inline_embed'; -+ } -+} -+ -+/** -+ * Implements hook_preprocess_HOOK(). -+ */ -+function ckeditor5_preprocess_media(&$variables) { -+ if ($variables['view_mode'] == 'ckeditor_inline') { -+ foreach ($variables['content'] as &$element) { -+ if (!empty($element['#theme']) && $element['#theme'] == 'field') { -+ $element['#theme'] = 'field__ckeditor_inline'; -+ } -+ } -+ } -+} -+ - /** - * Implements hook_module_implements_alter(). - */ -diff --git a/core/modules/ckeditor5/ckeditor5.post_update.php b/core/modules/ckeditor5/ckeditor5.post_update.php -index f9b4e846c16d064d8ba9b36b64e0322ade4a779e..e2e925e63eaecfd9cf9c65b90ed21db3ef02ada3 100644 ---- a/core/modules/ckeditor5/ckeditor5.post_update.php -+++ b/core/modules/ckeditor5/ckeditor5.post_update.php -@@ -5,6 +5,8 @@ - * Post update functions for CKEditor 5. - */ - -+use Drupal\Core\Config\FileStorage; -+ - // cspell:ignore multiblock - - /** -@@ -20,3 +22,39 @@ function ckeditor5_removed_post_updates() { - 'ckeditor5_post_update_list_start_reversed' => '11.0.0', - ]; - } -+ -+/** -+ * Creates the ckeditor_inline view mode for media entities. -+ */ -+function ckeditor5_post_update_create_ckeditor_inline_view_mode() { -+ $config_path = \Drupal::service('extension.list.module')->getPath('ckeditor5') . '/config/optional'; -+ $source = new FileStorage($config_path); -+ $entity_type_manager = \Drupal::entityTypeManager(); -+ $module_handler = \Drupal::moduleHandler(); -+ /** @var \Drupal\Core\Config\StorageInterface $config_storage */ -+ $config_storage = \Drupal::service('config.storage'); -+ -+ if ($module_handler->moduleExists('media') && !$config_storage->exists('core.entity_view_mode.media.ckeditor_inline')) { -+ $entity_type_manager->getStorage('entity_view_mode') -+ ->create($source->read('core.entity_view_mode.media.ckeditor_inline')) -+ ->save(); -+ } -+} -+ -+/** -+ * Amends the filter format settings to allow the drupal-media-inline tag. -+ */ -+function ckeditor5_post_update_allow_inline_media_element() { -+ $formats = filter_formats(); -+ foreach ($formats as $format_id => $format) { -+ $config = \Drupal::configFactory()->getEditable('filter.format.' . $format_id); -+ $filters = $config->get('filters'); -+ if (\array_key_exists('media_embed', $filters) -+ && \array_key_exists('filter_html', $filters) -+ && !\str_contains($filters['filter_html']['settings']['allowed_html'], 'drupal-media-inline')) { -+ $filters['filter_html']['settings']['allowed_html'] .= ' <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode data-caption data-align>'; -+ $config->set('filters', $filters); -+ $config->save(); -+ } -+ } -+} -diff --git a/core/modules/ckeditor5/config/optional/core.entity_view_mode.media.ckeditor_inline.yml b/core/modules/ckeditor5/config/optional/core.entity_view_mode.media.ckeditor_inline.yml -new file mode 100644 -index 0000000000000000000000000000000000000000..e659b44337d5e3f03c4720bbe63f7446d6536bbf ---- /dev/null -+++ b/core/modules/ckeditor5/config/optional/core.entity_view_mode.media.ckeditor_inline.yml -@@ -0,0 +1,10 @@ -+langcode: en -+status: true -+dependencies: -+ module: -+ - media -+id: media.ckeditor_inline -+label: 'Ckeditor Inline' -+description: 'View mode used for media embedded inline' -+targetEntityType: media -+cache: true -diff --git a/core/modules/ckeditor5/css/drupalmedia.css b/core/modules/ckeditor5/css/drupalmedia.css -index e69e9c7ed6fa6f183ae267317599e687e7c8917a..7fd2b0b188fca6a36f67cf9c96ae0c1a1ac50123 100644 ---- a/core/modules/ckeditor5/css/drupalmedia.css -+++ b/core/modules/ckeditor5/css/drupalmedia.css -@@ -13,6 +13,12 @@ - min-width: 50px; - } - -+.ck .drupal-media.drupal-media-inline { -+ display: inline-block; -+ clear: initial; -+ margin: 0; -+} -+ - .ck .drupal-media [data-drupal-media-preview] { - pointer-events: none; - } -diff --git a/core/modules/ckeditor5/js/build/drupalMedia.js b/core/modules/ckeditor5/js/build/drupalMedia.js -index 766eb373081549d9e4aad090332877178bbd30a8..bd81d5269440adfea48f5a404b138267d43d670d 100644 ---- a/core/modules/ckeditor5/js/build/drupalMedia.js -+++ b/core/modules/ckeditor5/js/build/drupalMedia.js -@@ -1 +1 @@ --!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalMedia=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/engine.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/engine.js")},"ckeditor5/src/ui.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/ui.js")},"ckeditor5/src/utils.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/utils.js")},"ckeditor5/src/widget.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/widget.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function i(n){var a=t[n];if(void 0!==a)return a.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,i),r.exports}i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{"use strict";i.d(n,{default:()=>ue});var e=i("ckeditor5/src/core.js"),t=i("ckeditor5/src/widget.js");function a(e){return!!e&&e.is("element","drupalMedia")}function r(e){return(0,t.isWidget)(e)&&!!e.getCustomProperty("drupalMedia")}function o(e){const t=e.getSelectedElement();return a(t)?t:e.getFirstPosition().findAncestor("drupalMedia")}function s(e){const t=e.getSelectedElement();if(t&&r(t))return t;if(null===e.getFirstPosition())return null;let i=e.getFirstPosition().parent;for(;i;){if(i.is("element")&&r(i))return i;i=i.parent}return null}function l(e){const t=typeof e;return null!=e&&("object"===t||"function"===t)}function d(e){for(const t of e){if(t.hasAttribute("data-drupal-media-preview"))return t;if(t.childCount){const e=d(t.getChildren());if(e)return e}}return null}function u(e){return`drupalElementStyle${e[0].toUpperCase()+e.substring(1)}`}class c extends e.Command{execute(e){const t=this.editor.plugins.get("DrupalMediaEditing"),i=Object.entries(t.attrs).reduce(((e,[t,i])=>(e[i]=t,e)),{}),n=Object.keys(e).reduce(((t,n)=>(i[n]&&(t[i[n]]=e[n]),t)),{});if(this.editor.plugins.has("DrupalElementStyleEditing")){const t=this.editor.plugins.get("DrupalElementStyleEditing"),{normalizedStyles:i}=t;for(const a of Object.keys(i))for(const i of t.normalizedStyles[a])if(e[i.attributeName]&&i.attributeValue===e[i.attributeName]){const e=u(a);n[e]=i.name}}this.editor.model.change((e=>{this.editor.model.insertObject(function(e,t){return e.createElement("drupalMedia",t)}(e,n))}))}refresh(){const e=this.editor.model,t=e.document.selection,i=e.schema.findAllowedParent(t.getFirstPosition(),"drupalMedia");this.isEnabled=null!==i}}const m="METADATA_ERROR";class p extends e.Plugin{static get requires(){return[t.Widget]}constructor(e){super(e),this.attrs={drupalMediaAlt:"alt",drupalMediaEntityType:"data-entity-type",drupalMediaEntityUuid:"data-entity-uuid"},this.converterAttributes=["drupalMediaEntityUuid","drupalElementStyleViewMode","drupalMediaEntityType","drupalMediaAlt"]}init(){const e=this.editor.config.get("drupalMedia");if(!e)return;const{previewURL:t,themeError:i}=e;this.previewUrl=t,this.labelError=Drupal.t("Preview failed"),this.themeError=i||`\n <p>${Drupal.t("An error occurred while trying to preview the media. Save your work and reload this page.")}<p>\n `,this._defineSchema(),this._defineConverters(),this._defineListeners(),this.editor.commands.add("insertDrupalMedia",new c(this.editor))}upcastDrupalMediaIsImage(e){const{model:t,plugins:i}=this.editor;i.get("DrupalMediaMetadataRepository").getMetadata(e).then((i=>{e&&t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",!!i.imageSourceMetadata,e)}))})).catch((i=>{e&&(console.warn(i.toString()),t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",m,e)})))}))}upcastDrupalMediaType(e){this.editor.plugins.get("DrupalMediaMetadataRepository").getMetadata(e).then((t=>{e&&this.editor.model.enqueueChange({isUndoable:!1},(i=>{i.setAttribute("drupalMediaType",t.type,e)}))})).catch((t=>{e&&(console.warn(t.toString()),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",m,e)})))}))}async _fetchPreview(e){const t={text:this._renderElement(e),uuid:e.getAttribute("drupalMediaEntityUuid")},i=await fetch(`${this.previewUrl}?${new URLSearchParams(t)}`,{headers:{"X-Drupal-MediaPreview-CSRF-Token":this.editor.config.get("drupalMedia").previewCsrfToken}});if(i.ok){return{label:i.headers.get("drupal-media-label"),preview:await i.text()}}return{label:this.labelError,preview:this.themeError}}_defineSchema(){this.editor.model.schema.register("drupalMedia",{inheritAllFrom:"$blockObject",allowAttributes:Object.keys(this.attrs)}),this.editor.editing.view.domConverter.blockElements.push("drupal-media")}_defineConverters(){const e=this.editor.conversion,i=this.editor.plugins.get("DrupalMediaMetadataRepository");e.for("upcast").elementToElement({view:{name:"drupal-media"},model:"drupalMedia"}).add((e=>{e.on("element:drupal-media",((e,t)=>{const[n]=t.modelRange.getItems();i.getMetadata(n).then((e=>{n&&(this.upcastDrupalMediaIsImage(n),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",e.type,n)})))})).catch((e=>{console.warn(e.toString())}))}),{priority:"lowest"})})),e.for("dataDowncast").elementToElement({model:"drupalMedia",view:{name:"drupal-media"}}),e.for("editingDowncast").elementToElement({model:"drupalMedia",view:(e,{writer:i})=>{const n=i.createContainerElement("figure",{class:"drupal-media"});if(!this.previewUrl){const e=i.createRawElement("div",{"data-drupal-media-preview":"unavailable"});i.insert(i.createPositionAt(n,0),e)}return i.setCustomProperty("drupalMedia",!0,n),(0,t.toWidget)(n,i,{label:Drupal.t("Media widget")})}}).add((e=>{const t=(e,t,i)=>{const n=i.writer,a=t.item,r=i.mapper.toViewElement(t.item);let o=d(r.getChildren());if(o){if("ready"!==o.getAttribute("data-drupal-media-preview"))return;n.setAttribute("data-drupal-media-preview","loading",o)}else o=n.createRawElement("div",{"data-drupal-media-preview":"loading"}),n.insert(n.createPositionAt(r,0),o);this._fetchPreview(a).then((({label:e,preview:t})=>{o&&this.editor.editing.view.change((i=>{const n=i.createRawElement("div",{"data-drupal-media-preview":"ready","aria-label":e},(e=>{e.innerHTML=t}));i.insert(i.createPositionBefore(o),n),i.remove(o)}))}))};return this.converterAttributes.forEach((i=>{e.on(`attribute:${i}:drupalMedia`,t)})),e})),e.for("editingDowncast").add((e=>{e.on("attribute:drupalElementStyleAlign:drupalMedia",((e,t,i)=>{const n={left:"drupal-media-style-align-left",right:"drupal-media-style-align-right",center:"drupal-media-style-align-center"},a=i.mapper.toViewElement(t.item),r=i.writer;n[t.attributeOldValue]&&r.removeClass(n[t.attributeOldValue],a),n[t.attributeNewValue]&&i.consumable.consume(t.item,e.name)&&r.addClass(n[t.attributeNewValue],a)}))})),Object.keys(this.attrs).forEach((t=>{const i={model:{key:t,name:"drupalMedia"},view:{name:"drupal-media",key:this.attrs[t]}};e.for("dataDowncast").attributeToAttribute(i),e.for("upcast").attributeToAttribute(i)}))}_defineListeners(){this.editor.model.on("insertContent",((e,[t])=>{a(t)&&(this.upcastDrupalMediaIsImage(t),this.upcastDrupalMediaType(t))}))}_renderElement(e){const t=this.editor.model.change((t=>{const i=t.createDocumentFragment(),n=t.cloneElement(e,!1);return["linkHref"].forEach((e=>{t.removeAttribute(e,n)})),t.append(n,i),i}));return this.editor.data.stringify(t)}static get pluginName(){return"DrupalMediaEditing"}}var g=i("ckeditor5/src/ui.js");class h extends e.Plugin{init(){const e=this.editor,t=this.editor.config.get("drupalMedia");if(!t)return;const{libraryURL:i,openDialog:n,dialogSettings:a={}}=t;i&&"function"==typeof n&&e.ui.componentFactory.add("drupalMedia",(t=>{const r=e.commands.get("insertDrupalMedia"),o=new g.ButtonView(t);return o.set({label:Drupal.t("Insert Media"),icon:'<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M19.1873 4.86414L10.2509 6.86414V7.02335H10.2499V15.5091C9.70972 15.1961 9.01793 15.1048 8.34069 15.3136C7.12086 15.6896 6.41013 16.8967 6.75322 18.0096C7.09631 19.1226 8.3633 19.72 9.58313 19.344C10.6666 19.01 11.3484 18.0203 11.2469 17.0234H11.2499V9.80173L18.1803 8.25067V14.3868C17.6401 14.0739 16.9483 13.9825 16.2711 14.1913C15.0513 14.5674 14.3406 15.7744 14.6836 16.8875C15.0267 18.0004 16.2937 18.5978 17.5136 18.2218C18.597 17.8877 19.2788 16.8982 19.1773 15.9011H19.1803V8.02687L19.1873 8.0253V4.86414Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M13.5039 0.743652H0.386932V12.1603H13.5039V0.743652ZM12.3379 1.75842H1.55289V11.1454H1.65715L4.00622 8.86353L6.06254 10.861L9.24985 5.91309L11.3812 9.22179L11.7761 8.6676L12.3379 9.45621V1.75842ZM6.22048 4.50869C6.22048 5.58193 5.35045 6.45196 4.27722 6.45196C3.20398 6.45196 2.33395 5.58193 2.33395 4.50869C2.33395 3.43546 3.20398 2.56543 4.27722 2.56543C5.35045 2.56543 6.22048 3.43546 6.22048 4.50869Z"/></svg>\n',tooltip:!0}),o.bind("isOn","isEnabled").to(r,"value","isEnabled"),this.listenTo(o,"execute",(()=>{n(i,(({attributes:t})=>{e.execute("insertDrupalMedia",t)}),a)})),o}))}}class f extends e.Plugin{static get requires(){return[t.WidgetToolbarRepository]}static get pluginName(){return"DrupalMediaToolbar"}afterInit(){const{editor:e}=this;var i;e.plugins.get(t.WidgetToolbarRepository).register("drupalMedia",{ariaLabel:Drupal.t("Drupal Media toolbar"),items:(i=e.config.get("drupalMedia.toolbar"),i.map((e=>l(e)?e.name:e))||[]),getRelatedElement:e=>s(e)})}}class b extends e.Command{refresh(){const e=o(this.editor.model.document.selection);this.isEnabled=!!e&&e.getAttribute("drupalMediaIsImage")&&e.getAttribute("drupalMediaIsImage")!==m,this.isEnabled?this.value=e.getAttribute("drupalMediaAlt"):this.value=!1}execute(e){const{model:t}=this.editor,i=o(t.document.selection);e.newValue=e.newValue.trim(),t.change((t=>{e.newValue.length>0?t.setAttribute("drupalMediaAlt",e.newValue,i):t.removeAttribute("drupalMediaAlt",i)}))}}class w extends e.Plugin{init(){this._data=new WeakMap}getMetadata(e){if(this._data.get(e))return new Promise((t=>{t(this._data.get(e))}));const t=this.editor.config.get("drupalMedia");if(!t)return new Promise(((e,t)=>{t(new Error("drupalMedia configuration is required for parsing metadata."))}));if(!e.hasAttribute("drupalMediaEntityUuid"))return new Promise(((e,t)=>{t(new Error("drupalMedia element must have drupalMediaEntityUuid attribute to retrieve metadata."))}));const{metadataUrl:i}=t;return(async e=>{const t=await fetch(e);if(t.ok)return JSON.parse(await t.text());throw new Error("Fetching media embed metadata from the server failed.")})(`${i}&${new URLSearchParams({uuid:e.getAttribute("drupalMediaEntityUuid")})}`).then((t=>(this._data.set(e,t),t)))}static get pluginName(){return"DrupalMediaMetadataRepository"}}class y extends e.Plugin{static get requires(){return[w]}static get pluginName(){return"MediaImageTextAlternativeEditing"}init(){const{editor:e,editor:{model:t,conversion:i}}=this;t.schema.extend("drupalMedia",{allowAttributes:["drupalMediaIsImage"]}),i.for("editingDowncast").add((e=>{e.on("attribute:drupalMediaIsImage",((e,t,i)=>{const{writer:n,mapper:a}=i,r=a.toViewElement(t.item);if(t.attributeNewValue!==m){const e=Array.from(r.getChildren()).find((e=>e.getCustomProperty("drupalMediaMetadataError")));return void(e&&(n.setCustomProperty("widgetLabel",e.getCustomProperty("drupalMediaOriginalWidgetLabel"),e),n.removeElement(e)))}const o=Drupal.t("Not all functionality may be available because some information could not be retrieved."),s=new g.Template({tag:"span",children:[{tag:"span",attributes:{class:"drupal-media__metadata-error-icon","data-cke-tooltip-text":o}}]}).render(),l=n.createRawElement("div",{class:"drupal-media__metadata-error"},((e,t)=>{t.setContentOf(e,s.outerHTML)}));n.setCustomProperty("drupalMediaMetadataError",!0,l);const d=r.getCustomProperty("widgetLabel");n.setCustomProperty("drupalMediaOriginalWidgetLabel",d,l),n.setCustomProperty("widgetLabel",`${d} (${o})`,r),n.insert(n.createPositionAt(r,0),l)}),{priority:"low"})})),e.commands.add("mediaImageTextAlternative",new b(this.editor))}}function v(e){const t=e.editing.view,i=g.BalloonPanelView.defaultPositions;return{target:t.domConverter.viewToDom(t.document.selection.getSelectedElement()),positions:[i.northArrowSouth,i.northArrowSouthWest,i.northArrowSouthEast,i.southArrowNorth,i.southArrowNorthWest,i.southArrowNorthEast]}}var E=i("ckeditor5/src/utils.js");class M extends g.View{constructor(t){super(t),this.focusTracker=new E.FocusTracker,this.keystrokes=new E.KeystrokeHandler,this.labeledInput=this._createLabeledInputView(),this.set("defaultAltText",void 0),this.defaultAltTextView=this._createDefaultAltTextView(),this.saveButtonView=this._createButton(Drupal.t("Save"),e.icons.check,"ck-button-save"),this.saveButtonView.type="submit",this.cancelButtonView=this._createButton(Drupal.t("Cancel"),e.icons.cancel,"ck-button-cancel","cancel"),this._focusables=new g.ViewCollection,this._focusCycler=new g.FocusCycler({focusables:this._focusables,focusTracker:this.focusTracker,keystrokeHandler:this.keystrokes,actions:{focusPrevious:"shift + tab",focusNext:"tab"}}),this.setTemplate({tag:"form",attributes:{class:["ck","ck-media-alternative-text-form","ck-vertical-form"],tabindex:"-1"},children:[this.defaultAltTextView,this.labeledInput,this.saveButtonView,this.cancelButtonView]}),(0,g.injectCssTransitionDisabler)(this)}render(){super.render(),this.keystrokes.listenTo(this.element),(0,g.submitHandler)({view:this}),[this.labeledInput,this.saveButtonView,this.cancelButtonView].forEach((e=>{this._focusables.add(e),this.focusTracker.add(e.element)}))}_createButton(e,t,i,n){const a=new g.ButtonView(this.locale);return a.set({label:e,icon:t,tooltip:!0}),a.extendTemplate({attributes:{class:i}}),n&&a.delegate("execute").to(this,n),a}_createLabeledInputView(){const e=new g.LabeledFieldView(this.locale,g.createLabeledInputText);return e.label=Drupal.t("Alternative text override"),e}_createDefaultAltTextView(){const e=g.Template.bind(this,this);return new g.Template({tag:"div",attributes:{class:["ck-media-alternative-text-form__default-alt-text",e.if("defaultAltText","ck-hidden",(e=>!e))]},children:[{tag:"strong",attributes:{class:"ck-media-alternative-text-form__default-alt-text-label"},children:[Drupal.t("Default alternative text:")]}," ",{tag:"span",attributes:{class:"ck-media-alternative-text-form__default-alt-text-value"},children:[{text:[e.to("defaultAltText")]}]}]})}}class k extends e.Plugin{static get requires(){return[g.ContextualBalloon]}static get pluginName(){return"MediaImageTextAlternativeUi"}init(){this._createButton(),this._createForm()}destroy(){super.destroy(),this._form.destroy()}_createButton(){const t=this.editor;t.ui.componentFactory.add("mediaImageTextAlternative",(i=>{const n=t.commands.get("mediaImageTextAlternative"),a=new g.ButtonView(i);return a.set({label:Drupal.t("Override media image alternative text"),icon:e.icons.lowVision,tooltip:!0}),a.bind("isVisible").to(n,"isEnabled"),this.listenTo(a,"execute",(()=>{this._showForm()})),a}))}_createForm(){const e=this.editor,t=e.editing.view.document;this._balloon=this.editor.plugins.get("ContextualBalloon"),this._form=new M(e.locale),this._form.render(),this.listenTo(this._form,"submit",(()=>{e.execute("mediaImageTextAlternative",{newValue:this._form.labeledInput.fieldView.element.value}),this._hideForm(!0)})),this.listenTo(this._form,"cancel",(()=>{this._hideForm(!0)})),this._form.keystrokes.set("Esc",((e,t)=>{this._hideForm(!0),t()})),this.listenTo(e.ui,"update",(()=>{s(t.selection)?this._isVisible&&function(e){const t=e.plugins.get("ContextualBalloon");if(s(e.editing.view.document.selection)){const i=v(e);t.updatePosition(i)}}(e):this._hideForm(!0)})),(0,g.clickOutsideHandler)({emitter:this._form,activator:()=>this._isVisible,contextElements:[this._balloon.view.element],callback:()=>this._hideForm()})}_showForm(){if(this._isVisible)return;const e=this.editor,t=e.commands.get("mediaImageTextAlternative"),i=e.plugins.get("DrupalMediaMetadataRepository"),n=this._form.labeledInput;this._form.disableCssTransitions(),this._isInBalloon||this._balloon.add({view:this._form,position:v(e)}),n.fieldView.element.value=t.value||"",n.fieldView.value=n.fieldView.element.value,this._form.defaultAltText="";const r=e.model.document.selection.getSelectedElement();a(r)&&i.getMetadata(r).then((e=>{this._form.defaultAltText=e.imageSourceMetadata?e.imageSourceMetadata.alt:""})).catch((e=>{console.warn(e.toString())})),this._form.labeledInput.fieldView.select(),this._form.enableCssTransitions()}_hideForm(e){this._isInBalloon&&(this._form.focusTracker.isFocused&&this._form.saveButtonView.focus(),this._balloon.remove(this._form),e&&this.editor.editing.view.focus())}get _isVisible(){return this._balloon.visibleView===this._form}get _isInBalloon(){return this._balloon.hasView(this._form)}}class A extends e.Plugin{static get requires(){return[y,k]}static get pluginName(){return"MediaImageTextAlternative"}}function D(e,t,i){if(t.attributes)for(const[n,a]of Object.entries(t.attributes))e.setAttribute(n,a,i);t.styles&&e.setStyle(t.styles,i),t.classes&&e.addClass(t.classes,i)}function C(e,t,i){if(!i.consumable.consume(t.item,e.name))return;const n=i.mapper.toViewElement(t.item);D(i.writer,t.attributeNewValue,n)}class _ extends e.Plugin{constructor(e){if(super(e),!e.plugins.has("GeneralHtmlSupport"))return;e.plugins.has("DataFilter")&&e.plugins.has("DataSchema")||console.error("DataFilter and DataSchema plugins are required for Drupal Media to integrate with General HTML Support plugin.");const{schema:t}=e.model,{conversion:i}=e,n=this.editor.plugins.get("DataFilter");this.editor.plugins.get("DataSchema").registerBlockElement({model:"drupalMedia",view:"drupal-media"}),n.on("register:drupal-media",((e,a)=>{"drupalMedia"===a.model&&(t.extend("drupalMedia",{allowAttributes:["htmlLinkAttributes","htmlAttributes"]}),i.for("upcast").add(function(e){return t=>{t.on("element:drupal-media",((t,i,n)=>{function a(t,a){const r=e.processViewAttributes(t,n);r&&n.writer.setAttribute(a,r,i.modelRange)}const r=i.viewItem,o=r.parent;a(r,"htmlAttributes"),o.is("element","a")&&a(o,"htmlLinkAttributes")}),{priority:"low"})}}(n)),i.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{if(!i.consumable.consume(t.item,"attribute:htmlLinkAttributes:drupalMedia"))return;const n=i.mapper.toViewElement(t.item),a=function(e,t,i){const n=e.createRangeOn(t);for(const{item:e}of n.getWalker())if(e.is("element",i))return e}(i.writer,n,"a");D(i.writer,t.item.getAttribute("htmlLinkAttributes"),a)}),{priority:"low"})})),i.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{if(!i.consumable.consume(t.item,"attribute:htmlLinkAttributes:drupalMedia"))return;const n=i.mapper.toViewElement(t.item).parent;D(i.writer,t.item.getAttribute("htmlLinkAttributes"),n)}),{priority:"low"}),e.on("attribute:htmlAttributes:drupalMedia",C,{priority:"low"})})),e.stop())}))}static get pluginName(){return"DrupalMediaGeneralHtmlSupport"}}class x extends e.Plugin{static get requires(){return[p,_,h,f,A]}static get pluginName(){return"DrupalMedia"}}var V=i("ckeditor5/src/engine.js");function S(e){return Array.from(e.getChildren()).find((e=>"drupal-media"===e.name))}function T(e){return t=>{t.on(`attribute:${e.id}:drupalMedia`,((t,i,n)=>{const a=n.mapper.toViewElement(i.item);let r=Array.from(a.getChildren()).find((e=>"a"===e.name));if(r=!r&&a.is("element","a")?a:Array.from(a.getAncestors()).find((e=>"a"===e.name)),r){for(const[t,i]of(0,E.toMap)(e.attributes))n.writer.setAttribute(t,i,r);e.classes&&n.writer.addClass(e.classes,r);for(const t in e.styles)Object.prototype.hasOwnProperty.call(e.styles,t)&&n.writer.setStyle(t,e.styles[t],r)}}))}}function I(e,t){return e=>{e.on("element:a",((e,i,n)=>{const a=i.viewItem;if(!S(a))return;const r=new V.Matcher(t._createPattern()).match(a);if(!r)return;if(!n.consumable.consume(a,r.match))return;const o=i.modelCursor.nodeBefore;n.writer.setAttribute(t.id,!0,o)}),{priority:"high"})}}class L extends e.Plugin{static get requires(){return["LinkEditing","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaEditing"}init(){const{editor:e}=this;e.model.schema.extend("drupalMedia",{allowAttributes:["linkHref"]}),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const n=t.viewItem,a=S(n);if(!a)return;if(!i.consumable.consume(n,{attributes:["href"],name:!0}))return;const r=n.getAttribute("href");if(null===r)return;const o=i.convertItem(a,t.modelCursor);t.modelRange=o.modelRange,t.modelCursor=o.modelCursor;const s=t.modelCursor.nodeBefore;s&&s.is("element","drupalMedia")&&i.writer.setAttribute("linkHref",r,s)}),{priority:"high"})})),e.conversion.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=Array.from(a.getChildren()).find((e=>"a"===e.name));if(r)t.attributeNewValue?n.setAttribute("href",t.attributeNewValue,r):(n.move(n.createRangeIn(r),n.createPositionAt(a,0)),n.remove(r));else{const e=Array.from(a.getChildren()).find((e=>e.getAttribute("data-drupal-media-preview"))),i=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionAt(a,0),i),n.move(n.createRangeOn(e),n.createPositionAt(i,0))}}),{priority:"high"})})),e.conversion.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionBefore(a),r),n.move(n.createRangeOn(a),n.createPositionAt(r,0))}),{priority:"high"})})),this._enableManualDecorators();if(e.commands.get("link").automaticDecorators.length>0)throw new Error("The Drupal Media plugin is not compatible with automatic link decorators. To use Drupal Media, disable any plugins providing automatic link decorators.")}_enableManualDecorators(){const e=this.editor,t=e.commands.get("link");for(const i of t.manualDecorators)e.model.schema.extend("drupalMedia",{allowAttributes:i.id}),e.conversion.for("downcast").add(T(i)),e.conversion.for("upcast").add(I(0,i))}}class P extends e.Plugin{static get requires(){return["LinkEditing","LinkUI","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaUi"}init(){const{editor:e}=this,t=e.editing.view.document;this.listenTo(t,"click",((t,i)=>{this._isSelectedLinkedMedia(e.model.document.selection)&&(i.preventDefault(),t.stop())}),{priority:"high"}),this._createToolbarLinkMediaButton()}_createToolbarLinkMediaButton(){const{editor:e}=this;e.ui.componentFactory.add("drupalLinkMedia",(t=>{const i=new g.ButtonView(t),n=e.plugins.get("LinkUI"),a=e.commands.get("link");return i.set({isEnabled:!0,label:Drupal.t("Link media"),icon:'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184z"/></svg>\n',keystroke:"Ctrl+K",tooltip:!0,isToggleable:!0}),i.bind("isEnabled").to(a,"isEnabled"),i.bind("isOn").to(a,"value",(e=>!!e)),this.listenTo(i,"execute",(()=>{this._isSelectedLinkedMedia(e.model.document.selection)?n._addActionsView():n._showUI(!0)})),i}))}_isSelectedLinkedMedia(e){const t=e.getSelectedElement();return!!t&&t.is("element","drupalMedia")&&t.hasAttribute("linkHref")}}class O extends e.Plugin{static get requires(){return[L,P]}static get pluginName(){return"DrupalLinkMedia"}}const{objectFullWidth:B,objectInline:N,objectLeft:R,objectRight:j,objectCenter:F,objectBlockLeft:U,objectBlockRight:H}=e.icons,$={get inline(){return{name:"inline",title:"In line",icon:N,modelElements:["imageInline"],isDefault:!0}},get alignLeft(){return{name:"alignLeft",title:"Left aligned image",icon:R,modelElements:["imageBlock","imageInline"],className:"image-style-align-left"}},get alignBlockLeft(){return{name:"alignBlockLeft",title:"Left aligned image",icon:U,modelElements:["imageBlock"],className:"image-style-block-align-left"}},get alignCenter(){return{name:"alignCenter",title:"Centered image",icon:F,modelElements:["imageBlock"],className:"image-style-align-center"}},get alignRight(){return{name:"alignRight",title:"Right aligned image",icon:j,modelElements:["imageBlock","imageInline"],className:"image-style-align-right"}},get alignBlockRight(){return{name:"alignBlockRight",title:"Right aligned image",icon:H,modelElements:["imageBlock"],className:"image-style-block-align-right"}},get block(){return{name:"block",title:"Centered image",icon:F,modelElements:["imageBlock"],isDefault:!0}},get side(){return{name:"side",title:"Side image",icon:j,modelElements:["imageBlock"],className:"image-style-side"}}},q={full:B,left:U,right:H,center:F,inlineLeft:R,inlineRight:j,inline:N},W=[{name:"imageStyle:wrapText",title:"Wrap text",defaultItem:"imageStyle:alignLeft",items:["imageStyle:alignLeft","imageStyle:alignRight"]},{name:"imageStyle:breakText",title:"Break text",defaultItem:"imageStyle:block",items:["imageStyle:alignBlockLeft","imageStyle:block","imageStyle:alignBlockRight"]}];function K(e){(0,E.logWarning)("image-style-configuration-definition-invalid",e)}const z={normalizeStyles:function(e){return(e.configuredStyles.options||[]).map((e=>function(e){e="string"==typeof e?$[e]?{...$[e]}:{name:e}:function(e,t){const i={...t};for(const n in e)Object.prototype.hasOwnProperty.call(t,n)||(i[n]=e[n]);return i}($[e.name],e);"string"==typeof e.icon&&(e.icon=q[e.icon]||e.icon);return e}(e))).filter((t=>function(e,{isBlockPluginLoaded:t,isInlinePluginLoaded:i}){const{modelElements:n,name:a}=e;if(!(n&&n.length&&a))return K({style:e}),!1;{const a=[t?"imageBlock":null,i?"imageInline":null];if(!n.some((e=>a.includes(e))))return(0,E.logWarning)("image-style-missing-dependency",{style:e,missingPlugins:n.map((e=>"imageBlock"===e?"ImageBlockEditing":"ImageInlineEditing"))}),!1}return!0}(t,e)))},getDefaultStylesConfiguration:function(e,t){return e&&t?{options:["inline","alignLeft","alignRight","alignCenter","alignBlockLeft","alignBlockRight","block","side"]}:e?{options:["block","side"]}:t?{options:["inline","alignLeft","alignRight"]}:{}},getDefaultDropdownDefinitions:function(e){return e.has("ImageBlockEditing")&&e.has("ImageInlineEditing")?[...W]:[]},warnInvalidStyle:K,DEFAULT_OPTIONS:$,DEFAULT_ICONS:q,DEFAULT_DROPDOWN_DEFINITIONS:W};function Z(e,t,i){for(const n of t)if(i.checkAttribute(e,n))return!0;return!1}function G(e,t,i){const n=e.getSelectedElement();if(n&&Z(n,i,t))return n;let{parent:a}=e.getFirstPosition();for(;a;){if(a.is("element")&&Z(a,i,t))return a;a=a.parent}return null}class J extends e.Command{constructor(e,t){super(e),this.styles={},Object.keys(t).forEach((e=>{this.styles[e]=new Map(t[e].map((e=>[e.name,e])))})),this.modelAttributes=[];for(const e of Object.keys(t)){const t=u(e);this.modelAttributes.push(t)}}refresh(){const{editor:e}=this,t=G(e.model.document.selection,e.model.schema,this.modelAttributes);this.isEnabled=!!t,this.isEnabled?this.value=this.getValue(t):this.value=!1}getValue(e){const t={};return Object.keys(this.styles).forEach((i=>{const n=u(i);if(e.hasAttribute(n))t[i]=e.getAttribute(n);else for(const[,e]of this.styles[i])e.isDefault&&(t[i]=e.name)})),t}execute(e={}){const{editor:{model:t}}=this,{value:i,group:n}=e,a=u(n);t.change((e=>{const r=G(t.document.selection,t.schema,this.modelAttributes);!i||this.styles[n].get(i).isDefault?e.removeAttribute(a,r):e.setAttribute(a,i,r)}))}}function X(e,t){for(const i of t)if(i.name===e)return i}class Q extends e.Plugin{init(){const{editor:t}=this,i=t.config.get("drupalElementStyles");this.normalizedStyles={},Object.keys(i).forEach((t=>{this.normalizedStyles[t]=i[t].map((t=>("string"==typeof t.icon&&e.icons[t.icon]&&(t.icon=e.icons[t.icon]),t.name&&(t.name=`${t.name}`),t))).filter((e=>e.isDefault||e.attributeName&&e.attributeValue?e.modelElements&&Array.isArray(e.modelElements)?!!e.name||(console.warn("drupalElementStyles options must include a name."),!1):(console.warn("drupalElementStyles options must include an array of supported modelElements."),!1):(console.warn(`${e.attributeValue} drupalElementStyles options must include attributeName and attributeValue.`),!1)))})),this._setupConversion(),t.commands.add("drupalElementStyle",new J(t,this.normalizedStyles))}_setupConversion(){const{editor:e}=this,{schema:t}=e.model;Object.keys(this.normalizedStyles).forEach((i=>{const n=u(i),a=(r=this.normalizedStyles[i],(e,t,i)=>{if(!i.consumable.consume(t.item,e.name))return;const n=X(t.attributeNewValue,r),a=X(t.attributeOldValue,r),o=i.mapper.toViewElement(t.item),s=i.writer;a&&("class"===a.attributeName?s.removeClass(a.attributeValue,o):s.removeAttribute(a.attributeName,o)),n&&("class"===n.attributeName?s.addClass(n.attributeValue,o):n.isDefault||s.setAttribute(n.attributeName,n.attributeValue,o))});var r;const o=function(e,t){const i=e.filter((e=>!e.isDefault));return(e,n,a)=>{if(!n.modelRange)return;const r=n.viewItem,o=(0,E.first)(n.modelRange.getItems());if(o&&a.schema.checkAttribute(o,t))for(const e of i)if("class"===e.attributeName)a.consumable.consume(r,{classes:e.attributeValue})&&a.writer.setAttribute(t,e.name,o);else if(a.consumable.consume(r,{attributes:[e.attributeName]}))for(const e of i)e.attributeValue===r.getAttribute(e.attributeName)&&a.writer.setAttribute(t,e.name,o)}}(this.normalizedStyles[i],n);e.editing.downcastDispatcher.on(`attribute:${n}`,a),e.data.downcastDispatcher.on(`attribute:${n}`,a);[...new Set(this.normalizedStyles[i].map((e=>e.modelElements)).flat())].forEach((e=>{t.extend(e,{allowAttributes:n})})),e.data.upcastDispatcher.on("element",o,{priority:"low"})}))}static get pluginName(){return"DrupalElementStyleEditing"}}const Y=e=>e,ee=(e,t)=>(e?`${e}: `:"")+t;function te(e,t){return`drupalElementStyle:${t}:${e}`}class ie extends e.Plugin{static get requires(){return[Q]}init(){const{plugins:e}=this.editor,t=this.editor.config.get("drupalMedia.toolbar")||[],i=e.get("DrupalElementStyleEditing").normalizedStyles;Object.keys(i).forEach((e=>{i[e].forEach((t=>{this._createButton(t,e,i[e])}))}));t.filter(l).filter((e=>{const t=[];if(!e.display)return console.warn("dropdown configuration must include a display key specifying either listDropdown or splitButton."),!1;e.items.includes(e.defaultItem)||console.warn("defaultItem must be part of items in the dropdown configuration.");for(const i of e.items){const e=i.split(":")[1];t.push(e)}return!!t.every((e=>e===t[0]))||(console.warn("dropdown configuration should only contain buttons from one group."),!1)})).forEach((e=>{if(e.items.length>=2){const t=e.name.split(":")[1];switch(e.display){case"splitButton":this._createDropdown(e,i[t]);break;case"listDropdown":this._createListDropdown(e,i[t])}}}))}updateOptionVisibility(e,t,i,n){const{selection:a}=this.editor.model.document,r={};r[n]=e;const o=a?a.getSelectedElement():G(a,this.editor.model.schema,r),s=e.filter((function(e){for(const[t,i]of(0,E.toMap)(e.modelAttributes))if(o&&o.hasAttribute(t))return i.includes(o.getAttribute(t));return!0}));i.hasOwnProperty("model")?s.includes(t)?i.model.set({class:""}):i.model.set({class:"ck-hidden"}):s.includes(t)?i.set({class:""}):i.set({class:"ck-hidden"})}_createDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s}=e,l=o.filter((e=>{const i=e.split(":")[1];return t.find((({name:t})=>te(t,i)===e))})).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==l.length&&z.warnInvalidStyle({dropdown:e});const d=(0,g.createDropdown)(n,g.SplitButtonView),u=d.buttonView;return(0,g.addToolbarToDropdown)(d,l),u.set({label:ee(s,a.label),class:null,tooltip:!0}),u.bind("icon").toMany(l,"isOn",((...e)=>{const t=e.findIndex(Y);return t<0?a.icon:l[t].icon})),u.bind("label").toMany(l,"isOn",((...e)=>{const t=e.findIndex(Y);return ee(s,t<0?a.label:l[t].label)})),u.bind("isOn").toMany(l,"isOn",((...e)=>e.some(Y))),u.bind("class").toMany(l,"isOn",((...e)=>e.some(Y)?"ck-splitbutton_flatten":null)),u.on("execute",(()=>{l.some((({isOn:e})=>e))?d.isOpen=!d.isOpen:a.fire("execute")})),d.bind("isEnabled").toMany(l,"isEnabled",((...e)=>e.some(Y))),d}))}_createButton(e,t,i){const n=e.name;this.editor.ui.componentFactory.add(te(n,t),(a=>{const r=this.editor.commands.get("drupalElementStyle"),o=new g.ButtonView(a);return o.set({label:e.title,icon:e.icon,tooltip:!0,isToggleable:!0}),o.bind("isEnabled").to(r,"isEnabled"),o.bind("isOn").to(r,"value",(e=>e&&e[t]===n)),o.on("execute",this._executeCommand.bind(this,n,t)),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(i,e,o,t)})),o}))}getDropdownListItemDefinitions(e,t,i){const n=new E.Collection;return e.forEach((t=>{const a={type:"button",model:new g.ViewModel({group:i,commandValue:t.name,label:t.title,withText:!0,class:""})};n.add(a),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(e,t,a,i)}))})),n}_createListDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s,defaultText:l}=e,d=e.name.split(":")[1],u=o.filter((e=>t.find((({name:t})=>te(t,d)===e)))).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==u.length&&z.warnInvalidStyle({dropdown:e});const c=(0,g.createDropdown)(n,g.DropdownButtonView),m=c.buttonView;m.set({label:ee(s,a.label),class:null,tooltip:l,withText:!0});const p=this.editor.commands.get("drupalElementStyle");return m.bind("label").to(p,"value",(e=>{if(e&&e[d])for(const i of t)if(i.name===e[d])return i.title;return l})),c.bind("isOn").to(p),c.bind("isEnabled").to(this),(0,g.addListToDropdown)(c,this.getDropdownListItemDefinitions(t,p,d)),this.listenTo(c,"execute",(e=>{this._executeCommand(e.source.commandValue,e.source.group)})),c}))}_executeCommand(e,t){this.editor.execute("drupalElementStyle",{value:e,group:t}),this.editor.editing.view.focus()}static get pluginName(){return"DrupalElementStyleUi"}}class ne extends e.Plugin{static get requires(){return[Q,ie]}static get pluginName(){return"DrupalElementStyle"}}function ae(e){const t=e.getFirstPosition().findAncestor("caption");return t&&a(t.parent)?t:null}function re(e){for(const t of e.getChildren())if(t&&t.is("element","caption"))return t;return null}class oe extends e.Command{refresh(){const e=this.editor.model.document.selection,t=e.getSelectedElement();if(!t)return this.isEnabled=!!o(e),void(this.value=!!ae(e));this.isEnabled=a(t),this.isEnabled?this.value=!!re(t):this.value=!1}execute(e={}){const{focusCaptionOnShow:t}=e;this.editor.model.change((e=>{this.value?this._hideDrupalMediaCaption(e):this._showDrupalMediaCaption(e,t)}))}_showDrupalMediaCaption(e,t){const i=this.editor.model.document.selection,n=this.editor.plugins.get("DrupalMediaCaptionEditing"),a=o(i),r=n._getSavedCaption(a)||e.createElement("caption");e.append(r,a),t&&e.setSelection(r,"in")}_hideDrupalMediaCaption(e){const t=this.editor,i=t.model.document.selection,n=t.plugins.get("DrupalMediaCaptionEditing");let a,r=i.getSelectedElement();r?a=re(r):(a=ae(i),r=o(i)),n._saveCaption(r,a),e.setSelection(r,"on"),e.remove(a)}}class se extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionEditing"}constructor(e){super(e),this._savedCaptionsMap=new WeakMap}init(){const e=this.editor,t=e.model.schema;t.isRegistered("caption")?t.extend("caption",{allowIn:"drupalMedia"}):t.register("caption",{allowIn:"drupalMedia",allowContentOf:"$block",isLimit:!0}),e.commands.add("toggleMediaCaption",new oe(e)),this._setupConversion()}_setupConversion(){const e=this.editor,i=e.editing.view;var n;e.conversion.for("upcast").add(function(e){const t=(t,i,n)=>{const{viewItem:a}=i,{writer:r,consumable:o}=n;if(!i.modelRange||!o.consume(a,{attributes:["data-caption"]}))return;const s=r.createElement("caption"),l=i.modelRange.start.nodeAfter,d=e.data.processor.toView(a.getAttribute("data-caption"));n.consumable.constructor.createFrom(d,n.consumable),n.convertChildren(d,s),r.append(s,l)};return e=>{e.on("element:drupal-media",t,{priority:"low"})}}(e)),e.conversion.for("editingDowncast").elementToElement({model:"caption",view:(e,{writer:n})=>{if(!a(e.parent))return null;const r=n.createEditableElement("figcaption");return r.placeholder=Drupal.t("Enter media caption"),(0,V.enablePlaceholder)({view:i,element:r,keepOnFocus:!0}),(0,t.toWidgetEditable)(r,n)}}),e.editing.mapper.on("modelToViewPosition",(n=i,(e,t)=>{const i=t.modelPosition,r=i.parent;if(!a(r))return;const o=t.mapper.toViewElement(r);t.viewPosition=n.createPositionAt(o,i.offset+1)})),e.conversion.for("dataDowncast").add(function(e){return t=>{t.on("insert:caption",((t,i,n)=>{const{consumable:r,writer:o,mapper:s}=n;if(!a(i.item.parent)||!r.consume(i.item,"insert"))return;const l=e.model.createRangeIn(i.item),d=o.createDocumentFragment();s.bindElements(i.item,d);for(const{item:t}of Array.from(l)){const i={item:t,range:e.model.createRangeOn(t)},a=`insert:${t.name||"$text"}`;e.data.downcastDispatcher.fire(a,i,n);for(const a of t.getAttributeKeys())Object.assign(i,{attributeKey:a,attributeOldValue:null,attributeNewValue:i.item.getAttribute(a)}),e.data.downcastDispatcher.fire(`attribute:${a}`,i,n)}for(const e of o.createRangeIn(d).getItems())s.unbindViewElement(e);s.unbindViewElement(d);const u=e.data.processor.toData(d);if(u){const e=s.toViewElement(i.item.parent);o.setAttribute("data-caption",u,e)}}))}}(e))}_getSavedCaption(e){const t=this._savedCaptionsMap.get(e);return t?V.Element.fromJSON(t):null}_saveCaption(e,t){this._savedCaptionsMap.set(e,t.toJSON())}}class le extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionUI"}init(){const{editor:t}=this,i=t.editing.view;t.ui.componentFactory.add("toggleDrupalMediaCaption",(n=>{const a=new g.ButtonView(n),r=t.commands.get("toggleMediaCaption");return a.set({label:Drupal.t("Caption media"),icon:e.icons.caption,tooltip:!0,isToggleable:!0}),a.bind("isOn","isEnabled").to(r,"value","isEnabled"),a.bind("label").to(r,"value",(e=>e?Drupal.t("Toggle caption off"):Drupal.t("Toggle caption on"))),this.listenTo(a,"execute",(()=>{t.execute("toggleMediaCaption",{focusCaptionOnShow:!0});const e=ae(t.model.document.selection);if(e){const n=t.editing.mapper.toViewElement(e);i.scrollToTheSelection(),i.change((e=>{e.addClass("drupal-media__caption_highlighted",n)}))}t.editing.view.focus()})),a}))}}class de extends e.Plugin{static get requires(){return[se,le]}static get pluginName(){return"DrupalMediaCaption"}}const ue={DrupalMedia:x,MediaImageTextAlternative:A,MediaImageTextAlternativeEditing:y,MediaImageTextAlternativeUi:k,DrupalLinkMedia:O,DrupalMediaCaption:de,DrupalElementStyle:ne}})(),n=n.default})())); -\ No newline at end of file -+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalMedia=t())}(globalThis,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/engine.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/engine.js")},"ckeditor5/src/ui.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/ui.js")},"ckeditor5/src/utils.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/utils.js")},"ckeditor5/src/widget.js":(e,t,i)=>{e.exports=i("dll-reference CKEditor5.dll")("./src/widget.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function i(n){var a=t[n];if(void 0!==a)return a.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,i),r.exports}i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var n={};return(()=>{"use strict";i.d(n,{default:()=>ce});var e=i("ckeditor5/src/core.js"),t=i("ckeditor5/src/widget.js"),a=i("ckeditor5/src/utils.js");function r(e,t){return void 0===t&&(t=!0),t?!!e&&(e.is("element","drupalMedia")||e.is("element","drupalMediaInline")):!!e&&e.is("element","drupalMedia")}function o(e,i){return void 0===i&&(i=!0),i?(0,t.isWidget)(e)&&(!!e.getCustomProperty("drupalMedia")||!!e.getCustomProperty("drupalMediaInline")):(0,t.isWidget)(e)&&!!e.getCustomProperty("drupalMedia")}function s(e,t){void 0===t&&(t=!0);const i=e.getSelectedElement();return r(i,t)?i:e.getFirstPosition().findAncestor(t?/drupalMedia(Inline)?/:/drupalMedia/)}function l(e,t){void 0===t&&(t=!0);const i=e.getSelectedElement();if(i&&o(i,t))return i;if(null===e.getFirstPosition())return null;let n=e.getFirstPosition().parent;for(;n;){if(n.is("element")&&o(n,t))return n;n=n.parent}return null}function d(e){const t=typeof e;return null!=e&&("object"===t||"function"===t)}function u(e){for(const t of e){if(t.hasAttribute("data-drupal-media-preview"))return t;if(t.childCount){const e=u(t.getChildren());if(e)return e}}return null}function c(e){return`drupalElementStyle${e[0].toUpperCase()+e.substring(1)}`}function m(e,t){const i=e.model.schema;return t.is("selection")?function(e,t){const i=(0,a.first)(t.getSelectedBlocks());return!i||e.isObject(i)||i.isEmpty&&"listItem"!==i.name?"drupalMedia":"drupalMediaInline"}(i,t):i.checkChild(t,"drupalMediaInline")?"drupalMediaInline":"drupalMedia"}class p extends e.Command{execute(e){const t=this.editor.plugins.get("DrupalMediaEditing"),i=Object.entries(t.attrs).reduce(((e,[t,i])=>(e[i]=t,e)),{}),n=Object.keys(e).reduce(((t,n)=>(i[n]&&(t[i[n]]=e[n]),t)),{});if(this.editor.plugins.has("DrupalElementStyleEditing")){const t=this.editor.plugins.get("DrupalElementStyleEditing"),{normalizedStyles:i}=t;for(const a of Object.keys(i))for(const i of t.normalizedStyles[a])if(e[i.attributeName]&&i.attributeValue===e[i.attributeName]){const e=c(a);n[e]=i.name}}const a=m(this.editor,this.editor.model.document.selection);this.editor.model.change((e=>{this.editor.model.insertObject(function(e,t,i){return e.createElement(i,t)}(e,n,a))}))}refresh(){const e=this.editor.model,t=e.document.selection,i=m(this.editor,t),n=e.schema.findAllowedParent(t.getFirstPosition(),i);this.isEnabled=null!==n}}const g="METADATA_ERROR";class h extends e.Plugin{static get requires(){return[t.Widget]}constructor(e){super(e),this.attrs={drupalMediaAlt:"alt",drupalMediaEntityType:"data-entity-type",drupalMediaEntityUuid:"data-entity-uuid"},this.converterAttributes=["drupalMediaEntityUuid","drupalElementStyleViewMode","drupalMediaEntityType","drupalMediaAlt"]}init(){const e=this.editor.config.get("drupalMedia");if(!e)return;const{previewURL:t,themeError:i}=e;this.previewUrl=t,this.labelError=Drupal.t("Preview failed"),this.themeError=i||`\n <p>${Drupal.t("An error occurred while trying to preview the media. Save your work and reload this page.")}<p>\n `,this._defineSchema(),this._defineConverters(),this._defineListeners(),this.editor.commands.add("insertDrupalMedia",new p(this.editor))}upcastDrupalMediaIsImage(e){const{model:t,plugins:i}=this.editor;i.get("DrupalMediaMetadataRepository").getMetadata(e).then((i=>{e&&t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",!!i.imageSourceMetadata,e)}))})).catch((i=>{e&&(console.warn(i.toString()),t.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaIsImage",g,e)})))}))}upcastDrupalMediaType(e){this.editor.plugins.get("DrupalMediaMetadataRepository").getMetadata(e).then((t=>{e&&this.editor.model.enqueueChange({isUndoable:!1},(i=>{i.setAttribute("drupalMediaType",t.type,e)}))})).catch((t=>{e&&(console.warn(t.toString()),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",g,e)})))}))}async _fetchPreview(e){const t={text:this._renderElement(e),uuid:e.getAttribute("drupalMediaEntityUuid")},i=await fetch(`${this.previewUrl}?${new URLSearchParams(t)}`,{headers:{"X-Drupal-MediaPreview-CSRF-Token":this.editor.config.get("drupalMedia").previewCsrfToken}});if(i.ok){return{label:i.headers.get("drupal-media-label"),preview:await i.text()}}return{label:this.labelError,preview:this.themeError}}_defineSchema(){const e=this.editor.model.schema;e.register("drupalMedia",{inheritAllFrom:"$blockObject",allowAttributes:Object.keys(this.attrs)}),e.register("drupalMediaInline",{inheritAllFrom:"$inlineObject",allowIn:["$block","$container","$text"],allowAttributes:Object.keys(this.attrs)}),this.editor.editing.view.domConverter.blockElements.push("drupal-media")}_defineConverters(){const e=this.editor.conversion,i=this.editor.plugins.get("DrupalMediaMetadataRepository"),n={"drupal-media":"drupalMedia","drupal-media-inline":"drupalMediaInline"};Object.keys(n).forEach((a=>{const r=n[a];e.for("upcast").elementToElement({view:{name:a},model:r}).add((e=>{e.on(`element:${a}`,((e,t)=>{const[n]=t.modelRange.getItems();i.getMetadata(n).then((e=>{n&&(this.upcastDrupalMediaIsImage(n),this.editor.model.enqueueChange({isUndoable:!1},(t=>{t.setAttribute("drupalMediaType",e.type,n)})))})).catch((e=>{console.warn(e.toString())}))}),{priority:"lowest"})})),e.for("dataDowncast").elementToElement({model:r,view:{name:a}}),e.for("editingDowncast").elementToElement({model:r,view:(e,{writer:i})=>{const n=i.createContainerElement("figure",{class:"drupal-media-inline"===a?`drupal-media ${a}`:a});if(!this.previewUrl){const e=i.createRawElement("div",{"data-drupal-media-preview":"unavailable"});i.insert(i.createPositionAt(n,0),e)}return i.setCustomProperty(r,!0,n),(0,t.toWidget)(n,i,{label:Drupal.t("Media widget")})}}).add((e=>{const t=(e,t,i)=>{const n=i.writer,a=t.item,r=i.mapper.toViewElement(t.item);let o=u(r.getChildren());if(o){if("ready"!==o.getAttribute("data-drupal-media-preview"))return;n.setAttribute("data-drupal-media-preview","loading",o)}else o=n.createRawElement("div",{"data-drupal-media-preview":"loading"}),n.insert(n.createPositionAt(r,0),o);this._fetchPreview(a).then((({label:e,preview:t})=>{o&&this.editor.editing.view.change((i=>{const n=i.createRawElement("div",{"data-drupal-media-preview":"ready","aria-label":e},(e=>{e.innerHTML=t}));i.insert(i.createPositionBefore(o),n),i.remove(o)}))}))};return this.converterAttributes.forEach((i=>{e.on(`attribute:${i}:${r}`,t)})),e})),e.for("editingDowncast").add((e=>{e.on(`attribute:drupalElementStyleAlign:${r}`,((e,t,i)=>{const n={left:"drupal-media-style-align-left",right:"drupal-media-style-align-right",center:"drupal-media-style-align-center"},a=i.mapper.toViewElement(t.item),r=i.writer;n[t.attributeOldValue]&&r.removeClass(n[t.attributeOldValue],a),n[t.attributeNewValue]&&i.consumable.consume(t.item,e.name)&&r.addClass(n[t.attributeNewValue],a)}))})),Object.keys(this.attrs).forEach((t=>{const i={model:{key:t,name:r},view:{name:a,key:this.attrs[t]}};e.for("dataDowncast").attributeToAttribute(i),e.for("upcast").attributeToAttribute(i)}))}))}_defineListeners(){this.editor.model.on("insertContent",((e,[t])=>{r(t)&&(this.upcastDrupalMediaIsImage(t),this.upcastDrupalMediaType(t))}))}_renderElement(e){const t=this.editor.model.change((t=>{const i=t.createDocumentFragment(),n=t.cloneElement(e,!1);return["linkHref"].forEach((e=>{t.removeAttribute(e,n)})),t.append(n,i),i}));return this.editor.data.stringify(t)}static get pluginName(){return"DrupalMediaEditing"}}var f=i("ckeditor5/src/ui.js");class b extends e.Plugin{init(){const e=this.editor,t=this.editor.config.get("drupalMedia");if(!t)return;const{libraryURL:i,openDialog:n,dialogSettings:a={}}=t;i&&"function"==typeof n&&e.ui.componentFactory.add("drupalMedia",(t=>{const r=e.commands.get("insertDrupalMedia"),o=new f.ButtonView(t);return o.set({label:Drupal.t("Insert Media"),icon:'<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M19.1873 4.86414L10.2509 6.86414V7.02335H10.2499V15.5091C9.70972 15.1961 9.01793 15.1048 8.34069 15.3136C7.12086 15.6896 6.41013 16.8967 6.75322 18.0096C7.09631 19.1226 8.3633 19.72 9.58313 19.344C10.6666 19.01 11.3484 18.0203 11.2469 17.0234H11.2499V9.80173L18.1803 8.25067V14.3868C17.6401 14.0739 16.9483 13.9825 16.2711 14.1913C15.0513 14.5674 14.3406 15.7744 14.6836 16.8875C15.0267 18.0004 16.2937 18.5978 17.5136 18.2218C18.597 17.8877 19.2788 16.8982 19.1773 15.9011H19.1803V8.02687L19.1873 8.0253V4.86414Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M13.5039 0.743652H0.386932V12.1603H13.5039V0.743652ZM12.3379 1.75842H1.55289V11.1454H1.65715L4.00622 8.86353L6.06254 10.861L9.24985 5.91309L11.3812 9.22179L11.7761 8.6676L12.3379 9.45621V1.75842ZM6.22048 4.50869C6.22048 5.58193 5.35045 6.45196 4.27722 6.45196C3.20398 6.45196 2.33395 5.58193 2.33395 4.50869C2.33395 3.43546 3.20398 2.56543 4.27722 2.56543C5.35045 2.56543 6.22048 3.43546 6.22048 4.50869Z"/></svg>\n',tooltip:!0}),o.bind("isOn","isEnabled").to(r,"value","isEnabled"),this.listenTo(o,"execute",(()=>{n(i,(({attributes:t})=>{e.execute("insertDrupalMedia",t)}),a)})),o}))}}class w extends e.Plugin{static get requires(){return[t.WidgetToolbarRepository]}static get pluginName(){return"DrupalMediaToolbar"}afterInit(){const{editor:e}=this;var i;e.plugins.get(t.WidgetToolbarRepository).register("drupalMedia",{ariaLabel:Drupal.t("Drupal Media toolbar"),items:(i=e.config.get("drupalMedia.toolbar"),i.map((e=>d(e)?e.name:e))||[]),getRelatedElement:e=>l(e)})}}class y extends e.Command{refresh(){const e=s(this.editor.model.document.selection);this.isEnabled=!!e&&e.getAttribute("drupalMediaIsImage")&&e.getAttribute("drupalMediaIsImage")!==g,this.isEnabled?this.value=e.getAttribute("drupalMediaAlt"):this.value=!1}execute(e){const{model:t}=this.editor,i=s(t.document.selection);e.newValue=e.newValue.trim(),t.change((t=>{e.newValue.length>0?t.setAttribute("drupalMediaAlt",e.newValue,i):t.removeAttribute("drupalMediaAlt",i)}))}}class v extends e.Plugin{init(){this._data=new WeakMap}getMetadata(e){if(this._data.get(e))return new Promise((t=>{t(this._data.get(e))}));const t=this.editor.config.get("drupalMedia");if(!t)return new Promise(((e,t)=>{t(new Error("drupalMedia configuration is required for parsing metadata."))}));if(!e.hasAttribute("drupalMediaEntityUuid"))return new Promise(((e,t)=>{t(new Error("drupalMedia element must have drupalMediaEntityUuid attribute to retrieve metadata."))}));const{metadataUrl:i}=t;return(async e=>{const t=await fetch(e);if(t.ok)return JSON.parse(await t.text());throw new Error("Fetching media embed metadata from the server failed.")})(`${i}&${new URLSearchParams({uuid:e.getAttribute("drupalMediaEntityUuid")})}`).then((t=>(this._data.set(e,t),t)))}static get pluginName(){return"DrupalMediaMetadataRepository"}}class E extends e.Plugin{static get requires(){return[v]}static get pluginName(){return"MediaImageTextAlternativeEditing"}init(){const{editor:e,editor:{model:t,conversion:i}}=this;t.schema.extend("drupalMedia",{allowAttributes:["drupalMediaIsImage"]}),i.for("editingDowncast").add((e=>{e.on("attribute:drupalMediaIsImage",((e,t,i)=>{const{writer:n,mapper:a}=i,r=a.toViewElement(t.item);if(t.attributeNewValue!==g){const e=Array.from(r.getChildren()).find((e=>e.getCustomProperty("drupalMediaMetadataError")));return void(e&&(n.setCustomProperty("widgetLabel",e.getCustomProperty("drupalMediaOriginalWidgetLabel"),e),n.removeElement(e)))}const o=Drupal.t("Not all functionality may be available because some information could not be retrieved."),s=new f.Template({tag:"span",children:[{tag:"span",attributes:{class:"drupal-media__metadata-error-icon","data-cke-tooltip-text":o}}]}).render(),l=n.createRawElement("div",{class:"drupal-media__metadata-error"},((e,t)=>{t.setContentOf(e,s.outerHTML)}));n.setCustomProperty("drupalMediaMetadataError",!0,l);const d=r.getCustomProperty("widgetLabel");n.setCustomProperty("drupalMediaOriginalWidgetLabel",d,l),n.setCustomProperty("widgetLabel",`${d} (${o})`,r),n.insert(n.createPositionAt(r,0),l)}),{priority:"low"})})),e.commands.add("mediaImageTextAlternative",new y(this.editor))}}function M(e){const t=e.editing.view,i=f.BalloonPanelView.defaultPositions;return{target:t.domConverter.viewToDom(t.document.selection.getSelectedElement()),positions:[i.northArrowSouth,i.northArrowSouthWest,i.northArrowSouthEast,i.southArrowNorth,i.southArrowNorthWest,i.southArrowNorthEast]}}class k extends f.View{constructor(t){super(t),this.focusTracker=new a.FocusTracker,this.keystrokes=new a.KeystrokeHandler,this.labeledInput=this._createLabeledInputView(),this.set("defaultAltText",void 0),this.defaultAltTextView=this._createDefaultAltTextView(),this.saveButtonView=this._createButton(Drupal.t("Save"),e.icons.check,"ck-button-save"),this.saveButtonView.type="submit",this.cancelButtonView=this._createButton(Drupal.t("Cancel"),e.icons.cancel,"ck-button-cancel","cancel"),this._focusables=new f.ViewCollection,this._focusCycler=new f.FocusCycler({focusables:this._focusables,focusTracker:this.focusTracker,keystrokeHandler:this.keystrokes,actions:{focusPrevious:"shift + tab",focusNext:"tab"}}),this.setTemplate({tag:"form",attributes:{class:["ck","ck-media-alternative-text-form","ck-vertical-form"],tabindex:"-1"},children:[this.defaultAltTextView,this.labeledInput,this.saveButtonView,this.cancelButtonView]}),(0,f.injectCssTransitionDisabler)(this)}render(){super.render(),this.keystrokes.listenTo(this.element),(0,f.submitHandler)({view:this}),[this.labeledInput,this.saveButtonView,this.cancelButtonView].forEach((e=>{this._focusables.add(e),this.focusTracker.add(e.element)}))}_createButton(e,t,i,n){const a=new f.ButtonView(this.locale);return a.set({label:e,icon:t,tooltip:!0}),a.extendTemplate({attributes:{class:i}}),n&&a.delegate("execute").to(this,n),a}_createLabeledInputView(){const e=new f.LabeledFieldView(this.locale,f.createLabeledInputText);return e.label=Drupal.t("Alternative text override"),e}_createDefaultAltTextView(){const e=f.Template.bind(this,this);return new f.Template({tag:"div",attributes:{class:["ck-media-alternative-text-form__default-alt-text",e.if("defaultAltText","ck-hidden",(e=>!e))]},children:[{tag:"strong",attributes:{class:"ck-media-alternative-text-form__default-alt-text-label"},children:[Drupal.t("Default alternative text:")]}," ",{tag:"span",attributes:{class:"ck-media-alternative-text-form__default-alt-text-value"},children:[{text:[e.to("defaultAltText")]}]}]})}}class A extends e.Plugin{static get requires(){return[f.ContextualBalloon]}static get pluginName(){return"MediaImageTextAlternativeUi"}init(){this._createButton(),this._createForm()}destroy(){super.destroy(),this._form.destroy()}_createButton(){const t=this.editor;t.ui.componentFactory.add("mediaImageTextAlternative",(i=>{const n=t.commands.get("mediaImageTextAlternative"),a=new f.ButtonView(i);return a.set({label:Drupal.t("Override media image alternative text"),icon:e.icons.lowVision,tooltip:!0}),a.bind("isVisible").to(n,"isEnabled"),this.listenTo(a,"execute",(()=>{this._showForm()})),a}))}_createForm(){const e=this.editor,t=e.editing.view.document;this._balloon=this.editor.plugins.get("ContextualBalloon"),this._form=new k(e.locale),this._form.render(),this.listenTo(this._form,"submit",(()=>{e.execute("mediaImageTextAlternative",{newValue:this._form.labeledInput.fieldView.element.value}),this._hideForm(!0)})),this.listenTo(this._form,"cancel",(()=>{this._hideForm(!0)})),this._form.keystrokes.set("Esc",((e,t)=>{this._hideForm(!0),t()})),this.listenTo(e.ui,"update",(()=>{l(t.selection)?this._isVisible&&function(e){const t=e.plugins.get("ContextualBalloon");if(l(e.editing.view.document.selection)){const i=M(e);t.updatePosition(i)}}(e):this._hideForm(!0)})),(0,f.clickOutsideHandler)({emitter:this._form,activator:()=>this._isVisible,contextElements:[this._balloon.view.element],callback:()=>this._hideForm()})}_showForm(){if(this._isVisible)return;const e=this.editor,t=e.commands.get("mediaImageTextAlternative"),i=e.plugins.get("DrupalMediaMetadataRepository"),n=this._form.labeledInput;this._form.disableCssTransitions(),this._isInBalloon||this._balloon.add({view:this._form,position:M(e)}),n.fieldView.element.value=t.value||"",n.fieldView.value=n.fieldView.element.value,this._form.defaultAltText="";const a=e.model.document.selection.getSelectedElement();r(a)&&i.getMetadata(a).then((e=>{this._form.defaultAltText=e.imageSourceMetadata?e.imageSourceMetadata.alt:""})).catch((e=>{console.warn(e.toString())})),this._form.labeledInput.fieldView.select(),this._form.enableCssTransitions()}_hideForm(e){this._isInBalloon&&(this._form.focusTracker.isFocused&&this._form.saveButtonView.focus(),this._balloon.remove(this._form),e&&this.editor.editing.view.focus())}get _isVisible(){return this._balloon.visibleView===this._form}get _isInBalloon(){return this._balloon.hasView(this._form)}}class D extends e.Plugin{static get requires(){return[E,A]}static get pluginName(){return"MediaImageTextAlternative"}}function C(e,t,i){if(t.attributes)for(const[n,a]of Object.entries(t.attributes))e.setAttribute(n,a,i);t.styles&&e.setStyle(t.styles,i),t.classes&&e.addClass(t.classes,i)}function _(e,t,i){if(!i.consumable.consume(t.item,e.name))return;const n=i.mapper.toViewElement(t.item);C(i.writer,t.attributeNewValue,n)}class x extends e.Plugin{constructor(e){if(super(e),!e.plugins.has("GeneralHtmlSupport"))return;e.plugins.has("DataFilter")&&e.plugins.has("DataSchema")||console.error("DataFilter and DataSchema plugins are required for Drupal Media to integrate with General HTML Support plugin.");const{schema:t}=e.model,{conversion:i}=e,n=this.editor.plugins.get("DataFilter"),a=this.editor.plugins.get("DataSchema"),r={"drupal-media":"drupalMedia","drupal-media-inline":"drupalMediaInline"};Object.keys(r).forEach((e=>{const o=r[e];a.registerBlockElement({model:o,view:e}),n.on(`register:${e}`,((a,r)=>{r.model===o&&(t.extend(o,{allowAttributes:["htmlLinkAttributes","htmlAttributes"]}),i.for("upcast").add(function(e,t,i){return t=>{t.on(`element:${i}`,((t,i,n)=>{function a(t,a){const r=e.processViewAttributes(t,n);r&&n.writer.setAttribute(a,r,i.modelRange)}const r=i.viewItem,o=r.parent;a(r,"htmlAttributes"),o.is("element","a")&&a(o,"htmlLinkAttributes")}),{priority:"low"})}}(n,0,e)),i.for("editingDowncast").add(function(e){return t=>{t.on(`attribute:linkHref:${e}`,((t,i,n)=>{if(!n.consumable.consume(i.item,`attribute:htmlLinkAttributes:${e}`))return;const a=n.mapper.toViewElement(i.item),r=function(e,t,i){const n=e.createRangeOn(t);for(const{item:e}of n.getWalker())if(e.is("element",i))return e}(n.writer,a,"a");C(n.writer,i.item.getAttribute("htmlLinkAttributes"),r)}),{priority:"low"})}}(o)),i.for("dataDowncast").add(function(e){return t=>{t.on(`attribute:linkHref:${e}`,((t,i,n)=>{if(!n.consumable.consume(i.item,`attribute:htmlLinkAttributes:${e}`))return;const a=n.mapper.toViewElement(i.item).parent;C(n.writer,i.item.getAttribute("htmlLinkAttributes"),a)}),{priority:"low"}),t.on(`attribute:htmlAttributes:${e}`,_,{priority:"low"})}}(o)),a.stop())}))}))}static get pluginName(){return"DrupalMediaGeneralHtmlSupport"}}class V extends e.Plugin{static get requires(){return[h,x,b,w,D]}static get pluginName(){return"DrupalMedia"}}var I=i("ckeditor5/src/engine.js");function S(e){return Array.from(e.getChildren()).find((e=>"drupal-media"===e.name))}function T(e){return t=>{t.on(`attribute:${e.id}:drupalMedia`,((t,i,n)=>{const r=n.mapper.toViewElement(i.item);let o=Array.from(r.getChildren()).find((e=>"a"===e.name));if(o=!o&&r.is("element","a")?r:Array.from(r.getAncestors()).find((e=>"a"===e.name)),o){for(const[t,i]of(0,a.toMap)(e.attributes))n.writer.setAttribute(t,i,o);e.classes&&n.writer.addClass(e.classes,o);for(const t in e.styles)Object.prototype.hasOwnProperty.call(e.styles,t)&&n.writer.setStyle(t,e.styles[t],o)}}))}}function L(e,t){return e=>{e.on("element:a",((e,i,n)=>{const a=i.viewItem;if(!S(a))return;const r=new I.Matcher(t._createPattern()).match(a);if(!r)return;if(!n.consumable.consume(a,r.match))return;const o=i.modelCursor.nodeBefore;n.writer.setAttribute(t.id,!0,o)}),{priority:"high"})}}class P extends e.Plugin{static get requires(){return["LinkEditing","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaEditing"}init(){const{editor:e}=this;e.model.schema.extend("drupalMedia",{allowAttributes:["linkHref"]}),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const n=t.viewItem,a=S(n);if(!a)return;if(!i.consumable.consume(n,{attributes:["href"],name:!0}))return;const r=n.getAttribute("href");if(null===r)return;const o=i.convertItem(a,t.modelCursor);t.modelRange=o.modelRange,t.modelCursor=o.modelCursor;const s=t.modelCursor.nodeBefore;s&&s.is("element","drupalMedia")&&i.writer.setAttribute("linkHref",r,s)}),{priority:"high"})})),e.conversion.for("editingDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=Array.from(a.getChildren()).find((e=>"a"===e.name));if(r)t.attributeNewValue?n.setAttribute("href",t.attributeNewValue,r):(n.move(n.createRangeIn(r),n.createPositionAt(a,0)),n.remove(r));else{const e=Array.from(a.getChildren()).find((e=>e.getAttribute("data-drupal-media-preview"))),i=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionAt(a,0),i),n.move(n.createRangeOn(e),n.createPositionAt(i,0))}}),{priority:"high"})})),e.conversion.for("dataDowncast").add((e=>{e.on("attribute:linkHref:drupalMedia",((e,t,i)=>{const{writer:n}=i;if(!i.consumable.consume(t.item,e.name))return;const a=i.mapper.toViewElement(t.item),r=n.createContainerElement("a",{href:t.attributeNewValue});n.insert(n.createPositionBefore(a),r),n.move(n.createRangeOn(a),n.createPositionAt(r,0))}),{priority:"high"})})),this._enableManualDecorators();if(e.commands.get("link").automaticDecorators.length>0)throw new Error("The Drupal Media plugin is not compatible with automatic link decorators. To use Drupal Media, disable any plugins providing automatic link decorators.")}_enableManualDecorators(){const e=this.editor,t=e.commands.get("link");for(const i of t.manualDecorators)e.model.schema.extend("drupalMedia",{allowAttributes:i.id}),e.conversion.for("downcast").add(T(i)),e.conversion.for("upcast").add(L(0,i))}}class O extends e.Plugin{static get requires(){return["LinkEditing","LinkUI","DrupalMediaEditing"]}static get pluginName(){return"DrupalLinkMediaUi"}init(){const{editor:e}=this,t=e.editing.view.document;this.listenTo(t,"click",((t,i)=>{this._isSelectedLinkedMedia(e.model.document.selection)&&(i.preventDefault(),t.stop())}),{priority:"high"}),this._createToolbarLinkMediaButton()}_createToolbarLinkMediaButton(){const{editor:e}=this;e.ui.componentFactory.add("drupalLinkMedia",(t=>{const i=new f.ButtonView(t),n=e.plugins.get("LinkUI"),a=e.commands.get("link");return i.set({isEnabled:!0,label:Drupal.t("Link media"),icon:'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184z"/></svg>\n',keystroke:"Ctrl+K",tooltip:!0,isToggleable:!0}),i.bind("isEnabled").to(a,"isEnabled"),i.bind("isOn").to(a,"value",(e=>!!e)),this.listenTo(i,"execute",(()=>{this._isSelectedLinkedMedia(e.model.document.selection)?n._addActionsView():n._showUI(!0)})),i}))}_isSelectedLinkedMedia(e){const t=e.getSelectedElement();return!!t&&t.is("element","drupalMedia")&&t.hasAttribute("linkHref")}}class B extends e.Plugin{static get requires(){return[P,O]}static get pluginName(){return"DrupalLinkMedia"}}const{objectFullWidth:N,objectInline:j,objectLeft:R,objectRight:F,objectCenter:$,objectBlockLeft:U,objectBlockRight:H}=e.icons,q={get inline(){return{name:"inline",title:"In line",icon:j,modelElements:["imageInline"],isDefault:!0}},get alignLeft(){return{name:"alignLeft",title:"Left aligned image",icon:R,modelElements:["imageBlock","imageInline"],className:"image-style-align-left"}},get alignBlockLeft(){return{name:"alignBlockLeft",title:"Left aligned image",icon:U,modelElements:["imageBlock"],className:"image-style-block-align-left"}},get alignCenter(){return{name:"alignCenter",title:"Centered image",icon:$,modelElements:["imageBlock"],className:"image-style-align-center"}},get alignRight(){return{name:"alignRight",title:"Right aligned image",icon:F,modelElements:["imageBlock","imageInline"],className:"image-style-align-right"}},get alignBlockRight(){return{name:"alignBlockRight",title:"Right aligned image",icon:H,modelElements:["imageBlock"],className:"image-style-block-align-right"}},get block(){return{name:"block",title:"Centered image",icon:$,modelElements:["imageBlock"],isDefault:!0}},get side(){return{name:"side",title:"Side image",icon:F,modelElements:["imageBlock"],className:"image-style-side"}}},W={full:N,left:U,right:H,center:$,inlineLeft:R,inlineRight:F,inline:j},K=[{name:"imageStyle:wrapText",title:"Wrap text",defaultItem:"imageStyle:alignLeft",items:["imageStyle:alignLeft","imageStyle:alignRight"]},{name:"imageStyle:breakText",title:"Break text",defaultItem:"imageStyle:block",items:["imageStyle:alignBlockLeft","imageStyle:block","imageStyle:alignBlockRight"]}];function z(e){(0,a.logWarning)("image-style-configuration-definition-invalid",e)}const Z={normalizeStyles:function(e){return(e.configuredStyles.options||[]).map((e=>function(e){e="string"==typeof e?q[e]?{...q[e]}:{name:e}:function(e,t){const i={...t};for(const n in e)Object.prototype.hasOwnProperty.call(t,n)||(i[n]=e[n]);return i}(q[e.name],e);"string"==typeof e.icon&&(e.icon=W[e.icon]||e.icon);return e}(e))).filter((t=>function(e,{isBlockPluginLoaded:t,isInlinePluginLoaded:i}){const{modelElements:n,name:r}=e;if(!(n&&n.length&&r))return z({style:e}),!1;{const r=[t?"imageBlock":null,i?"imageInline":null];if(!n.some((e=>r.includes(e))))return(0,a.logWarning)("image-style-missing-dependency",{style:e,missingPlugins:n.map((e=>"imageBlock"===e?"ImageBlockEditing":"ImageInlineEditing"))}),!1}return!0}(t,e)))},getDefaultStylesConfiguration:function(e,t){return e&&t?{options:["inline","alignLeft","alignRight","alignCenter","alignBlockLeft","alignBlockRight","block","side"]}:e?{options:["block","side"]}:t?{options:["inline","alignLeft","alignRight"]}:{}},getDefaultDropdownDefinitions:function(e){return e.has("ImageBlockEditing")&&e.has("ImageInlineEditing")?[...K]:[]},warnInvalidStyle:z,DEFAULT_OPTIONS:q,DEFAULT_ICONS:W,DEFAULT_DROPDOWN_DEFINITIONS:K};function G(e,t,i){for(const n of t)if(i.checkAttribute(e,n))return!0;return!1}function J(e,t,i){const n=e.getSelectedElement();if(n&&G(n,i,t))return n;let{parent:a}=e.getFirstPosition();for(;a;){if(a.is("element")&&G(a,i,t))return a;a=a.parent}return null}class X extends e.Command{constructor(e,t){super(e),this.styles={},Object.keys(t).forEach((e=>{this.styles[e]=new Map(t[e].map((e=>[e.name,e])))})),this.modelAttributes=[];for(const e of Object.keys(t)){const t=c(e);this.modelAttributes.push(t)}}refresh(){const{editor:e}=this,t=J(e.model.document.selection,e.model.schema,this.modelAttributes);this.isEnabled=!!t,this.isEnabled?this.value=this.getValue(t):this.value=!1}getValue(e){const t={};return Object.keys(this.styles).forEach((i=>{const n=c(i);if(e.hasAttribute(n))t[i]=e.getAttribute(n);else for(const[,e]of this.styles[i])e.isDefault&&(t[i]=e.name)})),t}execute(e={}){const{editor:{model:t}}=this,{value:i,group:n}=e,a=c(n);t.change((e=>{const r=J(t.document.selection,t.schema,this.modelAttributes);!i||this.styles[n].get(i).isDefault?e.removeAttribute(a,r):e.setAttribute(a,i,r)}))}}function Q(e,t){for(const i of t)if(i.name===e)return i}class Y extends e.Plugin{init(){const{editor:t}=this,i=t.config.get("drupalElementStyles");this.normalizedStyles={},Object.keys(i).forEach((t=>{this.normalizedStyles[t]=i[t].map((t=>("string"==typeof t.icon&&e.icons[t.icon]&&(t.icon=e.icons[t.icon]),t.name&&(t.name=`${t.name}`),t))).filter((e=>e.isDefault||e.attributeName&&e.attributeValue?e.modelElements&&Array.isArray(e.modelElements)?!!e.name||(console.warn("drupalElementStyles options must include a name."),!1):(console.warn("drupalElementStyles options must include an array of supported modelElements."),!1):(console.warn(`${e.attributeValue} drupalElementStyles options must include attributeName and attributeValue.`),!1)))})),this._setupConversion(),t.commands.add("drupalElementStyle",new X(t,this.normalizedStyles))}_setupConversion(){const{editor:e}=this,{schema:t}=e.model;Object.keys(this.normalizedStyles).forEach((i=>{const n=c(i),r=(o=this.normalizedStyles[i],(e,t,i)=>{if(!i.consumable.consume(t.item,e.name))return;const n=Q(t.attributeNewValue,o),a=Q(t.attributeOldValue,o),r=i.mapper.toViewElement(t.item),s=i.writer;a&&("class"===a.attributeName?s.removeClass(a.attributeValue,r):s.removeAttribute(a.attributeName,r)),n&&("class"===n.attributeName?s.addClass(n.attributeValue,r):n.isDefault||s.setAttribute(n.attributeName,n.attributeValue,r))});var o;const s=function(e,t){const i=e.filter((e=>!e.isDefault));return(e,n,r)=>{if(!n.modelRange)return;const o=n.viewItem,s=(0,a.first)(n.modelRange.getItems());if(s&&r.schema.checkAttribute(s,t))for(const e of i)if("class"===e.attributeName)r.consumable.consume(o,{classes:e.attributeValue})&&r.writer.setAttribute(t,e.name,s);else if(r.consumable.consume(o,{attributes:[e.attributeName]}))for(const e of i)e.attributeValue===o.getAttribute(e.attributeName)&&r.writer.setAttribute(t,e.name,s)}}(this.normalizedStyles[i],n);e.editing.downcastDispatcher.on(`attribute:${n}`,r),e.data.downcastDispatcher.on(`attribute:${n}`,r);[...new Set(this.normalizedStyles[i].map((e=>e.modelElements)).flat())].forEach((e=>{t.extend(e,{allowAttributes:n})})),e.data.upcastDispatcher.on("element",s,{priority:"low"})}))}static get pluginName(){return"DrupalElementStyleEditing"}}const ee=e=>e,te=(e,t)=>(e?`${e}: `:"")+t;function ie(e,t){return`drupalElementStyle:${t}:${e}`}class ne extends e.Plugin{static get requires(){return[Y]}init(){const{plugins:e}=this.editor,t=this.editor.config.get("drupalMedia.toolbar")||[],i=e.get("DrupalElementStyleEditing").normalizedStyles;Object.keys(i).forEach((e=>{i[e].forEach((t=>{this._createButton(t,e,i[e])}))}));t.filter(d).filter((e=>{const t=[];if(!e.display)return console.warn("dropdown configuration must include a display key specifying either listDropdown or splitButton."),!1;e.items.includes(e.defaultItem)||console.warn("defaultItem must be part of items in the dropdown configuration.");for(const i of e.items){const e=i.split(":")[1];t.push(e)}return!!t.every((e=>e===t[0]))||(console.warn("dropdown configuration should only contain buttons from one group."),!1)})).forEach((e=>{if(e.items.length>=2){const t=e.name.split(":")[1];switch(e.display){case"splitButton":this._createDropdown(e,i[t]);break;case"listDropdown":this._createListDropdown(e,i[t])}}}))}updateOptionVisibility(e,t,i,n){const{selection:r}=this.editor.model.document,o={};o[n]=e;const s=r?r.getSelectedElement():J(r,this.editor.model.schema,o),l=e.filter((function(e){for(const[t,i]of(0,a.toMap)(e.modelAttributes))if(s&&s.hasAttribute(t))return i.includes(s.getAttribute(t));return!0}));i.hasOwnProperty("model")?l.includes(t)?i.model.set({class:""}):i.model.set({class:"ck-hidden"}):l.includes(t)?i.set({class:""}):i.set({class:"ck-hidden"})}_createDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s}=e,l=o.filter((e=>{const i=e.split(":")[1];return t.find((({name:t})=>ie(t,i)===e))})).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==l.length&&Z.warnInvalidStyle({dropdown:e});const d=(0,f.createDropdown)(n,f.SplitButtonView),u=d.buttonView;return(0,f.addToolbarToDropdown)(d,l),u.set({label:te(s,a.label),class:null,tooltip:!0}),u.bind("icon").toMany(l,"isOn",((...e)=>{const t=e.findIndex(ee);return t<0?a.icon:l[t].icon})),u.bind("label").toMany(l,"isOn",((...e)=>{const t=e.findIndex(ee);return te(s,t<0?a.label:l[t].label)})),u.bind("isOn").toMany(l,"isOn",((...e)=>e.some(ee))),u.bind("class").toMany(l,"isOn",((...e)=>e.some(ee)?"ck-splitbutton_flatten":null)),u.on("execute",(()=>{l.some((({isOn:e})=>e))?d.isOpen=!d.isOpen:a.fire("execute")})),d.bind("isEnabled").toMany(l,"isEnabled",((...e)=>e.some(ee))),d}))}_createButton(e,t,i){const n=e.name;this.editor.ui.componentFactory.add(ie(n,t),(a=>{const r=this.editor.commands.get("drupalElementStyle"),o=new f.ButtonView(a);return o.set({label:e.title,icon:e.icon,tooltip:!0,isToggleable:!0}),o.bind("isEnabled").to(r,"isEnabled"),o.bind("isOn").to(r,"value",(e=>e&&e[t]===n)),o.on("execute",this._executeCommand.bind(this,n,t)),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(i,e,o,t)})),o}))}getDropdownListItemDefinitions(e,t,i){const n=new a.Collection;return e.forEach((t=>{const a={type:"button",model:new f.ViewModel({group:i,commandValue:t.name,label:t.title,withText:!0,class:""})};n.add(a),this.listenTo(this.editor.ui,"update",(()=>{this.updateOptionVisibility(e,t,a,i)}))})),n}_createListDropdown(e,t){const i=this.editor.ui.componentFactory;i.add(e.name,(n=>{let a;const{defaultItem:r,items:o,title:s,defaultText:l}=e,d=e.name.split(":")[1],u=o.filter((e=>t.find((({name:t})=>ie(t,d)===e)))).map((e=>{const t=i.create(e);return e===r&&(a=t),t}));o.length!==u.length&&Z.warnInvalidStyle({dropdown:e});const c=(0,f.createDropdown)(n,f.DropdownButtonView),m=c.buttonView;m.set({label:te(s,a.label),class:null,tooltip:l,withText:!0});const p=this.editor.commands.get("drupalElementStyle");return m.bind("label").to(p,"value",(e=>{if(e&&e[d])for(const i of t)if(i.name===e[d])return i.title;return l})),c.bind("isOn").to(p),c.bind("isEnabled").to(this),(0,f.addListToDropdown)(c,this.getDropdownListItemDefinitions(t,p,d)),this.listenTo(c,"execute",(e=>{this._executeCommand(e.source.commandValue,e.source.group)})),c}))}_executeCommand(e,t){this.editor.execute("drupalElementStyle",{value:e,group:t}),this.editor.editing.view.focus()}static get pluginName(){return"DrupalElementStyleUi"}}class ae extends e.Plugin{static get requires(){return[Y,ne]}static get pluginName(){return"DrupalElementStyle"}}function re(e){const t=e.getFirstPosition().findAncestor("caption");return t&&r(t.parent)?t:null}function oe(e){for(const t of e.getChildren())if(t&&t.is("element","caption"))return t;return null}class se extends e.Command{refresh(){const e=this.editor.model.document.selection,t=e.getSelectedElement();if(!t)return this.isEnabled=!!s(e,!1),void(this.value=!!re(e));this.isEnabled=r(t,!1),this.isEnabled?this.value=!!oe(t):this.value=!1}execute(e={}){const{focusCaptionOnShow:t}=e;this.editor.model.change((e=>{this.value?this._hideDrupalMediaCaption(e):this._showDrupalMediaCaption(e,t)}))}_showDrupalMediaCaption(e,t){const i=this.editor.model.document.selection,n=this.editor.plugins.get("DrupalMediaCaptionEditing"),a=s(i),r=n._getSavedCaption(a)||e.createElement("caption");e.append(r,a),t&&e.setSelection(r,"in")}_hideDrupalMediaCaption(e){const t=this.editor,i=t.model.document.selection,n=t.plugins.get("DrupalMediaCaptionEditing");let a,r=i.getSelectedElement();r?a=oe(r):(a=re(i),r=s(i)),n._saveCaption(r,a),e.setSelection(r,"on"),e.remove(a)}}class le extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionEditing"}constructor(e){super(e),this._savedCaptionsMap=new WeakMap}init(){const e=this.editor,t=e.model.schema;t.isRegistered("caption")?t.extend("caption",{allowIn:"drupalMedia"}):t.register("caption",{allowIn:"drupalMedia",allowContentOf:"$block",isLimit:!0}),e.commands.add("toggleMediaCaption",new se(e)),this._setupConversion()}_setupConversion(){const e=this.editor,i=e.editing.view;var n;e.conversion.for("upcast").add(function(e){const t=(t,i,n)=>{const{viewItem:a}=i,{writer:r,consumable:o}=n;if(!i.modelRange||!o.consume(a,{attributes:["data-caption"]}))return;const s=r.createElement("caption"),l=i.modelRange.start.nodeAfter,d=e.data.processor.toView(a.getAttribute("data-caption"));n.consumable.constructor.createFrom(d,n.consumable),n.convertChildren(d,s),r.append(s,l)};return e=>{e.on("element:drupal-media",t,{priority:"low"})}}(e)),e.conversion.for("editingDowncast").elementToElement({model:"caption",view:(e,{writer:n})=>{if(!r(e.parent))return null;const a=n.createEditableElement("figcaption");return a.placeholder=Drupal.t("Enter media caption"),(0,I.enablePlaceholder)({view:i,element:a,keepOnFocus:!0}),(0,t.toWidgetEditable)(a,n)}}),e.editing.mapper.on("modelToViewPosition",(n=i,(e,t)=>{const i=t.modelPosition,a=i.parent;if(!r(a))return;const o=t.mapper.toViewElement(a);t.viewPosition=n.createPositionAt(o,i.offset+1)})),e.conversion.for("dataDowncast").add(function(e){return t=>{t.on("insert:caption",((t,i,n)=>{const{consumable:a,writer:o,mapper:s}=n;if(!r(i.item.parent)||!a.consume(i.item,"insert"))return;const l=e.model.createRangeIn(i.item),d=o.createDocumentFragment();s.bindElements(i.item,d);for(const{item:t}of Array.from(l)){const i={item:t,range:e.model.createRangeOn(t)},a=`insert:${t.name||"$text"}`;e.data.downcastDispatcher.fire(a,i,n);for(const a of t.getAttributeKeys())Object.assign(i,{attributeKey:a,attributeOldValue:null,attributeNewValue:i.item.getAttribute(a)}),e.data.downcastDispatcher.fire(`attribute:${a}`,i,n)}for(const e of o.createRangeIn(d).getItems())s.unbindViewElement(e);s.unbindViewElement(d);const u=e.data.processor.toData(d);if(u){const e=s.toViewElement(i.item.parent);o.setAttribute("data-caption",u,e)}}))}}(e))}_getSavedCaption(e){const t=this._savedCaptionsMap.get(e);return t?I.Element.fromJSON(t):null}_saveCaption(e,t){this._savedCaptionsMap.set(e,t.toJSON())}}class de extends e.Plugin{static get requires(){return[]}static get pluginName(){return"DrupalMediaCaptionUI"}init(){const{editor:t}=this,i=t.editing.view;t.ui.componentFactory.add("toggleDrupalMediaCaption",(n=>{const a=new f.ButtonView(n),r=t.commands.get("toggleMediaCaption");return a.set({label:Drupal.t("Caption media"),icon:e.icons.caption,tooltip:!0,isToggleable:!0}),a.bind("isOn","isEnabled").to(r,"value","isEnabled"),a.bind("label").to(r,"value",(e=>e?Drupal.t("Toggle caption off"):Drupal.t("Toggle caption on"))),this.listenTo(a,"execute",(()=>{t.execute("toggleMediaCaption",{focusCaptionOnShow:!0});const e=re(t.model.document.selection);if(e){const n=t.editing.mapper.toViewElement(e);i.scrollToTheSelection(),i.change((e=>{e.addClass("drupal-media__caption_highlighted",n)}))}t.editing.view.focus()})),a}))}}class ue extends e.Plugin{static get requires(){return[le,de]}static get pluginName(){return"DrupalMediaCaption"}}const ce={DrupalMedia:V,MediaImageTextAlternative:D,MediaImageTextAlternativeEditing:E,MediaImageTextAlternativeUi:A,DrupalLinkMedia:B,DrupalMediaCaption:ue,DrupalElementStyle:ae}})(),n=n.default})())); -\ No newline at end of file -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js -index f1268afdab03696d93b1c76fc450f0699a8f8b13..a04133fba7731b36fb0585c3f9ea9ad5ba032883 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediacaption/drupalmediacaptioncommand.js -@@ -51,7 +51,7 @@ export default class ToggleDrupalMediaCaptionCommand extends Command { - if (!selectedElement) { - // Command should be enabled if `<drupalMedia>` element is part of the - // selection. -- this.isEnabled = !!getClosestSelectedDrupalMediaElement(selection); -+ this.isEnabled = !!getClosestSelectedDrupalMediaElement(selection, false); - // Check if the selection descends from a `<drupalMedia>` element that - // also includes a `<caption>`. - this.value = !!getMediaCaptionFromModelSelection(selection); -@@ -60,7 +60,7 @@ export default class ToggleDrupalMediaCaptionCommand extends Command { - } - - // If single element is selected, check if it's a `<drupalMedia>` element. -- this.isEnabled = isDrupalMedia(selectedElement); -+ this.isEnabled = isDrupalMedia(selectedElement, false); - - if (!this.isEnabled) { - this.value = false; -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js -index 9b9b582482a7f90b25e75d3d32e535a9651e00ba..34aef3fa935b29f02fab048609fd0a83e789dc59 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediaediting.js -@@ -210,6 +210,13 @@ export default class DrupalMediaEditing extends Plugin { - inheritAllFrom: '$blockObject', - allowAttributes: Object.keys(this.attrs), - }); -+ -+ schema.register('drupalMediaInline', { -+ inheritAllFrom: '$inlineObject', -+ allowIn: ['$block', '$container', '$text'], -+ allowAttributes: Object.keys(this.attrs), -+ }); -+ - // Register `<drupal-media>` as a block element in the DOM converter. This - // ensures that the DOM converter knows to handle the `<drupal-media>` as a - // block element. -@@ -227,212 +234,228 @@ export default class DrupalMediaEditing extends Plugin { - 'DrupalMediaMetadataRepository', - ); - -- conversion -- .for('upcast') -- .elementToElement({ -+ const viewToModelMap = { -+ 'drupal-media': 'drupalMedia', -+ 'drupal-media-inline': 'drupalMediaInline', -+ }; -+ -+ Object.keys(viewToModelMap).forEach((view) => { -+ const model = viewToModelMap[view]; -+ -+ conversion -+ .for('upcast') -+ .elementToElement({ -+ view: { -+ name: view, -+ }, -+ model, -+ }) -+ .add((dispatcher) => { -+ dispatcher.on( -+ `element:${view}`, -+ (evt, data) => { -+ const [modelElement] = data.modelRange.getItems(); -+ metadataRepository -+ .getMetadata(modelElement) -+ .then((metadata) => { -+ if (!modelElement) { -+ return; -+ } -+ // On upcast, get `drupalMediaIsImage` attribute value from media metadata -+ // repository. -+ this.upcastDrupalMediaIsImage(modelElement); -+ // Enqueue a model change after getting modelElement. -+ this.editor.model.enqueueChange( -+ { isUndoable: false }, -+ (writer) => { -+ writer.setAttribute( -+ 'drupalMediaType', -+ metadata.type, -+ modelElement, -+ ); -+ }, -+ ); -+ }) -+ .catch((e) => { -+ // There isn't any UI indication for errors because this should be -+ // always called after the Drupal Media has been upcast, which would -+ // already display an error in the UI. -+ console.warn(e.toString()); -+ }); -+ }, -+ // This converter needs to have the lowest priority to ensure that the -+ // model element and its attributes have already been converted. It is only used -+ // to gather metadata to make the UI tailored to the specific media entity that -+ // is being dealt with. -+ { priority: 'lowest' }, -+ ); -+ }); -+ -+ conversion.for('dataDowncast').elementToElement({ -+ model, - view: { -- name: 'drupal-media', -+ name: view, - }, -- model: 'drupalMedia', -- }) -- .add((dispatcher) => { -- dispatcher.on( -- 'element:drupal-media', -- (evt, data) => { -- const [modelElement] = data.modelRange.getItems(); -- metadataRepository -- .getMetadata(modelElement) -- .then((metadata) => { -- if (!modelElement) { -- return; -- } -- // On upcast, get `drupalMediaIsImage` attribute value from media metadata -- // repository. -- this.upcastDrupalMediaIsImage(modelElement); -- // Enqueue a model change after getting modelElement. -- this.editor.model.enqueueChange( -- { isUndoable: false }, -- (writer) => { -- writer.setAttribute( -- 'drupalMediaType', -- metadata.type, -- modelElement, -- ); -- }, -- ); -- }) -- .catch((e) => { -- // There isn't any UI indication for errors because this should be -- // always called after the Drupal Media has been upcast, which would -- // already display an error in the UI. -- console.warn(e.toString()); -- }); -- }, -- // This converter needs to have the lowest priority to ensure that the -- // model element and its attributes have already been converted. It is only used -- // to gather metadata to make the UI tailored to the specific media entity that -- // is being dealt with. -- { priority: 'lowest' }, -- ); - }); -- -- conversion.for('dataDowncast').elementToElement({ -- model: 'drupalMedia', -- view: { -- name: 'drupal-media', -- }, -- }); -- conversion -- .for('editingDowncast') -- .elementToElement({ -- model: 'drupalMedia', -- view: (modelElement, { writer }) => { -- const container = writer.createContainerElement('figure', { -- class: 'drupal-media', -- }); -- if (!this.previewUrl) { -- // If preview URL isn't available, insert empty preview element -- // which indicates that preview couldn't be loaded. -- const mediaPreview = writer.createRawElement('div', { -- 'data-drupal-media-preview': 'unavailable', -+ conversion -+ .for('editingDowncast') -+ .elementToElement({ -+ model, -+ view: (modelElement, { writer }) => { -+ const container = writer.createContainerElement('figure', { -+ class: -+ view === 'drupal-media-inline' ? `drupal-media ${view}` : view, - }); -- writer.insert(writer.createPositionAt(container, 0), mediaPreview); -- } -- writer.setCustomProperty('drupalMedia', true, container); -- -- return toWidget(container, writer, { -- label: Drupal.t('Media widget'), -- }); -- }, -- }) -- .add((dispatcher) => { -- const converter = (event, data, conversionApi) => { -- const viewWriter = conversionApi.writer; -- const modelElement = data.item; -- const container = conversionApi.mapper.toViewElement(data.item); -- -- // Search for preview container recursively from its children because -- // the preview container could be wrapped with an element such as -- // `<a>`. -- let media = getPreviewContainer(container.getChildren()); -- -- // Use pre-existing media preview container if one exists. If the -- // preview element doesn't exist, create a new element. -- if (media) { -- // Stop processing if media preview is unavailable or a preview is -- // already loading. -- if (media.getAttribute('data-drupal-media-preview') !== 'ready') { -- return; -+ if (!this.previewUrl) { -+ // If preview URL isn't available, insert empty preview element -+ // which indicates that preview couldn't be loaded. -+ const mediaPreview = writer.createRawElement('div', { -+ 'data-drupal-media-preview': 'unavailable', -+ }); -+ writer.insert( -+ writer.createPositionAt(container, 0), -+ mediaPreview, -+ ); - } -+ writer.setCustomProperty(model, true, container); - -- // Preview was ready meaning that a new preview can be loaded. -- // "Change the attribute to loading to prepare for the loading of -- // the updated preview. Preview is kept intact so that it remains -- // interactable in the UI until the new preview has been rendered. -- viewWriter.setAttribute( -- 'data-drupal-media-preview', -- 'loading', -- media, -- ); -- } else { -- media = viewWriter.createRawElement('div', { -- 'data-drupal-media-preview': 'loading', -+ return toWidget(container, writer, { -+ label: Drupal.t('Media widget'), - }); -- viewWriter.insert(viewWriter.createPositionAt(container, 0), media); -- } -- -- this._fetchPreview(modelElement).then(({ label, preview }) => { -- if (!media) { -- // Nothing to do if associated preview wrapped no longer exist. -- return; -- } -- // CKEditor 5 doesn't support async view conversion. Therefore, once -- // the promise is fulfilled, the editing view needs to be modified -- // manually. -- this.editor.editing.view.change((writer) => { -- const mediaPreview = writer.createRawElement( -- 'div', -- { 'data-drupal-media-preview': 'ready', 'aria-label': label }, -- (domElement) => { -- domElement.innerHTML = preview; -- }, -+ }, -+ }) -+ .add((dispatcher) => { -+ const converter = (event, data, conversionApi) => { -+ const viewWriter = conversionApi.writer; -+ const modelElement = data.item; -+ const container = conversionApi.mapper.toViewElement(data.item); -+ -+ // Search for preview container recursively from its children because -+ // the preview container could be wrapped with an element such as -+ // `<a>`. -+ let media = getPreviewContainer(container.getChildren()); -+ -+ // Use pre-existing media preview container if one exists. If the -+ // preview element doesn't exist, create a new element. -+ if (media) { -+ // Stop processing if media preview is unavailable or a preview is -+ // already loading. -+ if (media.getAttribute('data-drupal-media-preview') !== 'ready') { -+ return; -+ } -+ -+ // Preview was ready meaning that a new preview can be loaded. -+ // "Change the attribute to loading to prepare for the loading of -+ // the updated preview. Preview is kept intact so that it remains -+ // interactable in the UI until the new preview has been rendered. -+ viewWriter.setAttribute( -+ 'data-drupal-media-preview', -+ 'loading', -+ media, - ); -- // Insert the new preview before the previous preview element to -- // ensure that the location remains same even if it is wrapped -- // with another element. -- writer.insert(writer.createPositionBefore(media), mediaPreview); -- writer.remove(media); -+ } else { -+ media = viewWriter.createRawElement('div', { -+ 'data-drupal-media-preview': 'loading', -+ }); -+ viewWriter.insert( -+ viewWriter.createPositionAt(container, 0), -+ media, -+ ); -+ } -+ -+ this._fetchPreview(modelElement).then(({ label, preview }) => { -+ if (!media) { -+ // Nothing to do if associated preview wrapped no longer exist. -+ return; -+ } -+ // CKEditor 5 doesn't support async view conversion. Therefore, once -+ // the promise is fulfilled, the editing view needs to be modified -+ // manually. -+ this.editor.editing.view.change((writer) => { -+ const mediaPreview = writer.createRawElement( -+ 'div', -+ { 'data-drupal-media-preview': 'ready', 'aria-label': label }, -+ (domElement) => { -+ domElement.innerHTML = preview; -+ }, -+ ); -+ // Insert the new preview before the previous preview element to -+ // ensure that the location remains same even if it is wrapped -+ // with another element. -+ writer.insert(writer.createPositionBefore(media), mediaPreview); -+ writer.remove(media); -+ }); - }); -+ }; -+ -+ // List all attributes that should trigger re-rendering of the -+ // preview. -+ this.converterAttributes.forEach((attribute) => { -+ dispatcher.on(`attribute:${attribute}:${model}`, converter); - }); -- }; - -- // List all attributes that should trigger re-rendering of the -- // preview. -- this.converterAttributes.forEach((attribute) => { -- dispatcher.on(`attribute:${attribute}:drupalMedia`, converter); -+ return dispatcher; - }); - -- return dispatcher; -- }); -+ conversion.for('editingDowncast').add((dispatcher) => { -+ dispatcher.on( -+ `attribute:drupalElementStyleAlign:${model}`, -+ (evt, data, conversionApi) => { -+ const alignMapping = { -+ // This is a map of CSS classes representing Drupal element styles for alignments. -+ left: 'drupal-media-style-align-left', -+ right: 'drupal-media-style-align-right', -+ center: 'drupal-media-style-align-center', -+ }; -+ const viewElement = conversionApi.mapper.toViewElement(data.item); -+ const viewWriter = conversionApi.writer; -+ -+ // If the prior value is alignment related, it should be removed -+ // whether or not the module property is consumed. -+ if (alignMapping[data.attributeOldValue]) { -+ viewWriter.removeClass( -+ alignMapping[data.attributeOldValue], -+ viewElement, -+ ); -+ } - -- conversion.for('editingDowncast').add((dispatcher) => { -- dispatcher.on( -- 'attribute:drupalElementStyleAlign:drupalMedia', -- (evt, data, conversionApi) => { -- const alignMapping = { -- // This is a map of CSS classes representing Drupal element styles for alignments. -- left: 'drupal-media-style-align-left', -- right: 'drupal-media-style-align-right', -- center: 'drupal-media-style-align-center', -- }; -- const viewElement = conversionApi.mapper.toViewElement(data.item); -- const viewWriter = conversionApi.writer; -- -- // If the prior value is alignment related, it should be removed -- // whether or not the module property is consumed. -- if (alignMapping[data.attributeOldValue]) { -- viewWriter.removeClass( -- alignMapping[data.attributeOldValue], -+ // If the new value is not alignment related, do not proceed. -+ if (!alignMapping[data.attributeNewValue]) { -+ return; -+ } -+ -+ // The model property is already consumed, do not proceed. -+ if (!conversionApi.consumable.consume(data.item, evt.name)) { -+ return; -+ } -+ -+ // Add the alignment class in the view that corresponds to the value -+ // of the model's drupalElementStyle property. -+ viewWriter.addClass( -+ alignMapping[data.attributeNewValue], - viewElement, - ); -- } -- -- // If the new value is not alignment related, do not proceed. -- if (!alignMapping[data.attributeNewValue]) { -- return; -- } -- -- // The model property is already consumed, do not proceed. -- if (!conversionApi.consumable.consume(data.item, evt.name)) { -- return; -- } -- -- // Add the alignment class in the view that corresponds to the value -- // of the model's drupalElementStyle property. -- viewWriter.addClass( -- alignMapping[data.attributeNewValue], -- viewElement, -- ); -- }, -- ); -- }); -+ }, -+ ); -+ }); - -- // Set attributeToAttribute conversion for all supported attributes. -- Object.keys(this.attrs).forEach((modelKey) => { -- const attributeMapping = { -- model: { -- key: modelKey, -- name: 'drupalMedia', -- }, -- view: { -- name: 'drupal-media', -- key: this.attrs[modelKey], -- }, -- }; -- // Attributes should be rendered only in dataDowncast to avoid having -- // unfiltered data-attributes on the Drupal Media widget. -- conversion.for('dataDowncast').attributeToAttribute(attributeMapping); -- conversion.for('upcast').attributeToAttribute(attributeMapping); -+ // Set attributeToAttribute conversion for all supported attributes. -+ Object.keys(this.attrs).forEach((modelKey) => { -+ const attributeMapping = { -+ model: { -+ key: modelKey, -+ name: model, -+ }, -+ view: { -+ name: view, -+ key: this.attrs[modelKey], -+ }, -+ }; -+ // Attributes should be rendered only in dataDowncast to avoid having -+ // unfiltered data-attributes on the Drupal Media widget. -+ conversion.for('dataDowncast').attributeToAttribute(attributeMapping); -+ conversion.for('upcast').attributeToAttribute(attributeMapping); -+ }); - }); - } - -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js -index 66ca7dbee62ee9bb97f9bed0f95a2ec718903f18..d65914e909bb479bf859a0bec9f0c1c049f6fdfc 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/drupalmediageneralhtmlsupport.js -@@ -5,18 +5,22 @@ import { setViewAttributes } from '@ckeditor/ckeditor5-html-support/src/utils'; - - /** - * View-to-model conversion helper for Drupal Media. -- * Used for preserving allowed attributes on the Drupal Media model. -+ * Used for preserving allowed attributes on the Drupal Media models. - * -+ * @param {string} model -+ * The model name (DrupalMedia or DrupalMediaInline). -+ * @param {string} view -+ * The view name (drupal-media or drupal-media-inline). - * @param {module:html-support/datafilter~DataFilter} dataFilter - * The General HTML support data filter. - * - * @return {function} - * Function that adds an event listener to upcastDispatcher. - */ --function viewToModelDrupalMediaAttributeConverter(dataFilter) { -+function viewToModelDrupalMediaAttributeConverter(dataFilter, model, view) { - return (dispatcher) => { - dispatcher.on( -- 'element:drupal-media', -+ `element:${view}`, - (evt, data, conversionApi) => { - function preserveElementAttributes(viewElement, attributeName) { - const viewAttributes = dataFilter.processViewAttributes( -@@ -96,18 +100,21 @@ function modelToDataAttributeConverter(evt, data, conversionApi) { - /** - * Model to editing view attribute converter. - * -+ * @param {string} model -+ * The model name (DrupalMedia or DrupalMediaInline). -+ * - * @return {function} - * A function that adds an event listener to downcastDispatcher. - */ --function modelToEditingViewAttributeConverter() { -+function modelToEditingViewAttributeConverter(model) { - return (dispatcher) => { - dispatcher.on( -- 'attribute:linkHref:drupalMedia', -+ `attribute:linkHref:${model}`, - (evt, data, conversionApi) => { - if ( - !conversionApi.consumable.consume( - data.item, -- 'attribute:htmlLinkAttributes:drupalMedia', -+ `attribute:htmlLinkAttributes:${model}`, - ) - ) { - return; -@@ -134,18 +141,21 @@ function modelToEditingViewAttributeConverter() { - /** - * Model to data view attribute converter. - * -+ * @param {string} model -+ * The model name (DrupalMedia or DrupalMediaInline). -+ * - * @return {function} - * Function that adds an event listener to downcastDispatcher. - */ --function modelToDataViewAttributeConverter() { -+function modelToDataViewAttributeConverter(model) { - return (dispatcher) => { - dispatcher.on( -- 'attribute:linkHref:drupalMedia', -+ `attribute:linkHref:${model}`, - (evt, data, conversionApi) => { - if ( - !conversionApi.consumable.consume( - data.item, -- 'attribute:htmlLinkAttributes:drupalMedia', -+ `attribute:htmlLinkAttributes:${model}`, - ) - ) { - return; -@@ -163,7 +173,7 @@ function modelToDataViewAttributeConverter() { - ); - - dispatcher.on( -- 'attribute:htmlAttributes:drupalMedia', -+ `attribute:htmlAttributes:${model}`, - modelToDataAttributeConverter, - { priority: 'low' }, - ); -@@ -204,32 +214,45 @@ export default class DrupalMediaGeneralHtmlSupport extends Plugin { - const dataFilter = this.editor.plugins.get('DataFilter'); - const dataSchema = this.editor.plugins.get('DataSchema'); - -- // This needs to be initialized in ::constructor() to ensure this runs -- // before the General HTML Support has been initialized. -- // @see module:html-support/generalhtmlsupport~GeneralHtmlSupport -- dataSchema.registerBlockElement({ -- model: 'drupalMedia', -- view: 'drupal-media', -- }); -+ const viewToModelMap = { -+ 'drupal-media': 'drupalMedia', -+ 'drupal-media-inline': 'drupalMediaInline', -+ }; - -- dataFilter.on('register:drupal-media', (evt, definition) => { -- if (definition.model !== 'drupalMedia') { -- return; -- } -+ Object.keys(viewToModelMap).forEach((view) => { -+ const model = viewToModelMap[view]; - -- schema.extend('drupalMedia', { -- allowAttributes: ['htmlLinkAttributes', 'htmlAttributes'], -+ // This needs to be initialized in ::constructor() to ensure this runs -+ // before the General HTML Support has been initialized. -+ // @see module:html-support/generalhtmlsupport~GeneralHtmlSupport -+ dataSchema.registerBlockElement({ -+ model, -+ view, - }); - -- conversion -- .for('upcast') -- .add(viewToModelDrupalMediaAttributeConverter(dataFilter)); -- conversion -- .for('editingDowncast') -- .add(modelToEditingViewAttributeConverter()); -- conversion.for('dataDowncast').add(modelToDataViewAttributeConverter()); -+ dataFilter.on(`register:${view}`, (evt, definition) => { -+ if (definition.model !== model) { -+ return; -+ } - -- evt.stop(); -+ schema.extend(model, { -+ allowAttributes: ['htmlLinkAttributes', 'htmlAttributes'], -+ }); -+ -+ conversion -+ .for('upcast') -+ .add( -+ viewToModelDrupalMediaAttributeConverter(dataFilter, model, view), -+ ); -+ conversion -+ .for('editingDowncast') -+ .add(modelToEditingViewAttributeConverter(model)); -+ conversion -+ .for('dataDowncast') -+ .add(modelToDataViewAttributeConverter(model)); -+ -+ evt.stop(); -+ }); - }); - } - -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js -index bf9c6451e5330880b689017e3fcf8ade05f0f9f7..5cd6337426eb1cd499b7893dba0ccd7a9ddb921c 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/insertdrupalmedia.js -@@ -1,15 +1,42 @@ - /* eslint-disable import/no-extraneous-dependencies */ - // cspell:ignore insertdrupalmediacommand - import { Command } from 'ckeditor5/src/core'; -+import { first } from 'ckeditor5/src/utils'; - import { groupNameToModelAttributeKey } from './utils'; - - /** - * @module drupalMedia/insertdrupalmediacommand - */ - --function createDrupalMedia(writer, attributes) { -- const drupalMedia = writer.createElement('drupalMedia', attributes); -- return drupalMedia; -+function createDrupalMedia(writer, attributes, model) { -+ return writer.createElement(model, attributes); -+} -+ -+function determineImageTypeForInsertionAtSelection(schema, selection) { -+ const firstBlock = first(selection.getSelectedBlocks()); -+ // Insert a block media if the selection is not in/on block elements or it's -+ // on a block widget. -+ if (!firstBlock || schema.isObject(firstBlock)) { -+ return 'drupalMedia'; -+ } -+ // A block image should also be inserted into an empty block element -+ // (that is not an empty list item so the list won't get split). -+ if (firstBlock.isEmpty && firstBlock.name !== 'listItem') { -+ return 'drupalMedia'; -+ } -+ // Otherwise insert an inline media. -+ return 'drupalMediaInline'; -+} -+ -+function determineImageTypeForInsertion(editor, selectable) { -+ const schema = editor.model.schema; -+ // Try to replace the selected widget (e.g. another image). -+ if (selectable.is('selection')) { -+ return determineImageTypeForInsertionAtSelection(schema, selectable); -+ } -+ return schema.checkChild(selectable, 'drupalMediaInline') -+ ? 'drupalMediaInline' -+ : 'drupalMedia'; - } - - /** -@@ -83,9 +110,14 @@ export default class InsertDrupalMediaCommand extends Command { - } - } - -+ const insertedModel = determineImageTypeForInsertion( -+ this.editor, -+ this.editor.model.document.selection, -+ ); -+ - this.editor.model.change((writer) => { - this.editor.model.insertObject( -- createDrupalMedia(writer, modelAttributes), -+ createDrupalMedia(writer, modelAttributes, insertedModel), - ); - }); - } -@@ -93,9 +125,10 @@ export default class InsertDrupalMediaCommand extends Command { - refresh() { - const model = this.editor.model; - const selection = model.document.selection; -+ const mediaModel = determineImageTypeForInsertion(this.editor, selection); - const allowedIn = model.schema.findAllowedParent( - selection.getFirstPosition(), -- 'drupalMedia', -+ mediaModel, - ); - this.isEnabled = allowedIn !== null; - } -diff --git a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js -index afd77f4cd9828c77c226ddda92437775f3cd8686..180fdc3f3b2b01944f749c7788a9fed36f4e14d6 100644 ---- a/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js -+++ b/core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils.js -@@ -7,12 +7,25 @@ import { isWidget } from 'ckeditor5/src/widget'; - * - * @param {module:engine/model/element~Element} modelElement - * The model element to be checked. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {boolean} - * A boolean indicating if the element is a drupalMedia element. - * - * @private - */ --export function isDrupalMedia(modelElement) { -+export function isDrupalMedia(modelElement, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } -+ if (includeInline) { -+ return ( -+ !!modelElement && -+ (modelElement.is('element', 'drupalMedia') || -+ modelElement.is('element', 'drupalMediaInline')) -+ ); -+ } - return !!modelElement && modelElement.is('element', 'drupalMedia'); - } - -@@ -21,12 +34,25 @@ export function isDrupalMedia(modelElement) { - * - * @param {module:engine/view/element~Element} viewElement - * The view element. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {boolean} - * A boolean indicating if the element is a <drupal-media> element. - * - * @private - */ --export function isDrupalMediaWidget(viewElement) { -+export function isDrupalMediaWidget(viewElement, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } -+ if (includeInline) { -+ return ( -+ isWidget(viewElement) && -+ (!!viewElement.getCustomProperty('drupalMedia') || -+ !!viewElement.getCustomProperty('drupalMediaInline')) -+ ); -+ } - return ( - isWidget(viewElement) && !!viewElement.getCustomProperty('drupalMedia') - ); -@@ -37,6 +63,9 @@ export function isDrupalMediaWidget(viewElement) { - * - * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection - * The current selection. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {module:engine/model/element~Element|null} - * The `drupalMedia` element which could be either the current selected an - * ancestor of the selection. Returns null if the selection has no Drupal -@@ -44,12 +73,17 @@ export function isDrupalMediaWidget(viewElement) { - * - * @private - */ --export function getClosestSelectedDrupalMediaElement(selection) { -+export function getClosestSelectedDrupalMediaElement(selection, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } - const selectedElement = selection.getSelectedElement(); - -- return isDrupalMedia(selectedElement) -+ return isDrupalMedia(selectedElement, includeInline) - ? selectedElement -- : selection.getFirstPosition().findAncestor('drupalMedia'); -+ : selection -+ .getFirstPosition() -+ .findAncestor(includeInline ? /drupalMedia(Inline)?/ : /drupalMedia/); - } - - /** -@@ -57,14 +91,20 @@ export function getClosestSelectedDrupalMediaElement(selection) { - * - * @param {module:engine/model/selection~Selection} selection - * The current selection. -+ * @param {boolean} includeInline -+ * Optional. Whether to consider <drupal-media-inline> elements as -+ * media. Defaults to true. - * @return {module:engine/view/element~Element|null} - * The currently selected Drupal Media widget or null. - * - * @private - */ --export function getClosestSelectedDrupalMediaWidget(selection) { -+export function getClosestSelectedDrupalMediaWidget(selection, includeInline) { -+ if (typeof includeInline === 'undefined') { -+ includeInline = true; -+ } - const viewElement = selection.getSelectedElement(); -- if (viewElement && isDrupalMediaWidget(viewElement)) { -+ if (viewElement && isDrupalMediaWidget(viewElement, includeInline)) { - return viewElement; - } - -@@ -76,7 +116,7 @@ export function getClosestSelectedDrupalMediaWidget(selection) { - let parent = selection.getFirstPosition().parent; - - while (parent) { -- if (parent.is('element') && isDrupalMediaWidget(parent)) { -+ if (parent.is('element') && isDrupalMediaWidget(parent, includeInline)) { - return parent; - } - -diff --git a/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php b/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php -index c6b9f345a98ab15a08e76a86bcf5d40cda93788a..dbaadb59a293caa2088cc32a101f67425420a366 100644 ---- a/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php -+++ b/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/Media.php -@@ -121,7 +121,7 @@ private function configureViewModes(EditorInterface $editor) { - 'title' => $all_view_modes[$view_mode], - 'attributeName' => 'data-view-mode', - 'attributeValue' => $view_mode, -- 'modelElements' => ['drupalMedia'], -+ 'modelElements' => ['drupalMedia', 'drupalMediaInline'], - 'modelAttributes' => [ - 'drupalMediaType' => array_keys($media_bundles), - ], -@@ -133,7 +133,7 @@ private function configureViewModes(EditorInterface $editor) { - 'title' => $all_view_modes[$view_mode], - 'attributeName' => 'data-view-mode', - 'attributeValue' => $view_mode, -- 'modelElements' => ['drupalMedia'], -+ 'modelElements' => ['drupalMedia', 'drupalMediaInline'], - 'modelAttributes' => [ - 'drupalMediaType' => $specific_bundles, - ], -@@ -196,7 +196,7 @@ public function getElementsSubset(): array { - $subset = $this->getPluginDefinition()->getElements(); - $view_mode_override_enabled = $this->getConfiguration()['allow_view_mode_override']; - if (!$view_mode_override_enabled) { -- $subset = array_diff($subset, ['<drupal-media data-view-mode>']); -+ $subset = array_diff($subset, ['<drupal-media data-view-mode>', '<drupal-media-inline data-view-mode>']); - } - return $subset; - } -diff --git a/core/modules/ckeditor5/templates/field--ckeditor-inline.html.twig b/core/modules/ckeditor5/templates/field--ckeditor-inline.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..7f0ae06f8369db15ea4b70771cacfc9a61eff771 ---- /dev/null -+++ b/core/modules/ckeditor5/templates/field--ckeditor-inline.html.twig -@@ -0,0 +1,72 @@ -+{# -+/** -+ * @file -+ * Default theme implementation for a field. -+ * -+ * To override output, copy the "field.html.twig" from the templates directory -+ * to your theme's directory and customize it, just like customizing other -+ * Drupal templates such as page.html.twig or node.html.twig. -+ * -+ * Instead of overriding the theming for all fields, you can also just override -+ * theming for a subset of fields using -+ * @link themeable Theme hook suggestions. @endlink For example, -+ * here are some theme hook suggestions that can be used for a field_foo field -+ * on an article node type: -+ * - field--node--field-foo--article.html.twig -+ * - field--node--field-foo.html.twig -+ * - field--node--article.html.twig -+ * - field--field-foo.html.twig -+ * - field--text-with-summary.html.twig -+ * - field.html.twig -+ * -+ * Available variables: -+ * - attributes: HTML attributes for the containing element. -+ * - label_hidden: Whether to show the field label or not. -+ * - title_attributes: HTML attributes for the title. -+ * - label: The label for the field. -+ * - multiple: TRUE if a field can contain multiple items. -+ * - items: List of all the field items. Each item contains: -+ * - attributes: List of HTML attributes for each item. -+ * - content: The field item's content. -+ * - entity_type: The entity type to which the field belongs. -+ * - field_name: The name of the field. -+ * - field_type: The type of the field. -+ * - label_display: The display settings for the label. -+ * -+ * @see template_preprocess_field() -+ * -+ * @ingroup themeable -+ */ -+#} -+{% -+ set title_classes = [ -+ label_display == 'visually_hidden' ? 'visually-hidden', -+ ] -+%} -+ -+{% if label_hidden %} -+ {% if multiple %} -+ <span{{ attributes }}> -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ </span> -+ {% else %} -+ {% for item in items %} -+ <span{{ attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% endif %} -+{% else %} -+ <span{{ attributes }}> -+ <span{{ title_attributes.addClass(title_classes) }}>{{ label }}</span> -+ {% if multiple %} -+ <span> -+ {% endif %} -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% if multiple %} -+ </span> -+ {% endif %} -+ </span> -+{% endif %} -diff --git a/core/modules/ckeditor5/templates/media--ckeditor-inline-embed.html.twig b/core/modules/ckeditor5/templates/media--ckeditor-inline-embed.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..18a903f24544a90cae12c4a7d2008dd6b340fba5 ---- /dev/null -+++ b/core/modules/ckeditor5/templates/media--ckeditor-inline-embed.html.twig -@@ -0,0 +1,36 @@ -+{# -+/** -+ * @file -+ * Default theme implementation to present a media item. -+ * -+ * Available variables: -+ * - media: The media item, with limited access to object properties and -+ * methods. Only method names starting with "get", "has", or "is" and -+ * a few common methods such as "id", "label", and "bundle" are available. -+ * For example: -+ * - entity.getEntityTypeId() will return the entity type ID. -+ * - entity.hasField('field_example') returns TRUE if the entity includes -+ * field_example. (This does not indicate the presence of a value in this -+ * field.) -+ * Calling other methods, such as entity.delete(), will result in -+ * an exception. -+ * See \Drupal\Core\Entity\EntityInterface for a full list of methods. -+ * - name: Name of the media item. -+ * - content: Media content. -+ * - title_prefix: Additional output populated by modules, intended to be -+ * displayed in front of the main title tag that appears in the template. -+ * - title_suffix: Additional output populated by modules, intended to be -+ * displayed after the main title tag that appears in the template. -+ * - view_mode: View mode; for example, "teaser" or "full". -+ * - attributes: HTML attributes for the containing element. -+ * - title_attributes: Same as attributes, except applied to the main title -+ * tag that appears in the template. -+ * -+ * @see template_preprocess_media() -+ * -+ * @ingroup themeable -+ */ -+#} -+<span{{ attributes }}> -+ {{ content }} -+</span> -diff --git a/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php b/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php -index 8df587a4373f2e54f6e0f54f13652330f59773e1..c9ae714e1910ce4d41c91159120ab15116989fea 100644 ---- a/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php -+++ b/core/modules/ckeditor5/tests/src/Functional/MediaEntityMetadataApiTest.php -@@ -115,7 +115,7 @@ protected function setUp(): void { - 'status' => TRUE, - 'weight' => -10, - 'settings' => [ -- 'allowed_html' => "<p> <br> <drupal-media data-entity-type data-entity-uuid data-view-mode alt>", -+ 'allowed_html' => "<p> <br> <drupal-media data-entity-type data-entity-uuid data-view-mode alt> <drupal-media-inline data-entity-type data-entity-uuid data-view-mode alt>", - 'filter_html_help' => TRUE, - 'filter_html_nofollow' => TRUE, - ], -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php -index 65db1989a6497068867f2b4c9e3494c73c2351ba..40ed1bead31bc854920bd02a5ff206efe695bb58 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php -@@ -378,8 +378,8 @@ public function testMediaElementAllowedTags() { - $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_1]'); - $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_2]'); - -- $allowed_with_media = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode>'; -- $allowed_with_media_without_view_mode = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt>'; -+ $allowed_with_media = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode> <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode>'; -+ $allowed_with_media_without_view_mode = $this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt> <drupal-media-inline data-entity-type data-entity-uuid alt>'; - $page->clickLink('Media'); - $this->assertTrue($page->hasUncheckedField('editor[settings][plugins][media_media][allow_view_mode_override]')); - $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', $allowed_with_media_without_view_mode); -@@ -403,7 +403,7 @@ public function testMediaElementAllowedTags() { - // filter_align is enabled. - $page->checkField('filters[filter_align][status]'); - $assert_session->assertExpectedAjaxRequest(1); -- $this->assertEquals($this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode data-align>', $allowed_html_field->getValue()); -+ $this->assertEquals($this->allowedElements . ' <drupal-media data-entity-type data-entity-uuid alt data-view-mode data-align> <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode data-align>', $allowed_html_field->getValue()); - - // Disable media embed. - $this->assertTrue($page->hasCheckedField('filters[media_embed][status]')); -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php -index 209b1de962c1b7f071a8cb707c6ffee04a949f81..4427a53661d33cca9a3357d1f83a267b75b5f34e 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaLinkabilityTest.php -@@ -41,7 +41,7 @@ public function testLinkedMediaArbitraryHtml(bool $unrestricted): void { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href data-foo> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <div data-bar>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href data-foo> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <div data-bar> <drupal-media-inline data-entity-type data-entity-uuid alt data-view-mode data-caption data-align>', - ], - ]); - } -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php -index c9aca401b65cb2cc8de02b87dde466b7d4dcd7a4..6e71a6113f0fd9fcdecfa4aa971a3f22ae388b83 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php -@@ -98,7 +98,7 @@ public function testMediaSplitList() { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <ol> <ul> <li>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <drupal-media-inline data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <ol> <ul> <li>', - ], - ]); - $filter_format->save(); -@@ -153,7 +153,7 @@ public function testMediaArbitraryHtml() { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-foo data-view-mode> <div data-bar>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-caption alt data-foo data-view-mode> <drupal-media-inline data-entity-type data-entity-uuid data-align data-caption alt data-view-mode> <div data-bar>', - ], - ]); - $filter_format->save(); -@@ -688,7 +688,7 @@ public function testDrupalMediaStyleWithClass() { - $filter_format->setFilterConfig('filter_html', [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <h1 class> <div class> <section class> <drupal-media data-entity-type data-entity-uuid data-align data-caption data-view-mode alt class="layercake-side">', -+ 'allowed_html' => '<p> <br> <h1 class> <div class> <section class> <drupal-media data-entity-type data-entity-uuid data-align data-caption data-view-mode alt class="layercake-side"> <drupal-media-inline data-entity-type data-entity-uuid data-align data-caption data-view-mode alt>', - ], - ]); - $filter_format->save(); -@@ -1004,6 +1004,56 @@ public function testViewMode(bool $with_alignment) { - $this->assertNotEmpty($this->getBalloonButton('View Mode 4')); - } - -+ /** -+ * Tests inline embedding. -+ */ -+ public function testInlineMedia() { -+ // Reconfigure the text format to suit our needs. -+ /** @var \Drupal\filter\FilterFormatInterface $format */ -+ $format = FilterFormat::load($this->host->body->format); -+ $filter_html = $format->get('filters')['filter_html']; -+ $filter_html['settings']['allowed_html'] = $filter_html['settings']['allowed_html'] . '<ul> <li>'; -+ $format->setFilterConfig('filter_html', $filter_html); -+ $format->save(); -+ -+ // Setup the test content containing inline and normal media in the editor. -+ $assert_session = $this->assertSession(); -+ $original_value = $this->host->body->value; -+ $inline_value = \str_replace('drupal-media', 'drupal-media-inline', $original_value); -+ // @todo Captions on inline media are not supported yet. -+ $inline_value = \str_replace('data-caption="baz"', '', $inline_value); -+ $this->host->body->value = <<<END -+ <div> -+ $original_value -+ <p>This is a paragraph $inline_value with an inline media</p> -+ <ul> -+ <li>This is a list item $inline_value containing an inline media</li> -+ </ul> -+ </div> -+END; -+ $this->host->save(); -+ $this->drupalGet($this->host->toUrl('edit-form')); -+ -+ // Wait until media previews are fetched. -+ $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-widget.drupal-media-inline')); -+ $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-widget.drupal-media')); -+ -+ // Confirm that the inline media is contained as phrasing content inside -+ // flow content. -+ $assert_session->elementExists('css', 'p > .drupal-media-inline'); -+ $assert_session->elementExists('css', 'ul > li > .drupal-media-inline'); -+ // Also ensure that a media not embedded inside flow content is still -+ // rendered not using the inline templates. -+ $assert_session->elementExists('css', 'div > .drupal-media'); -+ -+ // Verify the rendered inline media is rendered with markup that is using -+ // the dedicated inline media and field templates. -+ $this->drupalGet($this->host->toUrl('canonical')); -+ $assert_session->elementExists('css', 'p > span.media-embedded-inline span img[src*="image-test.png"]'); -+ $assert_session->elementExists('css', 'ul > li > span.media-embedded-inline span img[src*="image-test.png"]'); -+ $assert_session->elementExists('css', 'div > figure.caption-drupal-media article.media img[src*="image-test.png"]'); -+ } -+ - /** - * For testing view modes in different scenarios. - */ -diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php -index 3384ec7fa483a06b2b41e0209a1775c72327963d..0d8faf4931463d30bb8c760a76bc8f19bfdb7c45 100644 ---- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php -+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTestBase.php -@@ -101,7 +101,7 @@ protected function setUp(): void { - 'filter_html' => [ - 'status' => TRUE, - 'settings' => [ -- 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-view-mode data-caption alt>', -+ 'allowed_html' => '<p> <br> <strong> <em> <a href> <drupal-media data-entity-type data-entity-uuid data-align data-view-mode data-caption alt> <drupal-media-inline data-entity-type data-entity-uuid data-align data-view-mode data-caption alt>', - ], - ], - 'filter_align' => ['status' => TRUE], -diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module -index 2ba66da8085e35686628703e882f4220a5f57f50..ef79b8e9614bcb9c73c3ca37b8d75392e09242a3 100644 ---- a/core/modules/filter/filter.module -+++ b/core/modules/filter/filter.module -@@ -705,7 +705,7 @@ function _filter_autop($text) { - // to avoid messing up code. We look for matched pairs and allow basic - // nesting. For example: - // "processed <pre> ignored <script> ignored </script> ignored </pre> processed" -- $chunks = preg_split('@(<!--.*?-->|</?(?:pre|script|style|object|iframe|drupal-media|svg|!--)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); -+ $chunks = preg_split('@(<!--.*?-->|</?(?:pre|script|style|object|iframe|drupal-media|drupal-media-inline|svg|!--)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - // Note: PHP ensures the array consists of alternating delimiters and literals - // and begins and ends with a literal (inserting NULL as required). - $ignore = FALSE; -diff --git a/core/modules/media/css/media.inline.css b/core/modules/media/css/media.inline.css -new file mode 100644 -index 0000000000000000000000000000000000000000..0ff40b92181fafb3edfb0d599d3e32376590f3ba ---- /dev/null -+++ b/core/modules/media/css/media.inline.css -@@ -0,0 +1,18 @@ -+/** -+ * @file -+ * Default styling for media that is embedded inline inside text, lists, etc. -+ */ -+ -+.media-embedded-inline { -+ display: inline-block; -+} -+ -+.drupal-media-inline .media-embedded-inline { -+ width: 100%; -+} -+ -+.media-embedded-inline video, -+.media-embedded-inline img, -+.media-embedded-inline audio { -+ display: inline-block; -+} -diff --git a/core/modules/media/media.libraries.yml b/core/modules/media/media.libraries.yml -index 41fc310fbb9a211b5a704715386379ef2444bc60..0e9527db1dbf30275718a68400b6c480aee5c8e4 100644 ---- a/core/modules/media/media.libraries.yml -+++ b/core/modules/media/media.libraries.yml -@@ -32,6 +32,12 @@ filter.caption: - dependencies: - - filter/caption - -+media.inline: -+ version: VERSION -+ css: -+ component: -+ css/media.inline.css: {} -+ - # Despite the name, this is actually not specific to CKEditor 4, and can be - # used by all text editor plugins. - media_embed_ckeditor_theme: -diff --git a/core/modules/media/src/Event/MediaBuildEmbedEvent.php b/core/modules/media/src/Event/MediaBuildEmbedEvent.php -new file mode 100644 -index 0000000000000000000000000000000000000000..98e5f4d5fb5e9044811810f5a013a1e838a0b6cb ---- /dev/null -+++ b/core/modules/media/src/Event/MediaBuildEmbedEvent.php -@@ -0,0 +1,95 @@ -+<?php -+ -+namespace Drupal\media\Event; -+ -+use Drupal\Component\EventDispatcher\Event; -+use Drupal\media\MediaInterface; -+ -+/** -+ * Event that gets triggered when a media is about to be embedded in ckeditor5. -+ */ -+class MediaBuildEmbedEvent extends Event { -+ -+ /** -+ * The view mode the embedded media will be rendered with. -+ * -+ * @var string -+ */ -+ protected string $viewMode; -+ -+ /** -+ * The media which will be embedded. -+ * -+ * @var \Drupal\media\MediaInterface -+ */ -+ protected MediaInterface $media; -+ -+ /** -+ * The build array of the media before it gets rendered for embedding. -+ * -+ * @var array -+ */ -+ protected array $build; -+ -+ /** -+ * The <drupal-media> DOM node that gets replaced by the embed. -+ * -+ * @var \DOMElement -+ */ -+ protected \DOMElement $node; -+ -+ /** -+ * Construct a MediaBuildEmbedEvent object. -+ * -+ * @param string $viewMode -+ * The view mode the embedded media will be rendered with. -+ * @param \Drupal\media\MediaInterface $media -+ * The media which will be embedded. -+ * @param array $build -+ * The build array of the media before it gets rendered for embedding. -+ * @param \DOMElement $node -+ * The <drupal-media> DOM node that gets replaced by the embed. -+ */ -+ public function __construct(string $viewMode, MediaInterface $media, array $build, \DOMElement $node) { -+ $this->viewMode = $viewMode; -+ $this->media = $media; -+ $this->build = $build; -+ $this->node = $node; -+ } -+ -+ /** -+ * Getter for the view mode. -+ */ -+ public function getViewMode(): string { -+ return $this->viewMode; -+ } -+ -+ /** -+ * Gets the media. -+ */ -+ public function getMedia(): MediaInterface { -+ return $this->media; -+ } -+ -+ /** -+ * Gets the build. -+ */ -+ public function getBuild(): array { -+ return $this->build; -+ } -+ -+ /** -+ * Sets the build. -+ */ -+ public function setBuild(array $build): void { -+ $this->build = $build; -+ } -+ -+ /** -+ * Gets the <drupal-media> DOM node. -+ */ -+ public function getNode(): \DOMElement { -+ return $this->node; -+ } -+ -+} -diff --git a/core/modules/media/src/Plugin/Filter/MediaEmbed.php b/core/modules/media/src/Plugin/Filter/MediaEmbed.php -index 118422bf2c28d594569aa58a806ef722e4e0c4e4..c138c2ea492cee4f1080d69afe76f007ea228091 100644 ---- a/core/modules/media/src/Plugin/Filter/MediaEmbed.php -+++ b/core/modules/media/src/Plugin/Filter/MediaEmbed.php -@@ -21,8 +21,10 @@ - use Drupal\filter\Plugin\FilterBase; - use Drupal\filter\Plugin\FilterInterface; - use Drupal\image\Plugin\Field\FieldType\ImageItem; -+use Drupal\media\Event\MediaBuildEmbedEvent; - use Drupal\media\MediaInterface; - use Symfony\Component\DependencyInjection\ContainerInterface; -+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - - /** - * Provides a filter to embed media items using a custom tag. -@@ -118,8 +120,10 @@ class MediaEmbed extends FilterBase implements ContainerFactoryPluginInterface, - * The renderer. - * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory - * The logger factory. -+ * @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $eventDispatcher -+ * The event dispatcher. - */ -- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityRepositoryInterface $entity_repository, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, EntityTypeBundleInfoInterface $bundle_info, RendererInterface $renderer, LoggerChannelFactoryInterface $logger_factory) { -+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityRepositoryInterface $entity_repository, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, EntityTypeBundleInfoInterface $bundle_info, RendererInterface $renderer, LoggerChannelFactoryInterface $logger_factory, protected EventDispatcherInterface $eventDispatcher) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->entityRepository = $entity_repository; - $this->entityTypeManager = $entity_type_manager; -@@ -142,7 +146,8 @@ public static function create(ContainerInterface $container, array $configuratio - $container->get('entity_display.repository'), - $container->get('entity_type.bundle.info'), - $container->get('renderer'), -- $container->get('logger.factory') -+ $container->get('logger.factory'), -+ $container->get('event_dispatcher'), - ); - } - -@@ -255,6 +260,7 @@ protected function renderMedia(MediaInterface $media, $view_mode, $langcode) { - // instead of only when #access allows this media to be viewed and hence - // only when media is actually rendered. - $build[':media_embed']['#attached']['library'][] = 'media/filter.caption'; -+ $build[':media_embed']['#attached']['library'][] = 'media/media.inline'; - - return $build; - } -@@ -278,18 +284,26 @@ protected function renderMissingMediaIndicator() { - public function process($text, $langcode) { - $result = new FilterProcessResult($text); - -- if (stristr($text, '<drupal-media') === FALSE) { -+ if (stristr($text, '<drupal-media') === FALSE && stristr($text, '<drupal-media-inline') === FALSE) { - return $result; - } - - $dom = Html::load($text); - $xpath = new \DOMXPath($dom); -+ $matched_attributes = '@data-entity-type="media" and normalize-space(@data-entity-uuid)!=""'; - -- foreach ($xpath->query('//drupal-media[@data-entity-type="media" and normalize-space(@data-entity-uuid)!=""]') as $node) { -+ foreach ($xpath->query('//drupal-media[' . $matched_attributes . ']|//drupal-media-inline[' . $matched_attributes . ']') as $node) { - /** @var \DOMElement $node */ - $uuid = $node->getAttribute('data-entity-uuid'); - $view_mode_id = $node->getAttribute('data-view-mode') ?: $this->settings['default_view_mode']; - -+ // Inline media needs a dedicated view mode in order to ensure rendering -+ // of valid (flow content) HTML when rendering inside flow content like -+ // <p> tags for example. -+ if ($node->tagName == 'drupal-media-inline') { -+ $view_mode_id = 'ckeditor_inline'; -+ } -+ - // Delete the consumed attributes. - $node->removeAttribute('data-entity-type'); - $node->removeAttribute('data-entity-uuid'); -@@ -314,9 +328,14 @@ public function process($text, $langcode) { - } - } - -- $build = $media && ($view_mode || $view_mode_id === EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE) -- ? $this->renderMedia($media, $view_mode_id, $langcode) -- : $this->renderMissingMediaIndicator(); -+ if ($media && ($view_mode || $view_mode_id === EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE)) { -+ $build = $this->renderMedia($media, $view_mode_id, $langcode); -+ $event = $this->eventDispatcher->dispatch(new MediaBuildEmbedEvent($view_mode_id, $media, $build, $node)); -+ $build = $event->getBuild(); -+ } -+ else { -+ $build = $this->renderMissingMediaIndicator(); -+ } - - if (empty($build['#attributes']['class'])) { - $build['#attributes']['class'] = []; -@@ -339,6 +358,10 @@ public function process($text, $langcode) { - } - } - -+ if ($node->tagName == 'drupal-media-inline') { -+ $build['#attributes']['class'][] = 'media-embedded-inline'; -+ } -+ - $this->renderIntoDomNode($build, $node, $result); - } - -diff --git a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php -index eb2d1d3bdcaa764666480b9567a2de63eb11071e..93da1306ee2b1223f8d49a83a1d6a1aa6c5b19fb 100644 ---- a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php -+++ b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php -@@ -40,7 +40,7 @@ public function testBasics(array $embed_attributes, $expected_view_mode, array $ - $this->assertEqualsCanonicalizing($expected_cacheability->getCacheContexts(), $result->getCacheContexts()); - $this->assertSame($expected_cacheability->getCacheMaxAge(), $result->getCacheMaxAge()); - $this->assertSame(['library'], array_keys($result->getAttachments())); -- $this->assertSame(['media/filter.caption'], $result->getAttachments()['library']); -+ $this->assertSame(['media/filter.caption', 'media/media.inline'], $result->getAttachments()['library']); - } - - /** -@@ -194,7 +194,7 @@ public static function providerAccessUnpublished() { - ]) - ->setCacheContexts(['timezone', 'user', 'user.permissions']) - ->setCacheMaxAge(Cache::PERMANENT), -- ['library' => ['media/filter.caption']], -+ ['library' => ['media/filter.caption', 'media/media.inline']], - ], - ]; - } -@@ -428,7 +428,7 @@ public function testFilterIntegration(array $filter_ids, array $additional_attri - * Data provider for testFilterIntegration(). - */ - public static function providerFilterIntegration() { -- $default_asset_libraries = ['media/filter.caption']; -+ $default_asset_libraries = ['media/filter.caption', 'media/media.inline']; - - $caption_additional_attributes = ['data-caption' => 'Yo.']; - $caption_verification_selector = 'figure > figcaption'; -@@ -445,14 +445,14 @@ public static function providerFilterIntegration() { - $caption_additional_attributes, - $caption_verification_selector, - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - ], - '`<a>` + `data-caption`; `filter_caption` + `media_embed` ⇒ caption present, link preserved' => [ - ['filter_caption', 'media_embed'], - $caption_additional_attributes, - 'figure > a[href="https://www.drupal.org"] + figcaption', - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - '<a href="https://www.drupal.org">', - '</a>', - ], -@@ -492,14 +492,14 @@ public static function providerFilterIntegration() { - $align_additional_attributes + $caption_additional_attributes, - 'figure.align-center > figcaption', - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - ], - '`<a>` + `data-caption` + `data-align`; `filter_align` + `filter_caption` + `media_embed` ⇒ aligned caption present, link preserved' => [ - ['filter_align', 'filter_caption', 'media_embed'], - $align_additional_attributes + $caption_additional_attributes, - 'figure.align-center > a[href="https://www.drupal.org"] + figcaption', - TRUE, -- ['filter/caption', 'media/filter.caption'], -+ ['filter/caption', 'media/filter.caption', 'media/media.inline'], - '<a href="https://www.drupal.org">', - '</a>', - ], -diff --git a/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml b/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml -index 60cd331cc875cdb4286b0be09ff99bb9c5a7db54..08518f40f78fa4f506ed54d06549cfd066db638c 100644 ---- a/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml -+++ b/core/profiles/demo_umami/config/install/editor.editor.basic_html.yml -@@ -55,6 +55,7 @@ settings: - - '<h6 id>' - - '<img src alt data-entity-type data-entity-uuid data-align data-caption width height loading>' - - '<drupal-media title>' -+ - '<drupal-media-inline title>' - media_media: - allow_view_mode_override: true - image_upload: -diff --git a/core/profiles/demo_umami/config/install/filter.format.basic_html.yml b/core/profiles/demo_umami/config/install/filter.format.basic_html.yml -index b57e2a67a7950fe32b2a422be14c9154f510ae97..8ad1363564d2631f7265d87faaa4bff5ef00c9ba 100644 ---- a/core/profiles/demo_umami/config/install/filter.format.basic_html.yml -+++ b/core/profiles/demo_umami/config/install/filter.format.basic_html.yml -@@ -40,7 +40,7 @@ filters: - status: true - weight: -10 - settings: -- allowed_html: '<a href hreflang> <em> <strong> <cite> <blockquote cite> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <br> <img src alt loading height width data-entity-type data-entity-uuid data-align data-caption> <drupal-media data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title>' -+ allowed_html: '<a href hreflang> <em> <strong> <cite> <blockquote cite> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <br> <img src alt loading height width data-entity-type data-entity-uuid data-align data-caption> <drupal-media data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title> <drupal-media-inline data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title>' - filter_html_help: false - filter_html_nofollow: false - filter_html_image_secure: -diff --git a/core/themes/stable9/css/media/media.inline.css b/core/themes/stable9/css/media/media.inline.css -new file mode 100644 -index 0000000000000000000000000000000000000000..0ff40b92181fafb3edfb0d599d3e32376590f3ba ---- /dev/null -+++ b/core/themes/stable9/css/media/media.inline.css -@@ -0,0 +1,18 @@ -+/** -+ * @file -+ * Default styling for media that is embedded inline inside text, lists, etc. -+ */ -+ -+.media-embedded-inline { -+ display: inline-block; -+} -+ -+.drupal-media-inline .media-embedded-inline { -+ width: 100%; -+} -+ -+.media-embedded-inline video, -+.media-embedded-inline img, -+.media-embedded-inline audio { -+ display: inline-block; -+} -diff --git a/core/themes/stable9/stable9.info.yml b/core/themes/stable9/stable9.info.yml -index cace78bf8a8251b94b68a20f488824a9d9019f8b..4f79c0a60f5bb6ad85714643183a829a3aa02f04 100644 ---- a/core/themes/stable9/stable9.info.yml -+++ b/core/themes/stable9/stable9.info.yml -@@ -177,6 +177,11 @@ libraries-override: - component: - css/filter.caption.css: css/media/filter.caption.css - -+ media/media.inline: -+ css: -+ component: -+ css/media.inline.css: css/media/media.inline.css -+ - media/oembed.formatter: - css: - component: -diff --git a/core/themes/stable9/templates/content/media--ckeditor-inline-embed.html.twig b/core/themes/stable9/templates/content/media--ckeditor-inline-embed.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..18a903f24544a90cae12c4a7d2008dd6b340fba5 ---- /dev/null -+++ b/core/themes/stable9/templates/content/media--ckeditor-inline-embed.html.twig -@@ -0,0 +1,36 @@ -+{# -+/** -+ * @file -+ * Default theme implementation to present a media item. -+ * -+ * Available variables: -+ * - media: The media item, with limited access to object properties and -+ * methods. Only method names starting with "get", "has", or "is" and -+ * a few common methods such as "id", "label", and "bundle" are available. -+ * For example: -+ * - entity.getEntityTypeId() will return the entity type ID. -+ * - entity.hasField('field_example') returns TRUE if the entity includes -+ * field_example. (This does not indicate the presence of a value in this -+ * field.) -+ * Calling other methods, such as entity.delete(), will result in -+ * an exception. -+ * See \Drupal\Core\Entity\EntityInterface for a full list of methods. -+ * - name: Name of the media item. -+ * - content: Media content. -+ * - title_prefix: Additional output populated by modules, intended to be -+ * displayed in front of the main title tag that appears in the template. -+ * - title_suffix: Additional output populated by modules, intended to be -+ * displayed after the main title tag that appears in the template. -+ * - view_mode: View mode; for example, "teaser" or "full". -+ * - attributes: HTML attributes for the containing element. -+ * - title_attributes: Same as attributes, except applied to the main title -+ * tag that appears in the template. -+ * -+ * @see template_preprocess_media() -+ * -+ * @ingroup themeable -+ */ -+#} -+<span{{ attributes }}> -+ {{ content }} -+</span> -diff --git a/core/themes/stable9/templates/field/field--ckeditor-inline.html.twig b/core/themes/stable9/templates/field/field--ckeditor-inline.html.twig -new file mode 100644 -index 0000000000000000000000000000000000000000..7f0ae06f8369db15ea4b70771cacfc9a61eff771 ---- /dev/null -+++ b/core/themes/stable9/templates/field/field--ckeditor-inline.html.twig -@@ -0,0 +1,72 @@ -+{# -+/** -+ * @file -+ * Default theme implementation for a field. -+ * -+ * To override output, copy the "field.html.twig" from the templates directory -+ * to your theme's directory and customize it, just like customizing other -+ * Drupal templates such as page.html.twig or node.html.twig. -+ * -+ * Instead of overriding the theming for all fields, you can also just override -+ * theming for a subset of fields using -+ * @link themeable Theme hook suggestions. @endlink For example, -+ * here are some theme hook suggestions that can be used for a field_foo field -+ * on an article node type: -+ * - field--node--field-foo--article.html.twig -+ * - field--node--field-foo.html.twig -+ * - field--node--article.html.twig -+ * - field--field-foo.html.twig -+ * - field--text-with-summary.html.twig -+ * - field.html.twig -+ * -+ * Available variables: -+ * - attributes: HTML attributes for the containing element. -+ * - label_hidden: Whether to show the field label or not. -+ * - title_attributes: HTML attributes for the title. -+ * - label: The label for the field. -+ * - multiple: TRUE if a field can contain multiple items. -+ * - items: List of all the field items. Each item contains: -+ * - attributes: List of HTML attributes for each item. -+ * - content: The field item's content. -+ * - entity_type: The entity type to which the field belongs. -+ * - field_name: The name of the field. -+ * - field_type: The type of the field. -+ * - label_display: The display settings for the label. -+ * -+ * @see template_preprocess_field() -+ * -+ * @ingroup themeable -+ */ -+#} -+{% -+ set title_classes = [ -+ label_display == 'visually_hidden' ? 'visually-hidden', -+ ] -+%} -+ -+{% if label_hidden %} -+ {% if multiple %} -+ <span{{ attributes }}> -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ </span> -+ {% else %} -+ {% for item in items %} -+ <span{{ attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% endif %} -+{% else %} -+ <span{{ attributes }}> -+ <span{{ title_attributes.addClass(title_classes) }}>{{ label }}</span> -+ {% if multiple %} -+ <span> -+ {% endif %} -+ {% for item in items %} -+ <span{{ item.attributes }}>{{ item.content }}</span> -+ {% endfor %} -+ {% if multiple %} -+ </span> -+ {% endif %} -+ </span> -+{% endif %} diff --git a/patches/d10/5963.diff b/patches/d10/5963.diff deleted file mode 100644 index 78eb62010b735f7b3018b22201ac897471b847dc..0000000000000000000000000000000000000000 --- a/patches/d10/5963.diff +++ /dev/null @@ -1,29 +0,0 @@ -diff --git a/src/Commands/DrushCommands.php b/src/Commands/DrushCommands.php -index 348ccd1d17..30a0dd6063 100644 ---- a/src/Commands/DrushCommands.php -+++ b/src/Commands/DrushCommands.php -@@ -20,6 +20,7 @@ - use GuzzleHttp\Middleware; - use Psr\Log\LoggerAwareInterface; - use Psr\Log\LoggerAwareTrait; -+use Psr\Log\LoggerInterface; - use Robo\Common\IO; - use Robo\Contract\ConfigAwareInterface; - use Robo\Contract\IOAwareInterface; -@@ -65,6 +66,16 @@ protected function io(): DrushStyle - return $this->io; - } - -+ /** -+ * {@inheritdoc} -+ */ -+ public function setLogger(LoggerInterface $logger): void -+ { -+ if ($this->logger === NULL) { -+ $this->logger = $logger; -+ } -+ } -+ - /** - * Returns a logger object. - */ diff --git a/patches/d10/book-validation.patch b/patches/d10/book-validation.patch deleted file mode 100644 index 5533c63b80d5e50e4a6bbafbe6eb9b449397c4c1..0000000000000000000000000000000000000000 --- a/patches/d10/book-validation.patch +++ /dev/null @@ -1,262 +0,0 @@ -From f81c6ebf721254f081a7d4f8ee520953a649960d Mon Sep 17 00:00:00 2001 -From: Wilbur Ince <45246-wylbur@users.noreply.drupalcode.org> -Date: Wed, 2 Oct 2024 18:37:48 +0000 -Subject: [PATCH] Merging the existing patch into the latest release Book - contrib module 1.0 - ---- - book.module | 10 ++ - book.permissions.yml | 1 + - .../BookOutlineConstraintValidator.php | 7 +- - .../Functional/BookContentModerationTest.php | 138 ++++++++++++++++++ - tests/src/Functional/BookTest.php | 1 + - 5 files changed, 156 insertions(+), 1 deletion(-) - -diff --git a/book.module b/book.module -index d6973c9..b2e8cb1 100644 ---- a/book.module -+++ b/book.module -@@ -155,6 +155,9 @@ function book_node_links_alter(array &$links, NodeInterface $node, array $contex - */ - function book_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id): void { - $node = $form_state->getFormObject()->getEntity(); -+ if (!book_type_is_allowed($node->getType())) { -+ return; -+ } - $account = \Drupal::currentUser(); - $access_check = new BookNodeOutlineAccessCheck($account); - $access_return = $access_check->access($node); -@@ -321,6 +324,10 @@ function book_node_predelete(EntityInterface $node): void { - * Implements hook_ENTITY_TYPE_prepare_form() for node entities. - */ - function book_node_prepare_form(NodeInterface $node, $operation, FormStateInterface $form_state): void { -+ if (!book_type_is_allowed($node->getType())) { -+ return; -+ } -+ - /** @var \Drupal\book\BookManagerInterface $book_manager */ - $book_manager = \Drupal::service('book.manager'); - -@@ -553,6 +560,9 @@ function template_preprocess_book_node_export_html(array &$variables): void { - * A Boolean TRUE if the node type can be included in books; otherwise, FALSE. - */ - function book_type_is_allowed(string $type): bool { -+ if (\Drupal::currentUser()->hasPermission('add any content to books')) { -+ return TRUE; -+ } - return in_array($type, \Drupal::config('book.settings')->get('allowed_types')); - } - -diff --git a/book.permissions.yml b/book.permissions.yml -index 184385e..e585bd2 100644 ---- a/book.permissions.yml -+++ b/book.permissions.yml -@@ -7,6 +7,7 @@ add content to books: - add any content to books: - title: 'Add non-book content to outlines' - description: 'This permission is only considered if a role is already granted the <em>Add content and child pages to books and manage their hierarchies</em> permission.' -+ restrict access: true - access printer-friendly version: - title: 'View printer-friendly books' - description: 'View a book page and all of its sub-pages as a single document for ease of printing. Can be performance heavy.' -diff --git a/src/Plugin/Validation/Constraint/BookOutlineConstraintValidator.php b/src/Plugin/Validation/Constraint/BookOutlineConstraintValidator.php -index d716040..e1232a9 100644 ---- a/src/Plugin/Validation/Constraint/BookOutlineConstraintValidator.php -+++ b/src/Plugin/Validation/Constraint/BookOutlineConstraintValidator.php -@@ -35,7 +35,12 @@ class BookOutlineConstraintValidator extends ConstraintValidator implements Cont - * {@inheritdoc} - */ - public function validate($value, Constraint $constraint): void { -- if (isset($value) && !$value->isNew() && !$value->isDefaultRevision()) { -+ // Validate the book structure when the user has access to manage book -+ // outlines. When the user can manage book outlines, the book variable will -+ // be populated even if the node is not part of the book. If the user cannot -+ // manage book outlines, the book variable will be empty, and we can safely -+ // ignore the constraints as the outline cannot be changed by this user. -+ if (isset($value) && !empty($value->book) && !$value->isNew() && !$value->isDefaultRevision()) { - /** @var \Drupal\Core\Entity\ContentEntityInterface $original */ - $original = $this->bookManager->loadBookLink($value->id(), FALSE) ?: [ - 'bid' => 0, -diff --git a/tests/src/Functional/BookContentModerationTest.php b/tests/src/Functional/BookContentModerationTest.php -index 69ab643..f602e4d 100644 ---- a/tests/src/Functional/BookContentModerationTest.php -+++ b/tests/src/Functional/BookContentModerationTest.php -@@ -6,6 +6,8 @@ namespace Drupal\Tests\book\Functional; - - use Drupal\Tests\BrowserTestBase; - use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait; -+use Drupal\user\Entity\Role; -+use Drupal\user\UserInterface; - - /** - * Tests Book and Content Moderation integration. -@@ -17,6 +19,13 @@ class BookContentModerationTest extends BrowserTestBase { - use BookTestTrait; - use ContentModerationTestTrait; - -+ /** -+ * A user with permission to make workflow transitions but not manage books. -+ * -+ * @var \Drupal\user\UserInterface -+ */ -+ protected UserInterface $nonBookAdminUser; -+ - /** - * Modules to install. - * -@@ -60,6 +69,19 @@ class BookContentModerationTest extends BrowserTestBase { - 'use editorial transition create_new_draft', - 'use editorial transition publish', - ]); -+ -+ // Another user without manage book permissions to test updates to nodes -+ // that are -+ // 1. Not part of a book outline. -+ // 2. Part of a book outline. -+ $this->nonBookAdminUser = $this->drupalCreateUser([ -+ 'create book content', -+ 'edit own book content', -+ 'use editorial transition create_new_draft', -+ 'use editorial transition publish', -+ 'access printer-friendly version', -+ 'view any unpublished content', -+ ]); - } - - /** -@@ -166,4 +188,120 @@ class BookContentModerationTest extends BrowserTestBase { - $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); - } - -+ /** -+ * Tests that users who cannot manage books can still make node updates. -+ * -+ * @throws \Behat\Mink\Exception\ExpectationException -+ * @throws \Drupal\Core\Entity\EntityStorageException -+ */ -+ public function testNonBookAdminNodeUpdates(): void { -+ // 1. First test that users who cannot manage books can make updates to -+ // nodes that are not part of a book outline. -+ $this->drupalLogin($this->nonBookAdminUser); -+ // Create a new book page without actually attaching it to a book and create -+ // a draft. -+ $this->drupalGet('node/add/book'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'title[0][value]' => 'Some moderated content', -+ 'moderation_state[0][state]' => 'draft', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ $this->assertSession()->pageTextContains('Some moderated content has been created.'); -+ $node = $this->drupalGetNodeByTitle($edit['title[0][value]']); -+ $this->assertNotEmpty($node); -+ -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ // Publish the content. -+ $edit = [ -+ 'body[0][value]' => 'Second change non book admin user', -+ 'moderation_state[0][state]' => 'published', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); -+ $this->assertSession()->pageTextContains('Some moderated content has been updated'); -+ -+ // Now update content again, it should be successfully updated and not throw -+ // any errors. -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'moderation_state[0][state]' => 'draft', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); -+ $this->assertSession()->pageTextContains('Some moderated content has been updated'); -+ -+ // 2. Now test that users who cannot manage books can make updates to nodes -+ // that are part of a book outline. As the non admin book user, publish the -+ // content created above in order to be added to a book. -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'moderation_state[0][state]' => 'published', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ -+ // Create a book (as a book admin user). -+ $book_1_nodes = $this->createBook(['moderation_state[0][state]' => 'published']); -+ $book_1 = $this->book; -+ -+ // Now add the node created previously by the non book admin user to the -+ // book created above. We need to grant additional permission for bookAuthor -+ // to be able to edit the node owned by nonBookAdminUser. -+ $role_ids = $this->bookAuthor->getRoles(TRUE); -+ $role_id = reset($role_ids); -+ $role = Role::load($role_id); -+ $role->grantPermission('edit any book content'); -+ $role->save(); -+ $this->drupalLogin($this->bookAuthor); -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'book[bid]' => $this->book->id(), -+ 'moderation_state[0][state]' => 'published', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ -+ // Assert that the node has been added to the book. -+ $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); -+ $this->assertSession()->pageTextContains('Some moderated content has been updated'); -+ $this->checkBookNode($book_1, [ -+ $book_1_nodes[0], -+ $book_1_nodes[3], -+ $book_1_nodes[4], -+ $node, -+ ], FALSE, FALSE, $book_1_nodes[0], []); -+ -+ // Try to update the non book admin's node in the book as the user -+ // that cannot manage books, it should be successfully updated and not -+ // throw any errors. -+ $this->drupalLogin($this->nonBookAdminUser); -+ $this->drupalGet('node/' . $node->id() . '/edit'); -+ $this->assertSession()->statusCodeEquals(200); -+ $edit = [ -+ 'body[0][value]' => 'Change by non book admin user again', -+ 'moderation_state[0][state]' => 'draft', -+ ]; -+ $this->submitForm($edit, 'Save'); -+ $this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.'); -+ $this->assertSession()->pageTextContains('Some moderated content has been updated'); -+ -+ $this->drupalLogout(); -+ $this->drupalLogin($this->bookAuthor); -+ // Check that the book outline did not change. -+ $this->book = $book_1; -+ $this->checkBookNode($book_1, [ -+ $book_1_nodes[0], -+ $book_1_nodes[3], -+ $book_1_nodes[4], -+ $node, -+ ], FALSE, FALSE, $book_1_nodes[0], []); -+ $this->checkBookNode($book_1_nodes[0], [ -+ $book_1_nodes[1], -+ $book_1_nodes[2], -+ ], FALSE, $book_1, $book_1_nodes[1], [$book_1]); -+ } -+ - } -diff --git a/tests/src/Functional/BookTest.php b/tests/src/Functional/BookTest.php -index f39ac94..4989d1a 100644 ---- a/tests/src/Functional/BookTest.php -+++ b/tests/src/Functional/BookTest.php -@@ -707,6 +707,7 @@ class BookTest extends BrowserTestBase { - 'create book content', - 'edit own book content', - 'add content to books', -+ 'add any content to books', - 'node test view', - 'edit any page content', - ]); --- -GitLab - diff --git a/patches/d10/mobimo.diff b/patches/d10/mobimo.diff deleted file mode 100644 index 34a1a1734065b28eb94361d844ea1aa3d9633694..0000000000000000000000000000000000000000 --- a/patches/d10/mobimo.diff +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php ---- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php -+++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php -@@ -38,7 +38,7 @@ - * - * @var array - */ -- protected $schema; -+ public $schema; - - /** - * @var array -diff --git a/core/modules/field/src/Entity/FieldStorageConfig.php b/core/modules/field/src/Entity/FieldStorageConfig.php ---- a/core/modules/field/src/Entity/FieldStorageConfig.php -+++ b/core/modules/field/src/Entity/FieldStorageConfig.php -@@ -216,7 +216,7 @@ - * - * @var array - */ -- protected $schema; -+ public $schema; - - /** - * An array of field property definitions. diff --git a/patches/d10/show-widget-2948377-2.patch b/patches/d10/show-widget-2948377-2.patch deleted file mode 100644 index 9763a4b37061ccee091e57a9840295d51bc4e790..0000000000000000000000000000000000000000 --- a/patches/d10/show-widget-2948377-2.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/Entity/WorkflowState.php b/src/Entity/WorkflowState.php -index ab9660b..437bc48 100644 ---- a/src/Entity/WorkflowState.php -+++ b/src/Entity/WorkflowState.php -@@ -323,7 +323,7 @@ class WorkflowState extends ConfigEntityBase { - $options = $this->getOptions($entity, $field_name, $account, $force); - $count = count($options); - // The easiest case first: more then one option: always show form. -- if ($count > 1) { -+ if ($count >= 1) { - return TRUE; - } - // #2226451: Even in Creation state, we must have 2 visible states to show the widget. diff --git a/patches/d10/slick-1.diff b/patches/d10/slick-1.diff deleted file mode 100644 index e4ba3417ec0bebd698f8de2d1fc445195adcfc69..0000000000000000000000000000000000000000 --- a/patches/d10/slick-1.diff +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/src/Plugin/Filter/SlickFilter.php b/src/Plugin/Filter/SlickFilter.php -index 56040ad..def5118 100644 ---- a/src/Plugin/Filter/SlickFilter.php -+++ b/src/Plugin/Filter/SlickFilter.php -@@ -11,6 +11,7 @@ use Drupal\blazy\Blazy; - use Drupal\blazy\Plugin\Filter\BlazyFilterBase; - use Drupal\blazy\Plugin\Filter\BlazyFilterUtil as Util; - use Drupal\slick\SlickDefault; -+use Drupal\slick\SlickManager; - use Symfony\Component\DependencyInjection\ContainerInterface; - - /** -@@ -34,6 +35,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface; - */ - class SlickFilter extends BlazyFilterBase { - -+ /** -+ * The slick manager. -+ * -+ * @var \Drupal\slick\SlickManager -+ */ -+ protected SlickManager $manager; -+ - /** - * {@inheritdoc} - */ diff --git a/patches/d11.json b/patches/d11.json deleted file mode 100644 index b170065b7662b86900a2ebd28f7458188d69f3e7..0000000000000000000000000000000000000000 --- a/patches/d11.json +++ /dev/null @@ -1,258 +0,0 @@ -{ - "patches": { - "drupal/architect": { - "#3137889 Drupal 9": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3137889.diff", - "#local Drupal 10": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/architect-d10.patch" - }, - "drupal/background_image": { - "#3161262 Pre render fix": "https://www.drupal.org/files/issues/2020-07-24/implement_trustedcallbackinterface.patch", - "#3170843 Dom Ready": "https://www.drupal.org/files/issues/2020-09-14/3170843-2.background_image.Library-drupaldomready-does-not-exist-in-Drupal-9.patch", - "#3173283 Cache": "https://www.drupal.org/files/issues/2020-09-26/3173283-2.background_image.Cache-context-also-requires-URL.patch", - "#3173340 Exception": "https://www.drupal.org/files/issues/2020-09-26/3173340-2.background_image.Error-Call-to-a-member-function-hasEntityToken-on-bool-in-DrupalbackgroundimageCacheContextBackgroundImageSettingsTextCacheContextgetContext.patch", - "#3128542 FileScan": "https://www.drupal.org/files/issues/2020-09-11/background_image-missing_folder-3128542-8.patch" - }, - "drupal/bootstrap_clean_blog": { - "#3146287 Drupal 9": "https://www.drupal.org/files/issues/2021-10-23/3146287_3.patch" - }, - "drupal/ckeditor_find": { - "#3252081 Library location": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3252081.diff" - }, - "drupal/codesnippet": { - "#3021431 Add Yaml as code format": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3021431.diff" - }, - "drupal/comment_notify": { - "#2926228 Always notify admin": "https://www.drupal.org/files/issues/2019-09-11/2926228-11.comment_notify.Optionally-notify-site-admin-of-all-comments.patch" - }, - "drupal/commerce_xquantity": { - "#3404050 Exceptions in actions of xquantity_stock": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3404050-2.diff" - }, - "drupal/config_devel": { - "#3219083 Export raw data": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3219083.diff", - "#3227881 Support config_rewrite": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227881.diff?v=2" - }, - "drupal/config_sync": { - "#3176955 Drupal 9 test compatibility": "https://www.drupal.org/files/issues/2021-03-19/config_sync-config-sync-test-core-3176955-4.patch" - }, - "drupal/config_update": { - "#3436790 Module weight": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3436790.diff?v=2", - "#3056249 Core hash for localisation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3056249.diff?v=2" - }, - "drupal/content_lock": { - "#2951652 No content lock für CLI commands": "https://www.drupal.org/files/issues/2022-04-05/2951652-13.patch", - "#3160781 Release lock when browsing away from locked form": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3160781-2.diff" - }, - "drupal/core_for_review": { - "#3227732 Book hierarchy migration": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227732.diff" - }, - "drupal/core": { - "#local Hot fix timezoone": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/timezone.patch", - "#2647292 Date/time Views filter tries strotime() relative to Unix epoch": "https://www.drupal.org/files/issues/2024-03-17/with-dependency-injection-2647292-75.patch", - "#2884879 Ignore empty fields for REST export": "https://www.drupal.org/files/issues/2884879-11.patch", - "#2966735 Workaround for DateTime exposed filter": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2966735.diff", - "#3043879 Layout Builder Config": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3043879-new.diff", - "#3195171 Fix view header group rendering": "https://www.drupal.org/files/issues/2022-01-05/3195171-7-fix-view-header-group-rendering.patch", - "#3180227 Missing options for entity refernces in views": "https://www.drupal.org/files/issues/2020-11-02/3180227-2.patch", - "#local Json API and entity reference warnings": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/jsonapi-entityref-warning-10.3.patch", - "#local Issue 3168966 Insert Media inline in CKEditor widget": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/5758-2.diff", - "#3228298 Empty path alias exception": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3228298.diff?v=2", - "#3227816 Hide password reset link": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227816.diff", - "#3249628 Comments and paragraphs": "https://www.drupal.org/files/issues/2022-12-11/1415-11.diff", - "#2840283 Views action path with EVA": "https://www.drupal.org/files/issues/problem_with_action-2840283-25.patch", - "#2816447 RSS feed titles wrong language": "https://www.drupal.org/files/issues/2023-08-03/2816447-58.patch", - "#2915705 TypedData any": "https://www.drupal.org/files/issues/2022-12-11/2915705-82.patch", - "#3302450 Views field default type": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3302450.diff", - "#3311849 Views exposed filter, empty sections": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311849.diff", - "#2797583 Actions for content moderation": "https://www.drupal.org/files/issues/2022-09-20/core-provide-moderation-states-as-actions-2797583-207.patch", - "#3194462 Big pipe explode issue": "https://www.drupal.org/files/issues/2023-02-28/3194462-30.patch", - "#3346430 DoPreSave load revision": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3346430.diff", - "#3340973 Media Lib type error on array_filter": "https://www.drupal.org/files/issues/2023-02-10/fix_media_library_php_error-3340973-2.patch", - "#3073822 Error: Call to a member function access() on null in Drupal\\comment\\CommentAccessControlHandler->checkAccess()": "https://www.drupal.org/files/issues/2023-01-12/3073822-12.patch", - "#2761273 Exposed filter values as token": "https://www.drupal.org/files/issues/2022-10-30/2761273-50.patch", - "#2955321 Non-translatable fields": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2955321.diff", - "#3420862 Empty comment body": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3420862.diff", - "#3053757 Filter for allowed moderation transitions": "https://www.drupal.org/files/issues/2024-03-17/content-moderation-user-filter-3053757-25.patch", - "#3298701 Local patch to prevent BC issue in 10.3": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3298701.diff", - "#3159113 Local patch to prevent BC issue in 10.3": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3159113.diff", - "#3392903 Local patch to work around %formatter in custom_field": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3392903-follow-up.diff", - "#3438893 Views entity reverse": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3438893.diff", - "#944582 File System Permissions": "https://www.drupal.org/files/issues/2024-01-16/944582-10.2.x-192.patch", - "#2849279 Inconsistent changed timestamps when moderated entity": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2849279.patch", - "#3043725 Provide a Entity Handler for user cancellation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3043725-104.diff", - "#3473763 locale_string_is_safe should accept \"<front>\" as being safe": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3473763.diff" - }, - "drupal/dashboards": { - "#3366586 Shortcut error": "https://git.drupalcode.org/project/dashboards/-/merge_requests/26.diff" - }, - "drupal/diff": { - "#3311234 Link to diff with prev rev": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311234.diff?v=3", - "#3311372 Configure date format": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3311372.diff?v=2", - "#3360589 Form validation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3360589.diff?v=4" - }, - "drupal/dynamic_entity_reference": { - "#3099176 Errors when new entity types are added": "https://www.drupal.org/files/issues/2023-09-08/3099176-3.x-16.diff" - }, - "drupal/entity_import": { - "#3061935 Make it work without content_translation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3061935.diff" - }, - "drupal/entity_share": { - "#3306745 File Share overrides the name of media entity": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3306745.diff", - "#3347453 VBO Dependency": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3347453.diff" - }, - "drupal/entitygroupfield": { - "#3228312 Group load fails": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3228312.diff" - }, - "drupal/fakeobjects": { - "#3208959 Library path": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3208959.diff" - }, - "drupal/field_group": { - "#2858336 Token support": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2858336.diff" - }, - "drupal/flag": { - "#3238783 Fix flag action": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3238783.diff", - "#2500091 Activate token support": "https://www.drupal.org/files/issues/2022-12-30/flag_hook-tokens_2500091-43.patch" - }, - "drupal/fullcalendar_view": { - "#3392567 Fix href": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3392567.diff", - "#3084395 Daterange basefield": "https://www.drupal.org/files/issues/2022-03-25/3084395-11.patch" - }, - "drupal/gin_toolbar": { - "#3319445 Permission check": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3319445.diff" - }, - "drupal/group": { - "#3416324 Related entity tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3416324.diff" - }, - "drupal/glossify": { - "#3360633 HTML entities": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3360633.diff" - }, - "drupal/http_client_manager": { - "#3385117 Code generator": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3385117.diff" - }, - "drupal/imagemagick": { - "#local debug": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/imagick-debug.diff" - }, - "drupal/inline_entity_form": { - "#3204051 Check access": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3204051.diff?v=4" - }, - "drupal/layout_library": { - "#3041543 Layout per display mode": "https://www.drupal.org/files/issues/2020-08-17/3041543-4.layout_library.Allow-created-layouts-to-be-limited-to-and-set-for-specific-view-modes.patch" - }, - "drupal/ldap": { - "#3227813 Hide password field": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227813.diff" - }, - "drupal/linkchecker": { - "#3376854 Base path": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3376854.diff", - "#3313343 Disable cron": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3313343-3.diff" - }, - "drupal/linkit": { - "#3442127 Deprecate Drupal.EditorFeature": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3442127.diff" - }, - "drupal/menu_block": { - "#3082445 Add option to always render parent menu item": "https://www.drupal.org/files/issues/2020-10-12/menu_block-render-parent-3082445-26.patch" - }, - "drupal/pdfpreview": { - "#local debug": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/pdfpreview-debug.diff" - }, - "drupal/pluginreference": { - "#2979414 Allow no selection": "https://www.drupal.org/files/issues/2018-07-18/pluginreference-allow_no_plugin_to_be_selected-2979414-2.patch" - }, - "drupal/profile": { - "#2899744 Multi-lingual": "https://www.drupal.org/files/issues/2022-04-17/2899744-54.patch" - }, - "drupal/project_browser": { - "#3490176 Extend source plugins": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3490176.diff" - }, - "drupal/prompt": { - "#3449733 ECA 2": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3449733.diff" - }, - "drupal/recaptcha": { - "#3113837 Downgrade unknown error": "https://www.drupal.org/files/issues/2020-02-15/3113837-2.recaptcha.Downgrade-unknown-error-info.patch" - }, - "drupal/role_theme_switcher": { - "#3414975 Missing keys": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3414975.diff" - }, - "drupal/select2": { - "#3211796 Z-Index in modal dialog": "https://www.drupal.org/files/issues/2023-04-18/select2-modal-zindex-mult-form-elem-fix-3211796-17.patch" - }, - "drupal/simple_menu_icons": { - "#3379128 Do not delete all": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3379128.diff", - "#3404501 Set version and license": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3404501.diff" - }, - "drupal/smart_date": { - "#3478471 Filter contains partly": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3478471.diff" - }, - "drupal/smart_login": { - "#2831386 Error 403 redirect": "https://www.drupal.org/files/issues/this_is_silently-2831386-2.patch", - "#2709529 Redirect after login": "https://www.drupal.org/files/issues/redirect_to_page_of-2709529-2.patch", - "#2901154 Properly handle destination": "https://www.drupal.org/files/issues/setting_form_redirect-2901154-2.patch" - }, - "drupal/smtp": { - "#3252427 Add tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3252427.diff" - }, - "drupal/social_link_field": { - "#3241310 Add more channels": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3241310.diff" - }, - "drupal/social_profile_field": { - "#3242595 Drupal 9": "https://www.drupal.org/files/issues/2021-10-11/3242595-3.patch", - "#2724653 Langparam": "https://www.drupal.org/files/issues/social_profile_field-fix-warnings-in-formatter-1.patch", - "#2799907 Alter icon path": "https://www.drupal.org/files/issues/allow_for_different-2799907-2.patch" - }, - "drupal/subgroup": { - "#3170683 Provide parent as token": "https://www.drupal.org/files/issues/2022-06-02/subgroup-parent_token-3170683-3.patch" - }, - "drupal/svg_image_field": { - "#3090673 URL field formatter": "https://www.drupal.org/files/issues/2019-10-29/3090673-2.svg_image_field.Add-support-for-a-URL-image-formatter.patch" - }, - "drupal/tamper": { - "#3268276 Timezone plugin": "https://www.drupal.org/files/issues/2023-04-06/0001-Issue-3268276-Reworking-timezones.patch", - "#3480140 Yaml encoding": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3480140.diff" - }, - "drupal/taxonomy_machine_name": { - "#3193233 Filter doesn't work": "https://www.drupal.org/files/issues/2021-01-17/taxonomy_machine_name-view-save-error-3193233.patch", - "#3128397 View can not be saved": "https://www.drupal.org/files/issues/2020-04-16/config_schema-3128397-3.patch", - "#3352586 Parameter to disable machine name in terms overview ": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/33525862-2.diff" - }, - "drupal/token": { - "#3437013 Referrer tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3437013.diff" - }, - "drupal/uikitty": { - "#3149175 Drupal 9": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3149175.diff", - "#local Drupal 10": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/uikitty-d10.patch" - }, - "drupal/views_data_export": { - "#3046184 Optional CSV Header (fix missing row on batch run)": "https://www.drupal.org/files/issues/2023-02-09/views_data_export-optional_csv_header-3046184-9.patch" - }, - "drupal/video_embed_field": { - "#3238136 Vimeo hash parameter on private video url": "https://www.drupal.org/files/issues/2022-08-23/video_embed_field-vimeo_hash_parameters_private_video-3238136-8.patch" - }, - "drupal/views_entity_embed": { - "#3307775 Fix for CKe5": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3307775.diff" - }, - "drupal/views_fieldsets": { - "#3395642 Fieldsets with only images don't display": "https://www.drupal.org/files/issues/2023-10-20/views_fieldset-allow_img.patch" - }, - "drupal/views_tree": { - "#3344199 Add event just once": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3344199.diff" - }, - "drupal/webform": { - "#3487530 Overview permissions": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3487530.diff" - }, - "drupal/webform_content_creator": { - "#3484892 Permission": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3484892.diff" - }, - "drupal/workflow": { - "#2948377 Do not hide widget when having only one option": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/show-widget-2948377-2.patch" - }, - "drupal/wysiwyg_template": { - "#3354588 Drupal 10 compatible": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3354588.diff" - }, - "grahl/ldap": { - "#Local Remove deprecation warnings": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/grahl-ldap.patch" - }, - "henrywhitaker3/healthchecks-io": { - "#76 Double slashes in URL": "https://patch-diff.githubusercontent.com/raw/henrywhitaker3/PHP-healthchecks.io/pull/77.diff" - }, - "phpcollection/phpcollection": { - "#31 PHP 8.1 deprecation": "https://patch-diff.githubusercontent.com/raw/schmittjoh/php-collection/pull/32.diff" - } - } -} diff --git a/patches/d9.json b/patches/d9.json deleted file mode 100644 index f8eb38aa9c357406c27f1d0b721384afa7364f9c..0000000000000000000000000000000000000000 --- a/patches/d9.json +++ /dev/null @@ -1,256 +0,0 @@ -{ - "patches": { - "drupal/anchor_link": { - "#3204935 Lib path": "https://git.drupalcode.org/project/anchor_link/-/merge_requests/1.diff" - }, - "drupal/analog_digital_clock": { - "#3203004 Drop seconds": "https://git.drupalcode.org/project/analog_digital_clock/-/merge_requests/1.patch" - }, - "drupal/architect": { - "#3137889 Drupal 9": "https://git.drupalcode.org/project/architect/-/merge_requests/2.diff" - }, - "drupal/background_image": { - "#3161262 Pre render fix": "https://www.drupal.org/files/issues/2020-07-24/implement_trustedcallbackinterface.patch", - "#3170843 Dom Ready": "https://www.drupal.org/files/issues/2020-09-14/3170843-2.background_image.Library-drupaldomready-does-not-exist-in-Drupal-9.patch", - "#3173283 Cache": "https://www.drupal.org/files/issues/2020-09-26/3173283-2.background_image.Cache-context-also-requires-URL.patch", - "#3173340 Exception": "https://www.drupal.org/files/issues/2020-09-26/3173340-2.background_image.Error-Call-to-a-member-function-hasEntityToken-on-bool-in-DrupalbackgroundimageCacheContextBackgroundImageSettingsTextCacheContextgetContext.patch", - "#3128542 FileScan": "https://www.drupal.org/files/issues/2020-09-11/background_image-missing_folder-3128542-8.patch" - }, - "drupal/bootstrap_clean_blog": { - "#3146287 Drupal 9": "https://www.drupal.org/files/issues/2021-10-23/3146287_3.patch" - }, - "drupal/ckeditor_find": { - "#3252081 Library location": "https://git.drupalcode.org/project/ckeditor_find/-/merge_requests/2.diff" - }, - "drupal/codesnippet": { - "#3021431 Add Yaml as code format": "https://git.drupalcode.org/project/codesnippet/-/merge_requests/4.diff" - }, - "drupal/colorbox": { - "#2808883 Responsive image styles": "https://www.drupal.org/files/issues/2024-03-17/colorbox-responsive-image-2808883-69.patch" - }, - "drupal/comment_notify": { - "#2926228 Always notify admin": "https://www.drupal.org/files/issues/2019-09-11/2926228-11.comment_notify.Optionally-notify-site-admin-of-all-comments.patch" - }, - "drupal/commerce_xquantity": { - "#3404050 Exceptions in actions of xquantity_stock": "https://git.drupalcode.org/project/commerce_xquantity/-/merge_requests/4.diff" - }, - "drupal/config_devel": { - "#3219083 Export raw data": "https://git.drupalcode.org/project/config_devel/-/merge_requests/5.diff", - "#3227881 Support config_rewrite": "https://git.drupalcode.org/project/config_devel/-/merge_requests/6.diff" - }, - "drupal/config_sync": { - "#3176955 Drupal 9 test compatibility": "https://www.drupal.org/files/issues/2021-03-19/config_sync-config-sync-test-core-3176955-4.patch" - }, - "drupal/config_update": { - "#3436790 Module weight": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3436790.diff", - "#3056249 Core hash for localisation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3056249.diff" - }, - "drupal/content_lock": { - "#2951652 No content lock für CLI commands": "https://www.drupal.org/files/issues/2022-04-05/2951652-13.patch", - "#3160781 Release lock when browsing away from locked form": "https://git.drupalcode.org/project/content_lock/-/merge_requests/12.diff" - }, - "drupal/core_for_review": { - "#3227732 Book hierarchy migration": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3227732.diff" - }, - "drupal/core": { - "#local Hot fix timezoone": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/timezone.patch", - "#2647292 Date/time Views filter tries strotime() relative to Unix epoch": "https://www.drupal.org/files/issues/2022-02-10/with-dependency-injection-2647292-67.patch", - "#2831233 Field tokens for historical data fields (revisions) contain a hyphen, breaking twig templates and throwing an assertion error": "https://www.drupal.org/files/issues/2023-03-07/2831233-168.patch", - "#2884879 Ignore empty fields for REST export": "https://www.drupal.org/files/issues/2884879-11.patch", - "#2966735 Workaround for DateTime exposed filter": "https://git.drupalcode.org/project/drupal/-/merge_requests/2151.diff", - "#2984504 Access to 'Reset to alphabetical' denied for users without administer permission": "https://www.drupal.org/files/issues/2023-11-24/2984504-93.patch", - "#3043879 Layout Builder Config": "https://git.drupalcode.org/project/drupal/-/merge_requests/2052.diff", - "#3195171 Fix view header group rendering": "https://www.drupal.org/files/issues/2022-01-05/3195171-7-fix-view-header-group-rendering.patch", - "#3180227 Missing options for entity refernces in views": "https://www.drupal.org/files/issues/2020-11-02/3180227-2.patch", - "#3228298 Empty path alias exception": "https://git.drupalcode.org/project/drupal/-/merge_requests/1062.diff?v=2", - "#3227816 Hide password reset link": "https://git.drupalcode.org/project/drupal/-/merge_requests/1046.diff", - "#3249628 Comments and paragraphs": "https://git.drupalcode.org/project/drupal/-/merge_requests/1415.diff?v=2", - "#2840283 Views action path with EVA": "https://www.drupal.org/files/issues/problem_with_action-2840283-25.patch", - "#2816447 RSS feed titles wrong language": "https://www.drupal.org/files/issues/2023-08-03/2816447-58.patch", - "#2915705 TypedData any": "https://www.drupal.org/files/issues/2022-12-11/2915705-82.patch", - "#3302450 Views field default type": "https://git.drupalcode.org/project/drupal/-/merge_requests/2615.diff", - "#3311849 Views exposed filter, empty sections": "https://git.drupalcode.org/project/drupal/-/merge_requests/2808.diff", - "#2797583 Actions for content moderation": "https://www.drupal.org/files/issues/2022-09-20/core-provide-moderation-states-as-actions-2797583-207.patch", - "#2918537 Save unpublished versions of published content without manage book privileges": "https://www.drupal.org/files/issues/2022-12-14/2918537-92.patch", - "#3047110 Taxonomy moderation": "https://www.drupal.org/files/issues/2021-07-23/3047110-32.patch", - "#3194462 Big pipe explode issue": "https://www.drupal.org/files/issues/2023-02-28/3194462-30.patch", - "#3090234 Broken node theming for book export": "https://www.drupal.org/files/issues/2020-10-06/3090234-9.patch", - "#3346430 DoPreSave load revision": "https://git.drupalcode.org/project/drupal/-/merge_requests/3599.diff", - "#3340973 Media Lib type error on array_filter": "https://www.drupal.org/files/issues/2023-02-10/fix_media_library_php_error-3340973-2.patch", - "#3053757 Filter for allowed moderation transitions": "https://www.drupal.org/files/issues/2020-09-09/content-moderation-user-filter-3053757-16.patch" - }, - "drupal/custom_field": { - "#3392561 Base field clone ignore": "https://git.drupalcode.org/project/custom_field/-/merge_requests/31.diff" - }, - "drupal/diff": { - "#3311234 Link to diff with prev rev": "https://git.drupalcode.org/project/diff/-/merge_requests/22.diff?v=3", - "#3311372 Configure date format": "https://git.drupalcode.org/project/diff/-/merge_requests/23.diff?v=2", - "#3360589 Form validation": "https://git.drupalcode.org/project/diff/-/merge_requests/31.diff?v=4" - }, - "drupal/dynamic_entity_reference": { - "#3099176 Added new entity types": "https://www.drupal.org/files/issues/2022-02-14/3099176-1.x-14.patch" - }, - "drupal/eca": { - "#3352393 Condition list contains": "https://git.drupalcode.org/project/eca/-/merge_requests/349.diff", - "#3368530 eca_log tokens": "https://git.drupalcode.org/project/eca/-/merge_requests/370.diff", - "#3373537 Plain token replacement": "https://git.drupalcode.org/project/eca/-/merge_requests/375.diff", - "#3351738 Views argument default plugin": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/eca_views_argument.patch", - "#3396915 Improve #3368530 to cleanup context": "https://www.drupal.org/files/issues/2023-10-26/3396915-2.diff" - }, - "drupal/elasticsearch_connector": { - "#2952301 Flatten keys": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/develop/patches/elasticsearch_connector.patch" - }, - "drupal/elastic_email": { - "#3197469 Fix plugin": "https://git.drupalcode.org/project/elastic_email/-/merge_requests/2.diff" - }, - "drupal/endroid_qr_code": { - "#3113642 Support link field type": "https://www.drupal.org/files/issues/2020-02-14/3113642-2.endroid_qr_code.Add-support-for-link-field-type.patch" - }, - "drupal/entity_import": { - "#3061935 Make it work without content_translation": "https://git.drupalcode.org/project/entity_import/-/merge_requests/2.diff" - }, - "drupal/entity_share": { - "#3306745 File Share overrides the name of media entity": "https://git.drupalcode.org/project/entity_share/-/merge_requests/55.diff", - "#3347453 VBO Dependency": "https://git.drupalcode.org/project/entity_share/-/merge_requests/70.diff" - }, - "drupal/entitygroupfield": { - "#3228312 Group load fails": "https://git.drupalcode.org/project/entitygroupfield/-/merge_requests/2.diff?v=2" - }, - "drupal/fakeobjects": { - "#3208959 Library path": "https://git.drupalcode.org/project/fakeobjects/-/merge_requests/2.diff" - }, - "drupal/field_group": { - "#2858336 Token support": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/2858336.diff" - }, - "drupal/flag": { - "#3238783 Fix flag action": "https://git.drupalcode.org/project/flag/-/merge_requests/29.diff", - "#2500091 Activate token support": "https://www.drupal.org/files/issues/2022-12-30/flag_hook-tokens_2500091-43.patch" - }, - "drupal/fullcalendar_view": { - "#3392567 Fix href": "https://git.drupalcode.org/project/fullcalendar_view/-/merge_requests/58.diff" - }, - "drupal/gin": { - "#3355054 Default value for empty actions": "https://git.drupalcode.org/project/gin/-/merge_requests/246.diff", - "#3439988 Sidebar and smalll desktop": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3439988.diff", - "#3441940 Support navigation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3441940.diff", - "#3387653 Dark mode": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3387653.diff?v=3" - }, - "drupal/gin_toolbar": { - "#3319445 Permission check": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/gin_toolbar-permissions.patch", - "#3442173 Support navigation": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3442173.diff" - }, - "drupal/glossify": { - "#3338553 PHP 8.1": "https://git.drupalcode.org/project/glossify/-/merge_requests/10.diff", - "#3360633 HTML entities": "https://git.drupalcode.org/project/glossify/-/merge_requests/13.diff" - }, - "drupal/group": { - "#3223957 Link to content entity": "https://git.drupalcode.org/project/group/-/merge_requests/27.diff" - }, - "drupal/inline_entity_form": { - "#3204051 Check access": "https://git.drupalcode.org/project/inline_entity_form/-/merge_requests/14.diff?v=4", - "#3208995 Migrate fix": "https://git.drupalcode.org/project/inline_entity_form/-/merge_requests/23.diff?v=2" - }, - "drupal/layout_library": { - "#3041543 Layout per display mode": "https://www.drupal.org/files/issues/2020-08-17/3041543-4.layout_library.Allow-created-layouts-to-be-limited-to-and-set-for-specific-view-modes.patch" - }, - "drupal/lazy_views": { - "#3352648 Unload support": "https://git.drupalcode.org/project/lazy_views/-/merge_requests/2.diff" - }, - "drupal/ldap": { - "#3227813 Hide password field": "https://git.drupalcode.org/project/ldap/-/merge_requests/21.diff" - }, - "drupal/legal": { - "#3252838 Avoid session_destroy warnings": "https://git.drupalcode.org/project/legal/-/commit/add472c7e449a4fca3e40c7b9ed800c92ecc7cab.diff" - }, - "drupal/link_attributes": { - "#3050455 Array ri string conversion": "https://www.drupal.org/files/issues/2019-04-24/3050455-2.link_attributes.Array-to-string-conversion-in-DrupalCoreTemplateAttributeArraytoString.patch" - }, - "drupal/linkchecker": { - "#3376854 Base path": "https://git.drupalcode.org/project/linkchecker/-/merge_requests/48.diff", - "#3313343 Disable cron": "https://www.drupal.org/files/issues/2022-10-03/support_disabling_cron-3313343-2.patch" - }, - "drupal/linkit": { - "#3022261 Support anchors": "https://www.drupal.org/files/issues/2023-10-12/3022261-39.patch" - }, - "drupal/menu_block": { - "#3082445 Add option to always render parent menu item": "https://www.drupal.org/files/issues/2020-10-12/menu_block-render-parent-3082445-26.patch" - }, - "drupal/pluginreference": { - "#2979414 Allow no selection": "https://www.drupal.org/files/issues/2018-07-18/pluginreference-allow_no_plugin_to_be_selected-2979414-2.patch" - }, - "drupal/profile": { - "#2899744 Multi-lingual": "https://www.drupal.org/files/issues/2022-04-17/2899744-54.patch" - }, - "drupal/push_framework": { - "#3372545 Rearrange ECA token creation": "https://git.drupalcode.org/project/push_framework/-/merge_requests/6.diff" - }, - "drupal/recaptcha": { - "#3113837 Downgrade unknown error": "https://www.drupal.org/files/issues/2020-02-15/3113837-2.recaptcha.Downgrade-unknown-error-info.patch" - }, - "drupal/simple_oauth": { - "#3257293 Non-registered claims error": "https://www.drupal.org/files/issues/2023-08-11/comment-27.diff" - }, - "drupal/smart_login": { - "#2831386 Error 403 redirect": "https://www.drupal.org/files/issues/this_is_silently-2831386-2.patch", - "#2709529 Redirect after login": "https://www.drupal.org/files/issues/redirect_to_page_of-2709529-2.patch", - "#2901154 Properly handle destination": "https://www.drupal.org/files/issues/setting_form_redirect-2901154-2.patch" - }, - "drupal/smtp": { - "#3252427 Add tokens": "https://git.drupalcode.org/project/smtp/-/merge_requests/8.diff" - }, - "drupal/social_link_field": { - "#3241310 Add more channels": "https://git.drupalcode.org/project/social_link_field/-/merge_requests/7.diff", - "#3267775 Add Drupal company link": "https://git.drupalcode.org/project/social_link_field/-/merge_requests/9.diff" - }, - "drupal/social_profile_field": { - "#3242595 Drupal 9": "https://www.drupal.org/files/issues/2021-10-11/3242595-3.patch", - "#2724653 Langparam": "https://www.drupal.org/files/issues/social_profile_field-fix-warnings-in-formatter-1.patch", - "#2799907 Alter icon path": "https://www.drupal.org/files/issues/allow_for_different-2799907-2.patch" - }, - "drupal/svg_image_field": { - "#3090673 URL field formatter": "https://www.drupal.org/files/issues/2019-10-29/3090673-2.svg_image_field.Add-support-for-a-URL-image-formatter.patch" - }, - "drupal/tamper": { - "#3279973 WordCount plugin": "https://git.drupalcode.org/project/tamper/-/merge_requests/9.diff", - "#3268276 Timezone plugin": "https://www.drupal.org/files/issues/2023-04-06/0001-Issue-3268276-Reworking-timezones.patch" - }, - "drupal/taxonomy_machine_name": { - "#3193233 Filter doesn't work": "https://www.drupal.org/files/issues/2021-01-17/taxonomy_machine_name-view-save-error-3193233.patch", - "#3128397 View can not be saved": "https://www.drupal.org/files/issues/2020-04-16/config_schema-3128397-3.patch", - "#3352586 Parameter to disable machine name in terms overview ": "https://git.drupalcode.org/project/taxonomy_machine_name/-/merge_requests/8.diff" - }, - "drupal/title": { - "#3172331 D9 compatibility": "https://www.drupal.org/files/issues/2021-08-24/D9-compatibility_3172331.patch" - }, - "drupal/token": { - "#3437013 Referrer tokens": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/d10/3437013.diff" - }, - "drupal/uikitty": { - "#3149175 Drupal 9": "https://git.drupalcode.org/project/uikitty/-/merge_requests/2.diff" - }, - "drupal/views_conditional": { - "#3402031 PHP 7.4": "https://git.drupalcode.org/project/views_conditional/-/merge_requests/5.diff?v=2" - }, - "drupal/views_data_export": { - "#3046184 Optional CSV Header (fix missing row on batch run)": "https://www.drupal.org/files/issues/2023-02-09/views_data_export-optional_csv_header-3046184-9.patch" - }, - "drupal/video_embed_field": { - "#3238136 Vimeo hash parameter on private video url": "https://www.drupal.org/files/issues/2022-08-23/video_embed_field-vimeo_hash_parameters_private_video-3238136-8.patch" - }, - "drupal/views_infinite_scroll": { - "#3252577 Fix infinite scroll": "https://www.drupal.org/files/issues/2021-12-12/views_infinite_scroll-3252577-9.2.x_support_3.patch" - }, - "drupal/views_tree": { - "#3344199 Add event just once": "https://git.drupalcode.org/project/views_tree/-/merge_requests/12.diff" - }, - "grahl/ldap": { - "#Local Remove deprecation warnings": "https://gitlab.lakedrops.com/composer/plugin/drupal-environment/-/raw/main/patches/grahl-ldap.patch" - }, - "henrywhitaker3/healthchecks-io": { - "#76 Double slashes in URL": "https://patch-diff.githubusercontent.com/raw/henrywhitaker3/PHP-healthchecks.io/pull/77.diff" - }, - "phpcollection/phpcollection": { - "#31 PHP 8.1 deprecation": "https://patch-diff.githubusercontent.com/raw/schmittjoh/php-collection/pull/32.diff" - } - } -} diff --git a/patches/daterange_display_mode.diff b/patches/daterange_display_mode.diff deleted file mode 100644 index 3f800b532c72a11228e72ec132fbc587e85413d1..0000000000000000000000000000000000000000 --- a/patches/daterange_display_mode.diff +++ /dev/null @@ -1,828 +0,0 @@ -diff --git a/core/modules/datetime_range/config/schema/datetime_range.schema.yml b/core/modules/datetime_range/config/schema/datetime_range.schema.yml -index f0f93259d34075cf2653bb89dc64c0f1cd4562ba..f0f2ca1eddaf805fc262358d3a3b9f0b853e3dbb 100644 ---- a/core/modules/datetime_range/config/schema/datetime_range.schema.yml -+++ b/core/modules/datetime_range/config/schema/datetime_range.schema.yml -@@ -31,6 +31,9 @@ field.formatter.settings.daterange_default: - type: field.formatter.settings.datetime_default - label: 'Date range default display format settings' - mapping: -+ from_to: -+ type: string -+ label: 'Display' - separator: - type: label - label: 'Separator' -@@ -40,6 +43,9 @@ field.formatter.settings.daterange_plain: - type: field.formatter.settings.datetime_plain - label: 'Date range plain display format settings' - mapping: -+ from_to: -+ type: string -+ label: 'Display' - separator: - type: label - label: 'Separator' -@@ -49,6 +55,9 @@ field.formatter.settings.daterange_custom: - type: field.formatter.settings.datetime_custom - label: 'Date range custom display format settings' - mapping: -+ from_to: -+ type: string -+ label: 'Display' - separator: - type: label - label: 'Separator' -diff --git a/core/modules/datetime_range/datetime_range.post_update.php b/core/modules/datetime_range/datetime_range.post_update.php -index 83df8ca9b1c8e539264877fdccbb27878770ada0..27d4abefa2802ad310a3023b5f2de382fc00cd2d 100644 ---- a/core/modules/datetime_range/datetime_range.post_update.php -+++ b/core/modules/datetime_range/datetime_range.post_update.php -@@ -5,6 +5,8 @@ - * Post-update functions for Datetime Range module. - */ - -+use Drupal\Core\Entity\Display\EntityViewDisplayInterface; -+ - /** - * Implements hook_removed_post_updates(). - */ -@@ -14,3 +16,22 @@ function datetime_range_removed_post_updates() { - 'datetime_range_post_update_views_string_plugin_id' => '9.0.0', - ]; - } -+ -+/** -+ * Updates date range field formatter configuration by adding a "from_to" setting to flagged entity view displays. -+ * -+ * @see \datetime_range_entity_view_display_presave -+ */ -+function datetime_range_post_update_from_to_configuration(): void { -+ // Load all entity_view_display entities that have been flagged for update. -+ $query = \Drupal::entityQuery('entity_view_display') -+ ->condition('data', ['datetime_range_updated' => TRUE]); -+ $entity_ids = $query->execute(); -+ -+ foreach ($entity_ids as $entity_id) { -+ $entity_view_display = \Drupal::entityTypeManager()->getStorage('entity_view_display')->load($entity_id); -+ if ($entity_view_display instanceof EntityViewDisplayInterface) { -+ $entity_view_display->save(); -+ } -+ } -+} -diff --git a/core/modules/datetime_range/src/DateTimeRangeConstants.php b/core/modules/datetime_range/src/DateTimeRangeConstants.php -new file mode 100644 -index 0000000000000000000000000000000000000000..09be3f0ec91cef7bf529778cd148584d27292a6f ---- /dev/null -+++ b/core/modules/datetime_range/src/DateTimeRangeConstants.php -@@ -0,0 +1,17 @@ -+<?php -+ -+namespace Drupal\datetime_range; -+ -+/** -+ * Declares constants used in the datetime range module. -+ */ -+class DateTimeRangeConstants { -+ -+ /** -+ * Values for the 'from_to' formatter setting. -+ */ -+ const BOTH = 'both'; -+ const START_DATE = 'start_date'; -+ const END_DATE = 'end_date'; -+ -+} -diff --git a/core/modules/datetime_range/src/DateTimeRangeTrait.php b/core/modules/datetime_range/src/DateTimeRangeTrait.php -index 3f05b82189b7e8c09b77e7e4906ccf6070b0d8f3..02d927d644afbb83fcf2bbf79d365fdeb31b6492 100644 ---- a/core/modules/datetime_range/src/DateTimeRangeTrait.php -+++ b/core/modules/datetime_range/src/DateTimeRangeTrait.php -@@ -2,13 +2,25 @@ - - namespace Drupal\datetime_range; - -+use Drupal\Core\Datetime\DrupalDateTime; - use Drupal\Core\Field\FieldItemListInterface; -+use Drupal\Core\Form\FormStateInterface; - - /** - * Provides friendly methods for datetime range. - */ - trait DateTimeRangeTrait { - -+ /** -+ * {@inheritdoc} -+ */ -+ public static function dateTimeRangeDefaultSettings() { -+ return [ -+ 'from_to' => DateTimeRangeConstants::BOTH, -+ 'separator' => '-', -+ ]; -+ } -+ - /** - * {@inheritdoc} - */ -@@ -24,11 +36,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { - $end_date = $item->end_date; - - if ($start_date->getTimestamp() !== $end_date->getTimestamp()) { -- $elements[$delta] = [ -- 'start_date' => $this->buildDateWithIsoAttribute($start_date), -- 'separator' => ['#plain_text' => ' ' . $separator . ' '], -- 'end_date' => $this->buildDateWithIsoAttribute($end_date), -- ]; -+ $elements[$delta] = $this->renderStartEndWithIsoAttribute($start_date, $separator, $end_date); - } - else { - $elements[$delta] = $this->buildDateWithIsoAttribute($start_date); -@@ -46,4 +54,149 @@ public function viewElements(FieldItemListInterface $items, $langcode) { - return $elements; - } - -+ /** -+ * {@inheritdoc} -+ */ -+ public function dateTimeRangeSettingsForm(array $form, FormStateInterface $form_state) { -+ $form['from_to'] = [ -+ '#type' => 'select', -+ '#title' => $this->t('Display'), -+ '#options' => $this->getFromToOptions(), -+ '#default_value' => $this->getSetting('from_to'), -+ ]; -+ -+ $form['separator'] = [ -+ '#type' => 'textfield', -+ '#title' => $this->t('Date separator'), -+ '#description' => $this->t('The string to separate the start and end dates'), -+ '#default_value' => $this->getSetting('separator'), -+ '#states' => [ -+ 'visible' => [ -+ ':input[name="options[settings][from_to]"]' => ['value' => DateTimeRangeConstants::BOTH], -+ ], -+ ], -+ ]; -+ -+ return $form; -+ } -+ -+ /** -+ * {@inheritdoc} -+ */ -+ public function dateTimeRangeSettingsSummary() { -+ $summary = []; -+ if ($from_to = $this->getSetting('from_to')) { -+ $from_to_options = $this->getFromToOptions(); -+ if (isset($from_to_options[$from_to])) { -+ $summary[] = $from_to_options[$from_to]; -+ } -+ } -+ -+ if (($separator = $this->getSetting('separator')) && $this->getSetting('from_to') === DateTimeRangeConstants::BOTH) { -+ $summary[] = $this->t('Separator: %separator', ['%separator' => $separator]); -+ } -+ -+ return $summary; -+ } -+ -+ /** -+ * Returns a list of possible values for the 'from_to' setting. -+ * -+ * @return array -+ * A list of 'from_to' options. -+ */ -+ protected function getFromToOptions() { -+ return [ -+ DateTimeRangeConstants::BOTH => $this->t('Display both start and end dates'), -+ DateTimeRangeConstants::START_DATE => $this->t('Display start date only'), -+ DateTimeRangeConstants::END_DATE => $this->t('Display end date only'), -+ ]; -+ } -+ -+ /** -+ * Returns whether or not the start date should be displayed. -+ * -+ * @return bool -+ * True if the start date should be displayed. False otherwise. -+ */ -+ public function startDateIsDisplayed() { -+ switch ($this->getSetting('from_to')) { -+ case DateTimeRangeConstants::BOTH: -+ case DateTimeRangeConstants::START_DATE: -+ return TRUE; -+ } -+ -+ return FALSE; -+ } -+ -+ /** -+ * Returns whether or not the end date should be displayed. -+ * -+ * @return bool -+ * True if the end date should be displayed. False otherwise. -+ */ -+ public function endDateIsDisplayed() { -+ switch ($this->getSetting('from_to')) { -+ case DateTimeRangeConstants::BOTH: -+ case DateTimeRangeConstants::END_DATE: -+ return TRUE; -+ } -+ -+ return FALSE; -+ } -+ -+ /** -+ * Creates a render array given start/end dates. -+ * -+ * @param \Drupal\Core\Datetime\DrupalDateTime $start_date -+ * The start date to be rendered. -+ * @param string $separator -+ * The separator string. -+ * @param \Drupal\Core\Datetime\DrupalDateTime $end_date -+ * The end date to be rendered. -+ * -+ * @return array -+ * A renderable array for a single date time range. -+ */ -+ protected function renderStartEnd(DrupalDateTime $start_date, string $separator, DrupalDateTime $end_date): array { -+ $element = []; -+ if ($this->startDateIsDisplayed()) { -+ $element[DateTimeRangeConstants::START_DATE] = $this->buildDate($start_date); -+ } -+ if ($this->startDateIsDisplayed() && $this->endDateIsDisplayed()) { -+ $element['separator'] = ['#plain_text' => ' ' . $separator . ' ']; -+ } -+ if ($this->endDateIsDisplayed()) { -+ $element[DateTimeRangeConstants::END_DATE] = $this->buildDate($end_date); -+ } -+ return $element; -+ } -+ -+ /** -+ * Creates a render array with ISO attributes given start/end dates. -+ * -+ * @param \Drupal\Core\Datetime\DrupalDateTime $start_date -+ * The start date to be rendered. -+ * @param string $separator -+ * The separator string. -+ * @param \Drupal\Core\Datetime\DrupalDateTime $end_date -+ * The end date to be rendered. -+ * -+ * @return array -+ * A renderable array for a single date time range. -+ */ -+ protected function renderStartEndWithIsoAttribute(DrupalDateTime $start_date, string $separator, DrupalDateTime $end_date): array { -+ $element = []; -+ if ($this->startDateIsDisplayed()) { -+ $element[DateTimeRangeConstants::START_DATE] = $this->buildDateWithIsoAttribute($start_date); -+ } -+ if ($this->startDateIsDisplayed() && $this->endDateIsDisplayed()) { -+ $element['separator'] = ['#plain_text' => ' ' . $separator . ' ']; -+ } -+ if ($this->endDateIsDisplayed()) { -+ $element[DateTimeRangeConstants::END_DATE] = $this->buildDateWithIsoAttribute($end_date); -+ } -+ return $element; -+ } -+ - } -diff --git a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeCustomFormatter.php b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeCustomFormatter.php -index 78aa8aafe21fecd2d62b4b2641f2784ebb4e47d1..5578d1b8b050e05ca482ed90a249437a7150144c 100644 ---- a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeCustomFormatter.php -+++ b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeCustomFormatter.php -@@ -29,9 +29,7 @@ class DateRangeCustomFormatter extends DateTimeCustomFormatter { - * {@inheritdoc} - */ - public static function defaultSettings() { -- return [ -- 'separator' => '-', -- ] + parent::defaultSettings(); -+ return static::dateTimeRangeDefaultSettings() + parent::defaultSettings(); - } - - /** -@@ -52,11 +50,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { - $end_date = $item->end_date; - - if ($start_date->getTimestamp() !== $end_date->getTimestamp()) { -- $elements[$delta] = [ -- 'start_date' => $this->buildDate($start_date), -- 'separator' => ['#plain_text' => ' ' . $separator . ' '], -- 'end_date' => $this->buildDate($end_date), -- ]; -+ $elements[$delta] = $this->renderStartEnd($start_date, $separator, $end_date); - } - else { - $elements[$delta] = $this->buildDate($start_date); -@@ -72,14 +66,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { - */ - public function settingsForm(array $form, FormStateInterface $form_state) { - $form = parent::settingsForm($form, $form_state); -- -- $form['separator'] = [ -- '#type' => 'textfield', -- '#title' => $this->t('Date separator'), -- '#description' => $this->t('The string to separate the start and end dates'), -- '#default_value' => $this->getSetting('separator'), -- ]; -- -+ $form = $this->dateTimeRangeSettingsForm($form, $form_state); - return $form; - } - -@@ -87,13 +74,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) { - * {@inheritdoc} - */ - public function settingsSummary() { -- $summary = parent::settingsSummary(); -- -- if ($separator = $this->getSetting('separator')) { -- $summary[] = $this->t('Separator: %separator', ['%separator' => $separator]); -- } -- -- return $summary; -+ return array_merge(parent::settingsSummary(), $this->dateTimeRangeSettingsSummary()); - } - - } -diff --git a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php -index b93851bbd012a72a51ff317c10f83cf5805d1e1f..367fd25ad7384594deef18d7d5b38cdabbcc95c9 100644 ---- a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php -+++ b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php -@@ -29,9 +29,7 @@ class DateRangeDefaultFormatter extends DateTimeDefaultFormatter { - * {@inheritdoc} - */ - public static function defaultSettings() { -- return [ -- 'separator' => '-', -- ] + parent::defaultSettings(); -+ return static::dateTimeRangeDefaultSettings() + parent::defaultSettings(); - } - - /** -@@ -39,13 +37,7 @@ public static function defaultSettings() { - */ - public function settingsForm(array $form, FormStateInterface $form_state) { - $form = parent::settingsForm($form, $form_state); -- -- $form['separator'] = [ -- '#type' => 'textfield', -- '#title' => $this->t('Date separator'), -- '#description' => $this->t('The string to separate the start and end dates'), -- '#default_value' => $this->getSetting('separator'), -- ]; -+ $form = $this->dateTimeRangeSettingsForm($form, $form_state); - - return $form; - } -@@ -54,13 +46,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) { - * {@inheritdoc} - */ - public function settingsSummary() { -- $summary = parent::settingsSummary(); -- -- if ($separator = $this->getSetting('separator')) { -- $summary[] = $this->t('Separator: %separator', ['%separator' => $separator]); -- } -- -- return $summary; -+ return array_merge(parent::settingsSummary(), $this->dateTimeRangeSettingsSummary()); - } - - } -diff --git a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangePlainFormatter.php b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangePlainFormatter.php -index 0f74fba6440046ccfc1afac5b4f3fd06c389ef6d..ca63461da6ed8843200b5325ca7eaebd7b84b8ac 100644 ---- a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangePlainFormatter.php -+++ b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangePlainFormatter.php -@@ -29,9 +29,7 @@ class DateRangePlainFormatter extends DateTimePlainFormatter { - * {@inheritdoc} - */ - public static function defaultSettings() { -- return [ -- 'separator' => '-', -- ] + parent::defaultSettings(); -+ return static::dateTimeRangeDefaultSettings() + parent::defaultSettings(); - } - - /** -@@ -49,11 +47,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { - $end_date = $item->end_date; - - if ($start_date->getTimestamp() !== $end_date->getTimestamp()) { -- $elements[$delta] = [ -- 'start_date' => $this->buildDate($start_date), -- 'separator' => ['#plain_text' => ' ' . $separator . ' '], -- 'end_date' => $this->buildDate($end_date), -- ]; -+ $elements[$delta] = $this->renderStartEnd($start_date, $separator, $end_date); - } - else { - $elements[$delta] = $this->buildDate($start_date); -@@ -76,14 +70,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { - */ - public function settingsForm(array $form, FormStateInterface $form_state) { - $form = parent::settingsForm($form, $form_state); -- -- $form['separator'] = [ -- '#type' => 'textfield', -- '#title' => $this->t('Date separator'), -- '#description' => $this->t('The string to separate the start and end dates'), -- '#default_value' => $this->getSetting('separator'), -- ]; -- -+ $form = $this->dateTimeRangeSettingsForm($form, $form_state); - return $form; - } - -@@ -91,13 +78,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) { - * {@inheritdoc} - */ - public function settingsSummary() { -- $summary = parent::settingsSummary(); -- -- if ($separator = $this->getSetting('separator')) { -- $summary[] = $this->t('Separator: %separator', ['%separator' => $separator]); -- } -- -- return $summary; -+ return array_merge(parent::settingsSummary(), $this->dateTimeRangeSettingsSummary()); - } - - } -diff --git a/core/modules/datetime_range/tests/fixtures/update/drupal.daterange-formatter-settings-2827055.php b/core/modules/datetime_range/tests/fixtures/update/drupal.daterange-formatter-settings-2827055.php -new file mode 100644 -index 0000000000000000000000000000000000000000..f8552a5018bd259d53905d8f8b316560b21449b0 ---- /dev/null -+++ b/core/modules/datetime_range/tests/fixtures/update/drupal.daterange-formatter-settings-2827055.php -@@ -0,0 +1,120 @@ -+<?php -+ -+/** -+ * @file -+ * Provides database changes for testing the daterange formatter upgrade path. -+ * -+ * @see \Drupal\Tests\datetime_range\Functional\DateRangeFormatterSettingsUpdateTest -+ */ -+ -+use Drupal\Core\Database\Database; -+use Drupal\field\Entity\FieldStorageConfig; -+ -+$connection = Database::getConnection(); -+ -+// Add all datetime_range_removed_post_updates() as existing updates. -+require_once __DIR__ . '/../../../../datetime_range/datetime_range.post_update.php'; -+$existing_updates = $connection->select('key_value') -+ ->fields('key_value', ['value']) -+ ->condition('collection', 'post_update') -+ ->condition('name', 'existing_updates') -+ ->execute() -+ ->fetchField(); -+$existing_updates = unserialize($existing_updates); -+$existing_updates = array_merge( -+ $existing_updates, -+ array_keys(datetime_range_removed_post_updates()) -+); -+$connection->update('key_value') -+ ->fields(['value' => serialize($existing_updates)]) -+ ->condition('collection', 'post_update') -+ ->condition('name', 'existing_updates') -+ ->execute(); -+ -+// Add a new timestamp field 'field_datetime_range'. -+$connection->insert('config') -+ ->fields(['collection', 'name', 'data'])->values([ -+ 'collection' => '', -+ 'name' => 'field.storage.node.field_datetime_range', -+ 'data' => $field_storage = 'a:16:{s:4:"uuid";s:36:"a01264e6-2821-4b94-bc79-ba2b346795bb";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:1:{s:6:"module";a:2:{i:0;s:14:"datetime_range";i:1;s:4:"node";}}s:2:"id";s:25:"node.field_datetime_range";s:10:"field_name";s:20:"field_datetime_range";s:11:"entity_type";s:4:"node";s:4:"type";s:9:"daterange";s:8:"settings";a:1:{s:13:"datetime_type";s:8:"datetime";}s:6:"module";s:14:"datetime_range";s:6:"locked";b:0;s:11:"cardinality";i:1;s:12:"translatable";b:1;s:7:"indexes";a:0:{}s:22:"persist_with_no_fields";b:0;s:14:"custom_storage";b:0;}', -+ ])->values([ -+ 'collection' => '', -+ 'name' => 'field.field.node.page.field_datetime_range', -+ 'data' => 'a:16:{s:4:"uuid";s:36:"678b9e68-cff5-4b2e-9111-43e5d9d6c826";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:2:{s:6:"config";a:2:{i:0;s:39:"field.storage.node.field_datetime_range";i:1;s:14:"node.type.page";}s:6:"module";a:1:{i:0;s:14:"datetime_range";}}s:2:"id";s:30:"node.page.field_datetime_range";s:10:"field_name";s:20:"field_datetime_range";s:11:"entity_type";s:4:"node";s:6:"bundle";s:4:"page";s:5:"label";s:14:"datetime range";s:11:"description";s:0:"";s:8:"required";b:0;s:12:"translatable";b:0;s:13:"default_value";a:0:{}s:22:"default_value_callback";s:0:"";s:8:"settings";a:0:{}s:10:"field_type";s:9:"daterange";}', -+ ])->execute(); -+ -+$connection->insert('key_value') -+ ->fields(['collection', 'name', 'value']) -+ ->values([ -+ 'collection' => 'config.entity.key_store.field_config', -+ 'name' => 'uuid:678b9e68-cff5-4b2e-9111-43e5d9d6c826', -+ 'value' => 'a:1:{i:0;s:42:"field.field.node.page.field_datetime_range";}', -+ ]) -+ ->values([ -+ 'collection' => 'config.entity.key_store.field_storage_config', -+ 'name' => 'uuid:a01264e6-2821-4b94-bc79-ba2b346795bb', -+ 'value' => 'a:1:{i:0;s:39:"field.storage.node.field_datetime_range";}', -+ ]) -+ ->values([ -+ 'collection' => 'entity.storage_schema.sql', -+ 'name' => 'node.field_schema_data.field_datetime_range', -+ 'value' => 'a:2:{s:26:"node__field_datetime_range";a:4:{s:11:"description";s:49:"Data storage for node field field_datetime_range.";s:6:"fields";a:8:{s:6:"bundle";a:5:{s:4:"type";s:13:"varchar_ascii";s:6:"length";i:128;s:8:"not null";b:1;s:7:"default";s:0:"";s:11:"description";s:88:"The field instance bundle to which this row belongs, used when deleting a field instance";}s:7:"deleted";a:5:{s:4:"type";s:3:"int";s:4:"size";s:4:"tiny";s:8:"not null";b:1;s:7:"default";i:0;s:11:"description";s:60:"A boolean indicating whether this data item has been deleted";}s:9:"entity_id";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:38:"The entity id this data is attached to";}s:11:"revision_id";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:47:"The entity revision id this data is attached to";}s:8:"langcode";a:5:{s:4:"type";s:13:"varchar_ascii";s:6:"length";i:32;s:8:"not null";b:1;s:7:"default";s:0:"";s:11:"description";s:37:"The language code for this data item.";}s:5:"delta";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:67:"The sequence number for this data item, used for multi-value fields";}s:26:"field_datetime_range_value";a:4:{s:11:"description";s:21:"The start date value.";s:4:"type";s:7:"varchar";s:6:"length";i:20;s:8:"not null";b:1;}s:30:"field_datetime_range_end_value";a:4:{s:11:"description";s:19:"The end date value.";s:4:"type";s:7:"varchar";s:6:"length";i:20;s:8:"not null";b:1;}}s:11:"primary key";a:4:{i:0;s:9:"entity_id";i:1;s:7:"deleted";i:2;s:5:"delta";i:3;s:8:"langcode";}s:7:"indexes";a:4:{s:6:"bundle";a:1:{i:0;s:6:"bundle";}s:11:"revision_id";a:1:{i:0;s:11:"revision_id";}s:26:"field_datetime_range_value";a:1:{i:0;s:26:"field_datetime_range_value";}s:30:"field_datetime_range_end_value";a:1:{i:0;s:30:"field_datetime_range_end_value";}}}s:35:"node_revision__field_datetime_range";a:4:{s:11:"description";s:61:"Revision archive storage for node field field_datetime_range.";s:6:"fields";a:8:{s:6:"bundle";a:5:{s:4:"type";s:13:"varchar_ascii";s:6:"length";i:128;s:8:"not null";b:1;s:7:"default";s:0:"";s:11:"description";s:88:"The field instance bundle to which this row belongs, used when deleting a field instance";}s:7:"deleted";a:5:{s:4:"type";s:3:"int";s:4:"size";s:4:"tiny";s:8:"not null";b:1;s:7:"default";i:0;s:11:"description";s:60:"A boolean indicating whether this data item has been deleted";}s:9:"entity_id";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:38:"The entity id this data is attached to";}s:11:"revision_id";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:47:"The entity revision id this data is attached to";}s:8:"langcode";a:5:{s:4:"type";s:13:"varchar_ascii";s:6:"length";i:32;s:8:"not null";b:1;s:7:"default";s:0:"";s:11:"description";s:37:"The language code for this data item.";}s:5:"delta";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:1;s:11:"description";s:67:"The sequence number for this data item, used for multi-value fields";}s:26:"field_datetime_range_value";a:4:{s:11:"description";s:21:"The start date value.";s:4:"type";s:7:"varchar";s:6:"length";i:20;s:8:"not null";b:1;}s:30:"field_datetime_range_end_value";a:4:{s:11:"description";s:19:"The end date value.";s:4:"type";s:7:"varchar";s:6:"length";i:20;s:8:"not null";b:1;}}s:11:"primary key";a:5:{i:0;s:9:"entity_id";i:1;s:11:"revision_id";i:2;s:7:"deleted";i:3;s:5:"delta";i:4;s:8:"langcode";}s:7:"indexes";a:4:{s:6:"bundle";a:1:{i:0;s:6:"bundle";}s:11:"revision_id";a:1:{i:0;s:11:"revision_id";}s:26:"field_datetime_range_value";a:1:{i:0;s:26:"field_datetime_range_value";}s:30:"field_datetime_range_end_value";a:1:{i:0;s:30:"field_datetime_range_end_value";}}}}', -+ ]) -+ ->execute(); -+ -+$data = $connection->select('key_value') -+ ->fields('key_value', ['value']) -+ ->condition('collection', 'entity.definitions.installed') -+ ->condition('name', 'node.field_storage_definitions') -+ ->execute() -+ ->fetchField(); -+$data = unserialize($data); -+$data['field_datetime_range'] = new FieldStorageConfig(unserialize($field_storage)); -+$connection->update('key_value') -+ ->fields(['value' => serialize($data)]) -+ ->condition('collection', 'entity.definitions.installed') -+ ->condition('name', 'node.field_storage_definitions') -+ ->execute(); -+ -+$data = $connection->select('config') -+ ->fields('config', ['data']) -+ ->condition('collection', '') -+ ->condition('name', 'core.entity_view_display.node.page.default') -+ ->execute() -+ ->fetchField(); -+$data = unserialize($data); -+$data['content']['field_datetime_range'] = [ -+ 'type' => 'daterange_default', -+ 'label' => 'above', -+ 'settings' => [ -+ 'timezone_override' => '', -+ 'format_type' => 'medium', -+ 'separator' => '-', -+ ], -+ 'third_party_settings' => [], -+ 'weight' => 102, -+ 'region' => 'content', -+]; -+$connection->update('config') -+ ->fields([ -+ 'data' => serialize($data), -+ ]) -+ ->condition('collection', '') -+ ->condition('name', 'core.entity_view_display.node.page.default') -+ ->execute(); -+ -+$extensions = $connection->select('config') -+ ->fields('config', ['data']) -+ ->condition('collection', '') -+ ->condition('name', 'core.extension') -+ ->execute() -+ ->fetchField(); -+$extensions = unserialize($extensions); -+$extensions['module']['datetime_range'] = 0; -+$connection->update('config') -+ ->fields([ -+ 'data' => serialize($extensions), -+ ]) -+ ->condition('collection', '') -+ ->condition('name', 'core.extension') -+ ->execute(); -diff --git a/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php b/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php -index f19d18fd1b1e543503308ddf2b16f409b2370da9..a5e1ba3eb7bb105eaecb7eedf3947101f207fb8d 100644 ---- a/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php -+++ b/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php -@@ -6,6 +6,7 @@ - use Drupal\Core\Datetime\DrupalDateTime; - use Drupal\Core\Datetime\Entity\DateFormat; - use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; -+use Drupal\datetime_range\DateTimeRangeConstants; - use Drupal\Tests\datetime\Functional\DateTestBase; - use Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem; - use Drupal\entity_test\Entity\EntityTest; -@@ -37,7 +38,7 @@ class DateRangeFieldTest extends DateTestBase { - * - * @var array - */ -- protected $defaultSettings = ['timezone_override' => '', 'separator' => '-']; -+ protected $defaultSettings = ['timezone_override' => '', 'separator' => '-', 'from_to' => DateTimeRangeConstants::BOTH]; - - /** - * {@inheritdoc} -@@ -1426,4 +1427,170 @@ public function testDateStorageSettings() { - $this->assertSession()->elementsCount('xpath', "//*[@id='edit-settings-datetime-type' and contains(@disabled, 'disabled')]", 1); - } - -+ /** -+ * Tests displaying dates with the 'from_to' setting. -+ * -+ * @dataProvider fromToSettingDataProvider -+ */ -+ public function testFromToSetting(array $expected, $datetime_type, $field_formatter_type, array $display_settings = []) { -+ $field_name = $this->fieldStorage->getName(); -+ -+ // Create a test content type. -+ $this->drupalCreateContentType(['type' => 'date_content']); -+ -+ // Ensure the field to a datetime field. -+ $this->fieldStorage->setSetting('datetime_type', $datetime_type); -+ $this->fieldStorage->save(); -+ -+ // Build up dates in the UTC timezone. -+ $value = '2012-12-31 00:00:00'; -+ $start_date = new DrupalDateTime($value, 'UTC'); -+ $end_value = '2013-06-06 00:00:00'; -+ $end_date = new DrupalDateTime($end_value, 'UTC'); -+ -+ // Submit a valid date and ensure it is accepted. -+ $date_format = DateFormat::load('html_date')->getPattern(); -+ -+ $edit = [ -+ "{$field_name}[0][value][date]" => $start_date->format($date_format), -+ "{$field_name}[0][end_value][date]" => $end_date->format($date_format), -+ ]; -+ -+ // Supply time as well when field is a datetime field. -+ if ($datetime_type === DateRangeItem::DATETIME_TYPE_DATETIME) { -+ $time_format = DateFormat::load('html_time')->getPattern(); -+ $edit["{$field_name}[0][value][time]"] = $start_date->format($time_format); -+ $edit["{$field_name}[0][end_value][time]"] = $end_date->format($time_format); -+ } -+ -+ $this->drupalGet('entity_test/add'); -+ $this->submitForm($edit, t('Save')); -+ preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match); -+ $id = $match[1]; -+ $this->assertSession()->pageTextContains(t('entity_test @id has been created.', ['@id' => $id])); -+ -+ // Now set display options. -+ $this->displayOptions = [ -+ 'type' => $field_formatter_type, -+ 'label' => 'hidden', -+ 'settings' => $display_settings + [ -+ 'format_type' => 'short', -+ 'separator' => 'THESEPARATOR', -+ ] + $this->defaultSettings, -+ ]; -+ -+ \Drupal::service('entity_display.repository')->getViewDisplay( -+ $this->field->getTargetEntityTypeId(), -+ $this->field->getTargetBundle(), -+ 'full') -+ ->setComponent($field_name, $this->displayOptions) -+ ->save(); -+ -+ $output = $this->renderTestEntity($id); -+ foreach ($expected as $content => $is_expected) { -+ if ($is_expected) { -+ $this->assertStringContainsString($content, $output); -+ } -+ else { -+ $this->assertStringNotContainsString($content, $output); -+ } -+ } -+ } -+ -+ /** -+ * The data provider for testing the 'from_to' setting. -+ * -+ * @return array -+ * An array of different date settings to test the behavior of the 'from_to' setting. -+ */ -+ public function fromToSettingDataProvider() { -+ $datetime_types = [ -+ DateRangeItem::DATETIME_TYPE_DATE => [ -+ 'daterange_default' => [ -+ DateTimeRangeConstants::START_DATE => '12/31/2012', -+ DateTimeRangeConstants::END_DATE => '06/06/2013', -+ ], -+ 'daterange_plain' => [ -+ DateTimeRangeConstants::START_DATE => '2012-12-31', -+ DateTimeRangeConstants::END_DATE => '2013-06-06', -+ ], -+ 'daterange_custom' => [ -+ DateTimeRangeConstants::START_DATE => '2012-12-31', -+ DateTimeRangeConstants::END_DATE => '2013-06-06', -+ ], -+ ], -+ DateRangeItem::DATETIME_TYPE_DATETIME => [ -+ 'daterange_default' => [ -+ DateTimeRangeConstants::START_DATE => '12/31/2012 - 00:00', -+ DateTimeRangeConstants::END_DATE => '06/06/2013 - 00:00', -+ ], -+ 'daterange_plain' => [ -+ DateTimeRangeConstants::START_DATE => '2012-12-31T00:00:00', -+ DateTimeRangeConstants::END_DATE => '2013-06-06T00:00:00', -+ ], -+ 'daterange_custom' => [ -+ DateTimeRangeConstants::START_DATE => '2012-12-31T00:00:00', -+ DateTimeRangeConstants::END_DATE => '2013-06-06T00:00:00', -+ ], -+ ], -+ DateRangeItem::DATETIME_TYPE_ALLDAY => [ -+ 'daterange_default' => [ -+ DateTimeRangeConstants::START_DATE => '12/31/2012', -+ DateTimeRangeConstants::END_DATE => '06/06/2013', -+ ], -+ 'daterange_plain' => [ -+ DateTimeRangeConstants::START_DATE => '2012-12-31', -+ DateTimeRangeConstants::END_DATE => '2013-06-06', -+ ], -+ 'daterange_custom' => [ -+ DateTimeRangeConstants::START_DATE => '2012-12-31', -+ DateTimeRangeConstants::END_DATE => '2013-06-06', -+ ], -+ ], -+ ]; -+ -+ $return = []; -+ $separator = ' THESEPARATOR '; -+ foreach ($datetime_types as $datetime_type => $field_formatters) { -+ foreach ($field_formatters as $field_formatter_type => $dates) { -+ // Both start and end date. -+ $return[$datetime_type . '-' . $field_formatter_type . '-both'] = [ -+ 'expected' => [ -+ $dates[DateTimeRangeConstants::START_DATE] => TRUE, -+ $separator => TRUE, -+ $dates[DateTimeRangeConstants::END_DATE] => TRUE, -+ ], -+ 'datetime_type' => $datetime_type, -+ 'field_formatter_type' => $field_formatter_type, -+ ]; -+ -+ // Only start date. -+ $return[$datetime_type . '-' . $field_formatter_type . '-start_date'] = [ -+ 'expected' => [ -+ $dates[DateTimeRangeConstants::START_DATE] => TRUE, -+ $separator => FALSE, -+ $dates[DateTimeRangeConstants::END_DATE] => FALSE, -+ ], -+ 'datetime_type' => $datetime_type, -+ 'field_formatter_type' => $field_formatter_type, -+ ['from_to' => DateTimeRangeConstants::START_DATE], -+ ]; -+ -+ // Only end date. -+ $return[$datetime_type . '-' . $field_formatter_type . '-end_date'] = [ -+ 'expected' => [ -+ $dates[DateTimeRangeConstants::START_DATE] => FALSE, -+ $separator => FALSE, -+ $dates[DateTimeRangeConstants::END_DATE] => TRUE, -+ ], -+ 'datetime_type' => $datetime_type, -+ 'field_formatter_type' => $field_formatter_type, -+ ['from_to' => DateTimeRangeConstants::END_DATE], -+ ]; -+ } -+ } -+ -+ return $return; -+ } -+ - } -diff --git a/core/modules/datetime_range/tests/src/Functional/DateRangeFormatterSettingsUpdateTest.php b/core/modules/datetime_range/tests/src/Functional/DateRangeFormatterSettingsUpdateTest.php -new file mode 100644 -index 0000000000000000000000000000000000000000..351d579c5cb2e3842839932cfba9f8bd83ef6e1f ---- /dev/null -+++ b/core/modules/datetime_range/tests/src/Functional/DateRangeFormatterSettingsUpdateTest.php -@@ -0,0 +1,55 @@ -+<?php -+ -+namespace Drupal\Tests\datetime_range\Functional; -+ -+use Drupal\FunctionalTests\Update\UpdatePathTestBase; -+ -+/** -+ * Tests the update path for daterange formatter settings. -+ * -+ * @group datetime -+ */ -+class DateRangeFormatterSettingsUpdateTest extends UpdatePathTestBase { -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected static $modules = [ -+ 'node', -+ 'datetime', -+ 'datetime_range', -+ ]; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected $defaultTheme = 'stark'; -+ -+ /** -+ * {@inheritdoc} -+ */ -+ protected function setDatabaseDumpFiles(): void { -+ $this->databaseDumpFiles = [ -+ __DIR__ . '/../../../../system/tests/fixtures/update/drupal-9.4.0.bare.standard.php.gz', -+ __DIR__ . '/../../fixtures/update/drupal.daterange-formatter-settings-2827055.php', -+ ]; -+ } -+ -+ /** -+ * Tests update path for the 'from_to' formatter setting. -+ * -+ * @covers \datetime_range_post_update_from_to_configuration -+ */ -+ public function testPostUpdateDateRangeFormatter() { -+ $config_factory = \Drupal::configFactory(); -+ // Check that 'from_to' is missing before update. -+ $settings = $config_factory->get('core.entity_view_display.node.page.default')->get('content.field_datetime_range.settings'); -+ $this->assertArrayNotHasKey('from_to', $settings); -+ -+ $this->runUpdates(); -+ -+ $settings = $config_factory->get('core.entity_view_display.node.page.default')->get('content.field_datetime_range.settings'); -+ $this->assertArrayHasKey('from_to', $settings); -+ } -+ -+} diff --git a/patches/eca_views_argument.patch b/patches/eca_views_argument.patch deleted file mode 100644 index 00023d5ca4d52cef4a49f9338561c398c6dd28cc..0000000000000000000000000000000000000000 --- a/patches/eca_views_argument.patch +++ /dev/null @@ -1,25 +0,0 @@ -diff --git a/modules/views/src/Plugin/views/argument_default/Eca.php b/modules/views/src/Plugin/views/argument_default/Eca.php -new file mode 100644 -index 0000000000000000000000000000000000000000..e390ac6cceb1821735270d257c8855e5f930fb5c ---- /dev/null -+++ b/modules/views/src/Plugin/views/argument_default/Eca.php -@@ -0,0 +1,19 @@ -+<?php -+ -+namespace Drupal\eca_views\Plugin\views\argument_default; -+ -+use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase; -+ -+/** -+ * The ECA argument default handler. -+ * -+ * @ingroup views_argument_default_plugins -+ * -+ * @ViewsArgumentDefault( -+ * id = "eca", -+ * title = @Translation("ECA") -+ * ) -+ */ -+class Eca extends ArgumentDefaultPluginBase { -+ -+} diff --git a/patches/elasticsearch_connector.patch b/patches/elasticsearch_connector.patch deleted file mode 100644 index e07652c26fc6062791dc5fc5f574922ba96e76b5..0000000000000000000000000000000000000000 --- a/patches/elasticsearch_connector.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff --git a/src/ElasticSearch/Parameters/Builder/SearchBuilder.php b/src/ElasticSearch/Parameters/Builder/SearchBuilder.php -index 0129ea3..8f171f8 100644 ---- a/src/ElasticSearch/Parameters/Builder/SearchBuilder.php -+++ b/src/ElasticSearch/Parameters/Builder/SearchBuilder.php -@@ -313,5 +313,5 @@ class SearchBuilder { - $element = NULL; - - if (is_array($key)) { -- $element = $this->luceneFlattenKeys($key, $parse_mode); -+ $element = $this->flattenKeys($key, $parse_mode, $fuzzy); - } diff --git a/patches/gin_toolbar-permissions.patch b/patches/gin_toolbar-permissions.patch deleted file mode 100644 index 551f6b5f4b9cb4ea4f3177169ce864dc11cb4ed6..0000000000000000000000000000000000000000 --- a/patches/gin_toolbar-permissions.patch +++ /dev/null @@ -1,41 +0,0 @@ -diff --git a/gin_toolbar.module b/gin_toolbar.module -index 02bfce3568b53852a49092f0d6a6b3db89216a9d..1ced229a9910d12d0243ebd465f576e6c78361cf 100644 ---- a/gin_toolbar.module -+++ b/gin_toolbar.module -@@ -8,6 +8,7 @@ - use Drupal\Component\Utility\Html; - use Drupal\Core\Entity\EntityInterface; - use Drupal\Core\Routing\RouteMatchInterface; -+use Drupal\Core\Url; - use Drupal\gin\GinNavigation; - use Drupal\gin\GinSettings; - use Drupal\gin\GinUserPicture; -@@ -194,8 +195,13 @@ function gin_toolbar_preprocess_toolbar(&$variables) { - $entity = \Drupal::request()->attributes->get($matches['entity_type_id']); - - if ($entity instanceof EntityInterface && $entity->hasLinkTemplate('edit-form')) { -- $variables['entity_title'] = $entity->label(); -- $variables['entity_edit_url'] = $entity->toUrl('edit-form'); -+ if ($entity->access('update')) { -+ $variables['entity_title'] = $entity->label(); -+ $variables['entity_edit_url'] = $entity->toUrl('edit-form'); -+ } -+ elseif (Url::fromRoute('system.admin_content')->access(\Drupal::currentUser())) { -+ $variables['access_admin_content'] = TRUE; -+ } - } - } - } -diff --git a/templates/toolbar.html.twig b/templates/toolbar.html.twig -index e82bc1ceae19f14a3a7109a8d433e78dbf0d5ef3..5380e54f1899ef763f3036edef08f91e5b5d0a88 100644 ---- a/templates/toolbar.html.twig -+++ b/templates/toolbar.html.twig -@@ -16,7 +16,7 @@ - <li class="gin-breadcrumb__item"> - {% if entity_edit_url and entity_title %} - <a class="gin-breadcrumb__link gin-back-to-admin" href="{{ entity_edit_url }}">{{ 'Edit %title'|t({ '%title': entity_title }) }}</a> -- {% else %} -+ {% elseif access_admin_content %} - <a class="gin-breadcrumb__link gin-back-to-admin" href="{{ path('system.admin_content') }}">{{ 'Back to Administration'|t }}</a> - {% endif %} - </li> diff --git a/patches/grahl-ldap.patch b/patches/grahl-ldap.patch deleted file mode 100644 index 6761294241e1741b595b503aa3c432669c1f7352..0000000000000000000000000000000000000000 --- a/patches/grahl-ldap.patch +++ /dev/null @@ -1,54 +0,0 @@ -diff --git a/Adapter/ExtLdap/Collection.php b/Adapter/ExtLdap/Collection.php -index 5531a7c..880afd4 100644 ---- a/Adapter/ExtLdap/Collection.php -+++ b/Adapter/ExtLdap/Collection.php -@@ -45,7 +45,7 @@ class Collection implements CollectionInterface - /** - * @return int - */ -- public function count() -+ public function count(): int - { - $con = $this->connection->getResource(); - $searches = $this->search->getResources(); -@@ -64,7 +64,7 @@ class Collection implements CollectionInterface - /** - * @return \Traversable - */ -- public function getIterator() -+ public function getIterator(): \Traversable - { - if (0 === $this->count()) { - return; -@@ -90,28 +90,28 @@ class Collection implements CollectionInterface - /** - * @return bool - */ -- public function offsetExists($offset) -+ public function offsetExists($offset): bool - { - $this->toArray(); - - return isset($this->entries[$offset]); - } - -- public function offsetGet($offset) -+ public function offsetGet($offset): mixed - { - $this->toArray(); - - return isset($this->entries[$offset]) ? $this->entries[$offset] : null; - } - -- public function offsetSet($offset, $value) -+ public function offsetSet($offset, $value): void - { - $this->toArray(); - - $this->entries[$offset] = $value; - } - -- public function offsetUnset($offset) -+ public function offsetUnset($offset): void - { - $this->toArray(); diff --git a/patches/imagick-debug.diff b/patches/imagick-debug.diff deleted file mode 100644 index 6b13b19adfc611ce0bad0f817ac2848ead31e6b7..0000000000000000000000000000000000000000 --- a/patches/imagick-debug.diff +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/src/ImagemagickExecManager.php b/src/ImagemagickExecManager.php -index ceb5256..af44d4e 100644 ---- a/src/ImagemagickExecManager.php -+++ b/src/ImagemagickExecManager.php -@@ -226,7 +226,9 @@ class ImagemagickExecManager implements ImagemagickExecManagerInterface { - array_push($cmdline, ...$args); - } - -+ \Drupal::logger('imagick debug')->debug('Calling command: %cmdline', ['%cmdline' => implode(' ', $cmdline)]); - $return_code = $this->runProcess($cmdline, $packageSuite->value, $output, $error); -+ \Drupal::logger('imagick debug')->info('Succeeded'); - - if ($return_code !== FALSE) { - // If the executable returned a non-zero code, log to the watchdog. diff --git a/patches/jquery-ui-order.diff b/patches/jquery-ui-order.diff deleted file mode 100644 index d03f4053c8c364eab2045bdd7a660b8580c4bea3..0000000000000000000000000000000000000000 --- a/patches/jquery-ui-order.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Asset/AssetResolver.php b/core/lib/Drupal/Core/Asset/AssetResolver.php -index 35d28bbe8ce92f6d870475b419d7b0e07a1c06f7..b1bcd66a81fc819fd6179a0e385ee9d09331d440 100644 ---- a/core/lib/Drupal/Core/Asset/AssetResolver.php -+++ b/core/lib/Drupal/Core/Asset/AssetResolver.php -@@ -264,7 +264,7 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize) { - - // Always add a tiny value to the weight, to conserve the insertion - // order. -- $options['weight'] += count($javascript) / 1000; -+ $options['weight'] += count($javascript) / 30000; - - // Local and external files must keep their name as the associative - // key so the same JavaScript file is not added twice. diff --git a/patches/jsonapi-entityref-warning-10.3.patch b/patches/jsonapi-entityref-warning-10.3.patch deleted file mode 100644 index e4610dcee69b874e1dd4b1da79b66d202e3df638..0000000000000000000000000000000000000000 --- a/patches/jsonapi-entityref-warning-10.3.patch +++ /dev/null @@ -1,25 +0,0 @@ -diff --git a/core/modules/jsonapi/src/ResourceType/ResourceTypeRepository.php b/core/modules/jsonapi/src/ResourceType/ResourceTypeRepository.php -index 25bc5f8de3..0f8b573bd3 100644 ---- a/core/modules/jsonapi/src/ResourceType/ResourceTypeRepository.php -+++ b/core/modules/jsonapi/src/ResourceType/ResourceTypeRepository.php -@@ -466,21 +466,6 @@ protected function getRelatableResourceTypesFromFieldDefinition(FieldDefinitionI - $relatable_resource_types[] = $resource_type; - continue; - } -- // Do not warn during site installation since system integrity -- // is not guaranteed during this period and may cause confusing and -- // unnecessary warnings. -- if (!InstallerKernel::installationAttempted()) { -- $this->getLogger('jsonapi')->warning( -- 'The "@name" at "@target_entity_type_id:@target_bundle" references the "@entity_type_id:@bundle" entity type that does not exist.', -- [ -- '@name' => $field_definition->getName(), -- '@target_entity_type_id' => $field_definition->getTargetEntityTypeId(), -- '@target_bundle' => $field_definition->getTargetBundle(), -- '@entity_type_id' => $entity_type_id, -- '@bundle' => $target_bundle, -- ], -- ); -- } - } - } diff --git a/patches/jsonapi-entityref-warning.patch b/patches/jsonapi-entityref-warning.patch deleted file mode 100644 index fda03a834c8439884c429367264bda7c64aff065..0000000000000000000000000000000000000000 --- a/patches/jsonapi-entityref-warning.patch +++ /dev/null @@ -1,25 +0,0 @@ -diff --git a/core/modules/jsonapi/src/ResourceType/ResourceTypeRepository.php b/core/modules/jsonapi/src/ResourceType/ResourceTypeRepository.php ---- a/core/modules/jsonapi/src/ResourceType/ResourceTypeRepository.php -+++ b/core/modules/jsonapi/src/ResourceType/ResourceTypeRepository.php -@@ -463,22 +463,6 @@ class ResourceTypeRepository implements ResourceTypeRepositoryInterface { - $relatable_resource_types[] = $resource_type; - continue; - } -- // Do not warn during site installation since system integrity -- // is not guaranteed during this period and may cause confusing and -- // unnecessary warnings. -- if (!InstallerKernel::installationAttempted()) { -- trigger_error( -- sprintf( -- 'The "%s" at "%s:%s" references the "%s:%s" entity type that does not exist.', -- $field_definition->getName(), -- $field_definition->getTargetEntityTypeId(), -- $field_definition->getTargetBundle(), -- $entity_type_id, -- $target_bundle -- ), -- E_USER_WARNING -- ); -- } - } - } diff --git a/patches/jsonapi_remove_code_issue_3218683.patch b/patches/jsonapi_remove_code_issue_3218683.patch deleted file mode 100644 index 235bf1923af525372abc2c2013b6b4961dd7a344..0000000000000000000000000000000000000000 --- a/patches/jsonapi_remove_code_issue_3218683.patch +++ /dev/null @@ -1,85 +0,0 @@ -diff --git a/core/src/ResourceType/ConfigurableResourceTypeRepository.php b/core/src/ResourceType/ConfigurableResourceTypeRepository.php -index 213f52ea4b13f563199fabc0c815e29b1834d6e9..1d4f8073c527bad01144adb5b5fa10137125c1ba 100644 ---- a/core/src/ResourceType/ConfigurableResourceTypeRepository.php -+++ b/core/src/ResourceType/ConfigurableResourceTypeRepository.php -@@ -255,46 +255,6 @@ class ConfigurableResourceTypeRepository extends ResourceTypeRepository { - return NULL; - } - -- /** -- * {@inheritdoc} -- * -- * @todo Remove this code when Drupal 8 support is dropped -- */ -- protected function getRelatableResourceTypesFromFieldDefinition(FieldDefinitionInterface $field_definition, array $resource_types) { -- $item_definition = $field_definition->getItemDefinition(); -- $entity_type_id = $item_definition->getSetting('target_type'); -- $handler_settings = $item_definition->getSetting('handler_settings'); -- $target_bundles = empty($handler_settings['target_bundles']) ? $this->getAllBundlesForEntityType( -- $entity_type_id -- ) : $handler_settings['target_bundles']; -- $relatable_resource_types = []; -- -- foreach ($target_bundles as $target_bundle) { -- if ($resource_type = static::lookupResourceType( -- $resource_types, -- $entity_type_id, -- $target_bundle -- )) { -- $relatable_resource_types[] = $resource_type; -- } -- else { -- trigger_error( -- sprintf( -- 'The "%s" at "%s:%s" references the "%s:%s" entity type that does not exist. Please take action.', -- $field_definition->getName(), -- $field_definition->getTargetEntityTypeId(), -- $field_definition->getTargetBundle(), -- $entity_type_id, -- $target_bundle -- ), -- E_USER_WARNING -- ); -- } -- } -- -- return $relatable_resource_types; -- } -- - /** - * {@inheritdoc} - */ -@@ -306,33 +266,4 @@ class ConfigurableResourceTypeRepository extends ResourceTypeRepository { - return array_map('strval', array_keys($this->entityTypeBundleInfo->getBundleInfo($entity_type_id))); - } - -- /** -- * Lookups resource type by the internal and public identifiers. -- * -- * @param \Drupal\jsonapi\ResourceType\ResourceType[] $resource_types -- * The list of resource types to do a lookup. -- * @param string $entity_type_id -- * The entity type of a seekable resource. -- * @param string $bundle -- * The entity bundle of a seekable resource. -- * -- * @return \Drupal\jsonapi\ResourceType\ResourceType|null -- * The resource type or NULL if it cannot be found. -- * -- * @todo Remove this code when Drupal 8 support is dropped -- */ -- protected static function lookupResourceType(array $resource_types, $entity_type_id, $bundle) { -- if (isset($resource_types["$entity_type_id--$bundle"])) { -- return $resource_types["$entity_type_id--$bundle"]; -- } -- -- foreach ($resource_types as $resource_type) { -- if ($resource_type->getEntityTypeId() === $entity_type_id && $resource_type->getBundle() === $bundle) { -- return $resource_type; -- } -- } -- -- return NULL; -- } -- - } diff --git a/patches/pdfpreview-debug.diff b/patches/pdfpreview-debug.diff deleted file mode 100644 index f6087767be8224bf4dd303effa8095c43e760554..0000000000000000000000000000000000000000 --- a/patches/pdfpreview-debug.diff +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/src/Plugin/Field/FieldFormatter/PDFPreviewFormatter.php b/src/Plugin/Field/FieldFormatter/PDFPreviewFormatter.php -index 5b14b30..56b116b 100644 ---- a/src/Plugin/Field/FieldFormatter/PDFPreviewFormatter.php -+++ b/src/Plugin/Field/FieldFormatter/PDFPreviewFormatter.php -@@ -208,7 +208,9 @@ class PDFPreviewFormatter extends ImageFormatter { - // Separate the PDF previews from the other files. - $show_preview = FALSE; - if ($file->getMimeType() == 'application/pdf') { -+ \Drupal::logger('pdf debug')->info('Calling imagick for file ' . $file->id()); - $preview_uri = $this->pdfPreviewGenerator->getPDFPreview($file); -+ \Drupal::logger('pdf debug')->info('Succeeded'); - $preview = $this->imageFactory->get($preview_uri); - if ($preview->isValid()) { - $show_preview = TRUE; diff --git a/patches/timezone.patch b/patches/timezone.patch deleted file mode 100644 index e81f70505beb76f3f702e9afc68690623f9d3fe9..0000000000000000000000000000000000000000 --- a/patches/timezone.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/core/lib/Drupal/Core/Datetime/Element/Datelist.php b/core/lib/Drupal/Core/Datetime/Element/Datelist.php ---- a/core/lib/Drupal/Core/Datetime/Element/Datelist.php -+++ b/core/lib/Drupal/Core/Datetime/Element/Datelist.php -@@ -76,7 +76,7 @@ - if (!empty($element['#default_value'])) { - $date = $element['#default_value']; - if ($date instanceof DrupalDateTime && !$date->hasErrors()) { -- $date->setTimezone(new \DateTimeZone($element['#date_timezone'])); -+ $date->setTimezone(new \DateTimeZone($element['#date_timezone'] ?? drupal_get_user_timezone())); - static::incrementRound($date, $increment); - foreach ($parts as $part) { - switch ($part) { diff --git a/patches/uikitty-d10.patch b/patches/uikitty-d10.patch deleted file mode 100644 index 508e39458ae796f494e7e22b774554a61e5e0082..0000000000000000000000000000000000000000 --- a/patches/uikitty-d10.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/uikitty.info.yml b/uikitty.info.yml -index 0a44d98..30a73e7 100644 ---- a/uikitty.info.yml -+++ b/uikitty.info.yml -@@ -1,8 +1,7 @@ - name: uikitty - type: theme - description: A building block theme using uikit awesomeness. --# core: 8.x --core_version_requirement: ^8.8 || ^9 -+core_version_requirement: ^10 || ^11 - base theme: classy - libraries: - - uikitty/global