Skip to content

Commit d9b4a02

Browse files
authored
Merge branch 'develop' into feature/digital_signature_ips
2 parents f5fac02 + 5fc32cb commit d9b4a02

File tree

11 files changed

+147
-39
lines changed

11 files changed

+147
-39
lines changed

.github/workflows/pr.yml

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,24 @@ jobs:
2323
runs-on: ubuntu-latest
2424
steps:
2525
- uses: actions/checkout@v4
26+
27+
- name: Mark repo as safe for git (container)
28+
run: |
29+
docker compose run --rm --user 0 php bash -lc \
30+
'git config --system --add safe.directory /app || git config --global --add safe.directory /app'
31+
32+
- name: Fix ownership (container)
33+
run: |
34+
docker compose run --rm --user 0 -e HOST_UID=$(id -u) -e HOST_GID=$(id -g) php bash -lc 'chown -R ${HOST_UID}:${HOST_GID} /app'
35+
2636
- name: Validate composer files
2737
run: |
28-
# Note that we don't use the --strict flag on validate due to the
29-
# package drupal/config_entity_revisions 2.0.x-dev being considered a
30-
# version cf.
31-
# https://getcomposer.org/doc/articles/versions.md#branches
32-
docker compose run --rm php composer validate composer.json
38+
docker compose run --rm --user "$(id -u):$(id -g)" php composer validate composer.json
39+
3340
- name: Check that composer file is normalized
3441
run: |
35-
docker compose run --rm php composer install
36-
docker compose run --rm php composer normalize --dry-run
42+
docker compose run --rm --user "$(id -u):$(id -g)" php composer install
43+
docker compose run --rm --user "$(id -u):$(id -g)" php composer normalize --dry-run
3744
3845
test-composer-files:
3946
name: Test composer files
@@ -43,29 +50,50 @@ jobs:
4350
dependency-version: [ prefer-lowest, prefer-stable ]
4451
steps:
4552
- uses: actions/checkout@v4
53+
54+
- name: Fix ownership (container)
55+
run: |
56+
docker compose run --rm --user 0 -e HOST_UID=$(id -u) -e HOST_GID=$(id -g) php bash -lc 'chown -R ${HOST_UID}:${HOST_GID} /app'
57+
58+
- name: Debug compose + php
59+
run: |
60+
ls -la
61+
docker compose config
62+
docker compose run --rm php php -v
63+
4664
- name: Check that dependencies resolve.
4765
run: |
48-
# Clean up before update (cf. https://www.drupal.org/project/simplesamlphp_auth/issues/3350773)
4966
rm -fr vendor/
50-
docker compose run --rm php composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction
67+
docker compose run --rm --user "$(id -u):$(id -g)" php composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction
5168
5269
php-coding-standards:
5370
name: PHP coding standards
5471
runs-on: ubuntu-latest
5572
steps:
5673
- uses: actions/checkout@v4
74+
75+
- name: Fix ownership (container)
76+
run: |
77+
docker compose run --rm --user 0 -e HOST_UID=$(id -u) -e HOST_GID=$(id -g) php bash -lc 'chown -R ${HOST_UID}:${HOST_GID} /app'
78+
5779
- name: Install Dependencies
5880
run: |
59-
docker compose run --rm php composer install
81+
docker compose run --rm --user "$(id -u):$(id -g)" php composer install
82+
6083
- name: PHPCS
6184
run: |
62-
docker compose run --rm php composer coding-standards-check/phpcs
85+
docker compose run --rm --user "$(id -u):$(id -g)" php composer coding-standards-check/phpcs
6386
6487
php-code-analysis:
6588
name: PHP code analysis
6689
runs-on: ubuntu-latest
6790
steps:
6891
- uses: actions/checkout@v4
92+
93+
- name: Fix ownership (container)
94+
run: |
95+
docker compose run --rm --user 0 -e HOST_UID=$(id -u) -e HOST_GID=$(id -g) php bash -lc 'chown -R ${HOST_UID}:${HOST_GID} /app'
96+
6997
- name: Code analysis
7098
run: |
7199
./scripts/code-analysis

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,17 @@ before starting to add changes. Use example [placed in the end of the page](#exa
1111

1212
## [Unreleased]
1313

14-
- Fix IP validation in digital signature file download (CIDR support)
14+
- [PR-305](https://github.com/OS2Forms/os2forms/pull/305)
15+
Fix IP validation in digital signature file download (CIDR support)
16+
- [PR-317](https://github.com/OS2Forms/os2forms/pull/317)
17+
Updated code analysis script.
18+
- [PR-306](https://github.com/OS2Forms/os2forms/pull/306)
19+
Made digital signature text placement configurable.
20+
- [#251](https://github.com/OS2Forms/os2forms/issues/251)
21+
Webform encrypt uninstall problem fix
22+
- git actions check
23+
- [PR-289](https://github.com/OS2Forms/os2forms/pull/289)
24+
Added required "Zoom control position" to map element
1525

1626
## [5.0.0] 2025-11-18
1727

compose.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@
22

33
services:
44
php:
5-
image: ddev/ddev-php-base
5+
image: ddev/ddev-php-base:v1.24.10-old
66
profiles:
77
- dev
88
working_dir: /app
99
volumes:
1010
- ./:/app
11+
environment:
12+
COMPOSER_HOME: /tmp/composer
13+
COMPOSER_CACHE_DIR: /tmp/composer-cache
14+
entrypoint:
15+
- bash
16+
- -lc
17+
- |
18+
git config --global --add safe.directory /app
19+
exec "$@"
20+
- bash
1121

1222
markdownlint:
1323
image: itkdev/markdownlint

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
"Add custom hook (hook_webform_post_load_data) for audit logging": "https://gist.githubusercontent.com/cableman/d26898fc8f65ee0a31001bf391583b59/raw/6189dc4c2ceaabb19d25cc4b98b0b3028a6b0e1e/gistfile1.txt"
129129
},
130130
"drupal/webform_encrypt": {
131-
"Ensure data is base64 encoded (https://www.drupal.org/project/webform_encrypt/issues/3399414)": "https://git.drupalcode.org/project/webform_encrypt/-/merge_requests/4.patch",
131+
"Ensure data is base64 encoded (https://www.drupal.org/project/webform_encrypt/issues/3399414)": "https://www.drupal.org/files/issues/2026-02-06/patch.diff",
132132
"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"
133133
},
134134
"drupal/webform_node_element": {

modules/os2forms_attachment/src/Element/AttachmentElement.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Drupal\os2forms_attachment\Element;
44

5+
use Drupal\os2forms_attachment\Os2formsAttachmentPrintBuilder;
56
use Drupal\webform\Entity\WebformSubmission;
67
use Drupal\webform\WebformSubmissionInterface;
78
use Drupal\webform_attachment\Element\WebformAttachmentBase;
@@ -21,6 +22,7 @@ public function getInfo() {
2122
'#view_mode' => 'html',
2223
'#export_type' => 'pdf',
2324
'#digital_signature' => FALSE,
25+
'#digital_signature_position' => Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT,
2426
'#template' => '',
2527
];
2628
}
@@ -77,7 +79,8 @@ public static function getFileContent(array $element, WebformSubmissionInterface
7779

7880
// Adding digital signature.
7981
if (isset($element['#digital_signature']) && $element['#digital_signature']) {
80-
$file_path = $print_builder->savePrintableDigitalSignature([$webform_submission], $print_engine, $scheme, $file_name);
82+
$signaturePosition = $element['#digital_signature_position'] ?? Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT;
83+
$file_path = $print_builder->savePrintableDigitalSignature([$webform_submission], $print_engine, $scheme, $file_name, TRUE, $signaturePosition);
8184
}
8285
else {
8386
$file_path = $print_builder->savePrintable([$webform_submission], $print_engine, $scheme, $file_name);

modules/os2forms_attachment/src/Os2formsAttachmentPrintBuilder.php

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
*/
1919
class Os2formsAttachmentPrintBuilder extends PrintBuilder {
2020

21+
public const SIGNATURE_POSITION_FOOTER = 'footer';
22+
public const SIGNATURE_POSITION_HEADER = 'header';
23+
public const SIGNATURE_POSITION_AFTER_CONTENT = 'after_content';
24+
public const SIGNATURE_POSITION_BEFORE_CONTENT = 'before_content';
25+
2126
/**
2227
* {@inheritdoc}
2328
*/
@@ -44,16 +49,16 @@ public function printHtml(EntityInterface $entity, $use_default_css = TRUE, $opt
4449
/**
4550
* Modified version of the original savePrintable() function.
4651
*
47-
* The only difference is modified call to prepareRenderer with digitalPost
48-
* flag TRUE.
52+
* The only difference is modified call to prepareRenderer with a
53+
* signature position parameter.
4954
*
5055
* @see PrintBuilder::savePrintable()
5156
*
5257
* @return string
5358
* FALSE or the URI to the file. E.g. public://my-file.pdf.
5459
*/
55-
public function savePrintableDigitalSignature(array $entities, PrintEngineInterface $print_engine, $scheme = 'public', $filename = FALSE, $use_default_css = TRUE) {
56-
$renderer = $this->prepareRenderer($entities, $print_engine, $use_default_css, TRUE);
60+
public function savePrintableDigitalSignature(array $entities, PrintEngineInterface $print_engine, $scheme = 'public', $filename = FALSE, $use_default_css = TRUE, string $signaturePosition = self::SIGNATURE_POSITION_AFTER_CONTENT) {
61+
$renderer = $this->prepareRenderer($entities, $print_engine, $use_default_css, $signaturePosition);
5762

5863
// Allow other modules to alter the generated Print object.
5964
$this->dispatcher->dispatch(new PreSendPrintEvent($print_engine, $entities), PrintEvents::PRE_SEND);
@@ -82,15 +87,16 @@ public function savePrintableDigitalSignature(array $entities, PrintEngineInterf
8287
* The print engine.
8388
* @param bool $use_default_css
8489
* TRUE if we want the default CSS included.
85-
* @param bool $digitalSignature
86-
* If the digital signature message needs to be added.
90+
* @param string $signaturePosition
91+
* The position for the digital signature validation text. Empty string
92+
* means no signature. Use the SIGNATURE_POSITION_* class constants.
8793
*
8894
* @return \Drupal\entity_print\Renderer\RendererInterface
8995
* A print renderer.
9096
*
9197
* @see PrintBuilder::prepareRenderer
9298
*/
93-
protected function prepareRenderer(array $entities, PrintEngineInterface $print_engine, $use_default_css, $digitalSignature = FALSE) {
99+
protected function prepareRenderer(array $entities, PrintEngineInterface $print_engine, $use_default_css, string $signaturePosition = '') {
94100
if (empty($entities)) {
95101
throw new \InvalidArgumentException('You must pass at least 1 entity');
96102
}
@@ -106,12 +112,30 @@ protected function prepareRenderer(array $entities, PrintEngineInterface $print_
106112
'#attached' => [],
107113
];
108114

109-
// Adding hardcoded negative margin to avoid margins in <fieldset> <legend>
110-
// structure. That margin is automatically added in PDF and PDF only.
111115
$generatedHtml = (string) $renderer->generateHtml($entities, $render, $use_default_css, TRUE);
112-
$generatedHtml .= "<style>fieldset legend {margin-left: -12px;}</style>";
113-
if ($digitalSignature) {
114-
$generatedHtml .= $this->t('You can validate the signature on this PDF file via validering.nemlog-in.dk.');
116+
117+
// Place signature according to the passed parameter.
118+
if ($signaturePosition) {
119+
$signatureHtml = '<div class="validate-signature-element"><p>' . $this->t('You can validate the signature on this PDF file via validering.nemlog-in.dk.') . '</p></div>';
120+
121+
switch ($signaturePosition) {
122+
case self::SIGNATURE_POSITION_HEADER:
123+
$generatedHtml = str_replace('</header>', $signatureHtml . '</header>', $generatedHtml);
124+
break;
125+
126+
case self::SIGNATURE_POSITION_BEFORE_CONTENT:
127+
$generatedHtml = str_replace('<div class="page">', '<div class="page">' . $signatureHtml, $generatedHtml);
128+
break;
129+
130+
case self::SIGNATURE_POSITION_FOOTER:
131+
$generatedHtml = str_replace('</footer>', $signatureHtml . '</footer>', $generatedHtml);
132+
break;
133+
134+
case self::SIGNATURE_POSITION_AFTER_CONTENT:
135+
default:
136+
$generatedHtml = str_replace('</body>', $signatureHtml . '</body>', $generatedHtml);
137+
break;
138+
}
115139
}
116140

117141
$print_engine->addPage($generatedHtml);

modules/os2forms_attachment/src/Plugin/WebformElement/AttachmentElement.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Drupal\Core\Form\FormStateInterface;
66
use Drupal\webform\Twig\WebformTwigExtension;
77
use Drupal\webform\Utility\WebformElementHelper;
8+
use Drupal\os2forms_attachment\Os2formsAttachmentPrintBuilder;
89
use Drupal\webform_attachment\Plugin\WebformElement\WebformAttachmentBase;
910

1011
/**
@@ -28,6 +29,7 @@ protected function defineDefaultProperties() {
2829
'template' => '',
2930
'export_type' => '',
3031
'digital_signature' => '',
32+
'digital_signature_position' => Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT,
3133
'exclude_empty' => '',
3234
'exclude_empty_checkbox' => '',
3335
'excluded_elements' => '',
@@ -93,6 +95,23 @@ public function form(array $form, FormStateInterface $form_state) {
9395
'#type' => 'checkbox',
9496
'#title' => $this->t('Digital signature'),
9597
];
98+
$form['attachment']['digital_signature_position'] = [
99+
'#type' => 'select',
100+
'#title' => $this->t('Digital signature position'),
101+
'#description' => $this->t('Select where the digital signature validation text should be placed in the PDF document.'),
102+
'#options' => [
103+
Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_FOOTER => $this->t('Footer (repeats on every page)'),
104+
Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_HEADER => $this->t('Header (repeats on every page)'),
105+
Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT => $this->t('After content (end of document)'),
106+
Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_BEFORE_CONTENT => $this->t('Before content (start of document)'),
107+
],
108+
'#default_value' => Os2formsAttachmentPrintBuilder::SIGNATURE_POSITION_AFTER_CONTENT,
109+
'#states' => [
110+
'visible' => [
111+
':input[name="properties[digital_signature]"]' => ['checked' => TRUE],
112+
],
113+
],
114+
];
96115

97116
// Set #access so that help is always visible.
98117
WebformElementHelper::setPropertyRecursive($form['attachment']['help'], '#access', TRUE);

modules/os2forms_webform_maps/os2forms_webform_maps.libraries.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
webformmap:
22
version: 1.x
3-
css:
4-
theme:
5-
css/webform_map.css: {}
63
js:
74
js/webform_map.js: {}
85
dependencies:

modules/os2forms_webform_maps/src/Element/WebformLeafletMapField.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Drupal\Core\Form\FormStateInterface;
66
use Drupal\Core\Render\Element\FormElement;
77
use Drupal\webform\Element\WebformCompositeFormElementTrait;
8+
use Drupal\os2forms_webform_maps\Plugin\WebformElement\WebformLeafletMapField as WebformLeafletMapElement;
89

910
/**
1011
* Provides a webform_map_field.
@@ -36,7 +37,7 @@ public function getInfo() {
3637
'#minZoom' => 1,
3738
'#maxZoom' => 18,
3839
'#zoomFiner' => 0,
39-
'#position' => 'topleft',
40+
'#position' => WebformLeafletMapElement::LEAFLET_POSITION_TOP_LEFT,
4041
'#marker' => 'defaultMarker',
4142
'#drawPolyline' => 0,
4243
'#drawRectangle' => 0,
@@ -90,6 +91,7 @@ public static function processWebformMapElement(&$element, FormStateInterface $f
9091
'zoomFiner' => $element['#zoomFiner'],
9192
'minZoom' => $element['#minZoom'],
9293
'maxZoom' => $element['#maxZoom'],
94+
'zoomControlPosition' => $element['#zoomControlPosition'] ?? WebformLeafletMapElement::LEAFLET_POSITION_TOP_LEFT,
9395
'center' => [
9496
'lat' => (float) $element['#lat'],
9597
'lon' => (float) $element['#lon'],

modules/os2forms_webform_maps/src/Plugin/WebformElement/WebformLeafletMapField.php

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ class WebformLeafletMapField extends WebformElementBase {
2020

2121
use LeafletSettingsElementsTrait;
2222

23+
// Valid Leaflet control positions (cf.
24+
// https://github.com/Leaflet/Leaflet/blob/main/src/control/Control.js).
25+
const string LEAFLET_POSITION_TOP_LEFT = 'topleft';
26+
const string LEAFLET_POSITION_TOP_RIGHT = 'topright';
27+
const string LEAFLET_POSITION_BOTTOM_LEFT = 'bottomleft';
28+
const string LEAFLET_POSITION_BOTTOM_RIGHT = 'bottomright';
29+
2330
/**
2431
* {@inheritdoc}
2532
*/
@@ -33,10 +40,11 @@ public function defineDefaultProperties(): array {
3340
'minZoom' => 1,
3441
'maxZoom' => 18,
3542
'zoomFiner' => 0,
43+
'zoomControlPosition' => self::LEAFLET_POSITION_TOP_LEFT,
3644
'scrollWheelZoom' => 0,
3745
'doubleClickZoom' => 1,
3846

39-
'position' => 'topleft',
47+
'position' => self::LEAFLET_POSITION_TOP_LEFT,
4048
'marker' => 'defaultMarker',
4149
'drawPolyline' => 0,
4250
'drawRectangle' => 0,
@@ -72,6 +80,13 @@ public function form(array $form, FormStateInterface $form_state) {
7280
$form = parent::form($form, $form_state);
7381
$map_keys = array_keys(leaflet_map_get_info());
7482

83+
$positionOptions = [
84+
self::LEAFLET_POSITION_TOP_LEFT => $this->t('topleft'),
85+
self::LEAFLET_POSITION_TOP_RIGHT => $this->t('topright'),
86+
self::LEAFLET_POSITION_BOTTOM_LEFT => $this->t('bottomleft'),
87+
self::LEAFLET_POSITION_BOTTOM_RIGHT => $this->t('bottomright'),
88+
];
89+
7590
$form['mapstyles'] = [
7691
'#type' => 'fieldset',
7792
'#title' => $this->t('Map settings'),
@@ -139,6 +154,11 @@ public function form(array $form, FormStateInterface $form_state) {
139154
'#step' => 1,
140155
'#description' => $this->t('Value that might/will be added to default Fit Elements Bounds Zoom. (-5 / +5)'),
141156
],
157+
'zoomControlPosition' => [
158+
'#type' => 'select',
159+
'#title' => $this->t('Zoom control position'),
160+
'#options' => $positionOptions,
161+
],
142162
'scrollWheelZoom' => [
143163
'#type' => 'checkbox',
144164
'#title' => $this->t('Enable Scroll Wheel Zoom on click'),
@@ -159,12 +179,7 @@ public function form(array $form, FormStateInterface $form_state) {
159179
'position' => [
160180
'#type' => 'select',
161181
'#title' => $this->t('Toolbar position.'),
162-
'#options' => [
163-
'topleft' => $this->t('topleft'),
164-
'topright' => $this->t('topright'),
165-
'bottomleft' => $this->t('bottomleft'),
166-
'bottomright' => $this->t('bottomright'),
167-
],
182+
'#options' => $positionOptions,
168183
],
169184
'marker' => [
170185
'#type' => 'radios',

0 commit comments

Comments
 (0)