@@ -36,27 +36,26 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
36
36
$ changeableOptions = $ this ->settingsManager ->getSetting ('profile.changeable_options ' , true ) ?? [];
37
37
$ visibleOptions = $ this ->settingsManager ->getSetting ('profile.visible_options ' , true ) ?? [];
38
38
39
- // Fine-grained (category + key): profile.profile_fields_visibility (JSON )
40
- $ visibilitySetting = $ this ->settingsManager ->getSetting ('profile.profile_fields_visibility ' , true ) ?? [];
41
- if (\is_string ($ visibilitySetting )) {
39
+ // Fine-grained JSON (authoritative if present )
40
+ $ rawFine = $ this ->settingsManager ->getSetting ('profile.profile_fields_visibility ' , true ) ?? [];
41
+ if (\is_string ($ rawFine )) {
42
42
try {
43
- $ decoded = \json_decode ($ visibilitySetting , true , 512 , \JSON_THROW_ON_ERROR );
44
- if (\is_array ($ decoded )) {
45
- $ visibilitySetting = $ decoded ;
46
- }
43
+ $ decoded = \json_decode ($ rawFine , true , 512 , \JSON_THROW_ON_ERROR );
44
+ $ rawFine = \is_array ($ decoded ) ? $ decoded : [];
47
45
} catch (\Throwable ) {
48
- $ visibilitySetting = [];
46
+ $ rawFine = [];
49
47
}
50
48
}
51
49
$ fieldsVisibility = [];
52
- if (\is_array ($ visibilitySetting )) {
53
- $ fieldsVisibility = $ visibilitySetting ['options ' ] ?? $ visibilitySetting ;
50
+ if (\is_array ($ rawFine )) {
51
+ $ fieldsVisibility = $ rawFine ['options ' ] ?? $ rawFine ;
54
52
if (!\is_array ($ fieldsVisibility )) {
55
53
$ fieldsVisibility = [];
56
54
}
57
55
}
56
+ $ hasFine = !empty ($ fieldsVisibility ); // strict mode if true
58
57
59
- // Expand aliases used by high-level settings
58
+ // Expand aliases used by high-level settings (fallbacks only)
60
59
$ expandMap = [
61
60
'name ' => ['firstname ' , 'lastname ' ],
62
61
'surname ' => ['lastname ' ],
@@ -103,7 +102,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
103
102
'label ' => 'Date of birth ' ,
104
103
'required ' => false ,
105
104
'form_options ' => [
106
- // Plain input + flatpickr in Twig (better UX) - html5 off to avoid native picker
107
105
'widget ' => 'single_text ' ,
108
106
'html5 ' => false ,
109
107
'format ' => 'yyyy-MM-dd ' ,
@@ -116,26 +114,49 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
116
114
],
117
115
],
118
116
],
117
+ // Timezone will be added below if visible (fine JSON or fallback)
118
+ 'timezone ' => [
119
+ 'field ' => 'timezone ' ,
120
+ 'type ' => ChoiceType::class,
121
+ 'label ' => 'Timezone ' ,
122
+ 'required ' => false ,
123
+ 'form_options ' => static function (): array {
124
+ $ timezones = DateTimeZone::listIdentifiers ();
125
+ sort ($ timezones );
126
+ $ choices = array_combine ($ timezones , $ timezones );
127
+ return [
128
+ 'choices ' => $ choices ,
129
+ 'placeholder ' => '' ,
130
+ 'choice_translation_domain ' => false ,
131
+ ];
132
+ },
133
+ ],
119
134
];
120
135
121
- // Priority rules (core):
122
- // - If key exists in fine-grained map: boolean means editable=true/false; presence means visible
123
- // - If not present: visible/editable fall back to high-level lists
124
- $ isCoreVisible = function (string $ key ) use ($ fieldsVisibility , $ visibleHigh ): bool {
125
- if (\array_key_exists ( $ key , $ fieldsVisibility ) ) {
126
- return true ; // listed → visible
136
+ // Visibility (core):
137
+ // Strict when $hasFine: only keys present in $fieldsVisibility are visible.
138
+ // Otherwise, fallback to visible_options.
139
+ $ isCoreVisible = function (string $ key ) use ($ fieldsVisibility , $ visibleHigh, $ hasFine ): bool {
140
+ if ($ hasFine ) {
141
+ return \array_key_exists ( $ key , $ fieldsVisibility );
127
142
}
128
143
return \in_array ($ key , $ visibleHigh , true );
129
144
};
145
+
146
+ // Editability (core):
147
+ // If key is in fine JSON, its boolean decides; otherwise fallback to changeable_options.
130
148
$ isCoreEditable = function (string $ key ) use ($ fieldsVisibility , $ editableHigh ): bool {
131
149
if (\array_key_exists ($ key , $ fieldsVisibility )) {
132
- return (bool ) $ fieldsVisibility [$ key ]; // json boolean drives editability
150
+ return (bool ) $ fieldsVisibility [$ key ];
133
151
}
134
152
return \in_array ($ key , $ editableHigh , true );
135
153
};
136
154
137
- // Build core fields
155
+ // Build core fields (except timezone; decide after)
138
156
foreach ($ fieldsMap as $ key => $ fieldConfig ) {
157
+ if ($ key === 'timezone ' ) {
158
+ continue ;
159
+ }
139
160
if (!$ isCoreVisible ($ key )) {
140
161
continue ;
141
162
}
@@ -156,8 +177,11 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
156
177
}
157
178
}
158
179
159
- if (isset ($ fieldConfig ['form_options ' ]) && \is_array ($ fieldConfig ['form_options ' ])) {
160
- $ opts = array_merge ($ opts , $ fieldConfig ['form_options ' ]);
180
+ if (isset ($ fieldConfig ['form_options ' ])) {
181
+ $ extra = \is_callable ($ fieldConfig ['form_options ' ])
182
+ ? ($ fieldConfig ['form_options ' ])()
183
+ : (array ) $ fieldConfig ['form_options ' ];
184
+ $ opts = array_merge ($ opts , $ extra );
161
185
}
162
186
163
187
if (!$ isCoreEditable ($ key )) {
@@ -167,43 +191,48 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
167
191
$ builder ->add ($ fieldConfig ['field ' ], $ fieldConfig ['type ' ], $ opts );
168
192
}
169
193
170
- // Timezone (optional, independent from the visibility settings above)
171
- if ('true ' === $ this ->settingsManager ->getSetting ('profile.use_users_timezone ' , true )) {
172
- $ timezones = DateTimeZone::listIdentifiers ();
173
- sort ($ timezones );
174
- $ timezoneChoices = array_combine ($ timezones , $ timezones );
175
-
176
- $ builder ->add (
177
- 'timezone ' ,
178
- ChoiceType::class,
179
- [
180
- 'label ' => 'Timezone ' ,
181
- 'choices ' => $ timezoneChoices ,
182
- 'required ' => false ,
183
- 'placeholder ' => '' ,
184
- 'choice_translation_domain ' => false ,
185
- ]
186
- );
194
+ // Timezone: only show if visible (fine JSON present with key, or fallback says visible)
195
+ if ($ isCoreVisible ('timezone ' )) {
196
+ $ tzCfg = $ fieldsMap ['timezone ' ];
197
+ $ opts = [
198
+ 'label ' => $ tzCfg ['label ' ],
199
+ 'required ' => $ tzCfg ['required ' ],
200
+ 'mapped ' => true ,
201
+ ];
202
+ $ extra = ($ tzCfg ['form_options ' ])();
203
+ $ opts = array_merge ($ opts , $ extra );
204
+ if (!$ isCoreEditable ('timezone ' )) {
205
+ $ opts ['disabled ' ] = true ;
206
+ }
207
+ $ builder ->add ($ tzCfg ['field ' ], $ tzCfg ['type ' ], $ opts );
187
208
}
188
209
189
- // Build ExtraFieldType with allowlist + editable map derived from JSON
190
- // Consider as "core" the keys present in $fieldsMap; the rest of JSON keys affect "extra" fields.
210
+ // Build ExtraFieldType with allowlist + editable map derived from fine JSON (strict when present)
191
211
$ coreKeys = array_keys ($ fieldsMap );
192
-
193
212
$ extraAllowlist = [];
194
213
$ extraEditableMap = [];
195
- foreach ($ fieldsVisibility as $ key => $ bool ) {
196
- if (!\in_array ($ key , $ coreKeys , true )) {
197
- $ extraAllowlist [] = $ key ; // presence in JSON ⇒ visible
198
- $ extraEditableMap [$ key ] = (bool ) $ bool ; // boolean ⇒ editable
214
+
215
+ if ($ hasFine ) {
216
+ // Strict: only extras listed in fine JSON
217
+ foreach ($ fieldsVisibility as $ key => $ bool ) {
218
+ if (!\in_array ($ key , $ coreKeys , true )) {
219
+ $ extraAllowlist [] = $ key ; // visible
220
+ $ extraEditableMap [$ key ] = (bool ) $ bool ; // editable
221
+ }
199
222
}
223
+ } else {
224
+ // Fallback: show all extras (no allowlist) and let ExtraField configuration drive editability
225
+ $ extraAllowlist = []; // empty = render all extras
226
+ $ extraEditableMap = []; // let EF config decide
200
227
}
201
228
202
229
$ builder ->add ('extra_fields ' , ExtraFieldType::class, [
203
230
'mapped ' => false ,
204
231
'label ' => false ,
205
- 'visibility_allowlist ' => $ extraAllowlist , // empty → show all; non-empty → filter
206
- 'visibility_editable_map ' => $ extraEditableMap , // editable control per extra
232
+ 'visibility_allowlist ' => $ extraAllowlist ,
233
+ 'visibility_editable_map ' => $ extraEditableMap ,
234
+ 'visibility_strict ' => $ hasFine ,
235
+ 'item ' => $ builder ->getData (),
207
236
]);
208
237
}
209
238
0 commit comments