Skip to content

Commit d2c0791

Browse files
committed
fix: stabilize policy migration and runtime for signature_text
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent 40b771b commit d2c0791

3 files changed

Lines changed: 133 additions & 53 deletions

File tree

lib/Migration/Version18001Date20260320000000.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ private function migrateSignatureTextSettingsType(): void {
118118
];
119119

120120
// Normalize and encode the consolidated value
121-
$encodedValue = \OCA\Libresign\Service\Policy\Provider\SignatureText\SignatureTextPolicyValue::encode($consolidatedValue);
121+
$encodedValue = $this->encodeSignatureTextPolicyValue($consolidatedValue);
122122

123123
// Check if there's an existing consolidated value
124124
$existingValue = $this->appConfig->getValueString(
@@ -160,6 +160,27 @@ private function deleteLegacySignatureTextKeys(): void {
160160
}
161161
}
162162

163+
/**
164+
* @param array<string, mixed> $rawValue
165+
*/
166+
private function encodeSignatureTextPolicyValue(array $rawValue): string {
167+
$renderMode = strtolower(trim((string)($rawValue['render_mode'] ?? 'default')));
168+
if (!in_array($renderMode, ['default', 'graphic', 'text'], true)) {
169+
$renderMode = 'default';
170+
}
171+
172+
$normalized = [
173+
'template' => (string)($rawValue['template'] ?? ''),
174+
'template_font_size' => max(0.1, (float)($rawValue['template_font_size'] ?? 9.0)),
175+
'signature_font_size' => max(0.1, (float)($rawValue['signature_font_size'] ?? 9.0)),
176+
'signature_width' => max(0.1, (float)($rawValue['signature_width'] ?? 90.0)),
177+
'signature_height' => max(0.1, (float)($rawValue['signature_height'] ?? 60.0)),
178+
'render_mode' => $renderMode,
179+
];
180+
181+
return json_encode($normalized, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
182+
}
183+
163184
private function migrateGroupsRequestSignType(): void {
164185
$legacyValue = $this->readLegacyString(RequestSignGroupsPolicy::SYSTEM_APP_CONFIG_KEY);
165186
if ($legacyValue !== null) {

lib/Service/Policy/Provider/SignatureText/SignatureTextPolicy.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ final class SignatureTextPolicy implements IPolicyDefinitionProvider {
3636
#[\Override]
3737
public function keys(): array {
3838
return [
39+
self::KEY,
3940
self::KEY_TEMPLATE,
4041
self::KEY_TEMPLATE_FONT_SIZE,
4142
self::KEY_SIGNATURE_WIDTH,
@@ -50,6 +51,15 @@ public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
5051
$normalizedKey = $this->normalizePolicyKey($policyKey);
5152

5253
return match ($normalizedKey) {
54+
self::KEY => new PolicySpec(
55+
key: self::KEY,
56+
defaultSystemValue: $this->encodeConsolidatedValue($this->defaultConsolidatedValue()),
57+
allowedValues: [],
58+
normalizer: fn (mixed $rawValue): string => $this->encodeConsolidatedValue(
59+
$this->normalizeConsolidatedValue($rawValue),
60+
),
61+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY,
62+
),
5363
self::KEY_TEMPLATE => new PolicySpec(
5464
key: self::KEY_TEMPLATE,
5565
defaultSystemValue: '',
@@ -107,4 +117,57 @@ private function normalizePolicyKey(string|\BackedEnum $policyKey): string {
107117

108118
return $policyKey;
109119
}
120+
121+
/**
122+
* @return array<string, mixed>
123+
*/
124+
private function defaultConsolidatedValue(): array {
125+
return [
126+
'template' => '',
127+
'template_font_size' => 9.0,
128+
'signature_font_size' => 9.0,
129+
'signature_width' => 90.0,
130+
'signature_height' => 60.0,
131+
'render_mode' => 'default',
132+
];
133+
}
134+
135+
/**
136+
* @return array<string, mixed>
137+
*/
138+
private function normalizeConsolidatedValue(mixed $rawValue): array {
139+
$defaults = $this->defaultConsolidatedValue();
140+
141+
if (is_string($rawValue)) {
142+
$decoded = json_decode($rawValue, true);
143+
if (is_array($decoded)) {
144+
$rawValue = $decoded;
145+
}
146+
}
147+
148+
if (!is_array($rawValue)) {
149+
return $defaults;
150+
}
151+
152+
$renderMode = (string)($rawValue['render_mode'] ?? $defaults['render_mode']);
153+
if (!in_array($renderMode, ['default', 'graphic', 'text'], true)) {
154+
$renderMode = 'default';
155+
}
156+
157+
return [
158+
'template' => (string)($rawValue['template'] ?? $defaults['template']),
159+
'template_font_size' => max(0.1, (float)($rawValue['template_font_size'] ?? $defaults['template_font_size'])),
160+
'signature_font_size' => max(0.1, (float)($rawValue['signature_font_size'] ?? $defaults['signature_font_size'])),
161+
'signature_width' => max(0.1, (float)($rawValue['signature_width'] ?? $defaults['signature_width'])),
162+
'signature_height' => max(0.1, (float)($rawValue['signature_height'] ?? $defaults['signature_height'])),
163+
'render_mode' => $renderMode,
164+
];
165+
}
166+
167+
/**
168+
* @param array<string, mixed> $value
169+
*/
170+
private function encodeConsolidatedValue(array $value): string {
171+
return json_encode($value, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
172+
}
110173
}

tests/php/Unit/Migration/Version18001Date20260320000000Test.php

Lines changed: 48 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -58,32 +58,36 @@ public function testMigratesLegacyFooterSettingsIntoStructuredPayload(): void {
5858
$deletedKeys = [];
5959

6060
$this->appConfig
61-
->expects($this->once())
61+
->expects($this->atLeastOnce())
6262
->method('deleteKey')
6363
->willReturnCallback(static function (string $app, string $key) use (&$deletedKeys): void {
6464
$deletedKeys[] = [$app, $key];
6565
});
6666

6767
$this->appConfig
68-
->expects($this->once())
68+
->expects($this->atLeastOnce())
6969
->method('setValueString')
70-
->with(
71-
Application::APP_ID,
72-
'add_footer',
73-
FooterPolicyValue::encode([
74-
'enabled' => true,
75-
'writeQrcodeOnFooter' => false,
76-
'validationSite' => 'https://validator.example/base/',
77-
'customizeFooterTemplate' => true,
78-
]),
79-
);
70+
->willReturnCallback(static function (string $app, string $key, string $value): bool {
71+
if ($key === 'add_footer') {
72+
TestCase::assertSame(Application::APP_ID, $app);
73+
TestCase::assertSame(
74+
FooterPolicyValue::encode([
75+
'enabled' => true,
76+
'writeQrcodeOnFooter' => false,
77+
'validationSite' => 'https://validator.example/base/',
78+
'customizeFooterTemplate' => true,
79+
]),
80+
$value,
81+
);
82+
}
83+
84+
return true;
85+
});
8086

8187
$migration = new Version18001Date20260320000000($this->appConfig);
8288
$migration->preSchemaChange($this->createMock(IOutput::class), static fn () => null, []);
8389

84-
self::assertSame([
85-
[Application::APP_ID, 'add_footer'],
86-
], $deletedKeys);
90+
self::assertContains([Application::APP_ID, 'add_footer'], $deletedKeys);
8791
}
8892

8993
public function testReadsLegacyBooleanWhenAddFooterHasTypedBoolValue(): void {
@@ -149,25 +153,29 @@ public function testReadsLegacyBooleanWhenAddFooterHasTypedBoolValue(): void {
149153
});
150154

151155
$this->appConfig
152-
->expects($this->once())
156+
->expects($this->atLeastOnce())
153157
->method('setValueString')
154-
->with(
155-
Application::APP_ID,
156-
'add_footer',
157-
FooterPolicyValue::encode([
158-
'enabled' => false,
159-
'writeQrcodeOnFooter' => true,
160-
'validationSite' => '',
161-
'customizeFooterTemplate' => false,
162-
]),
163-
);
158+
->willReturnCallback(static function (string $app, string $key, string $value): bool {
159+
if ($key === 'add_footer') {
160+
TestCase::assertSame(Application::APP_ID, $app);
161+
TestCase::assertSame(
162+
FooterPolicyValue::encode([
163+
'enabled' => false,
164+
'writeQrcodeOnFooter' => true,
165+
'validationSite' => '',
166+
'customizeFooterTemplate' => false,
167+
]),
168+
$value,
169+
);
170+
}
171+
172+
return true;
173+
});
164174

165175
$migration = new Version18001Date20260320000000($this->appConfig);
166176
$migration->preSchemaChange($this->createMock(IOutput::class), static fn () => null, []);
167177

168-
self::assertSame([
169-
[Application::APP_ID, 'add_footer'],
170-
], $deletedKeys);
178+
self::assertContains([Application::APP_ID, 'add_footer'], $deletedKeys);
171179
}
172180

173181
public function testMigratesGroupsRequestSignFromTypedArrayToCanonicalString(): void {
@@ -251,14 +259,6 @@ public function testMigratesSignatureTextFloatSettingsFromLegacyStrings(): void
251259
$deleted[] = [$app, $key];
252260
});
253261

254-
$savedFloats = [];
255-
$this->appConfig
256-
->method('setValueFloat')
257-
->willReturnCallback(static function (string $app, string $key, float $value) use (&$savedFloats): bool {
258-
$savedFloats[$key] = $value;
259-
return true;
260-
});
261-
262262
$savedStrings = [];
263263
$this->appConfig
264264
->method('setValueString')
@@ -270,10 +270,13 @@ public function testMigratesSignatureTextFloatSettingsFromLegacyStrings(): void
270270
$migration = new Version18001Date20260320000000($this->appConfig);
271271
$migration->preSchemaChange($this->createMock(IOutput::class), static fn () => null, []);
272272

273-
self::assertSame(11.5, $savedFloats[SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_TEMPLATE_FONT_SIZE]);
274-
self::assertSame(350.0, $savedFloats[SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_WIDTH]);
275-
self::assertSame(100.25, $savedFloats[SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_HEIGHT]);
276-
self::assertSame(18.0, $savedFloats[SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_FONT_SIZE]);
273+
self::assertArrayHasKey(SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY, $savedStrings);
274+
$decoded = json_decode($savedStrings[SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY], true);
275+
self::assertIsArray($decoded);
276+
self::assertSame(11.5, $decoded['template_font_size']);
277+
self::assertEquals(350.0, $decoded['signature_width']);
278+
self::assertSame(100.25, $decoded['signature_height']);
279+
self::assertEquals(18.0, $decoded['signature_font_size']);
277280
self::assertContains([Application::APP_ID, SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_TEMPLATE_FONT_SIZE], $deleted);
278281
self::assertContains([Application::APP_ID, SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_WIDTH], $deleted);
279282
self::assertContains([Application::APP_ID, SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_HEIGHT], $deleted);
@@ -321,28 +324,21 @@ public function testNormalizesSignatureTextRenderModeToCanonicalPolicyValue(): v
321324
$this->appConfig
322325
->method('setValueString')
323326
->willReturnCallback(static function (string $app, string $key, string $value) use (&$renderModeWasNormalized): bool {
324-
if ($key === SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_RENDER_MODE) {
327+
if ($key === SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY) {
325328
TestCase::assertSame(Application::APP_ID, $app);
326-
TestCase::assertSame('default', $value);
329+
$decoded = json_decode($value, true);
330+
TestCase::assertIsArray($decoded);
331+
TestCase::assertSame('default', $decoded['render_mode'] ?? null);
327332
$renderModeWasNormalized = true;
328333
}
329334
return true;
330335
});
331336

332-
$savedFloats = [];
333-
$this->appConfig
334-
->method('setValueFloat')
335-
->willReturnCallback(static function (string $app, string $key, float $value) use (&$savedFloats): bool {
336-
$savedFloats[$key] = $value;
337-
return true;
338-
});
339-
340337
$migration = new Version18001Date20260320000000($this->appConfig);
341338
$migration->preSchemaChange($this->createMock(IOutput::class), static fn () => null, []);
342339

343340
self::assertTrue($renderModeWasNormalized);
344341
self::assertContains([Application::APP_ID, SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_RENDER_MODE], $deleted);
345-
self::assertSame([], $savedFloats);
346342
}
347343

348344
public function testMigratesPendingBooleanPoliciesFromLegacyStrings(): void {

0 commit comments

Comments
 (0)