Skip to content

Commit f4afb5f

Browse files
committed
feat(stage20): consolidate signature_text family into unified policy model
- Refactor SignatureTextPolicy to use single chained key with JSON payload - Create SignatureTextPolicyValue for encoding/decoding consolidated config - Update PolicyProviders to register only signature_text key (not 6 individual) - Update Migration to consolidate 6 legacy appconfig keys into JSON - Create Workbench realDefinition for signature_text with unified editor - Create SignatureTextRuleEditor Vue component with all 6 fields - Add signature_text to realDefinitions export - Remove legacy loadState from Admin.php (now via effective_policies) - Keep non-policy preferences (signature_preview_zoom_level, signature_background_type) This enables batch migration of the signature_text family as a cohesive unit, avoiding fragile intermediate states. Backend stores single JSON, frontend presents unified UX with synchronized 6-field editor.
1 parent 209a3d9 commit f4afb5f

9 files changed

Lines changed: 406 additions & 98 deletions

File tree

lib/Migration/Version18001Date20260320000000.php

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -107,42 +107,57 @@ private function normalizeSignatureFlowValue(string $value): string {
107107
}
108108

109109
private function migrateSignatureTextSettingsType(): void {
110-
$signatureTextPolicy = new SignatureTextPolicy();
111-
$renderModeDefinition = $signatureTextPolicy->get(SignatureTextPolicy::KEY_RENDER_MODE);
110+
// First, consolidate individual keys into a JSON payload
111+
$consolidatedValue = [
112+
'template' => $this->readLegacyString(SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_TEMPLATE) ?? '',
113+
'template_font_size' => (float)($this->readLegacyString(SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_TEMPLATE_FONT_SIZE) ?? 9.0),
114+
'signature_font_size' => (float)($this->readLegacyString(SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_FONT_SIZE) ?? 9.0),
115+
'signature_width' => (float)($this->readLegacyString(SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_WIDTH) ?? 90.0),
116+
'signature_height' => (float)($this->readLegacyString(SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_HEIGHT) ?? 60.0),
117+
'render_mode' => $this->readLegacyString(SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_RENDER_MODE) ?? 'default',
118+
];
119+
120+
// Normalize and encode the consolidated value
121+
$encodedValue = \OCA\Libresign\Service\Policy\Provider\SignatureText\SignatureTextPolicyValue::encode($consolidatedValue);
112122

113-
$floatKeys = [
123+
// Check if there's an existing consolidated value
124+
$existingValue = $this->appConfig->getValueString(
125+
Application::APP_ID,
126+
SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY,
127+
'',
128+
);
129+
130+
// Only update if we have legacy values or no existing consolidated value
131+
if (!empty($existingValue) && $existingValue !== '') {
132+
// Already consolidated, just clean up legacy keys
133+
$this->deleteLegacySignatureTextKeys();
134+
return;
135+
}
136+
137+
// Delete all individual legacy keys
138+
$this->deleteLegacySignatureTextKeys();
139+
140+
// Save the consolidated JSON value
141+
$this->appConfig->setValueString(
142+
Application::APP_ID,
143+
SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY,
144+
$encodedValue,
145+
);
146+
}
147+
148+
private function deleteLegacySignatureTextKeys(): void {
149+
$legacyKeys = [
150+
SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_TEMPLATE,
114151
SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_TEMPLATE_FONT_SIZE,
115152
SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_WIDTH,
116153
SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_HEIGHT,
117154
SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_SIGNATURE_FONT_SIZE,
155+
SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_RENDER_MODE,
118156
];
119157

120-
foreach ($floatKeys as $key) {
121-
$legacyValue = $this->readLegacyString($key);
122-
if ($legacyValue === null || $legacyValue === '') {
123-
continue;
124-
}
125-
126-
if (!is_numeric(trim($legacyValue))) {
127-
continue;
128-
}
129-
158+
foreach ($legacyKeys as $key) {
130159
$this->appConfig->deleteKey(Application::APP_ID, $key);
131-
$this->appConfig->setValueFloat(Application::APP_ID, $key, (float)$legacyValue);
132-
}
133-
134-
$renderMode = $this->readLegacyString(SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_RENDER_MODE);
135-
if ($renderMode === null || $renderMode === '') {
136-
return;
137-
}
138-
139-
$normalizedRenderMode = (string)$renderModeDefinition->normalizeValue($renderMode);
140-
if ($normalizedRenderMode === $renderMode) {
141-
return;
142160
}
143-
144-
$this->appConfig->deleteKey(Application::APP_ID, SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_RENDER_MODE);
145-
$this->appConfig->setValueString(Application::APP_ID, SignatureTextPolicy::SYSTEM_APP_CONFIG_KEY_RENDER_MODE, $normalizedRenderMode);
146161
}
147162

148163
private function migrateGroupsRequestSignType(): void {

lib/Service/Policy/Provider/PolicyProviders.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ final class PolicyProviders {
2525
RequestSignGroupsPolicy::KEY => RequestSignGroupsPolicy::class,
2626
SignatureFlowPolicy::KEY => SignatureFlowPolicy::class,
2727
IdentificationDocumentsPolicy::KEY => IdentificationDocumentsPolicy::class,
28-
SignatureTextPolicy::KEY_TEMPLATE => SignatureTextPolicy::class,
29-
SignatureTextPolicy::KEY_TEMPLATE_FONT_SIZE => SignatureTextPolicy::class,
30-
SignatureTextPolicy::KEY_SIGNATURE_WIDTH => SignatureTextPolicy::class,
31-
SignatureTextPolicy::KEY_SIGNATURE_HEIGHT => SignatureTextPolicy::class,
32-
SignatureTextPolicy::KEY_SIGNATURE_FONT_SIZE => SignatureTextPolicy::class,
33-
SignatureTextPolicy::KEY_RENDER_MODE => SignatureTextPolicy::class,
28+
SignatureTextPolicy::KEY => SignatureTextPolicy::class,
3429
];
3530
}

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

Lines changed: 19 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
final class SignatureTextPolicy implements IPolicyDefinitionProvider {
1616
public const KEY = 'signature_text';
17+
public const SYSTEM_APP_CONFIG_KEY = 'signature_text';
1718

19+
// Legacy keys for migration purposes (will be consolidated into KEY)
1820
public const KEY_TEMPLATE = 'signature_text_template';
1921
public const KEY_TEMPLATE_FONT_SIZE = 'template_font_size';
2022
public const KEY_SIGNATURE_WIDTH = 'signature_width';
@@ -31,69 +33,23 @@ final class SignatureTextPolicy implements IPolicyDefinitionProvider {
3133

3234
#[\Override]
3335
public function keys(): array {
34-
return [
35-
self::KEY_TEMPLATE,
36-
self::KEY_TEMPLATE_FONT_SIZE,
37-
self::KEY_SIGNATURE_WIDTH,
38-
self::KEY_SIGNATURE_HEIGHT,
39-
self::KEY_SIGNATURE_FONT_SIZE,
40-
self::KEY_RENDER_MODE,
41-
];
36+
return [self::KEY];
4237
}
4338

4439
#[\Override]
4540
public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
4641
$normalizedKey = $this->normalizePolicyKey($policyKey);
4742

4843
return match ($normalizedKey) {
49-
self::KEY_TEMPLATE => new PolicySpec(
50-
key: self::KEY_TEMPLATE,
51-
defaultSystemValue: '',
44+
self::KEY => new PolicySpec(
45+
key: self::KEY,
46+
defaultSystemValue: SignatureTextPolicyValue::encode(SignatureTextPolicyValue::DEFAULTS),
5247
allowedValues: [],
53-
normalizer: static fn (mixed $rawValue): string => (string)$rawValue,
54-
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_TEMPLATE,
55-
),
56-
self::KEY_TEMPLATE_FONT_SIZE => new PolicySpec(
57-
key: self::KEY_TEMPLATE_FONT_SIZE,
58-
defaultSystemValue: 9.0,
59-
allowedValues: [],
60-
normalizer: static fn (mixed $rawValue): float => (float)$rawValue,
61-
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_TEMPLATE_FONT_SIZE,
62-
),
63-
self::KEY_SIGNATURE_WIDTH => new PolicySpec(
64-
key: self::KEY_SIGNATURE_WIDTH,
65-
defaultSystemValue: 90.0,
66-
allowedValues: [],
67-
normalizer: static fn (mixed $rawValue): float => (float)$rawValue,
68-
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_SIGNATURE_WIDTH,
69-
),
70-
self::KEY_SIGNATURE_HEIGHT => new PolicySpec(
71-
key: self::KEY_SIGNATURE_HEIGHT,
72-
defaultSystemValue: 60.0,
73-
allowedValues: [],
74-
normalizer: static fn (mixed $rawValue): float => (float)$rawValue,
75-
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_SIGNATURE_HEIGHT,
76-
),
77-
self::KEY_SIGNATURE_FONT_SIZE => new PolicySpec(
78-
key: self::KEY_SIGNATURE_FONT_SIZE,
79-
defaultSystemValue: 9.0,
80-
allowedValues: [],
81-
normalizer: static fn (mixed $rawValue): float => (float)$rawValue,
82-
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_SIGNATURE_FONT_SIZE,
83-
),
84-
self::KEY_RENDER_MODE => new PolicySpec(
85-
key: self::KEY_RENDER_MODE,
86-
defaultSystemValue: 'default',
87-
allowedValues: [
88-
'default',
89-
'graphic',
90-
'text',
91-
],
92-
normalizer: static fn (mixed $rawValue): string => match ((string)$rawValue) {
93-
'default', 'graphic', 'text' => (string)$rawValue,
94-
default => 'default',
48+
normalizer: function (mixed $rawValue): string {
49+
$normalized = SignatureTextPolicyValue::normalize($rawValue);
50+
return SignatureTextPolicyValue::encode($normalized);
9551
},
96-
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_RENDER_MODE,
52+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY,
9753
),
9854
default => throw new \InvalidArgumentException('Unknown policy key: ' . $normalizedKey),
9955
};
@@ -107,3 +63,12 @@ private function normalizePolicyKey(string|\BackedEnum $policyKey): string {
10763
return $policyKey;
10864
}
10965
}
66+
67+
private function normalizePolicyKey(string|\BackedEnum $policyKey): string {
68+
if ($policyKey instanceof \BackedEnum) {
69+
return (string)$policyKey->value;
70+
}
71+
72+
return $policyKey;
73+
}
74+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2026 LibreCode coop and LibreCode contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Service\Policy\Provider\SignatureText;
10+
11+
final class SignatureTextPolicyValue {
12+
/** @var array<string, mixed> */
13+
public const DEFAULTS = [
14+
'template' => '',
15+
'template_font_size' => 9.0,
16+
'signature_font_size' => 9.0,
17+
'signature_width' => 90.0,
18+
'signature_height' => 60.0,
19+
'render_mode' => 'default',
20+
];
21+
22+
/**
23+
* @param mixed $rawValue
24+
* @return array<string, mixed>
25+
*/
26+
public static function normalize(mixed $rawValue, ?array $defaults = null): array {
27+
$defaults ??= self::DEFAULTS;
28+
29+
if (is_string($rawValue)) {
30+
try {
31+
$decoded = json_decode($rawValue, true);
32+
if (is_array($decoded)) {
33+
$rawValue = $decoded;
34+
}
35+
} catch (\JsonException) {
36+
// Fallback to defaults
37+
}
38+
}
39+
40+
if (!is_array($rawValue)) {
41+
return $defaults;
42+
}
43+
44+
return [
45+
'template' => self::normalizeString($rawValue['template'] ?? $defaults['template']),
46+
'template_font_size' => self::normalizeFloat($rawValue['template_font_size'] ?? $defaults['template_font_size']),
47+
'signature_font_size' => self::normalizeFloat($rawValue['signature_font_size'] ?? $defaults['signature_font_size']),
48+
'signature_width' => self::normalizeFloat($rawValue['signature_width'] ?? $defaults['signature_width']),
49+
'signature_height' => self::normalizeFloat($rawValue['signature_height'] ?? $defaults['signature_height']),
50+
'render_mode' => self::normalizeRenderMode($rawValue['render_mode'] ?? $defaults['render_mode']),
51+
];
52+
}
53+
54+
/**
55+
* @param array<string, mixed> $value
56+
*/
57+
public static function encode(array $value): string {
58+
$normalized = self::normalize($value);
59+
return json_encode($normalized, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
60+
}
61+
62+
private static function normalizeString(mixed $value): string {
63+
return (string)($value ?? '');
64+
}
65+
66+
private static function normalizeFloat(mixed $value): float {
67+
$float = (float)($value ?? 0);
68+
return max(0.1, $float);
69+
}
70+
71+
private static function normalizeRenderMode(mixed $value): string {
72+
$mode = (string)($value ?? 'default');
73+
return match ($mode) {
74+
'default', 'graphic', 'text' => $mode,
75+
default => 'default',
76+
};
77+
}
78+
}

lib/Settings/Admin.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,10 @@ public function getForm(): TemplateResponse {
6868
$this->initialState->provideInitialState('certificate_policies_oid', $this->certificatePolicyService->getOid());
6969
$this->initialState->provideInitialState('certificate_policies_cps', $this->certificatePolicyService->getCps());
7070
$this->initialState->provideInitialState('config_path', $this->appConfig->getValueString(Application::APP_ID, 'config_path'));
71-
$this->initialState->provideInitialState('default_signature_font_size', SignatureTextService::SIGNATURE_DEFAULT_FONT_SIZE);
72-
$this->initialState->provideInitialState('default_signature_height', SignatureTextService::DEFAULT_SIGNATURE_HEIGHT);
73-
$this->initialState->provideInitialState('default_signature_text_template', $this->signatureTextService->getDefaultTemplate());
74-
$this->initialState->provideInitialState('default_signature_width', SignatureTextService::DEFAULT_SIGNATURE_WIDTH);
75-
$this->initialState->provideInitialState('default_template_font_size', $this->signatureTextService->getDefaultTemplateFontSize());
7671
$this->initialState->provideInitialState('identify_methods', $this->identifyMethodService->getIdentifyMethodsSettings());
7772
$this->initialState->provideInitialState('legal_information', $this->appConfig->getValueString(Application::APP_ID, 'legal_information', ''));
7873
$this->initialState->provideInitialState('signature_available_variables', $this->signatureTextService->getAvailableVariables());
7974
$this->initialState->provideInitialState('signature_background_type', $this->signatureBackgroundService->getSignatureBackgroundType());
80-
$this->initialState->provideInitialState('signature_font_size', $this->signatureTextService->getSignatureFontSize());
81-
$this->initialState->provideInitialState('signature_height', $this->signatureTextService->getFullSignatureHeight());
8275
$this->initialState->provideInitialState('signature_preview_zoom_level', $this->appConfig->getValueFloat(Application::APP_ID, 'signature_preview_zoom_level', 100));
8376
$this->initialState->provideInitialState('footer_preview_zoom_level', $this->appConfig->getValueFloat(Application::APP_ID, 'footer_preview_zoom_level', 100));
8477
$this->initialState->provideInitialState('footer_preview_width', $this->appConfig->getValueInt(Application::APP_ID, 'footer_preview_width', 595));
@@ -87,10 +80,6 @@ public function getForm(): TemplateResponse {
8780
$this->initialState->provideInitialState('footer_template', $this->footerService->getTemplate());
8881
$this->initialState->provideInitialState('footer_template_is_default', $this->footerService->isDefaultTemplate());
8982
$this->initialState->provideInitialState('signature_engine', $this->getSignatureEngineInitialState());
90-
$this->initialState->provideInitialState('signature_render_mode', $this->signatureTextService->getRenderMode());
91-
$this->initialState->provideInitialState('signature_text_template', $this->signatureTextService->getTemplate());
92-
$this->initialState->provideInitialState('signature_width', $this->signatureTextService->getFullSignatureWidth());
93-
$this->initialState->provideInitialState('template_font_size', $this->signatureTextService->getTemplateFontSize());
9483
$this->initialState->provideInitialState('tsa_url', $this->appConfig->getValueString(Application::APP_ID, 'tsa_url', ''));
9584
$this->initialState->provideInitialState('tsa_policy_oid', $this->appConfig->getValueString(Application::APP_ID, 'tsa_policy_oid', ''));
9685
$this->initialState->provideInitialState('tsa_auth_type', $this->appConfig->getValueString(Application::APP_ID, 'tsa_auth_type', 'none'));

src/views/Settings/PolicyWorkbench/settings/realDefinitions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { identificationDocumentsRealDefinition } from './identification-document
99
import { requestSignGroupsRealDefinition } from './request-sign-groups/realDefinition'
1010
import { signatureFooterRealDefinition } from './signature-footer/realDefinition'
1111
import { signatureFlowRealDefinition } from './signature-flow/realDefinition'
12+
import { signatureTextRealDefinition } from './signature-text/realDefinition'
1213
import type { RealPolicySettingDefinition } from './realTypes'
1314

1415
export const realDefinitions = {
@@ -18,4 +19,5 @@ export const realDefinitions = {
1819
docmdp: docMdpRealDefinition,
1920
identification_documents: identificationDocumentsRealDefinition,
2021
groups_request_sign: requestSignGroupsRealDefinition,
22+
signature_text: signatureTextRealDefinition,
2123
} satisfies Record<string, RealPolicySettingDefinition>

0 commit comments

Comments
 (0)