diff --git a/app/Views/utenti/dettagli_utente.php b/app/Views/utenti/dettagli_utente.php
index fc3dbf110..51a36cbae 100644
--- a/app/Views/utenti/dettagli_utente.php
+++ b/app/Views/utenti/dettagli_utente.php
@@ -35,9 +35,20 @@
'standard' => __('Standard')
];
+$genderLabels = [
+ 'M' => __('Maschio'),
+ 'F' => __('Femmina'),
+ 'Altro' => __('Altro'),
+];
+
+$sessoKey = trim((string)$sesso);
+$sessoLabel = $genderLabels[$sessoKey] ?? $sessoKey;
+
$display = static function (?string $value, string $placeholder = '—'): string {
$value = trim((string)$value);
- return $value !== '' ? HtmlHelper::e($value) : $placeholder;
+ return $value !== ''
+ ? htmlspecialchars(HtmlHelper::decode($value), ENT_QUOTES, 'UTF-8')
+ : $placeholder;
};
?>
@@ -147,7 +158,7 @@
= __("Sesso") ?>
- = $display($sesso); ?>
+ = $display($sessoLabel); ?>
= __("Codice Fiscale") ?>
diff --git a/tests/issue-255-registration.spec.js b/tests/issue-255-registration.spec.js
index f49f3b31c..2fbad975e 100644
--- a/tests/issue-255-registration.spec.js
+++ b/tests/issue-255-registration.spec.js
@@ -1,6 +1,6 @@
// @ts-check
/**
- * Issue #255 — configurable registration fields. 29-check E2E suite.
+ * Issue #255 — configurable registration fields. 30-check E2E suite.
*
* Coverage (all through the real browser):
* 1-4 defaults + SERVER-side enforcement of each built-in requirement
@@ -18,7 +18,7 @@
* 23-24 sanitization: a tag-carrying label is neutralised on save; a
* script-carrying VALUE is escaped when rendered back (admin detail).
* 25 an inactive (attivo=0) field disappears from the public form.
- * 26-29 profile/admin-detail regressions and destructive type-change guard.
+ * 26-30 profile/admin-detail regressions and destructive type-change guard.
*/
const { test, expect } = require('@playwright/test');
@@ -117,7 +117,7 @@ function userCount(email) {
return Number(dbQuery(`SELECT COUNT(*) FROM utenti WHERE email='${email}'`));
}
-test.describe.serial('Issue #255 — configurable registration fields (29 checks)', () => {
+test.describe.serial('Issue #255 — configurable registration fields (30 checks)', () => {
/** @type {import('@playwright/test').Page} */
let admin;
/** @type {Map} setting key -> original HEX(setting_value) */
@@ -511,4 +511,32 @@ test.describe.serial('Issue #255 — configurable registration fields (29 checks
expect(dbQuery(`SELECT tipo FROM registrazione_campi WHERE id=${id}`)).toBe('text');
await expect(admin.locator('body')).toContainText(/non puoi cambiare il tipo|cannot change the type|ne pouvez pas modifier le type|Typ eines Feldes/i);
});
+
+ test('30. admin user details localize every stored sesso enum value', async ({ page }) => {
+ await setToggles(admin, false, false, false);
+ const email = `zz-255-sesso-${TOKEN}@example.test`;
+ await fillRegistration(page, {
+ fields: { nome: 'Sesso30', email, password: 'Password255!ok', password_confirm: 'Password255!ok' },
+ });
+ await page.selectOption('select[name="sesso"]', 'M');
+ await submitNoValidate(page);
+ expect(userCount(email)).toBe(1);
+
+ const uid = dbQuery(`SELECT id FROM utenti WHERE email='${email}'`);
+ expect(dbQuery(`SELECT sesso FROM utenti WHERE id=${uid}`)).toBe('M');
+
+ await admin.goto(`${BASE}/admin/users/edit/${uid}`);
+ /** @type {Record} */
+ const expectedLabels = {};
+ const genderValues = ['M', 'F', 'Altro'];
+ for (const value of genderValues) {
+ expectedLabels[value] = (await admin.locator(`select[name="sesso"] option[value="${value}"]`).innerText()).trim();
+ }
+
+ for (const value of genderValues) {
+ if (value !== 'M') dbQuery(`UPDATE utenti SET sesso='${value}' WHERE id=${uid}`);
+ await admin.goto(`${BASE}/admin/users/details/${uid}`);
+ await expect(admin.locator('[data-field="sesso"]')).toHaveText(expectedLabels[value]);
+ }
+ });
});