diff --git a/.github/workflows/test.yml b/.github/workflows/tests.yml
similarity index 93%
rename from .github/workflows/test.yml
rename to .github/workflows/tests.yml
index bf21ccc4e..17b425c78 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/tests.yml
@@ -12,6 +12,7 @@ on:
env:
DRUPAL_TESTING_COMPOSER_PROJECT: thunder/thunder-project
DRUPAL_TESTING_COMPOSER_PROJECT_VERSION: "^4.0@stable"
+ DRUPAL_TESTING_CLEANUP: false
DRUPAL_TESTING_DATABASE_USER: root
DRUPAL_TESTING_DATABASE_PASSWORD: root
DRUPAL_TESTING_TEST_DUMP_FILE: site-dump.tar.gz
@@ -28,6 +29,7 @@ env:
DRUPAL_TESTING_PARALLEL_TESTING: false
MINK_DRIVER_ARGS_WEBDRIVER: '["chrome", {"browserName":"chrome","chromeOptions":{"args":["--disable-gpu","--headless", "--no-sandbox", "--disable-dev-shm-usage"]}}, "http://127.0.0.1:9515"]'
SIMPLETEST_BASE_URL: http://thunder-testing:8888
+ BROWSERTEST_OUTPUT_DIRECTORY: /tmp
SKIP_TEST_CLEANUP: true
# The following variable set the version that the upgrade test starts with.
DRUPAL_TESTING_UPGRADE_COMPOSER_PROJECT_VERSION: 3.0.12
@@ -38,7 +40,7 @@ jobs:
build:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
services:
mysql:
@@ -50,7 +52,7 @@ jobs:
strategy:
matrix:
- PHP_VERSION: [ '8.1' ]
+ PHP_VERSION: [ '8.1', '8.3' ]
env:
DRUPAL_TESTING_TEST_DEPRECATION: true
@@ -106,7 +108,7 @@ jobs:
test-max:
needs: build
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
services:
mysql:
@@ -124,7 +126,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- PHP_VERSION: [ '8.1' ]
+ PHP_VERSION: [ '8.3' ]
CHUNK: [ 1, 2, 3 ]
steps:
@@ -168,9 +170,16 @@ jobs:
THUNDER_TEST_CHUNK: ${{ matrix.CHUNK }}
DRUPAL_TESTING_TEST_PATH: /tmp/test/thunder/install/docroot/profiles/contrib/thunder/tests/src/TestSuites/ThunderTestSuite.php
+ - name: Upload test output
+ uses: actions/upload-artifact@v4
+ if: success() || failure()
+ with:
+ name: text-max-output-${{ matrix.CHUNK }}-${{ matrix.PHP_VERSION }}
+ path: /tmp/test/thunder/install/docroot/sites/simpletest/browser_output/
+
test-upgrade:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
services:
mysql:
@@ -281,7 +290,7 @@ jobs:
test-min:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
services:
mysql:
@@ -336,7 +345,9 @@ jobs:
run: test-drupal-project prepare_build
- name: Install suggested dependencies
- run: composer require "league/container:^4.2" "consolidation/config:^2.1.2" --no-update --no-progress --working-dir=/tmp/test/thunder/install
+ run: |
+ composer require "league/container:^4.2" "drupal/core-dev:^10.3" --no-update --no-progress --working-dir=/tmp/test/thunder/install
+ composer config allow-plugins.php-http/discovery true --no-plugins --working-dir=/tmp/test/thunder/install
- name: Build the docroot
run: test-drupal-project build
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f6899bab..1456541b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,34 @@
# Changelog
+## [7.3.3](https://github.com/thunder/thunder-distribution/tree/7.3.3) 2024-08-22
+
+[Full Changelog](https://github.com/thunder/thunder-distribution/compare/7.3.2...7.3.3)
+
+Add search api GraphQl schema and data producer.
+
+## [7.3.2](https://github.com/thunder/thunder-distribution/tree/7.3.2) 2024-08-14
+
+[Full Changelog](https://github.com/thunder/thunder-distribution/compare/7.3.1...7.3.2)
+
+* [Issue #3462165: Add focal_point patch](https://www.drupal.org/node/3462165)
+
+## [7.3.1](https://github.com/thunder/thunder-distribution/tree/7.3.1) 2024-06-024
+
+[Full Changelog](https://github.com/thunder/thunder-distribution/compare/7.3.0...7.3.1)
+
+Add patches for upstream issues.
+
+* [Issue #3465364: Fatal error when changing password when password_policy_history is enabled](https://www.drupal.org/project/password_policy/issues/3465364)
+* [Issue #3455558: There is no visible change to a toggle when pressed (but it does trigger conditional fields, value is saved, etc)](https://www.drupal.org/project/gin/issues/3455558)
+
+## [7.3.0](https://github.com/thunder/thunder-distribution/tree/7.1.0) 2024-06-024
+
+[Full Changelog](https://github.com/thunder/thunder-distribution/compare/7.2.2...7.3.0)
+
+* Drupal 10.3 compatibility.
+* Updated Gin theme.
+* PHP8.3 compatibility.
+
## [7.2.2](https://github.com/thunder/thunder-distribution/tree/7.2.2) 2024-04-30
[Full Changelog](https://github.com/thunder/thunder-distribution/compare/7.2.1...7.2.2)
diff --git a/README.md b/README.md
index 0569bc307..35e66e0a9 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@ For general help using Thunder, please refer to [the official Thunder documentat
### Community support
-For additional help, you can use one of this channel to ask question:
+For additional help, you can use one of these channels to ask question:
* [Slack](https://thunder.org/contact-us) (highly recommended for faster support)
* [Twitter](https://twitter.com/ThunderCoreTeam)
diff --git a/composer.json b/composer.json
index 8cbdad155..4a0390142 100644
--- a/composer.json
+++ b/composer.json
@@ -36,14 +36,16 @@
"patches": {
"drupal/core": {
"Fix Claro styles for exposed views filters wrapped in fieldsets": "https://www.drupal.org/files/issues/2021-05-31/3133639.19.patch",
- "Let users configure the text of the \"Add media\" button": "https://www.drupal.org/files/issues/2021-10-01/3169956-34.patch",
- "Issue #3388913: Checkbox for Media library modal missing after search": "https://www.drupal.org/files/issues/2024-01-30/3388913-48-d10.1.patch"
+ "Let users configure the text of the \"Add media\" button": "https://www.drupal.org/files/issues/2021-10-01/3169956-34.patch"
},
"drupal/diff": {
"Back button for comparison page": "https://www.drupal.org/files/issues/back_button_for-2853193-4.patch"
},
"drupal/focal_point": {
- "Preview link accidentally closes the media library": "https://www.drupal.org/files/issues/2020-10-11/preview_link_accidentally_closes_the_media_library-3162210-19.patch"
+ "Issue #3462165: Preview results in Error: Call to a member function getDefinitions() on null": "https://www.drupal.org/files/issues/2024-07-18/Preview-results-in-Error-3462165.patch"
+ },
+ "drupal/gin": {
+ "Issue #3455558: There is no visible change to a toggle when pressed (but it does trigger conditional fields, value is saved, etc)": "https://www.drupal.org/files/issues/2024-08-06/3455558-Refactor-toggle-styles-mr438.patch"
}
}
},
@@ -53,25 +55,25 @@
"drupal/access_unpublished": "^1.5",
"drupal/admin_toolbar": "^3.4",
"drupal/autofill": "^1.1",
- "drupal/autosave_form": "^1.2",
+ "drupal/autosave_form": "^1.6",
"drupal/blazy": "^2.14",
"drupal/checklistapi": "^2.1",
- "drupal/core-recommended": "~10.2.4@stable",
+ "drupal/core-recommended": "~10.3.0@stable",
"drupal/config_selector": "^2.1",
"drupal/config_update": "^1.7 || ^2.0",
"drupal/content_lock": "^2.1",
"drupal/crop": "^2.2",
"drupal/default_content": "^1.0-alpha7||^2.0@ALPHA",
- "drupal/diff": "1.3",
+ "drupal/diff": "1.7",
"drupal/dropzonejs": "^2.8",
"drupal/empty_fields": "^1.0-alpha1",
- "drupal/entity_reference_actions": "^1.0",
+ "drupal/entity_reference_actions": "^1.1.1",
"drupal/entity_reference_revisions": "^1.3",
"drupal/field_group": "^3.4",
- "drupal/focal_point": "2.0.2",
+ "drupal/focal_point": "2.1.1",
"drupal/facets": "^2.0.6",
- "drupal/gin": "3.0-rc9",
- "drupal/gin_toolbar": "^1.0-rc5",
+ "drupal/gin": "3.0-rc11",
+ "drupal/gin_toolbar": "^1.0-rc6",
"drupal/graphql": "^4.7",
"drupal/inline_entity_form": "^1.0-rc14",
"drupal/ivw_integration": "^2.0",
@@ -82,18 +84,18 @@
"drupal/media_entity_slideshow": "^2.0-alpha1",
"drupal/media_entity_twitter": "^2.5",
"drupal/media_expire": "^2.6",
- "drupal/media_library_media_modify": "^1.0.0-beta14",
+ "drupal/media_library_media_modify": "^1.0.0",
"drupal/media_file_delete": "^1.2",
"drupal/metatag": "^1.26",
"drupal/metatag_async_widget": "^1.0-alpha2",
"drupal/paragraphs": "^1.12",
"drupal/paragraphs_features": "^2.0.0-beta3",
"drupal/paragraphs_paste": "^2.0-beta3",
- "drupal/password_policy": "^4.0",
+ "drupal/password_policy": "^4.0.3",
"drupal/pathauto": "^1.11",
"drupal/responsive_preview": "^2.1",
"drupal/redirect": "^1.7",
- "drupal/scheduler": "^2.0",
+ "drupal/scheduler": "^2.0.2",
"drupal/scheduler_content_moderation_integration": "^2.0",
"drupal/schema_metatag": "^2.4",
"drupal/select2": "^1.7",
@@ -114,7 +116,8 @@
"npm-asset/select2": "^4.0.7",
"caxy/php-htmldiff": "^0.1.14",
"webonyx/graphql-php": "^14.11.8",
- "drupal/jquery_ui": "^1.6"
+ "drupal/jquery_ui": "^1.6",
+ "drupal/jquery_ui_draggable": "^2.1"
},
"suggest": {
"drupal/search_api_solr": "Provides a Apache Solr backend for the Search API module"
diff --git a/config/install/editor.editor.basic_html.yml b/config/install/editor.editor.basic_html.yml
index 84cc9573c..0b7bb8be6 100644
--- a/config/install/editor.editor.basic_html.yml
+++ b/config/install/editor.editor.basic_html.yml
@@ -60,7 +60,7 @@ image_upload:
status: true
scheme: public
directory: inline-images
- max_size: ''
+ max_size: null
max_dimensions:
- width: 0
- height: 0
+ width: null
+ height: null
diff --git a/config/install/editor.editor.full_html.yml b/config/install/editor.editor.full_html.yml
index 51f25c736..7a67aa5b4 100644
--- a/config/install/editor.editor.full_html.yml
+++ b/config/install/editor.editor.full_html.yml
@@ -98,7 +98,7 @@ image_upload:
status: true
scheme: public
directory: inline-images
- max_size: ''
+ max_size: null
max_dimensions:
- width: 0
- height: 0
+ width: null
+ height: null
diff --git a/config/install/gin.settings.yml b/config/install/gin.settings.yml
index f57d0a8fa..19738d29f 100644
--- a/config/install/gin.settings.yml
+++ b/config/install/gin.settings.yml
@@ -24,3 +24,4 @@ focus_color: ''
layout_density: small
show_description_toggle: true
show_user_theme_settings: true
+sticky_action_buttons: true
diff --git a/config/install/image.style.linkit_result_thumbnail.yml b/config/install/image.style.linkit_result_thumbnail.yml
index 1d6d88ea4..703d123e7 100644
--- a/config/install/image.style.linkit_result_thumbnail.yml
+++ b/config/install/image.style.linkit_result_thumbnail.yml
@@ -12,3 +12,9 @@ effects:
width: 50
height: 50
anchor: center-center
+ 451bc36f-b256-43cc-adb5-4f3151fd892d:
+ uuid: 451bc36f-b256-43cc-adb5-4f3151fd892d
+ id: image_convert
+ weight: 2
+ data:
+ extension: webp
diff --git a/config/install/image.style.thumbnail.yml b/config/install/image.style.thumbnail.yml
index 2780cc9c6..3059d4bde 100644
--- a/config/install/image.style.thumbnail.yml
+++ b/config/install/image.style.thumbnail.yml
@@ -14,3 +14,9 @@ effects:
width: 100
height: 100
crop_type: focal_point
+ c4eb9942-2c9e-4a81-949f-6161a44b6559:
+ uuid: c4eb9942-2c9e-4a81-949f-6161a44b6559
+ id: image_convert
+ weight: 2
+ data:
+ extension: webp
diff --git a/config/install/views.view.media_library.yml b/config/install/views.view.media_library.yml
index 8e1a64aec..a329d29c4 100644
--- a/config/install/views.view.media_library.yml
+++ b/config/install/views.view.media_library.yml
@@ -150,6 +150,7 @@ display:
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
+ pagination_heading_level: h4
exposed_form:
type: basic
options:
diff --git a/config/install/views.view.scheduler_scheduled_content.yml b/config/install/views.view.scheduler_scheduled_content.yml
index 73e241292..1be50ec8f 100644
--- a/config/install/views.view.scheduler_scheduled_content.yml
+++ b/config/install/views.view.scheduler_scheduled_content.yml
@@ -537,6 +537,7 @@ display:
previous: '‹ previous'
first: '« first'
last: 'last »'
+ pagination_heading_level: h4
exposed_form:
type: basic
options:
@@ -1080,7 +1081,7 @@ display:
admin_label: ''
entity_type: node
entity_field: uid
- plugin_id: numeric
+ plugin_id: entity_target_id
default_action: empty
exception:
value: all
@@ -1112,6 +1113,7 @@ display:
roles: { }
break_phrase: false
not: false
+ target_entity_type_id: user
defaults:
empty: false
access: false
diff --git a/config/install/views.view.scheduler_scheduled_media.yml b/config/install/views.view.scheduler_scheduled_media.yml
index d2235ad80..2ef91dc19 100644
--- a/config/install/views.view.scheduler_scheduled_media.yml
+++ b/config/install/views.view.scheduler_scheduled_media.yml
@@ -563,6 +563,7 @@ display:
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
+ pagination_heading_level: h4
exposed_form:
type: basic
options:
@@ -1077,7 +1078,8 @@ display:
field: uid
entity_type: media
entity_field: uid
- plugin_id: numeric
+ plugin_id: entity_target_id
+ target_entity_type_id: user
defaults:
empty: false
access: false
diff --git a/config/install/views.view.scheduler_scheduled_taxonomy_term.yml b/config/install/views.view.scheduler_scheduled_taxonomy_term.yml
index 382044bb8..8fdac6400 100644
--- a/config/install/views.view.scheduler_scheduled_taxonomy_term.yml
+++ b/config/install/views.view.scheduler_scheduled_taxonomy_term.yml
@@ -499,6 +499,7 @@ display:
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
+ pagination_heading_level: h4
exposed_form:
type: basic
options:
diff --git a/config/optional/image.style.facebook.yml b/config/optional/image.style.facebook.yml
index ef5a3bd73..d61808606 100644
--- a/config/optional/image.style.facebook.yml
+++ b/config/optional/image.style.facebook.yml
@@ -14,3 +14,9 @@ effects:
width: 1200
height: 630
crop_type: focal_point
+ ddffc1b0-0ba5-4137-bc44-90354696de13:
+ uuid: ddffc1b0-0ba5-4137-bc44-90354696de13
+ id: image_convert
+ weight: 2
+ data:
+ extension: webp
diff --git a/config/optional/image.style.gallery.yml b/config/optional/image.style.gallery.yml
index 32ad3e162..b2281adf2 100644
--- a/config/optional/image.style.gallery.yml
+++ b/config/optional/image.style.gallery.yml
@@ -14,3 +14,9 @@ effects:
width: 853
height: 480
crop_type: focal_point
+ 9d5cfaec-9903-444e-964b-4ea2ccdb6608:
+ uuid: 9d5cfaec-9903-444e-964b-4ea2ccdb6608
+ id: image_convert
+ weight: 2
+ data:
+ extension: webp
diff --git a/config/optional/image.style.media_image.yml b/config/optional/image.style.media_image.yml
index c00cd73f4..3bd0d82e6 100644
--- a/config/optional/image.style.media_image.yml
+++ b/config/optional/image.style.media_image.yml
@@ -14,3 +14,9 @@ effects:
width: 938
height: 527
crop_type: focal_point
+ 8963e07c-e414-417d-a0dd-59b2c29399d6:
+ uuid: 8963e07c-e414-417d-a0dd-59b2c29399d6
+ id: image_convert
+ weight: 2
+ data:
+ extension: webp
diff --git a/config/optional/image.style.media_image_mobile.yml b/config/optional/image.style.media_image_mobile.yml
index c32a291b0..c2e73a7dc 100644
--- a/config/optional/image.style.media_image_mobile.yml
+++ b/config/optional/image.style.media_image_mobile.yml
@@ -14,3 +14,9 @@ effects:
width: 560
height: 315
crop_type: focal_point
+ ee591d84-f108-4b47-a0a4-bd3802b38284:
+ uuid: ee591d84-f108-4b47-a0a4-bd3802b38284
+ id: image_convert
+ weight: 2
+ data:
+ extension: webp
diff --git a/config/optional/image.style.media_image_tablet.yml b/config/optional/image.style.media_image_tablet.yml
index baa63534b..c2d4b7d65 100644
--- a/config/optional/image.style.media_image_tablet.yml
+++ b/config/optional/image.style.media_image_tablet.yml
@@ -14,3 +14,9 @@ effects:
width: 850
height: 478
crop_type: focal_point
+ 3898254c-5ffd-47d9-ac3c-6f2d0be0d9ec:
+ uuid: 3898254c-5ffd-47d9-ac3c-6f2d0be0d9ec
+ id: image_convert
+ weight: 2
+ data:
+ extension: webp
\ No newline at end of file
diff --git a/config/optional/image.style.twitter.yml b/config/optional/image.style.twitter.yml
index 47ab518ea..c24a32c82 100644
--- a/config/optional/image.style.twitter.yml
+++ b/config/optional/image.style.twitter.yml
@@ -14,3 +14,9 @@ effects:
width: 1024
height: 512
crop_type: focal_point
+ 2c85b491-12ad-48cd-9a33-2d32905c2cf5:
+ uuid: 2c85b491-12ad-48cd-9a33-2d32905c2cf5
+ id: image_convert
+ weight: 2
+ data:
+ extension: webp
diff --git a/config/optional/node.type.article.yml b/config/optional/node.type.article.yml
index dababc5f2..81324fb24 100644
--- a/config/optional/node.type.article.yml
+++ b/config/optional/node.type.article.yml
@@ -22,7 +22,7 @@ third_party_settings:
name: Article
type: article
description: 'Use articles for evergreen content.'
-help: ''
+help: null
new_revision: true
preview_mode: 1
display_submitted: true
diff --git a/config/optional/node.type.news_article.yml b/config/optional/node.type.news_article.yml
index 8ef812c03..c8b47a217 100644
--- a/config/optional/node.type.news_article.yml
+++ b/config/optional/node.type.news_article.yml
@@ -25,7 +25,7 @@ third_party_settings:
name: 'News Article'
type: news_article
description: 'Use news articles for time-sensitive content like news, press releases or blog posts.'
-help: ''
+help: null
new_revision: true
preview_mode: 1
display_submitted: true
diff --git a/config/optional/node.type.page.yml b/config/optional/node.type.page.yml
index 4c1a522ad..755e8ed8c 100644
--- a/config/optional/node.type.page.yml
+++ b/config/optional/node.type.page.yml
@@ -4,7 +4,7 @@ dependencies: { }
name: 'Basic page'
type: page
description: "Use basic pages for your static content, such as an 'About us' page."
-help: ''
+help: null
new_revision: true
preview_mode: 1
display_submitted: false
diff --git a/config/optional/taxonomy.vocabulary.channel.yml b/config/optional/taxonomy.vocabulary.channel.yml
index 8293c1bba..b9173d86c 100644
--- a/config/optional/taxonomy.vocabulary.channel.yml
+++ b/config/optional/taxonomy.vocabulary.channel.yml
@@ -3,5 +3,6 @@ status: true
dependencies: { }
name: Channel
vid: channel
-description: ''
+description: null
weight: 0
+new_revision: false
diff --git a/config/optional/taxonomy.vocabulary.tags.yml b/config/optional/taxonomy.vocabulary.tags.yml
index 4c754e86c..0d0313cf6 100644
--- a/config/optional/taxonomy.vocabulary.tags.yml
+++ b/config/optional/taxonomy.vocabulary.tags.yml
@@ -5,3 +5,4 @@ name: Tags
vid: tags
description: 'Use tags to group articles on similar topics into categories.'
weight: 0
+new_revision: false
diff --git a/config/optional/views.view.content.yml b/config/optional/views.view.content.yml
index 3b4677b27..d90a10b33 100644
--- a/config/optional/views.view.content.yml
+++ b/config/optional/views.view.content.yml
@@ -412,6 +412,7 @@ display:
previous: '‹ Previous'
first: '« First'
last: 'Last »'
+ pagination_heading_level: h4
exposed_form:
type: basic
options:
diff --git a/config/optional/views.view.media.yml b/config/optional/views.view.media.yml
index 6f98eaff8..6f44b8942 100644
--- a/config/optional/views.view.media.yml
+++ b/config/optional/views.view.media.yml
@@ -602,6 +602,7 @@ display:
offset: false
offset_label: Offset
quantity: 9
+ pagination_heading_level: h4
exposed_form:
type: basic
options:
diff --git a/docs/developer-guide/migration/migrate-7-8.md b/docs/developer-guide/migration/migrate-7-8.md
new file mode 100644
index 000000000..53026603b
--- /dev/null
+++ b/docs/developer-guide/migration/migrate-7-8.md
@@ -0,0 +1,39 @@
+# Update Thunder 7 -> Thunder 8
+
+## Prerequisites
+
+These are the instructions to manually update your existing Thunder 7 installation to Thunder 8. If
+you want to do a fresh installation of thunder please visit [install Thunder](../setup.md#install-thunder).
+
+You have to make sure that your Thunder 7 project and all its dependencies are fully updated to the most current
+versions. Run the following command in your docroot:
+
+```bash
+drush ev "print drupal_get_installed_schema_version('thunder') . PHP_EOL;"
+```
+
+This should print the number XXXX or greater. If that is not the case, update your project.
+
+```bash
+cd ..
+composer update
+```
+
+This should update to Thunder 7.3 or greater.
+
+Now run database updates:
+
+```bash
+cd docroot
+drush updb
+```
+
+You should at least see the Thunder XXXX schema update. If not, double check that the correct version of Thunder
+is installed, and that `drush updb` did not throw any errors.
+
+Before you start with the code and database update please disable the jQuery UI Draggable module or require it on your own.
+
+```bash
+composer require drupal/jquery_ui_draggable
+```
+
diff --git a/modules/thunder_article/src/Form/ThunderNodeFormHelper.php b/modules/thunder_article/src/Form/ThunderNodeFormHelper.php
deleted file mode 100644
index ded7f1fba..000000000
--- a/modules/thunder_article/src/Form/ThunderNodeFormHelper.php
+++ /dev/null
@@ -1,199 +0,0 @@
-currentUser = $current_user;
- $this->messenger = $messenger;
- $this->request = $requestStack->getCurrentRequest();
- $this->entityTypeManager = $entity_type_manager;
- $this->themeManager = $theme_manager;
- $this->moderationInfo = $moderationInfo;
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container): self {
- return new static(
- $container->get('current_user'),
- $container->get('messenger'),
- $container->get('request_stack'),
- $container->get('entity_type.manager'),
- $container->get('theme.manager'),
- $container->get('content_moderation.moderation_information', ContainerInterface::NULL_ON_INVALID_REFERENCE)
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function formAlter(array &$form, FormStateInterface $form_state): array {
- /** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */
- $form_object = $form_state->getFormObject();
- /** @var \Drupal\node\NodeInterface $entity */
- $entity = $form_object->getEntity();
-
- /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
- $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId());
- $latest_revision_id = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $entity->language()->getId());
- if ($latest_revision_id !== NULL && $this->moderationInfo && $this->moderationInfo->hasPendingRevision($entity)) {
- $this->messenger->addWarning($this->t('This %entity_type has unpublished changes from user %user.', [
- '%entity_type' => $entity->get('type')->entity->label(),
- '%user' => $entity->getRevisionUser()->label(),
- ]));
- }
-
- $form['actions'] = array_merge($form['actions'], $this->actions($entity));
-
- return $form;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function actions(NodeInterface $entity): array {
- /** @var \Drupal\node\Entity\Node $entity */
- /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
- $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId());
- $latest_revision_id = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $entity->language()->getId());
-
- if ($latest_revision_id == NULL || !$this->moderationInfo || !$this->moderationInfo->isModeratedEntity($entity)) {
- return [];
- }
-
- $element = [];
- // @todo Remove after seven / thunder_admin support is dropped.
- $activeTheme = $this->themeManager->getActiveTheme();
- $activeThemes = array_keys($activeTheme->getBaseThemeExtensions());
- $activeThemes[] = $activeTheme->getName();
-
- if (!empty(array_intersect($activeThemes, ['seven', 'thunder_admin']))) {
- /** @var \Drupal\content_moderation\ContentModerationState $state */
- $state = $this->moderationInfo->getWorkflowForEntity($entity)->getTypePlugin()->getState($entity->moderation_state->value);
- $element['status'] = [
- '#type' => 'item',
- '#markup' => $entity->isNew() || !$this->moderationInfo->isDefaultRevisionPublished($entity) ? $this->t('of unpublished @entity_type', ['@entity_type' => strtolower($entity->type->entity->label())]) : $this->t('of published @entity_type', ['@entity_type' => strtolower($entity->type->entity->label())]),
- '#weight' => 200,
- '#wrapper_attributes' => [
- 'class' => ['status'],
- ],
- '#access' => !$state->isDefaultRevisionState(),
- ];
-
- $element['moderation_state_current'] = [
- '#type' => 'item',
- '#markup' => $state->label(),
- '#weight' => 210,
- '#wrapper_attributes' => [
- 'class' => ['status', $state->id()],
- ],
- ];
- }
-
- if ($this->moderationInfo->hasPendingRevision($entity)) {
- $route_info = Url::fromRoute('node.revision_revert_default_confirm', [
- 'node' => $entity->id(),
- 'node_revision' => $entity->getRevisionId(),
- ]);
- if ($this->request->query->has('destination')) {
- $query = $route_info->getOption('query');
- $query['destination'] = $this->request->query->get('destination');
- $route_info->setOption('query', $query);
- }
-
- $element['revert_to_default'] = [
- '#type' => 'link',
- '#title' => $this->t('Revert to default revision'),
- '#access' => $entity->access('revert revision', $this->currentUser),
- '#weight' => 101,
- '#attributes' => [
- 'class' => ['button', 'button--danger'],
- ],
- ];
- $element['revert_to_default']['#url'] = $route_info;
- }
-
- return $element;
- }
-
-}
diff --git a/modules/thunder_gqls/graphql/thunder_menu.base.graphqls b/modules/thunder_gqls/graphql/thunder_menu.base.graphqls
index 68b0bb4df..7e233832b 100644
--- a/modules/thunder_gqls/graphql/thunder_menu.base.graphqls
+++ b/modules/thunder_gqls/graphql/thunder_menu.base.graphqls
@@ -1,7 +1,6 @@
type Menu {
id: String!
name: String!
-
items: [MenuItem]
}
diff --git a/modules/thunder_gqls/graphql/thunder_search_api.base.graphqls b/modules/thunder_gqls/graphql/thunder_search_api.base.graphqls
new file mode 100644
index 000000000..29db90559
--- /dev/null
+++ b/modules/thunder_gqls/graphql/thunder_search_api.base.graphqls
@@ -0,0 +1,4 @@
+type SearchApiResult {
+ items: [Page!]
+ total: Int!
+}
diff --git a/modules/thunder_gqls/graphql/thunder_search_api.extension.graphqls b/modules/thunder_gqls/graphql/thunder_search_api.extension.graphqls
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/thunder_gqls/src/GraphQL/Buffers/SearchApiResultBuffer.php b/modules/thunder_gqls/src/GraphQL/Buffers/SearchApiResultBuffer.php
new file mode 100644
index 000000000..354dfa53f
--- /dev/null
+++ b/modules/thunder_gqls/src/GraphQL/Buffers/SearchApiResultBuffer.php
@@ -0,0 +1,101 @@
+entityTypeManager = $entityTypeManager;
+ }
+
+ /**
+ * Add an item to the buffer.
+ *
+ * @param string|int|null $index
+ * The entity type of the given entity ids.
+ * @param array|int $id
+ * The entity id(s) to load.
+ *
+ * @return \Closure
+ * The callback to invoke to load the result for this buffer item.
+ */
+ public function add($index, $id) {
+ $item = new \ArrayObject([
+ 'index' => $index,
+ 'id' => $id,
+ ]);
+
+ return $this->createBufferResolver($item);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getBufferId($item) {
+ return $item['index'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function resolveBufferArray(array $buffer) {
+ $index = reset($buffer)['index'];
+ $ids = array_map(function (\ArrayObject $item) {
+ return (array) $item['id'];
+ }, $buffer);
+
+ $ids = call_user_func_array('array_merge', $ids);
+ $ids = array_values(array_unique($ids));
+
+ // Load the buffered entities.
+ /** @var \Drupal\search_api\IndexInterface $index */
+ $index = $this->entityTypeManager
+ ->getStorage('search_api_index')
+ ->load($index);
+
+ $resultSet = $index->loadItemsMultiple($ids);
+ $entities = [];
+
+ foreach ($resultSet as $key => $resultItem) {
+ if ($resultItem instanceof EntityAdapter) {
+ $entities[$key] = $resultItem->getEntity();
+ }
+ }
+
+ return array_map(function ($item) use ($entities) {
+ if (is_array($item['id'])) {
+ return array_reduce($item['id'], static function ($carry, $current) use ($entities) {
+ if (!empty($entities[$current])) {
+ $carry[] = $entities[$current];
+ return $carry;
+ }
+
+ return $carry;
+ }, []);
+ }
+
+ return $entities[$item['id']] ?? NULL;
+ }, $buffer);
+ }
+
+}
diff --git a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/EntitiesWithTerm.php b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/EntitiesWithTerm.php
index 8788b2539..9ac9953ae 100644
--- a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/EntitiesWithTerm.php
+++ b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/EntitiesWithTerm.php
@@ -4,7 +4,6 @@
use Drupal\graphql\GraphQL\Execution\FieldContext;
use Drupal\taxonomy\TermInterface;
-use Drupal\thunder_gqls\Wrappers\EntityListResponse;
use Drupal\thunder_gqls\Wrappers\EntityListResponseInterface;
/**
@@ -119,7 +118,7 @@ public function resolve(TermInterface $term, string $type, array $bundles, strin
$cacheContext
);
- return new EntityListResponse($query);
+ return $this->entityListResponse($query);
}
/**
diff --git a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntityList.php b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntityList.php
index 3afcc2965..43ea0aa03 100644
--- a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntityList.php
+++ b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntityList.php
@@ -3,7 +3,6 @@
namespace Drupal\thunder_gqls\Plugin\GraphQL\DataProducer;
use Drupal\graphql\GraphQL\Execution\FieldContext;
-use Drupal\thunder_gqls\Wrappers\EntityListResponse;
use Drupal\thunder_gqls\Wrappers\EntityListResponseInterface;
/**
@@ -97,7 +96,7 @@ protected function resolve(string $type, array $bundles, int $offset, int $limit
$cacheContext
);
- return new EntityListResponse($query);
+ return $this->entityListResponse($query);
}
}
diff --git a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntityListProducerBase.php b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntityListProducerBase.php
index 610700ab2..c2ad6bbe8 100644
--- a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntityListProducerBase.php
+++ b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntityListProducerBase.php
@@ -2,12 +2,13 @@
namespace Drupal\thunder_gqls\Plugin\GraphQL\DataProducer;
-use Drupal\Core\Entity\EntityTypeManager;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\graphql\GraphQL\Execution\FieldContext;
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
+use Drupal\thunder_gqls\Wrappers\EntityListResponse;
use GraphQL\Error\UserError;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -21,32 +22,43 @@ abstract class ThunderEntityListProducerBase extends DataProducerPluginBase impl
/**
* The entity type manager service.
*
- * @var \Drupal\Core\Entity\EntityTypeManager
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
- protected $entityTypeManager;
+ protected EntityTypeManagerInterface $entityTypeManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
- protected $currentUser;
+ protected AccountInterface $currentUser;
+
+ /**
+ * The response wrapper service.
+ *
+ * @var \Drupal\thunder_gqls\Wrappers\EntityListResponse
+ */
+ protected EntityListResponse $responseWrapper;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
- return new static(
+ $instance = new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('current_user')
);
+
+ $instance->setResponseWrapper($container->get('thunder_gqls.entity_list_response_wrapper'));
+
+ return $instance;
}
/**
- * EntityLoad constructor.
+ * ThunderEntityListProducerBase constructor.
*
* @param array $configuration
* The plugin configuration array.
@@ -54,21 +66,51 @@ public static function create(ContainerInterface $container, array $configuratio
* The plugin id.
* @param array $pluginDefinition
* The plugin definition array.
- * @param \Drupal\Core\Entity\EntityTypeManager $entityTypeManager
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager service.
- * @param \Drupal\Core\Session\AccountInterface $current_user
+ * @param \Drupal\Core\Session\AccountInterface $currentUser
* The current user.
*/
public function __construct(
array $configuration,
string $pluginId,
array $pluginDefinition,
- EntityTypeManager $entityTypeManager,
- AccountInterface $current_user,
+ EntityTypeManagerInterface $entityTypeManager,
+ AccountInterface $currentUser,
) {
parent::__construct($configuration, $pluginId, $pluginDefinition);
+ $this->setEntityTypeManager($entityTypeManager);
+ $this->setCurrentUser($currentUser);
+ }
+
+ /**
+ * Set the entity type manager service.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+ * The entity type manager service.
+ */
+ public function setEntityTypeManager(EntityTypeManagerInterface $entityTypeManager): void {
$this->entityTypeManager = $entityTypeManager;
- $this->currentUser = $current_user;
+ }
+
+ /**
+ * Set the current user.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $currentUser
+ * The current user.
+ */
+ public function setCurrentUser(AccountInterface $currentUser): void {
+ $this->currentUser = $currentUser;
+ }
+
+ /**
+ * Set the response wrapper service.
+ *
+ * @param \Drupal\thunder_gqls\Wrappers\EntityListResponse $responseWrapper
+ * The response wrapper service.
+ */
+ public function setResponseWrapper(EntityListResponse $responseWrapper): void {
+ $this->responseWrapper = $responseWrapper;
}
/**
@@ -163,10 +205,7 @@ protected function query(
$query->range($offset, $limit);
$storage = $this->entityTypeManager->getStorage($type);
- $entityType = $storage->getEntityType();
-
- $cacheContext->addCacheTags($entityType->getListCacheTags());
- $cacheContext->addCacheContexts($entityType->getListCacheContexts());
+ $cacheContext->addCacheableDependency($storage->getEntityType());
return $query;
}
@@ -202,4 +241,17 @@ protected function createPublishedCondition(string $type, array $conditions) {
];
}
+ /**
+ * The entity list response.
+ *
+ * @param \Drupal\Core\Entity\Query\QueryInterface $query
+ * The entity query.
+ *
+ * @return \Drupal\thunder_gqls\Wrappers\EntityListResponse
+ * The entity list response.
+ */
+ protected function entityListResponse(QueryInterface $query): EntityListResponse {
+ return $this->responseWrapper->setQuery($query);
+ }
+
}
diff --git a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderSearchApi.php b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderSearchApi.php
new file mode 100644
index 000000000..d26ecc9c6
--- /dev/null
+++ b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderSearchApi.php
@@ -0,0 +1,122 @@
+ 'status',
+ 'value' => TRUE,
+ 'operator' => '=',
+ ],
+ [
+ 'field' => 'search_api_language',
+ 'value' => $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(),
+ 'operator' => '=',
+ ],
+ ];
+
+ // Add default sorts.
+ $sortBy = $sortBy ?: [
+ [
+ 'field' => 'search_api_relevance',
+ 'direction' => QueryInterface::SORT_DESC,
+ ],
+ ];
+
+ $query = $this->buildBaseQuery(
+ $limit,
+ $offset,
+ $index,
+ $sortBy,
+ $conditions,
+ $search,
+ $cacheContext
+ );
+
+ return $this->searchApiResponse($query);
+ }
+
+}
diff --git a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderSearchApiProducerBase.php b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderSearchApiProducerBase.php
new file mode 100644
index 000000000..e568b6971
--- /dev/null
+++ b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderSearchApiProducerBase.php
@@ -0,0 +1,181 @@
+setEntityTypeManager($container->get('entity_type.manager'));
+ $instance->setLanguageManager($container->get('language_manager'));
+ $instance->setResponseWrapper($container->get('thunder_gqls.search_api_response_wrapper'));
+
+ return $instance;
+ }
+
+ /**
+ * Set the entity type manager service.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+ * The entity type manager service.
+ */
+ public function setEntityTypeManager(EntityTypeManagerInterface $entityTypeManager): void {
+ $this->entityTypeManager = $entityTypeManager;
+ }
+
+ /**
+ * Set the language manager service.
+ *
+ * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager
+ * The language manager service.
+ */
+ public function setLanguageManager(LanguageManagerInterface $languageManager): void {
+ $this->languageManager = $languageManager;
+ }
+
+ /**
+ * Set the response wrapper service.
+ *
+ * @param \Drupal\thunder_gqls\Wrappers\SearchApiResponse $responseWrapper
+ * The response wrapper service.
+ */
+ public function setResponseWrapper(SearchApiResponse $responseWrapper): void {
+ $this->responseWrapper = $responseWrapper;
+ }
+
+ /**
+ * Build base search api query.
+ *
+ * @param int $limit
+ * Limit of the query.
+ * @param int $offset
+ * Offset of the query.
+ * @param string $index
+ * Id of the search api index.
+ * @param array|null $sortBy
+ * List of sorts.
+ * @param array|null $conditions
+ * List of conditions to filter the result.
+ * @param string|null $search
+ * Query Search.
+ * @param \Drupal\graphql\GraphQL\Execution\FieldContext $cacheContext
+ * The caching context related to the current field.
+ *
+ * @return \Drupal\search_api\Query\QueryInterface|null
+ * The query interface.
+ *
+ * @throws \Drupal\search_api\SearchApiException
+ */
+ protected function buildBaseQuery(
+ int $limit,
+ int $offset,
+ string $index,
+ ?array $sortBy,
+ ?array $conditions,
+ ?string $search,
+ FieldContext $cacheContext,
+ ): ?QueryInterface {
+
+ // Make sure offset is zero or positive.
+ $offset = max($offset, 0);
+
+ // Make sure limit is positive and cap the max items.
+ if ($limit <= 0) {
+ $limit = 10;
+ }
+ if ($limit > static::MAX_ITEMS) {
+ throw new UserError(
+ sprintf('Exceeded maximum query limit: %s.', static::MAX_ITEMS)
+ );
+ }
+
+ $searchIndex = Index::load($index);
+ if (!$searchIndex) {
+ return NULL;
+ }
+
+ $query = $searchIndex->query();
+
+ foreach ($conditions as $condition) {
+ $query->addCondition($condition['field'], $condition['value'], $condition['operator']);
+ }
+
+ foreach ($sortBy as $sort) {
+ $direction = $sort['direction'] ?? QueryInterface::SORT_ASC;
+ $query->sort($sort['field'], $direction);
+ }
+
+ if (!empty($search)) {
+ $query->keys($search);
+ }
+
+ $query->range($offset, $limit);
+ $cacheContext->addCacheableDependency($searchIndex);
+
+ return $query;
+ }
+
+ /**
+ * The search api response.
+ *
+ * @param \Drupal\search_api\Query\QueryInterface $query
+ * The search api query.
+ *
+ * @return \Drupal\thunder_gqls\Wrappers\SearchApiResponse
+ * The search api response.
+ */
+ protected function searchApiResponse(QueryInterface $query): SearchApiResponse {
+ return $this->responseWrapper->setQuery($query);
+ }
+
+}
diff --git a/modules/thunder_gqls/src/Plugin/GraphQL/SchemaExtension/ThunderSearchApiSchemaExtension.php b/modules/thunder_gqls/src/Plugin/GraphQL/SchemaExtension/ThunderSearchApiSchemaExtension.php
new file mode 100644
index 000000000..dc9c2fbeb
--- /dev/null
+++ b/modules/thunder_gqls/src/Plugin/GraphQL/SchemaExtension/ThunderSearchApiSchemaExtension.php
@@ -0,0 +1,39 @@
+addFieldResolverIfNotExists('SearchApiResult', 'total',
+ $this->builder->callback(function (SearchApiResponse $result) {
+ return $result->total();
+ })
+ );
+
+ $this->addFieldResolverIfNotExists('SearchApiResult', 'items',
+ $this->builder->callback(function (SearchApiResponse $result) {
+ return $result->items();
+ })
+ );
+ }
+
+}
diff --git a/modules/thunder_gqls/src/Traits/ResolverHelperTrait.php b/modules/thunder_gqls/src/Traits/ResolverHelperTrait.php
index d09fbca9e..86d66c1e9 100644
--- a/modules/thunder_gqls/src/Traits/ResolverHelperTrait.php
+++ b/modules/thunder_gqls/src/Traits/ResolverHelperTrait.php
@@ -4,6 +4,7 @@
use Drupal\graphql\GraphQL\Resolver\ResolverInterface;
use Drupal\graphql\GraphQL\ResolverBuilder;
+use Drupal\graphql\GraphQL\ResolverRegistryInterface;
/**
* Helper functions for field resolvers.
@@ -15,14 +16,14 @@ trait ResolverHelperTrait {
*
* @var \Drupal\graphql\GraphQL\ResolverBuilder
*/
- protected $builder;
+ protected ResolverBuilder $builder;
/**
* ResolverRegistryInterface.
*
* @var \Drupal\graphql\GraphQL\ResolverRegistryInterface
*/
- protected $registry;
+ protected ResolverRegistryInterface $registry;
/**
* Add field resolver to registry, if it does not already exist.
diff --git a/modules/thunder_gqls/src/Wrappers/EntityListResponse.php b/modules/thunder_gqls/src/Wrappers/EntityListResponse.php
index d2502ac23..4b25e5044 100644
--- a/modules/thunder_gqls/src/Wrappers/EntityListResponse.php
+++ b/modules/thunder_gqls/src/Wrappers/EntityListResponse.php
@@ -2,29 +2,66 @@
namespace Drupal\thunder_gqls\Wrappers;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\graphql\GraphQL\Buffers\EntityBuffer;
use GraphQL\Deferred;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* The thunder entity list response class.
*/
-class EntityListResponse implements EntityListResponseInterface {
+class EntityListResponse implements EntityListResponseInterface, ContainerInjectionInterface {
/**
* The query interface.
*
* @var \Drupal\Core\Entity\Query\QueryInterface
*/
- protected $query;
+ protected QueryInterface $query;
+
+ /**
+ * The entity buffer.
+ *
+ * @var \Drupal\graphql\GraphQL\Buffers\EntityBuffer
+ */
+ protected EntityBuffer $buffer;
/**
* EntityListResponse constructor.
*
+ * @param \Drupal\Core\Entity\Query\QueryInterface|\Drupal\graphql\GraphQL\Buffers\EntityBuffer $buffer
+ * The query or buffer parameter.
+ */
+ public function __construct(QueryInterface|EntityBuffer $buffer) {
+ if ($buffer instanceof QueryInterface) {
+ // phpcs:ignore
+ @trigger_error('Calling the constructor with a query parameter is deprecated in Thunder 7.3.3 and will be removed in Thunder 8.0. Use service injection and ::setQuery() instead.', E_USER_DEPRECATED);
+ $this->setQuery($buffer);
+ // phpcs:ignore
+ $buffer = \Drupal::service('graphql.buffer.entity');
+ }
+ $this->buffer = $buffer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public static function create(ContainerInterface $container): self {
+ return new static(
+ $container->get('graphql.buffer.entity'),
+ );
+ }
+
+ /**
+ * Set query.
+ *
* @param \Drupal\Core\Entity\Query\QueryInterface $query
- * The query interface.
+ * The query.
*/
- public function __construct(QueryInterface $query) {
+ public function setQuery(QueryInterface $query): EntityListResponse {
$this->query = $query;
+ return $this;
}
/**
@@ -36,7 +73,7 @@ public function __construct(QueryInterface $query) {
public function total(): int {
$query = clone $this->query;
$query->range(NULL, NULL)->count();
- return intval($query->execute());
+ return (int) $query->execute();
}
/**
@@ -51,8 +88,7 @@ public function items() {
return [];
}
- $buffer = \Drupal::service('graphql.buffer.entity');
- $callback = $buffer->add($this->query->getEntityTypeId(), array_values($result));
+ $callback = $this->buffer->add($this->query->getEntityTypeId(), array_values($result));
return new Deferred(fn() => $callback());
}
diff --git a/modules/thunder_gqls/src/Wrappers/SearchApiResponse.php b/modules/thunder_gqls/src/Wrappers/SearchApiResponse.php
new file mode 100644
index 000000000..612c56d03
--- /dev/null
+++ b/modules/thunder_gqls/src/Wrappers/SearchApiResponse.php
@@ -0,0 +1,273 @@
+get('thunder_gqls.buffer.search_api_result'),
+ $container->get('entity_field.manager'),
+ );
+ }
+
+ /**
+ * Set query.
+ *
+ * @param \Drupal\search_api\Query\QueryInterface $query
+ * The query.
+ */
+ public function setQuery(QueryInterface $query): SearchApiResponse {
+ $this->query = $query;
+ return $this;
+ }
+
+ /**
+ * Set Facet mapping.
+ *
+ * @param array $facetMapping
+ * The facet mapping.
+ */
+ public function setFacetMapping(array $facetMapping): SearchApiResponse {
+ $this->facetMapping = $facetMapping;
+ return $this;
+ }
+
+ /**
+ * Set bundle.
+ *
+ * @param string $bundle
+ * The bundle.
+ */
+ public function setBundle(string $bundle): SearchApiResponse {
+ $this->bundle = $bundle;
+ return $this;
+ }
+
+ /**
+ * Set facets.
+ *
+ * @param array $facets
+ * The facets.
+ */
+ public function setFacets(array $facets): SearchApiResponse {
+ $this->facets = $facets;
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @throws \Drupal\search_api\SearchApiException
+ */
+ public function facets(): array {
+ if (!$this->facets || !$this->facetMapping) {
+ return [];
+ }
+
+ if (!$this->result) {
+ $this->result = $this->query->execute();
+ }
+
+ $facets = [];
+
+ $facetData = $this->result->getExtraData('search_api_facets');
+ foreach ($facetData as $facetFieldId => $facetResults) {
+ $facets[] = [
+ 'key' => $this->facetMapping[$facetFieldId],
+ 'values' => $this->processFacetResults($this->facets[$facetFieldId], $facetResults),
+ ];
+ }
+
+ return $facets;
+ }
+
+ /**
+ * Get search result items.
+ *
+ * @return array|\GraphQL\Deferred
+ * The search result items.
+ *
+ * @throws \Drupal\search_api\SearchApiException
+ */
+ public function items(): array|Deferred {
+ if (!$this->result) {
+ $this->result = $this->query->execute();
+ }
+
+ $ids = array_map(static function ($item) {
+ return $item->getId();
+ }, $this->result->getResultItems());
+
+ $ids = array_unique($ids);
+
+ if (empty($ids)) {
+ return [];
+ }
+
+ $callback = $this->buffer->add(
+ $this->query->getIndex()->id(),
+ array_values($ids)
+ );
+
+ return new Deferred(function () use ($callback) {
+ return $callback();
+ });
+ }
+
+ /**
+ * Returns the total results.
+ *
+ * @return int
+ * The total results.
+ *
+ * @throws \Drupal\search_api\SearchApiException
+ */
+ public function total(): int {
+ $query = clone $this->query;
+ $query->range(0, NULL);
+ $result = $query->execute();
+
+ return (int) $result->getResultCount();
+ }
+
+ /**
+ * Handles processing of facet values.
+ *
+ * @param \Drupal\facets\Entity\Facet $facet
+ * The facet to process.
+ * @param array $facetResults
+ * The facet results.
+ *
+ * @return array
+ * The processed facet results.
+ */
+ private function processFacetResults(
+ Facet $facet,
+ array $facetResults,
+ ): array {
+ // First process facet results which contain filter like filter=""9"".
+ // @see Drupal\facets\Plugin\facets\query_type\SearchApiString#build().
+ foreach ($facetResults as $i => $facetResult) {
+ $facetResult['filter'] = $facetResult['filter'] ?? '';
+
+ if ($facetResult['filter'][0] === '"') {
+ $facetResult['filter'] = substr($facetResult['filter'], 1);
+ }
+ if ($facetResult['filter'][strlen($facetResult['filter']) - 1] === '"') {
+ $facetResult['filter'] = substr($facetResult['filter'], 0, -1);
+ }
+
+ $facetResults[$i] = $facetResult;
+ }
+
+ return $this->processFacetResultsFromFieldConfig($facet, $facetResults);
+ }
+
+ /**
+ * Populates label for facet values from allowed options field config.
+ *
+ * @param \Drupal\facets\Entity\Facet $facet
+ * The facet.
+ * @param array $facetResults
+ * The facet results.
+ *
+ * @return array
+ * The processed facet results.
+ */
+ private function processFacetResultsFromFieldConfig(
+ Facet $facet,
+ array $facetResults,
+ ): array {
+ if (!$this->bundle) {
+ return $facetResults;
+ }
+
+ $fieldName = $facet->getFieldIdentifier();
+ $fieldConfig = $this->entityFieldManager->getFieldDefinitions('node', $this->bundle);
+
+ if (isset($fieldConfig[$fieldName])) {
+ $allowedValues = options_allowed_values($fieldConfig[$fieldName]->getFieldStorageDefinition());
+
+ // Use order of allowedValues.
+ foreach ($facetResults as $key => $facetResult) {
+ $facetResults[$key]['label'] = $allowedValues[$facetResult['filter']] ?? $facetResult['filter'];
+ $facetResults[$key]['value'] = $facetResult['filter'];
+ }
+
+ $allowedValueKeys = array_keys($allowedValues);
+ usort($facetResults, function ($a, $b) use ($allowedValueKeys) {
+ $indexA = array_search($a['filter'], $allowedValueKeys, TRUE);
+ $indexB = array_search($b['filter'], $allowedValueKeys, TRUE);
+
+ return $indexA < $indexB ? -1 : 1;
+ });
+ }
+
+ return $facetResults;
+ }
+
+}
diff --git a/modules/thunder_gqls/src/Wrappers/SearchApiResponseInterface.php b/modules/thunder_gqls/src/Wrappers/SearchApiResponseInterface.php
new file mode 100644
index 000000000..82d2158aa
--- /dev/null
+++ b/modules/thunder_gqls/src/Wrappers/SearchApiResponseInterface.php
@@ -0,0 +1,18 @@
+logWithRole('administrator');
+
+ $this->drupalGet('admin/config/search/search-api/index/content');
+ $this->submitForm([], 'Index now');
+ $this->assertSession()->statusCodeEquals(200);
+ $this->checkForMetaRefresh();
+
+ $options = [
+ 'index' => 'content',
+ 'search' => 'the',
+ 'limit' => 10,
+ 'offset' => 0,
+ ];
+
+ $result = $this->executeDataProducer('thunder_search_api', $options);
+ $this->assertEquals(3, $result->total());
+
+ $items = $result->items();
+ $items->runQueue();
+ $this->assertEquals('Burda Launches Open-Source CMS Thunder', $items->result[0]->getTitle());
+
+ // Change sort order.
+ $options['sortBy'] = [
+ [
+ 'field' => 'search_api_relevance',
+ 'direction' => QueryInterface::SORT_ASC,
+ ],
+ ];
+
+ $this->container->get('kernel')->rebuildContainer();
+ $result = $this->executeDataProducer('thunder_search_api', $options);
+
+ $items = $result->items();
+ $items->runQueue();
+ $this->assertEquals('Legal notice', $items->result[0]->getTitle());
+
+ // Get articles only.
+ $options['conditions'] = [
+ [
+ 'field' => 'type',
+ 'value' => 'article',
+ 'operator' => '=',
+ ],
+ ];
+
+ $this->container->get('kernel')->rebuildContainer();
+ $result = $this->executeDataProducer('thunder_search_api', $options);
+
+ $items = $result->items();
+ $items->runQueue();
+ $this->assertEquals('Come to DrupalCon New Orleans', $items->result[0]->getTitle());
+
+ }
+
+}
diff --git a/modules/thunder_gqls/tests/src/Kernel/DataProducer/ThunderRedirectTest.php b/modules/thunder_gqls/tests/src/Kernel/DataProducer/ThunderRedirectTest.php
index cd72e851e..422909e1e 100644
--- a/modules/thunder_gqls/tests/src/Kernel/DataProducer/ThunderRedirectTest.php
+++ b/modules/thunder_gqls/tests/src/Kernel/DataProducer/ThunderRedirectTest.php
@@ -43,6 +43,7 @@ class ThunderRedirectTest extends GraphQLTestBase {
*/
public function setUp(): void {
parent::setUp();
+ $this->installEntitySchema('path_alias');
$this->installConfig(['redirect']);
$this->installEntitySchema('redirect');
diff --git a/modules/thunder_gqls/thunder_gqls.services.yml b/modules/thunder_gqls/thunder_gqls.services.yml
new file mode 100644
index 000000000..863299712
--- /dev/null
+++ b/modules/thunder_gqls/thunder_gqls.services.yml
@@ -0,0 +1,12 @@
+services:
+ _defaults:
+ autowire: true
+ thunder_gqls.buffer.search_api_result:
+ class: Drupal\thunder_gqls\GraphQL\Buffers\SearchApiResultBuffer
+ Drupal\thunder_gqls\GraphQL\Buffers\SearchApiResultBuffer: '@thunder_gqls.buffer.search_api_result'
+ thunder_gqls.search_api_response_wrapper:
+ class: Drupal\thunder_gqls\Wrappers\SearchApiResponse
+ thunder_gqls.entity_list_response_wrapper:
+ autowire: false
+ class: Drupal\thunder_gqls\Wrappers\EntityListResponse
+ arguments: [ '@graphql.buffer.entity' ]
diff --git a/modules/thunder_media/tests/src/Functional/FilenameTransliterationTest.php b/modules/thunder_media/tests/src/Functional/FilenameTransliterationTest.php
index 4e7e0b5e6..d5e6845c5 100644
--- a/modules/thunder_media/tests/src/Functional/FilenameTransliterationTest.php
+++ b/modules/thunder_media/tests/src/Functional/FilenameTransliterationTest.php
@@ -2,7 +2,7 @@
namespace Drupal\Tests\thunder_media\Functional;
-use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\File\FileExists;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\file\Entity\File;
use Drupal\Tests\thunder\Functional\ThunderTestBase;
@@ -55,7 +55,7 @@ public function testFileTransliteration(): void {
// Upload with replace to guarantee there's something there.
$edit = [
- 'file_test_replace' => FileSystemInterface::EXISTS_RENAME,
+ 'file_test_replace' => FileExists::Rename->name,
'files[file_test_upload]' => \Drupal::service('file_system')->realpath('public://foo°.png'),
];
$this->drupalGet('file-test/upload');
diff --git a/modules/thunder_workflow/src/ThunderWorkflowFormHelper.php b/modules/thunder_workflow/src/ThunderWorkflowFormHelper.php
index 3be126b1e..091662a10 100644
--- a/modules/thunder_workflow/src/ThunderWorkflowFormHelper.php
+++ b/modules/thunder_workflow/src/ThunderWorkflowFormHelper.php
@@ -204,6 +204,11 @@ public function moveStateToActions(NodeInterface $entity, array $form): array {
$form['actions']['moderation_state'] = $form['moderation_state'];
unset($form['moderation_state']);
+ // Promote moderation_state in gin theme to not end up in
+ // dropdown button.
+ $form['actions']['moderation_state']['#gin_action_item'] = TRUE;
+ $form['actions']['moderation_state']['widget'][0]['#attributes']['form'] = $form['#id'];
+
return $form;
}
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index ea4730437..576ad8cca 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1,60 +1,5 @@
parameters:
ignoreErrors:
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_article/src/Form/NodeRevisionRevertDefaultForm.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_article/src/Form/ThunderNodeFormHelper.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_article/src/Plugin/Derivative/DynamicLocalTasks.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/EntityLinks.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/FocalPoint.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/MenuLinksActiveTrail.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/MetaTags.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntityListProducerBase.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderEntitySubRequestBase.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderImage.php
-
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderRedirect.php
-
-
message: "#^Access to an undefined property Drupal\\\\Core\\\\Entity\\\\ContentEntityInterface\\:\\:\\$field_teaser_media\\.$#"
count: 1
@@ -75,11 +20,6 @@ parameters:
count: 4
path: modules/thunder_gqls/tests/src/Kernel/DataProducer/EntityLinksTest.php
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: modules/thunder_taxonomy/src/ThunderTaxonomyPermissions.php
-
-
message: "#^Access to an undefined property Drupal\\\\Core\\\\Entity\\\\EntityInterface\\:\\:\\$status\\.$#"
count: 1
@@ -230,11 +170,6 @@ parameters:
count: 1
path: tests/src/TestSuites/ThunderTestSuite.php
- -
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
- count: 1
- path: tests/src/TestSuites/ThunderTestSuite.php
-
-
message: "#^Call to method id\\(\\) on an unknown class Drupal\\\\entity_browser\\\\Entity\\\\EntityBrowser\\.$#"
count: 1
diff --git a/phpstan.neon b/phpstan.neon
index 654ea7304..a604bc45c 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,8 +1,11 @@
parameters:
customRulesetUsed: true
- checkGenericClassInNonGenericObjectType: false
- checkMissingIterableValueType: false
reportUnmatchedIgnoredErrors: true
level: 6
+ ignoreErrors:
+ # new static() is a best practice in Drupal, so we cannot fix that.
+ - "#^Unsafe usage of new static#"
+ - identifier: missingType.generics
+ - identifier: missingType.iterableValue
includes:
- ./phpstan-baseline.neon
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 1bf1a0520..f63099d0d 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,17 +1,27 @@
-
-
+
+
+
+ ./includes
+ ./lib
+ ./modules
+ ../modules
+ ../sites
+
+
+ ./modules/*/src/Tests
+ ./modules/*/tests
+ ../modules/*/src/Tests
+ ../modules/*/tests
+ ../modules/*/*/src/Tests
+ ../modules/*/*/tests
+
+
@@ -31,11 +41,11 @@
-
+
-
+
-
+
@@ -59,24 +69,4 @@
-
-
- ./includes
- ./lib
-
- ./modules
-
- ./modules/*/src/Tests
- ./modules/*/tests
-
- ../modules
-
- ../modules/*/src/Tests
- ../modules/*/tests
- ../modules/*/*/src/Tests
- ../modules/*/*/tests
-
- ../sites
-
-
diff --git a/tests/src/Functional/InstalledConfigurationTest.php b/tests/src/Functional/InstalledConfigurationTest.php
index 4b0d60c91..1fbeb35e8 100644
--- a/tests/src/Functional/InstalledConfigurationTest.php
+++ b/tests/src/Functional/InstalledConfigurationTest.php
@@ -278,7 +278,17 @@ class InstalledConfigurationTest extends ThunderTestBase {
],
'views.view.locked_content' => [
'display' => [
- 'default' => ['display_options' => ['sorts' => ['created' => ['expose' => ['field_identifier' => TRUE]]]]],
+ 'default' => [
+ 'display_options' => [
+ 'sorts' => ['created' => ['expose' => ['field_identifier' => TRUE]]],
+ 'pager' => ['options' => ['pagination_heading_level' => TRUE]],
+ ],
+ ],
+ ],
+ ],
+ 'views.view.redirect' => [
+ 'display' => [
+ 'default' => ['display_options' => ['pager' => ['options' => ['pagination_heading_level' => TRUE]]]],
],
],
];
diff --git a/tests/src/Functional/ThunderTestBase.php b/tests/src/Functional/ThunderTestBase.php
index 5b7f7546a..44e79c2fd 100644
--- a/tests/src/Functional/ThunderTestBase.php
+++ b/tests/src/Functional/ThunderTestBase.php
@@ -4,6 +4,7 @@
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\thunder\Traits\ThunderGinTestTrait;
use Drupal\Tests\thunder\Traits\ThunderTestTrait;
use Prophecy\PhpUnit\ProphecyTrait;
@@ -15,6 +16,7 @@ abstract class ThunderTestBase extends BrowserTestBase {
use ThunderTestTrait;
use StringTranslationTrait;
use ProphecyTrait;
+ use ThunderGinTestTrait;
/**
* {@inheritdoc}
diff --git a/tests/src/FunctionalJavascript/Integration/FocalPointTest.php b/tests/src/FunctionalJavascript/Integration/FocalPointTest.php
new file mode 100644
index 000000000..cfa49561d
--- /dev/null
+++ b/tests/src/FunctionalJavascript/Integration/FocalPointTest.php
@@ -0,0 +1,25 @@
+loadNodeByUuid('0bd5c257-2231-450f-b4c2-ab156af7b78d');
+ $this->drupalGet($node->toUrl('edit-form'));
+ $this->clickDrupalSelector('edit-field-teaser-media-selection-0-edit');
+ $this->clickDrupalSelector('edit-field-image-0-preview-preview-link');
+
+ $this->assertSession()->elementExists('css', '#focal-point-derivatives .focal-point-derivative-preview-image');
+ $this->assertSession()->elementExists('css', '.focal-point-original-image > #focal-point-preview-image');
+ }
+
+}
diff --git a/tests/src/FunctionalJavascript/MediaImageModifyTest.php b/tests/src/FunctionalJavascript/MediaImageModifyTest.php
index 5891b7aa8..701fc3e8e 100644
--- a/tests/src/FunctionalJavascript/MediaImageModifyTest.php
+++ b/tests/src/FunctionalJavascript/MediaImageModifyTest.php
@@ -88,7 +88,9 @@ public function testRemoveAdd(): void {
$this->clickAjaxButtonCssSelector('[name="field_paragraphs_0_collapse"]');
/** @var \Drupal\file\FileInterface $file */
$file = $image2->field_image->entity;
- $this->assertEquals([$file->getFilename()], $this->getSession()->evaluateScript('jQuery(\'[data-drupal-selector="edit-field-paragraphs-0-preview"] article.media--view-mode-paragraph-preview img\').attr(\'src\').split(\'?\')[0].split(\'/\').splice(-1)'), 'Image file should be identical to previously selected.');
+ // On installs that use Drupal 10.3 onwards, the image will be converted to
+ // a webp image.
+ $this->assertMatchesRegularExpression('/^' . preg_quote($file->getFilename()) . '(.webp)?$/', $this->getSession()->evaluateScript('jQuery(\'[data-drupal-selector="edit-field-paragraphs-0-preview"] article.media--view-mode-paragraph-preview img\').attr(\'src\').split(\'?\')[0].split(\'/\').splice(-1)')[0], 'Image file should be identical to previously selected.');
// Go to the media view and try deleting the image media.
$this->drupalGet('admin/content/media');
@@ -98,14 +100,16 @@ public function testRemoveAdd(): void {
/** @var \Drupal\file\FileInterface $file */
$file = $media->get($media->getSource()->getConfiguration()['source_field'])->entity;
$this->assertFileExists($file->getFileUri());
- $this->getSession()->getPage()->find('css', 'div.gin-sidebar')->clickLink('Delete');
+ $this->getSession()->getPage()->find('css', '[data-drupal-selector="edit-actions"] .gin-more-actions__trigger')->click();
+ $this->getSession()->getPage()->find('css', '[data-drupal-selector="edit-actions"]')->clickLink('Delete');
$this->assertSession()->assertWaitOnAjaxRequest();
$this->assertNotEmpty($this->assertSession()->waitForElementVisible('css', '#drupal-modal'));
$this->assertSession()->fieldNotExists('also_delete_file');
$this->assertSession()->pageTextContains('This action cannot be undone.The file attached to this media is owned by admin so will be retained.');
Role::load(static::$defaultUserRole)->grantPermission('delete any file')->save();
$this->getSession()->reload();
- $this->getSession()->getPage()->find('css', 'div.gin-sidebar')->clickLink('Delete');
+ $this->getSession()->getPage()->find('css', '[data-drupal-selector="edit-actions"] .gin-more-actions__trigger')->click();
+ $this->getSession()->getPage()->find('css', '[data-drupal-selector="edit-actions"]')->clickLink('Delete');
$this->assertSession()->assertWaitOnAjaxRequest();
$this->assertNotEmpty($this->assertSession()->waitForElementVisible('css', '#drupal-modal'));
$this->assertSession()->fieldExists('also_delete_file')->check();
diff --git a/tests/src/FunctionalJavascript/MetaInformationTest.php b/tests/src/FunctionalJavascript/MetaInformationTest.php
index 25a561a1b..43bb678ee 100644
--- a/tests/src/FunctionalJavascript/MetaInformationTest.php
+++ b/tests/src/FunctionalJavascript/MetaInformationTest.php
@@ -104,8 +104,8 @@ class MetaInformationTest extends ThunderJavascriptTestBase {
'[node:title]' => 'Test Note Title',
// For testing Media:1 is used for teaser.
- '[node:field_teaser_media:entity:field_image:facebook]' => 'LIKE:/files/styles/facebook/public/2016-05/thunder.jpg?',
- '[node:field_teaser_media:entity:field_image:facebook:mimetype]' => 'image/jpeg',
+ '[node:field_teaser_media:entity:field_image:facebook]' => 'LIKE:/files/styles/facebook/public/2016-05/thunder.jpg.webp?',
+ '[node:field_teaser_media:entity:field_image:facebook:mimetype]' => 'image/webp',
'[node:field_teaser_media:entity:field_image:facebook:height]' => '630',
'[node:field_teaser_media:entity:field_image:facebook:width]' => '1200',
];
@@ -204,6 +204,8 @@ protected function checkSavedConfiguration(string $configurationUrl, array $conf
* Test Meta Tag default configuration and custom configuration for article.
*
* @dataProvider providerContentTypes
+ *
+ * @group NoUpdate
*/
public function testArticleMetaTags(string $contentType): void {
$globalConfigs = $this->generateMetaTagConfiguration([static::$globalMetaTags]);
diff --git a/tests/src/Kernel/MetatagTest.php b/tests/src/Kernel/MetatagTest.php
index 565cfb81e..23b76d3bb 100644
--- a/tests/src/Kernel/MetatagTest.php
+++ b/tests/src/Kernel/MetatagTest.php
@@ -37,6 +37,7 @@ class MetatagTest extends KernelTestBase {
'views',
'image',
'file',
+ 'filter',
'focal_point',
'crop',
'media_expire',
@@ -113,14 +114,14 @@ public function testTagDefaultValues(): void {
$this->assertStringContainsString('/files/styles/facebook/public/image-test.png', $elements['og_image_0']['#attributes']['content']);
$this->assertEquals('630', $elements['og_image_height']['#attributes']['content']);
$this->assertEquals('1200', $elements['og_image_width']['#attributes']['content']);
- $this->assertEquals('image/png', $elements['og_image_type']['#attributes']['content']);
+ $this->assertEquals('image/webp', $elements['og_image_type']['#attributes']['content']);
$this->assertEquals('Test Site', $elements['og_site_name']['#attributes']['content']);
$this->assertEquals($title, $elements['og_title']['#attributes']['content']);
$this->assertNotEmpty($elements['og_updated_time']['#attributes']['content']);
$this->assertStringEndsWith('/node/1', $elements['og_url']['#attributes']['content']);
$this->assertEquals($description, $elements['twitter_cards_description']['#attributes']['content']);
- $this->assertStringContainsString('/files/styles/twitter/public/image-test.png', $elements['twitter_cards_image']['#attributes']['content']);
+ $this->assertStringContainsString('/files/styles/twitter/public/image-test.png.webp', $elements['twitter_cards_image']['#attributes']['content']);
$this->assertEquals('512', $elements['twitter_cards_image_height']['#attributes']['content']);
$this->assertEquals('1024', $elements['twitter_cards_image_width']['#attributes']['content']);
$this->assertEquals('summary_large_image', $elements['twitter_cards_type']['#attributes']['content']);
@@ -129,7 +130,7 @@ public function testTagDefaultValues(): void {
$this->assertEquals($title, $elements['schema_article_headline']['#attributes']['content']);
$this->assertEquals('Title', $elements['schema_article_name']['#attributes']['content']);
$this->assertEquals($description, $elements['schema_article_description']['#attributes']['content']);
- $this->assertStringContainsString('/files/styles/facebook/public/image-test.png', $elements['schema_article_image']['#attributes']['content']['url']);
+ $this->assertStringContainsString('/files/styles/facebook/public/image-test.png.webp', $elements['schema_article_image']['#attributes']['content']['url']);
$this->assertEquals('Test Site', $elements['schema_article_publisher']['#attributes']['content']['name']);
$this->assertEquals('Organization', $elements['schema_article_publisher']['#attributes']['content']['@type']);
}
diff --git a/tests/src/Traits/ThunderGinTestTrait.php b/tests/src/Traits/ThunderGinTestTrait.php
new file mode 100644
index 000000000..6672f993c
--- /dev/null
+++ b/tests/src/Traits/ThunderGinTestTrait.php
@@ -0,0 +1,104 @@
+assertSession();
+ $submit_button = $assert_session->buttonExists($submit);
+
+ // Check if button has a form attribute set.
+ if ($form_id = $submit_button->getAttribute('form')) {
+ $form = $assert_session->elementExists('xpath', "//form[@id='$form_id']");
+ $action = $form->getAttribute('action');
+ }
+ // Get the form.
+ elseif (isset($form_html_id)) {
+ $form = $assert_session->elementExists('xpath', "//form[@id='$form_html_id']");
+ $submit_button = $assert_session->buttonExists($submit, $form);
+ $action = $form->getAttribute('action');
+ }
+ else {
+ // Gin Form Test: Change check to include //form
+ // so we keep the search in scope of a form.
+ $submit_button = $assert_session->elementExists('xpath', "//input[@value='$submit']");
+ $form = $assert_session->elementExists('xpath', './ancestor::form', $submit_button);
+ $action = $form->getAttribute('action');
+ }
+
+ // Edit the form values.
+ foreach ($edit as $name => $value) {
+ // Fields / buttons in static area are not in form context.
+ $field = $assert_session->fieldExists($name);
+
+ // Provide support for the values '1' and '0' for checkboxes instead of
+ // TRUE and FALSE.
+ // @todo Get rid of supporting 1/0 by converting all tests cases using
+ // this to boolean values.
+ $field_type = $field->getAttribute('type');
+ if ($field_type === 'checkbox') {
+ $value = (bool) $value;
+ }
+ $field->setValue($value);
+ }
+
+ // Submit form.
+ $this->prepareRequest();
+ $submit_button->press();
+
+ // Ensure that any changes to variables in the other thread are picked up.
+ $this->refreshVariables();
+
+ // Check if there are any meta refresh redirects (like Batch API pages).
+ if ($this->checkForMetaRefresh()) {
+ // We are finished with all meta refresh redirects, so reset the counter.
+ $this->metaRefreshCount = 0;
+ }
+
+ // Log only for WebDriverTestBase tests because for tests using
+ // DrupalTestBrowser we log with ::getResponseLogHandler.
+ if ($this->htmlOutputEnabled && !$this->isTestUsingGuzzleClient()) {
+ $out = $this->getSession()->getPage()->getContent();
+ $html_output = 'POST request to: ' . $action .
+ '
Ending URL: ' . $this->getSession()->getCurrentUrl();
+ $html_output .= '
' . $out;
+ $html_output .= $this->getHtmlOutputHeaders();
+ $this->htmlOutput($html_output);
+ }
+
+ }
+
+}
diff --git a/thunder.info.yml b/thunder.info.yml
index 0ba6b3747..0a166329a 100644
--- a/thunder.info.yml
+++ b/thunder.info.yml
@@ -2,8 +2,8 @@ name: Thunder
type: profile
description: 'The Drupal based CMS for professional publishing.'
project: thunder
-core_version_requirement: ~10.2.4
-version: '7.2.2'
+core_version_requirement: ~10.3.0
+version: '7.3.3'
distribution:
name: Thunder
diff --git a/thunder.install b/thunder.install
index 0fb23417b..c31c7c6a1 100644
--- a/thunder.install
+++ b/thunder.install
@@ -44,3 +44,16 @@ function _thunder_mark_update_checklist(string $update_id, bool $successful, Upd
function thunder_update_last_removed(): int {
return 8327;
}
+
+/**
+ * Add publish state and unpublish state fields to newly supported entity types.
+ *
+ * In Drupal 10.3 taxonomy terms can be moderated.
+ */
+function thunder_update_8328(): \Stringable|string {
+ if (\Drupal::moduleHandler()->moduleExists('scheduler_content_moderation_integration')) {
+ $output = _scheduler_content_moderation_integration_add_fields();
+ return $output ? implode('', $output) : t('No update required.');
+ }
+ return t('No update required.');
+}
diff --git a/thunder.post_update.php b/thunder.post_update.php
index d8e8af5f7..e34b956ee 100644
--- a/thunder.post_update.php
+++ b/thunder.post_update.php
@@ -21,7 +21,6 @@ function thunder_post_update_0001_upgrade_to_thunder7(array &$sandbox): string {
'media_library_media_modify',
'gin_toolbar',
'jquery_ui',
- 'jquery_ui_draggable',
'ckeditor5',
]);
@@ -115,3 +114,14 @@ function thunder_post_update_0002_enable_paragraphs_split(array &$sandbox): stri
// Output logged messages to related channel of update execution.
return $updater->logger()->output();
}
+
+/**
+ * Enable sticky action buttons for the Gin theme.
+ */
+function thunder_post_update_0003_enable_sticky_action_buttons(array &$sandbox): string {
+ \Drupal::configFactory()->getEditable('gin.settings')
+ ->set('sticky_action_buttons', TRUE)
+ ->save();
+
+ return t('Sticky action buttons enabled.');
+}