diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 00382b62..64946986 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -39,7 +39,7 @@ jobs: - name: Check that composer file is normalized run: | - docker compose run --rm --user "$(id -u):$(id -g)" php composer install + docker compose run --rm --user "$(id -u):$(id -g)" php ./scripts/ci-composer install docker compose run --rm --user "$(id -u):$(id -g)" php composer normalize --dry-run test-composer-files: @@ -64,7 +64,7 @@ jobs: - name: Check that dependencies resolve. run: | rm -fr vendor/ - docker compose run --rm --user "$(id -u):$(id -g)" php composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction + docker compose run --rm --user "$(id -u):$(id -g)" php ./scripts/ci-composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction php-coding-standards: name: PHP coding standards @@ -78,7 +78,7 @@ jobs: - name: Install Dependencies run: | - docker compose run --rm --user "$(id -u):$(id -g)" php composer install + docker compose run --rm --user "$(id -u):$(id -g)" php ./scripts/ci-composer install - name: PHPCS run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ddc05ab..7b180f58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,20 @@ before starting to add changes. Use example [placed in the end of the page](#exa ## [Unreleased] +- Moved digital signature configuration (which attachment to sign and the + signature validation text position) from the *OS2Forms Attachment* element + onto the *Digital Signature* webform handler. Includes an automatic + migration update hook for existing webforms. +- Adding children information to Attachment SBSYS +- os2forms_webform_maps translation page fix + +## [6.0.0] 2026-09-30 + +- [PR-346](https://github.com/OS2Forms/os2forms/pull/346) + Drupal 11 compatibility. +- [#355](https://github.com/OS2Forms/os2forms/issues/355) + Children select elements warning fix. + ## [5.1.0] 2026-06-03 - [PR-326](https://github.com/OS2Forms/os2forms/pull/326) diff --git a/compose.yaml b/compose.yaml index a64bf5fb..d3083932 100644 --- a/compose.yaml +++ b/compose.yaml @@ -9,6 +9,7 @@ services: volumes: - ./:/app environment: + HOME: /tmp COMPOSER_HOME: /tmp/composer COMPOSER_CACHE_DIR: /tmp/composer-cache entrypoint: diff --git a/composer.json b/composer.json index ba5f18b2..00a05602 100644 --- a/composer.json +++ b/composer.json @@ -8,12 +8,11 @@ "ext-dom": "*", "ext-soap": "*", "cweagans/composer-patches": "^1.6.5", - "dompdf/dompdf": "^2.0", + "dompdf/dompdf": "^2.0 || ^3.0", "drupal/admin_toolbar": "^3.0", "drupal/advancedqueue": "^1.0", "drupal/cache_control_override": "^1.1 || ^2.0", "drupal/coc_forms_auto_export": "^3.0@alpha", - "drupal/config_entity_revisions": "2.0.x-dev", "drupal/diff": "^1.0", "drupal/entity_print": "^2.1", "drupal/eu_cookie_compliance": "^1.8", @@ -33,8 +32,8 @@ "drupal/r4032login": "^2.1", "drupal/redirect": "^1.4", "drupal/simplesamlphp_auth": "^4.0", - "drupal/smtp": "^1.0@beta", - "drupal/sodium": "^2.4", + "drupal/smtp": "^1.0@dev", + "drupal/sodium": "^2.4 || ^3.0", "drupal/switch_page_theme": "^4.0", "drupal/telephone_validation": "^2.2", "drupal/token": "^1.5", @@ -46,7 +45,7 @@ "drupal/webform_node_element": "^1.2", "drupal/webform_remote_handlers": "^3.0", "drupal/webform_remote_select": "^1.0.6", - "drupal/webform_rest": "^4.0.0-beta2", + "drupal/webform_rest": "^4.2", "drupal/webform_validation": "^2.0", "drupal/webform_views": "^5.0@alpha", "drupal/workflow_participants": "^3.0@RC", @@ -73,7 +72,7 @@ "mglaman/phpstan-drupal": "^1.1", "phpstan/extension-installer": "^1.3", "phpstan/phpstan-deprecation-rules": "^1.1", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^9.6 || ^10.5 || ^11.5", "wsdltophp/packagegenerator": "^4.0" }, "repositories": { @@ -107,7 +106,9 @@ "drupal-lenient": { "allowed-list": [ "drupal/coc_forms_auto_export", - "drupal/webform_node_element" + "drupal/webform_node_element", + "drupal/events_logging", + "drupal/switch_page_theme" ] }, "enable-patching": true, @@ -122,6 +123,12 @@ "drupal/entity_print": { "2733781 - Add Export to Word Support": "https://www.drupal.org/files/issues/2019-11-22/2733781-47.patch" }, + "drupal/events_logging": { + "3430351 - Automated Drupal 11 compatibility fixes for events_logging": "https://www.drupal.org/files/issues/2024-03-17/events_logging.2.0.0-beta2.rector.patch" + }, + "drupal/switch_page_theme": { + "3489081 - Drupal 11 compatibility for Switch Page Theme": "https://www.drupal.org/files/issues/2025-10-30/switch_page_theme-3489081-20.patch" + }, "drupal/webform": { "Unlock possibility of using Entity print module export to Word": "https://www.drupal.org/files/issues/2020-02-29/3096552-6.patch", "Webform computed element post save alter": "https://www.drupal.org/files/issues/2024-06-25/webform_computed_post_save_field_alter.patch", @@ -132,7 +139,7 @@ "PHP Warning if unserialize fails (https://www.drupal.org/project/webform_encrypt/issues/3292305)": "https://www.drupal.org/files/issues/2022-06-23/unserialize-php-notice.patch" }, "drupal/webform_node_element": { - "3290637 - Automated Drupal 10 compatibility fixes": "https://www.drupal.org/files/issues/2023-05-12/webform_node_element_d10-3290637-11.patch" + "3495835 - Drupal 11 compatibility fixes for Webform Node Element": "https://git.drupalcode.org/project/webform_node_element/-/merge_requests/5.diff" } } }, diff --git a/modules/os2forms_attachment/os2forms_attachment.info.yml b/modules/os2forms_attachment/os2forms_attachment.info.yml index 1687014f..6835b74e 100644 --- a/modules/os2forms_attachment/os2forms_attachment.info.yml +++ b/modules/os2forms_attachment/os2forms_attachment.info.yml @@ -2,7 +2,7 @@ name: 'OS2forms Attachment' type: module description: 'Provides OS2forms attachment field' package: OS2Forms -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:os2forms' diff --git a/modules/os2forms_attachment/src/Element/AttachmentElement.php b/modules/os2forms_attachment/src/Element/AttachmentElement.php index bbc33f0b..6476833f 100644 --- a/modules/os2forms_attachment/src/Element/AttachmentElement.php +++ b/modules/os2forms_attachment/src/Element/AttachmentElement.php @@ -58,6 +58,28 @@ public static function getFileContent(array $element, WebformSubmissionInterface if ($element['#export_type'] === 'pdf') { $file_path = NULL; + // Digital signature settings live on the digital signature handler. + // Resolving them here ensures all consumers of the element (email + // handlers, attachment downloads) serve an already signed document + // and render the validation text consistently. + // Webform skips handlers whose per-submission conditions do not match, + // so the same check is applied here to avoid rendering signature + // validation text on a document that was never signed. + $elementKey = $element['#webform_key'] ?? NULL; + if ($elementKey !== NULL) { + foreach ($webform_submission->getWebform()->getHandlers('os2forms_digital_signature') as $handler) { + $settings = $handler->getConfiguration()['settings'] ?? []; + if ($handler->isEnabled() + && $handler->checkConditions($webform_submission) + && ($settings['attachment_element'] ?? '') === $elementKey) { + $element['#digital_signature'] = TRUE; + $element['#digital_signature_position'] = $settings['signature_position'] + ?? Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT; + break; + } + } + } + // If attachment with digital signatur, check if we already have one. if (isset($element['#digital_signature']) && $element['#digital_signature']) { // Get scheme. diff --git a/modules/os2forms_attachment/src/Form/AttachmentComponentForm.php b/modules/os2forms_attachment/src/Form/AttachmentComponentForm.php index 2a096e31..ba32ff03 100644 --- a/modules/os2forms_attachment/src/Form/AttachmentComponentForm.php +++ b/modules/os2forms_attachment/src/Form/AttachmentComponentForm.php @@ -113,9 +113,11 @@ public function form(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { - parent::save($form, $form_state); + $result = parent::save($form, $form_state); $form_state->setRedirect('entity.os2forms_attachment_component.list'); + + return $result; } /** diff --git a/modules/os2forms_attachment/src/Plugin/WebformElement/AttachmentElement.php b/modules/os2forms_attachment/src/Plugin/WebformElement/AttachmentElement.php index e19c531a..4f2215d6 100644 --- a/modules/os2forms_attachment/src/Plugin/WebformElement/AttachmentElement.php +++ b/modules/os2forms_attachment/src/Plugin/WebformElement/AttachmentElement.php @@ -5,7 +5,6 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\webform\Twig\WebformTwigExtension; use Drupal\webform\Utility\WebformElementHelper; -use Drupal\os2forms_attachment\Os2formsAttachmentPrintBuilder; use Drupal\webform_attachment\Plugin\WebformElement\WebformAttachmentBase; /** @@ -28,8 +27,6 @@ protected function defineDefaultProperties() { 'view_mode' => 'html', 'template' => '', 'export_type' => '', - 'digital_signature' => '', - 'digital_signature_position' => Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT, 'exclude_empty' => '', 'exclude_empty_checkbox' => '', 'excluded_elements' => '', @@ -91,28 +88,6 @@ public function form(array $form, FormStateInterface $form_state) { 'html' => $this->t('HTML'), ], ]; - $form['attachment']['digital_signature'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Digital signature'), - ]; - $form['attachment']['digital_signature_position'] = [ - '#type' => 'select', - '#title' => $this->t('Digital signature position'), - '#description' => $this->t('Select where the digital signature validation text should be placed in the PDF document.'), - '#options' => [ - Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_FOOTER => $this->t('Footer (repeats on every page)'), - Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_HEADER => $this->t('Header (repeats on every page)'), - Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT => $this->t('After content (end of document)'), - Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_BEFORE_CONTENT => $this->t('Before content (start of document)'), - ], - '#default_value' => Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT, - '#states' => [ - 'visible' => [ - ':input[name="properties[digital_signature]"]' => ['checked' => TRUE], - ], - ], - ]; - // Set #access so that help is always visible. WebformElementHelper::setPropertyRecursive($form['attachment']['help'], '#access', TRUE); diff --git a/modules/os2forms_autocomplete/os2forms_autocomplete.info.yml b/modules/os2forms_autocomplete/os2forms_autocomplete.info.yml index 22db8a02..81bdc3eb 100644 --- a/modules/os2forms_autocomplete/os2forms_autocomplete.info.yml +++ b/modules/os2forms_autocomplete/os2forms_autocomplete.info.yml @@ -2,7 +2,7 @@ name: 'OS2forms Autocomplete' type: module description: 'Provides autocomplete field' package: OS2Forms -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:os2forms' diff --git a/modules/os2forms_dawa/os2forms_dawa.info.yml b/modules/os2forms_dawa/os2forms_dawa.info.yml index bab9de83..6ab1b3d8 100644 --- a/modules/os2forms_dawa/os2forms_dawa.info.yml +++ b/modules/os2forms_dawa/os2forms_dawa.info.yml @@ -2,7 +2,7 @@ name: 'OS2forms DAWA' type: module description: 'Provides integration with Danish Addresses Web API (DAWA https://dawa.aws.dk) and provide address autocomplete fields.' package: OS2Forms -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:os2forms' diff --git a/modules/os2forms_digital_post/modules/os2forms_digital_post_examples/os2forms_digital_post_examples.info.yml b/modules/os2forms_digital_post/modules/os2forms_digital_post_examples/os2forms_digital_post_examples.info.yml index 3863214f..3be7eb8c 100644 --- a/modules/os2forms_digital_post/modules/os2forms_digital_post_examples/os2forms_digital_post_examples.info.yml +++ b/modules/os2forms_digital_post/modules/os2forms_digital_post_examples/os2forms_digital_post_examples.info.yml @@ -3,6 +3,6 @@ type: module description: 'Example forms for OS2Forms Digital Post.' package: 'OS2Forms' -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'os2forms_digital_post:os2forms_digital_post' diff --git a/modules/os2forms_digital_post/os2forms_digital_post.info.yml b/modules/os2forms_digital_post/os2forms_digital_post.info.yml index 0e1408cc..ec5a66c9 100644 --- a/modules/os2forms_digital_post/os2forms_digital_post.info.yml +++ b/modules/os2forms_digital_post/os2forms_digital_post.info.yml @@ -2,7 +2,7 @@ name: 'OS2Forms Digital Post' type: module description: 'Provides integration to Print service provided by Serviceplatformen.' package: 'OS2Forms' -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'beskedfordeler:beskedfordeler' - 'drupal:advancedqueue' diff --git a/modules/os2forms_digital_post/src/Form/SettingsForm.php b/modules/os2forms_digital_post/src/Form/SettingsForm.php index e03f2d8a..ad51220c 100644 --- a/modules/os2forms_digital_post/src/Form/SettingsForm.php +++ b/modules/os2forms_digital_post/src/Form/SettingsForm.php @@ -3,6 +3,7 @@ namespace Drupal\os2forms_digital_post\Form; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; @@ -26,17 +27,18 @@ final class SettingsForm extends ConfigFormBase { * * @var \Drupal\Core\Entity\EntityStorageInterface */ - private readonly EntityStorageInterface $queueStorage; + protected readonly EntityStorageInterface $queueStorage; /** * Constructor. */ public function __construct( ConfigFactoryInterface $config_factory, + TypedConfigManagerInterface $typedConfigManager, EntityTypeManagerInterface $entityTypeManager, - private readonly Settings $settings, + protected readonly Settings $settings, ) { - parent::__construct($config_factory); + parent::__construct($config_factory, $typedConfigManager); $this->queueStorage = $entityTypeManager->getStorage('advancedqueue_queue'); } @@ -48,6 +50,7 @@ public function __construct( public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), + $container->get('config.typed'), $container->get('entity_type.manager'), $container->get(Settings::class), ); diff --git a/modules/os2forms_digital_post/src/Helper/BeskedfordelerHelper.php b/modules/os2forms_digital_post/src/Helper/BeskedfordelerHelper.php index c256dc17..e92c3f53 100644 --- a/modules/os2forms_digital_post/src/Helper/BeskedfordelerHelper.php +++ b/modules/os2forms_digital_post/src/Helper/BeskedfordelerHelper.php @@ -61,7 +61,6 @@ public function createMessage(int $submissionId, MeMoMessage $message, string $r * @see Message::__set() */ public function loadMessage(string $messageUUID): ?Message { - // @phpstan-ignore-next-line (fetchObject invoked with 2 parameters) return $this->database ->select(self::TABLE_NAME, 'm') ->fields('m') diff --git a/modules/os2forms_digital_post/src/Plugin/AdvancedQueue/JobType/SendDigitalPostSF1601.php b/modules/os2forms_digital_post/src/Plugin/AdvancedQueue/JobType/SendDigitalPostSF1601.php index 3952d333..15b3dad1 100644 --- a/modules/os2forms_digital_post/src/Plugin/AdvancedQueue/JobType/SendDigitalPostSF1601.php +++ b/modules/os2forms_digital_post/src/Plugin/AdvancedQueue/JobType/SendDigitalPostSF1601.php @@ -25,7 +25,7 @@ final class SendDigitalPostSF1601 extends JobTypeBase implements ContainerFactor * * @var \Drupal\os2forms_digital_post\Helper\WebformHelperSF1601 */ - private WebformHelperSF1601 $helper; + protected WebformHelperSF1601 $helper; /** * {@inheritdoc} diff --git a/modules/os2forms_digital_signature/README.md b/modules/os2forms_digital_signature/README.md index d863182e..5dd93e2c 100644 --- a/modules/os2forms_digital_signature/README.md +++ b/modules/os2forms_digital_signature/README.md @@ -13,12 +13,20 @@ The signature server consists of two parts. A frontend module ### Activating Digital Signature -1. Add the OS2forms attachment element to the form. -2. Indicate that the OS2Forms attachment requires a digital signature. -3. Add the Digital Signature Handler to the webform. +1. Add an attachment-style element to the form (either *OS2Forms Attachment* — generated PDF — or + *OS2forms digital signature document* — uploaded PDF). +2. Add the **Digital Signature** handler to the webform. +3. In the handler configuration, pick the attachment element to sign from the **Attachment element to sign** + dropdown, and choose where the signature validation text should be placed in the generated PDF + (the position only applies to *OS2Forms Attachment* elements; it is ignored for uploaded PDFs). 4. If the form requires an email handler, ensure the trigger is set to **...when submission is locked** in the handler’s *Additional settings*. +> [!NOTE] +> Prior to this release, the attachment element exposed `Digital signature` and `Digital signature position` properties +> directly. These settings have moved to the handler. Existing forms are migrated automatically by +> `os2forms_digital_signature_update_10001()`. + ### Flow Explained 1. Upon form submission, a PDF is generated, saved in the private directory, and sent to the signature service via URL. diff --git a/modules/os2forms_digital_signature/os2forms_digital_signature.info.yml b/modules/os2forms_digital_signature/os2forms_digital_signature.info.yml index 29547e43..974986f9 100644 --- a/modules/os2forms_digital_signature/os2forms_digital_signature.info.yml +++ b/modules/os2forms_digital_signature/os2forms_digital_signature.info.yml @@ -2,8 +2,9 @@ name: 'OS2Forms Digital Signature' type: module description: 'Provides digital signature functionality' package: 'OS2Forms' -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'webform:webform' + - 'os2forms:os2forms_attachment' configure: os2forms_digital_signature.settings diff --git a/modules/os2forms_digital_signature/os2forms_digital_signature.install b/modules/os2forms_digital_signature/os2forms_digital_signature.install new file mode 100644 index 00000000..5c360940 --- /dev/null +++ b/modules/os2forms_digital_signature/os2forms_digital_signature.install @@ -0,0 +1,147 @@ +accessCheck(FALSE)->execute()); + $sandbox['migrated'] = 0; + } + + // array_splice() removes the chunk from the sandbox, so the next pass + // continues with the remaining ids. + /** @var \Drupal\webform\WebformInterface[] $webforms */ + $webforms = Webform::loadMultiple(array_splice($sandbox['ids'], 0, 25)); + foreach ($webforms as $webform) { + $handlers = $webform->getHandlers(); + $signatureHandler = NULL; + foreach ($handlers as $handler) { + if ($handler->getPluginId() === 'os2forms_digital_signature') { + $signatureHandler = $handler; + break; + } + } + + $elements = $webform->getElementsDecodedAndFlattened(); + $needsSave = FALSE; + + if ($signatureHandler) { + // Mirror the pre-update runtime selection: the handler signed the + // first os2forms_digital_signature_document element, falling back to + // the first os2forms_attachment element. The #digital_signature + // property never influenced the selection, only the rendering of the + // validation text. + $signedKey = NULL; + $signedPosition = Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT; + $candidates = []; + foreach (['os2forms_digital_signature_document', 'os2forms_attachment'] as $type) { + foreach ($elements as $key => $element) { + if (($element['#type'] ?? NULL) === $type) { + $candidates[] = $key; + if ($signedKey === NULL) { + $signedKey = $key; + if ($type === 'os2forms_attachment') { + $signedPosition = $element['#digital_signature_position'] ?? $signedPosition; + } + } + } + } + } + + if ($signedKey === NULL) { + $logger->warning('Skipped webform @id during digital signature config migration: the handler is enabled but the webform has no os2forms_digital_signature_document or os2forms_attachment element. Configure the handler manually.', [ + '@id' => $webform->id(), + ]); + } + else { + if (count($candidates) > 1) { + $logger->warning('Webform @id has multiple signable elements (@all); migrating @chosen to match the pre-update type-priority selection.', [ + '@id' => $webform->id(), + '@all' => implode(', ', $candidates), + '@chosen' => $signedKey, + ]); + } + foreach ($elements as $key => $element) { + if (!empty($element['#digital_signature']) && $key !== $signedKey) { + $logger->warning('Webform @id: element @key had digital signature enabled, but @chosen is migrated instead to match the pre-update behavior.', [ + '@id' => $webform->id(), + '@key' => $key, + '@chosen' => $signedKey, + ]); + } + } + + // Write the new config onto the handler. + $config = $signatureHandler->getConfiguration(); + $config['settings']['attachment_element'] = $signedKey; + $config['settings']['signature_position'] = $signedPosition; + $signatureHandler->setConfiguration($config); + $needsSave = TRUE; + $sandbox['migrated']++; + } + } + + // Strip the legacy properties whether or not the handler is present, so + // no webform is left with inert digital signature configuration. + foreach ($elements as $element) { + if (array_key_exists('#digital_signature', $element) || array_key_exists('#digital_signature_position', $element)) { + $rawElements = $webform->getElementsDecoded(); + _os2forms_digital_signature_strip_legacy_props($rawElements); + $webform->setElements($rawElements); + $needsSave = TRUE; + break; + } + } + + if ($needsSave) { + $webform->save(); + } + } + + // Anything below 1 makes the runner call us again. Done when no ids remain. + $sandbox['#finished'] = empty($sandbox['ids']) ? 1 : 0; + + if ($sandbox['#finished'] >= 1) { + return t('Migrated digital signature config on @count webform(s).', ['@count' => $sandbox['migrated']]); + } +} + +/** + * Recursively remove legacy digital signature element properties. + * + * @param array $elements + * The decoded elements tree, modified in place. + */ +function _os2forms_digital_signature_strip_legacy_props(array &$elements) { + foreach ($elements as $key => &$value) { + if (!is_array($value)) { + continue; + } + if (str_starts_with((string) $key, '#')) { + continue; + } + unset($value['#digital_signature'], $value['#digital_signature_position']); + _os2forms_digital_signature_strip_legacy_props($value); + } +} diff --git a/modules/os2forms_digital_signature/src/Plugin/WebformHandler/DigitalSignatureWebformHandler.php b/modules/os2forms_digital_signature/src/Plugin/WebformHandler/DigitalSignatureWebformHandler.php index 9a616bfc..f2a572cd 100644 --- a/modules/os2forms_digital_signature/src/Plugin/WebformHandler/DigitalSignatureWebformHandler.php +++ b/modules/os2forms_digital_signature/src/Plugin/WebformHandler/DigitalSignatureWebformHandler.php @@ -7,9 +7,11 @@ use Drupal\Core\File\FileExists; use Drupal\Core\File\FileSystemInterface; use Drupal\Core\File\FileUrlGeneratorInterface; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Site\Settings; use Drupal\Core\Url; use Drupal\file\FileRepositoryInterface; +use Drupal\os2forms_attachment\Os2formsAttachmentPrintBuilder; use Drupal\os2forms_digital_signature\Service\SigningService; use Drupal\webform\Plugin\WebformElementManagerInterface; use Drupal\webform\Plugin\WebformHandlerBase; @@ -37,56 +39,175 @@ class DigitalSignatureWebformHandler extends WebformHandlerBase { * * @var \Drupal\Core\Extension\ModuleHandlerInterface */ - private readonly ModuleHandlerInterface $moduleHandler; + protected readonly ModuleHandlerInterface $moduleHandler; /** * The webform element plugin manager. * * @var \Drupal\webform\Plugin\WebformElementManagerInterface */ - private readonly WebformElementManagerInterface $elementManager; + protected readonly WebformElementManagerInterface $elementManager; /** * Logger for channel - os2forms_digital_signature. * * @var \Psr\Log\LoggerInterface */ - private readonly LoggerInterface $logger; + protected readonly LoggerInterface $logger; /** * File system interface. * * @var \Drupal\Core\File\FileSystemInterface */ - private readonly FileSystemInterface $fileSystem; + protected readonly FileSystemInterface $fileSystem; /** * File repository. * * @var \Drupal\file\FileRepositoryInterface */ - private readonly FileRepositoryInterface $fileRepository; + protected readonly FileRepositoryInterface $fileRepository; /** * File URL generator. * * @var \Drupal\Core\File\FileUrlGeneratorInterface */ - private readonly FileUrlGeneratorInterface $fileUrlGenerator; + protected readonly FileUrlGeneratorInterface $fileUrlGenerator; /** * OS2Forms signing service. * * @var \Drupal\os2forms_digital_signature\Service\SigningService */ - private readonly SigningService $signingService; + protected readonly SigningService $signingService; /** * Settings service. * * @var \Drupal\Core\Site\Settings */ - private readonly Settings $settings; + protected readonly Settings $settings; + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'attachment_element' => '', + 'signature_position' => Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + $form['attachment_element'] = [ + '#type' => 'select', + '#title' => $this->t('Attachment element to sign'), + '#description' => $this->t('Select the webform element whose generated or uploaded PDF should be signed. Only OS2Forms Attachment elements with export type PDF and Digital Signature Document elements are listed.'), + '#options' => $this->getAttachmentElementOptions(), + '#empty_option' => $this->t('- Select -'), + '#default_value' => $this->configuration['attachment_element'], + '#required' => TRUE, + ]; + + $form['signature_position'] = [ + '#type' => 'select', + '#title' => $this->t('Signature validation text position'), + '#description' => $this->t('Where the digital signature validation text is placed in the generated PDF. Only applies when the selected element is an OS2Forms Attachment; ignored for uploaded PDF documents.'), + '#options' => [ + Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_FOOTER => $this->t('Footer (repeats on every page)'), + Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_HEADER => $this->t('Header (repeats on every page)'), + Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT => $this->t('After content (end of document)'), + Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_BEFORE_CONTENT => $this->t('Before content (start of document)'), + ], + '#default_value' => $this->configuration['signature_position'], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + parent::submitConfigurationForm($form, $form_state); + + $values = $form_state->getValues(); + $this->configuration['attachment_element'] = $values['attachment_element'] ?? ''; + $this->configuration['signature_position'] = $values['signature_position'] + ?? Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT; + } + + /** + * {@inheritdoc} + */ + public function getSummary() { + $elementKey = $this->configuration['attachment_element'] ?? ''; + $position = $this->configuration['signature_position'] ?? Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT; + + return [ + '#markup' => $this->t('Sign attachment: @element
Signature position: @position', [ + '@element' => $elementKey !== '' ? $elementKey : $this->t('not configured'), + '@position' => $position, + ]), + ]; + } + + /** + * Build the dropdown options for the attachment element selector. + * + * The signing flow requires a PDF, so only the two element types that + * provide one are listed: os2forms_digital_signature_document (uploaded + * PDF) and os2forms_attachment (generated PDF). + * + * @return array + * Map of element key => human label. + * + * @see self::isSignableElement() + */ + protected function getAttachmentElementOptions(): array { + $webform = $this->getWebform(); + if (!$webform) { + return []; + } + + $elements = $webform->getElementsInitializedAndFlattened(); + $options = []; + foreach ($webform->getElementsAttachments() as $key) { + $element = $elements[$key] ?? NULL; + if (!$element || !$this->isSignableElement($element)) { + continue; + } + $type = $element['#type'] ?? ''; + $title = $element['#title'] ?? $key; + $options[$key] = sprintf('%s (%s) [%s]', $title, $key, $type); + } + return $options; + } + + /** + * Check whether an element provides a PDF that can be signed. + * + * @param array $element + * The flattened webform element. + * + * @return bool + * TRUE if the element can be signed. + */ + protected function isSignableElement(array $element): bool { + return match ($element['#type'] ?? '') { + 'os2forms_digital_signature_document' => TRUE, + // List only os2forms_attachment elements with PDF export type. + 'os2forms_attachment' => ($element['#export_type'] ?? '') === 'pdf', + default => FALSE, + }; + } /** * {@inheritdoc} @@ -177,59 +298,62 @@ public function preSave(WebformSubmissionInterface $webform_submission) { /** * Get OS2forms file attachment. * + * Resolves the attachment element configured on the handler, asks its + * plugin for the email attachment payload, and returns the first item. + * * @param \Drupal\webform\WebformSubmissionInterface $webform_submission * A webform submission. * * @return array|null - * Array of attachment data. + * Array of attachment data, or NULL when no attachment is available. * * @throws \Exception */ protected function getSubmissionAttachment(WebformSubmissionInterface $webform_submission) { - $attachments = NULL; - $attachment = NULL; - - // Getting all element types that are added to the webform. - // - // Priority is the following: check for os2forms_digital_signature_document, - // is not found try serving os2forms_attachment. - $elementTypes = array_column($this->getWebform()->getElementsDecodedAndFlattened(), '#type'); - $attachmentType = ''; - if (in_array('os2forms_digital_signature_document', $elementTypes)) { - $attachmentType = 'os2forms_digital_signature_document'; - } - elseif (in_array('os2forms_attachment', $elementTypes)) { - $attachmentType = 'os2forms_attachment'; + $elementKey = $this->configuration['attachment_element'] ?? ''; + if ($elementKey === '') { + $this->logger->error('Digital signature handler has no attachment_element configured for webform %webform.', [ + '%webform' => $this->getWebform()->id(), + ]); + return NULL; } $elements = $this->getWebform()->getElementsInitializedAndFlattened(); - $element_attachments = $this->getWebform()->getElementsAttachments(); - foreach ($element_attachments as $element_attachment) { - // Check if the element attachment key is excluded and should not attach - // any files. - if (isset($this->configuration['excluded_elements'][$element_attachment])) { - continue; - } + if (!isset($elements[$elementKey])) { + $this->logger->error('Configured attachment element %element does not exist on webform %webform.', [ + '%element' => $elementKey, + '%webform' => $this->getWebform()->id(), + ]); + return NULL; + } + + $element = $elements[$elementKey]; - $element = $elements[$element_attachment]; + // Ensure that the element provides a PDF that can be signed during runtime. + if (!$this->isSignableElement($element)) { + $this->logger->error('Configured attachment element %element on webform %webform does not provide a PDF and cannot be signed.', [ + '%element' => $elementKey, + '%webform' => $this->getWebform()->id(), + ]); + return NULL; + } - if ($element['#type'] == $attachmentType) { - /** @var \Drupal\webform\Plugin\WebformElementAttachmentInterface $element_plugin */ - $element_plugin = $this->elementManager->getElementInstance($element); - $attachments = $element_plugin->getEmailAttachments($element, $webform_submission); + /** @var \Drupal\webform\Plugin\WebformElementAttachmentInterface $element_plugin */ + $element_plugin = $this->elementManager->getElementInstance($element); + $attachments = $element_plugin->getEmailAttachments($element, $webform_submission); - // If we are dealing with an uploaded file, attach the FID. - if ($fid = $webform_submission->getElementData($element_attachment)) { - $attachments[0]['fid'] = $fid; - } - break; - } + if (empty($attachments)) { + return NULL; } - if (!empty($attachments)) { - $attachment = reset($attachments); + // If the source is an uploaded managed file, attach the FID so the + // signed file can replace the upload rather than creating a new file. + if ($fid = $webform_submission->getElementData($elementKey)) { + $attachments[0]['fid'] = $fid; } + $attachment = reset($attachments); + // For SwiftMailer && Mime Mail use filecontent and not the filepath. // @see \Drupal\swiftmailer\Plugin\Mail\SwiftMailer::attachAsMimeMail // @see \Drupal\mimemail\Utility\MimeMailFormatHelper::mimeMailFile diff --git a/modules/os2forms_encrypt/os2forms_encrypt.info.yml b/modules/os2forms_encrypt/os2forms_encrypt.info.yml index 5e1e7edf..049b10de 100644 --- a/modules/os2forms_encrypt/os2forms_encrypt.info.yml +++ b/modules/os2forms_encrypt/os2forms_encrypt.info.yml @@ -3,7 +3,7 @@ description: Encryption functionality for OS2Forms web-forms package: OS2Forms type: module version: 1.0.0 -core_version_requirement: ^8 || ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:webform_encrypt' - 'drupal:sodium' diff --git a/modules/os2forms_encrypt/src/Form/SettingsForm.php b/modules/os2forms_encrypt/src/Form/SettingsForm.php index e80341a8..0820c0eb 100644 --- a/modules/os2forms_encrypt/src/Form/SettingsForm.php +++ b/modules/os2forms_encrypt/src/Form/SettingsForm.php @@ -3,6 +3,7 @@ namespace Drupal\os2forms_encrypt\Form; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Link; @@ -28,10 +29,10 @@ class SettingsForm extends ConfigFormBase { * * @var \Drupal\encrypt\EncryptionProfileManager */ - private EncryptionProfileManager $encryptionProfileManager; + protected EncryptionProfileManager $encryptionProfileManager; - public function __construct(ConfigFactoryInterface $config_factory, EncryptionProfileManager $encryptionProfileManager) { - parent::__construct($config_factory); + public function __construct(ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typedConfigManager, EncryptionProfileManager $encryptionProfileManager) { + parent::__construct($config_factory, $typedConfigManager); $this->encryptionProfileManager = $encryptionProfileManager; } @@ -41,6 +42,7 @@ public function __construct(ConfigFactoryInterface $config_factory, EncryptionPr public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), + $container->get('config.typed'), $container->get('encrypt.encryption_profile.manager') ); } diff --git a/modules/os2forms_fasit/os2forms_fasit.info.yml b/modules/os2forms_fasit/os2forms_fasit.info.yml index fe55c94a..c32678bc 100644 --- a/modules/os2forms_fasit/os2forms_fasit.info.yml +++ b/modules/os2forms_fasit/os2forms_fasit.info.yml @@ -2,7 +2,7 @@ name: 'OS2Forms Fasit' type: module description: 'Fasit integration' package: OS2Forms -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - drupal:webform - drupal:advancedqueue diff --git a/modules/os2forms_fasit/src/Form/SettingsForm.php b/modules/os2forms_fasit/src/Form/SettingsForm.php index 2cfed651..89cf1752 100644 --- a/modules/os2forms_fasit/src/Form/SettingsForm.php +++ b/modules/os2forms_fasit/src/Form/SettingsForm.php @@ -3,6 +3,7 @@ namespace Drupal\os2forms_fasit\Form; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; @@ -33,9 +34,10 @@ final class SettingsForm extends ConfigFormBase { */ public function __construct( ConfigFactoryInterface $config_factory, - private readonly FasitHelper $helper, + TypedConfigManagerInterface $typedConfigManager, + protected readonly FasitHelper $helper, ) { - parent::__construct($config_factory); + parent::__construct($config_factory, $typedConfigManager); } /** @@ -46,6 +48,7 @@ public function __construct( public static function create(ContainerInterface $container): self { return new static( $container->get('config.factory'), + $container->get('config.typed'), $container->get(FasitHelper::class) ); } diff --git a/modules/os2forms_fasit/src/Plugin/AdvancedQueue/JobType/Fasit.php b/modules/os2forms_fasit/src/Plugin/AdvancedQueue/JobType/Fasit.php index efc8b2bd..d7d6a20d 100644 --- a/modules/os2forms_fasit/src/Plugin/AdvancedQueue/JobType/Fasit.php +++ b/modules/os2forms_fasit/src/Plugin/AdvancedQueue/JobType/Fasit.php @@ -26,7 +26,7 @@ class Fasit extends JobTypeBase implements ContainerFactoryPluginInterface { * * @var \Drupal\os2forms_fasit\Helper\FasitHelper */ - private FasitHelper $helper; + protected FasitHelper $helper; /** * The submission logger. diff --git a/modules/os2forms_fbs_handler/os2forms_fbs_handler.info.yml b/modules/os2forms_fbs_handler/os2forms_fbs_handler.info.yml index e6251ac6..abf35943 100644 --- a/modules/os2forms_fbs_handler/os2forms_fbs_handler.info.yml +++ b/modules/os2forms_fbs_handler/os2forms_fbs_handler.info.yml @@ -2,7 +2,7 @@ name: 'FBS Handler' type: module description: 'Provides integration to FBS.' package: 'OS2Forms' -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'webform:webform' - 'advancedqueue:advancedqueue' diff --git a/modules/os2forms_forloeb/os2forms_forloeb.info.yml b/modules/os2forms_forloeb/os2forms_forloeb.info.yml index 9aee352b..04c27a6e 100644 --- a/modules/os2forms_forloeb/os2forms_forloeb.info.yml +++ b/modules/os2forms_forloeb/os2forms_forloeb.info.yml @@ -2,7 +2,7 @@ name: 'OS2forms med Forløb Module' description: 'Adds a Maestro workflow engine and advanced workflow functionality to OS2forms.' package: OS2Forms type: module -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 configure: os2forms_forloeb.settings diff --git a/modules/os2forms_forloeb/src/Form/SettingsForm.php b/modules/os2forms_forloeb/src/Form/SettingsForm.php index deb91bb6..0b43c15b 100644 --- a/modules/os2forms_forloeb/src/Form/SettingsForm.php +++ b/modules/os2forms_forloeb/src/Form/SettingsForm.php @@ -3,6 +3,7 @@ namespace Drupal\os2forms_forloeb\Form; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Extension\ModuleExtensionList; @@ -28,11 +29,12 @@ class SettingsForm extends ConfigFormBase { */ public function __construct( ConfigFactoryInterface $configFactory, - private readonly RoleStorageInterface $roleStorage, - private readonly EntityStorageInterface $queueStorage, - private readonly ModuleExtensionList $moduleHandler, + TypedConfigManagerInterface $typedConfigManager, + protected readonly RoleStorageInterface $roleStorage, + protected readonly EntityStorageInterface $queueStorage, + protected readonly ModuleExtensionList $moduleHandler, ) { - parent::__construct($configFactory); + parent::__construct($configFactory, $typedConfigManager); } /** @@ -41,6 +43,7 @@ public function __construct( public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), + $container->get('config.typed'), $container->get('entity_type.manager')->getStorage('user_role'), $container->get('entity_type.manager')->getStorage('advancedqueue_queue'), $container->get('extension.list.module'), diff --git a/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php b/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php index 1cf267de..5c67aa61 100644 --- a/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php +++ b/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php @@ -40,7 +40,7 @@ public function __construct( array $configuration, $plugin_id, $plugin_definition, - private readonly MaestroHelper $helper, + protected readonly MaestroHelper $helper, ) { parent::__construct($configuration, $plugin_id, $plugin_definition); } diff --git a/modules/os2forms_nemid/os2forms_nemid.info.yml b/modules/os2forms_nemid/os2forms_nemid.info.yml index 3523807f..f9b7ceaf 100644 --- a/modules/os2forms_nemid/os2forms_nemid.info.yml +++ b/modules/os2forms_nemid/os2forms_nemid.info.yml @@ -2,7 +2,7 @@ name: 'OS2forms NemID' type: module description: 'Provides custom NemID fields and integration with NemID' package: OS2Forms -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:os2forms' diff --git a/modules/os2forms_nemid/src/Plugin/WebformElement/MitidChildElementBase.php b/modules/os2forms_nemid/src/Plugin/WebformElement/MitidChildElementBase.php index fbdd7dca..79d78cc4 100644 --- a/modules/os2forms_nemid/src/Plugin/WebformElement/MitidChildElementBase.php +++ b/modules/os2forms_nemid/src/Plugin/WebformElement/MitidChildElementBase.php @@ -12,7 +12,7 @@ * @see \Drupal\webform\Plugin\WebformElementInterface * @see \Drupal\webform\Annotation\WebformElement */ -abstract class MitidChildElementBase extends WebformElementBase { +abstract class MitidChildElementBase extends WebformElementBase implements NemidChildElementInterface { /** * {@inheritdoc} diff --git a/modules/os2forms_nemid/src/Plugin/WebformElement/NemidChildElementInterface.php b/modules/os2forms_nemid/src/Plugin/WebformElement/NemidChildElementInterface.php new file mode 100644 index 00000000..967b3c77 --- /dev/null +++ b/modules/os2forms_nemid/src/Plugin/WebformElement/NemidChildElementInterface.php @@ -0,0 +1,10 @@ +' . $element['#address_protection_help_text'] . ''; } } diff --git a/modules/os2forms_nemid/src/Plugin/WebformElement/NemidChildrenSelect.php b/modules/os2forms_nemid/src/Plugin/WebformElement/NemidChildrenSelect.php index 9d2e0497..18b3144f 100644 --- a/modules/os2forms_nemid/src/Plugin/WebformElement/NemidChildrenSelect.php +++ b/modules/os2forms_nemid/src/Plugin/WebformElement/NemidChildrenSelect.php @@ -20,7 +20,7 @@ * @see \Drupal\os2forms_nemid\Plugin\NemidElementBase * @see \Drupal\os2forms_nemid\Element\NemidChildrenRadios */ -class NemidChildrenSelect extends Select implements NemidElementPersonalInterface, NemidPrepopulateFieldInterface { +class NemidChildrenSelect extends Select implements NemidElementPersonalInterface, NemidPrepopulateFieldInterface, NemidChildElementInterface { /** * {@inheritdoc} @@ -155,7 +155,7 @@ public function handleElementPrepopulate(array &$element, FormStateInterface &$f $element['#options'] = $options; - if ($showAddressNameProtectionMessage) { + if ($showAddressNameProtectionMessage && isset($element['#address_protection_help_text'])) { $element['#suffix'] = '
' . $element['#address_protection_help_text'] . '
'; } } diff --git a/modules/os2forms_permissions_by_term/os2forms_permissions_by_term.info.yml b/modules/os2forms_permissions_by_term/os2forms_permissions_by_term.info.yml index 05721595..c409f903 100644 --- a/modules/os2forms_permissions_by_term/os2forms_permissions_by_term.info.yml +++ b/modules/os2forms_permissions_by_term/os2forms_permissions_by_term.info.yml @@ -2,7 +2,7 @@ name: OS2Forms permissions by term type: module description: Adds taxonomy to webform configuration package: OS2Web -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:webform' - 'drupal:permissions_by_term' diff --git a/modules/os2forms_permissions_by_term/src/Form/SettingsForm.php b/modules/os2forms_permissions_by_term/src/Form/SettingsForm.php index ef748f84..72186baa 100644 --- a/modules/os2forms_permissions_by_term/src/Form/SettingsForm.php +++ b/modules/os2forms_permissions_by_term/src/Form/SettingsForm.php @@ -24,7 +24,7 @@ class SettingsForm extends ConfigFormBase { * * @var \Drupal\Core\Entity\EntityFieldManagerInterface */ - private $entityFieldTypeManager; + protected $entityFieldTypeManager; /** * Constructs an SettingsForm object. diff --git a/modules/os2forms_sbsys/os2forms_sbsys.info.yml b/modules/os2forms_sbsys/os2forms_sbsys.info.yml index e6358f66..79d92fe5 100644 --- a/modules/os2forms_sbsys/os2forms_sbsys.info.yml +++ b/modules/os2forms_sbsys/os2forms_sbsys.info.yml @@ -2,7 +2,7 @@ name: 'OS2forms SBSYS integration' type: module description: 'Provides integration with SBSYS ESDH provider.' package: OS2Forms -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:os2forms' diff --git a/modules/os2forms_sbsys/src/Element/WebformAttachmentSbsysXml.php b/modules/os2forms_sbsys/src/Element/WebformAttachmentSbsysXml.php index b5a9db69..6f5374c9 100644 --- a/modules/os2forms_sbsys/src/Element/WebformAttachmentSbsysXml.php +++ b/modules/os2forms_sbsys/src/Element/WebformAttachmentSbsysXml.php @@ -271,6 +271,10 @@ protected static function getWebformElementsAsList(WebformSubmissionInterface $w * Element configuration value */ public static function getConfigurationValue($name, array $config, WebformSubmissionInterface $webform_submission) { + if (!isset($config[$name])) { + return ''; + } + if ($config[$name] == 'default_nemid_value') { return 'default_nemid_value'; } diff --git a/modules/os2forms_sbsys/src/Plugin/WebformElement/WebformAttachmentSbsysXml.php b/modules/os2forms_sbsys/src/Plugin/WebformElement/WebformAttachmentSbsysXml.php index 4fde7727..739ad561 100644 --- a/modules/os2forms_sbsys/src/Plugin/WebformElement/WebformAttachmentSbsysXml.php +++ b/modules/os2forms_sbsys/src/Plugin/WebformElement/WebformAttachmentSbsysXml.php @@ -5,6 +5,7 @@ use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Form\FormStateInterface; use Drupal\os2forms\Plugin\WebformElement\WebformAttachmentXml; +use Drupal\os2forms_nemid\Plugin\WebformElement\NemidChildElementInterface; use Drupal\os2forms_nemid\Plugin\WebformElement\NemidElementBase; use Drupal\webform\Plugin\WebformElement\DateBase; use Drupal\webform\Plugin\WebformElement\TextBase; @@ -123,7 +124,11 @@ public function form(array $form, FormStateInterface $form_state) { } // Skipping if is of type we do not support. - if (!$elementInstance instanceof TextBase && !$elementInstance instanceof NemidElementBase && !$elementInstance instanceof DateBase && !$elementInstance instanceof WebformComputedBase) { + if (!$elementInstance instanceof TextBase + && !$elementInstance instanceof NemidElementBase + && !$elementInstance instanceof DateBase + && !$elementInstance instanceof WebformComputedBase + && !$elementInstance instanceof NemidChildElementInterface) { continue; } diff --git a/modules/os2forms_webform_list/os2forms_webform_list.info.yml b/modules/os2forms_webform_list/os2forms_webform_list.info.yml index c9b90d42..c10bba79 100644 --- a/modules/os2forms_webform_list/os2forms_webform_list.info.yml +++ b/modules/os2forms_webform_list/os2forms_webform_list.info.yml @@ -3,7 +3,7 @@ type: module description: Alters display of webform list and maestro template list package: OS2Web core: 8.x -core_version_requirement: ^8 || ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:webform' - 'drupal:maestro' diff --git a/modules/os2forms_webform_maps/os2forms_webform_maps.info.yml b/modules/os2forms_webform_maps/os2forms_webform_maps.info.yml index 6de28b20..9d2c6943 100644 --- a/modules/os2forms_webform_maps/os2forms_webform_maps.info.yml +++ b/modules/os2forms_webform_maps/os2forms_webform_maps.info.yml @@ -2,7 +2,7 @@ name: 'OS2forms Webform maps' type: module description: 'Provides integration with Leaflet maps and provides map element for webform.' package: OS2Forms -core_version_requirement: ^9.4 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:os2forms' diff --git a/modules/os2forms_webform_maps/os2forms_webform_maps.module b/modules/os2forms_webform_maps/os2forms_webform_maps.module index af545a5d..a0991a8c 100644 --- a/modules/os2forms_webform_maps/os2forms_webform_maps.module +++ b/modules/os2forms_webform_maps/os2forms_webform_maps.module @@ -27,7 +27,13 @@ function os2forms_webform_maps_theme() { function os2forms_webform_maps_locale_translation_projects_alter(&$projects) { $module_handler = \Drupal::service('module_handler'); $path = $module_handler->getModule('os2forms_webform_maps')->getPath(); - $projects['os2forms_webform_maps']['info']['interface translation server pattern'] = $path . '/translations/%language.po'; + $projects['os2forms_webform_maps'] = [ + 'project_type' => 'module', + 'info' => [ + 'name' => 'OS2Forms Webform Maps', + 'interface translation server pattern' => $path . '/translations/%language.po' + ], + ]; } /** diff --git a/modules/os2forms_webform_texts/os2forms_webform_texts.info.yml b/modules/os2forms_webform_texts/os2forms_webform_texts.info.yml index a341d77c..a2a4985c 100644 --- a/modules/os2forms_webform_texts/os2forms_webform_texts.info.yml +++ b/modules/os2forms_webform_texts/os2forms_webform_texts.info.yml @@ -2,7 +2,7 @@ name: 'OS2forms webform texts' type: module description: 'Provides reusable webform tekst.' package: OS2Forms -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:webform_node_element' diff --git a/modules/os2forms_webform_texts/templates/node--webform-element.html.twig b/modules/os2forms_webform_texts/templates/node--webform-element.html.twig index 11157c0f..165cae37 100644 --- a/modules/os2forms_webform_texts/templates/node--webform-element.html.twig +++ b/modules/os2forms_webform_texts/templates/node--webform-element.html.twig @@ -13,7 +13,6 @@ view_mode ? 'node--view-mode-' ~ view_mode|clean_class, ] %} -{{ attach_library('classy/node') }} {{ content }} diff --git a/os2forms.info.yml b/os2forms.info.yml index d7e530d0..f7acc729 100644 --- a/os2forms.info.yml +++ b/os2forms.info.yml @@ -2,7 +2,7 @@ name: 'OS2Forms' type: module description: 'Provides advanced webform functionality for Danish Municipalities.' package: OS2Forms -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10 || ^11 dependencies: - 'drupal:editor' diff --git a/scripts/ci-composer b/scripts/ci-composer new file mode 100755 index 00000000..7f77443a --- /dev/null +++ b/scripts/ci-composer @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Install the leniency plugin before resolving this lockless Drupal module. +composer global config --no-plugins allow-plugins.mglaman/composer-drupal-lenient true +composer global require --no-interaction --no-progress --prefer-dist mglaman/composer-drupal-lenient:^1.0 + +exec composer "$@" diff --git a/scripts/code-analysis b/scripts/code-analysis index a0bdb083..bd8fb549 100755 --- a/scripts/code-analysis +++ b/scripts/code-analysis @@ -11,9 +11,9 @@ drupal_composer() { composer --working-dir="$drupal_dir" --no-interaction "$@" } -# Create new Drupal 10 project +# Create new Drupal 11 project if [ ! -f "$drupal_dir/composer.json" ]; then - composer --no-interaction create-project drupal/recommended-project:^10 "$drupal_dir" + composer --no-interaction create-project drupal/recommended-project:^11 "$drupal_dir" fi # Copy our code into the modules folder @@ -38,15 +38,15 @@ drupal_composer config minimum-stability dev # https://getcomposer.org/doc/06-config.md#allow-plugins drupal_composer config --no-plugins allow-plugins true -# Making Drupal 10 compatible -drupal_composer require psr/http-message:^1.0 +# Making Drupal 10/11 compatible +drupal_composer require "psr/http-message:^1.1 || ^2.0" drupal_composer require mglaman/composer-drupal-lenient:^1.0 -drupal_composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/coc_forms_auto_export", "drupal/webform_node_element"]' +drupal_composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/coc_forms_auto_export", "drupal/webform_node_element", "drupal/events_logging", "drupal/switch_page_theme"]' drupal_composer require wikimedia/composer-merge-plugin drupal_composer config extra.merge-plugin.include "$module_path/composer.json" # https://www.drupal.org/project/drupal/issues/3220043#comment-14845434 -drupal_composer require --dev symfony/phpunit-bridge +drupal_composer require --dev symfony/phpunit-bridge -W # Run PHPStan (cd "$drupal_dir/$module_path" && ../../../../vendor/bin/phpstan) diff --git a/src/Plugin/WebformHandler/SaveToFileWebformHandler.php b/src/Plugin/WebformHandler/SaveToFileWebformHandler.php index 58910d17..af38c4ba 100644 --- a/src/Plugin/WebformHandler/SaveToFileWebformHandler.php +++ b/src/Plugin/WebformHandler/SaveToFileWebformHandler.php @@ -6,6 +6,7 @@ use Drupal\Core\File\Exception\FileWriteException; use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Routing\TrustedRedirectResponse; use Drupal\Core\Serialization\Yaml; use Drupal\file\Entity\File; @@ -645,7 +646,7 @@ protected function isConvertEnabled() { * @param string $type * The type of message to be displayed to the end use. */ - protected function debug($message, $state, $file_path, $type = 'warning') { + protected function debug($message, $state, $file_path, $type = MessengerInterface::TYPE_WARNING) { if (empty($this->configuration['debug'])) { return; } @@ -699,7 +700,7 @@ protected function debug($message, $state, $file_path, $type = 'warning') { '#markup' => $message, ]; - $this->messenger()->addMessage(\Drupal::service('renderer')->renderPlain($build), $file_type); + $this->messenger()->addMessage(\Drupal::service('renderer')->renderPlain($build), $type); } /**