Configurable registration fields + admin-defined custom fields (#255)#256
Conversation
…ields Small communities can't force members to disclose personal data, so the registration form's hardcoded requirements become admin choices: - Per-field toggles (Settings → Email → Registrazione utenti) decide whether surname, phone and address are required at self-registration. Defaults keep the historical all-required behaviour; the toggles flow through ConfigStore (registration.require_cognome/telefono/indirizzo) into the public form (labels + required attributes), the registration controller, the profile and the admin user forms. An optional surname is stored as an empty string on purpose — the column stays NOT NULL because 70+ display paths CONCAT nome+cognome and a NULL there would blank the whole name; empty phone and address are stored as NULL (the columns were already nullable). - Custom fields: the admin defines label/type(text, textarea, email, url, number, checkbox)/required/active rows that render on the registration form and in the user profile, with server-side type validation and a 1000-char cap. Values are shown read-only on the admin user detail. Definitions live in registrazione_campi, per-user values in utenti_campi_valori (PK utente_id+campo_id, FK CASCADE both ways); account row and custom values insert atomically at registration. - Migration migrate_0.7.37.sql (CREATE TABLE IF NOT EXISTS only, idempotent) + the same tables in schema.sql for fresh installs; every reader degrades gracefully while the tables are not migrated yet. version.json → 0.7.37, What's New section added. - i18n: nine new strings in all four locales (key parity verified). - Tests: migration-0.7.37.unit.php (real migration against sandbox tables, idempotency, FK cascades, validation contract — 14 checks) and issue-255-registration.spec.js (real-browser flow: defaults required → admin relaxes + defines a Telegram field → registration without surname/phone/address stores the custom value → restore), wired into ci-e2e as a required step. Closes #255 (custom-field API exposure for notification integrations lands with the notifications work).
📝 WalkthroughWalkthroughLa versione 0.7.37 aggiunge requisiti configurabili, campi personalizzati con validazione e persistenza, visualizzazione nei profili, supporto Mobile API e verifiche automatizzate. ChangesCampi di registrazione configurabili
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Admin
participant RegistrationController
participant RegistrationFields
participant Database
Admin->>RegistrationController: configura requisiti e campi
RegistrationController->>RegistrationFields: valida i dati inviati
RegistrationController->>Database: inserisce l’utente
RegistrationController->>RegistrationFields: salva i valori custom
RegistrationFields->>Database: persiste i valori associati
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci-e2e.yml:
- Around line 254-259: Make the migration database checks mandatory: in
.github/workflows/ci-e2e.yml lines 254-259, add php
tests/migration-0.7.37.unit.php as a required workflow step; in
tests/migration-0.7.37.unit.php lines 87-90, update the database connection
failure path to return a non-zero error status instead of success.
In `@app/Controllers/ProfileController.php`:
- Around line 165-176: Propaga i tre requisiti configurabili in tutti i flussi:
in app/Controllers/ProfileController.php:165-176 valida anche telefono e
indirizzo prima dell'UPDATE; in app/Views/profile/index.php:406-408 applica
indicatore, required e aria-required a entrambi; in
app/Controllers/UsersController.php:91-127 normalizza l'indirizzo prima della
creazione e applica require_indirizzo; in
app/Controllers/UsersController.php:332-339 applica require_indirizzo durante
l'aggiornamento, riutilizzando RegistrationFields.
In `@app/Controllers/SettingsController.php`:
- Around line 253-305: Wrap the custom-field persistence flow in
SettingsController’s update/delete/insert logic
(app/Controllers/SettingsController.php:253-305) in a database transaction,
check every prepare, execute, and bind operation, roll back and propagate the
failure to the caller instead of silently returning or logging success; commit
only after all operations succeed. Apply the same transaction boundary to the
profile and custom-value saves in ProfileController
(app/Controllers/ProfileController.php:201-223), rolling back both when either
persistence path fails.
In `@app/Support/RegistrationFields.php`:
- Around line 93-100: Nel flusso che costruisce $values a partire da $submitted,
valida ogni valore prima di convertirlo con trim o cast a stringa: accetta
soltanto valori scalari o null e tratta array/oggetti come valori assenti o non
validi. Aggiorna il ciclo sui $definitions mantenendo invariato il comportamento
dei valori scalari e impedendo che input non scalari venga persistito o
considerato selezionato.
In `@app/Views/profile/index.php`:
- Around line 465-469: Sostituisci le chiamate a App\Support\HtmlHelper::e() nei
valori di textarea e input della view con htmlspecialchars(..., ENT_QUOTES,
'UTF-8'), mantenendo invariati gli attributi e il comportamento di escaping
esistente.
In `@app/Views/utenti/modifica_utente.php`:
- Around line 110-123: Replace both HtmlHelper::e() calls in the
customFieldValues rendering with htmlspecialchars(..., ENT_QUOTES, 'UTF-8'),
preserving the existing escaped output for cfv['etichetta'] and cfv['valore']
while leaving the checkbox label handling unchanged.
In `@tests/issue-255-registration.spec.js`:
- Around line 82-83: Update both form-save flows in the registration test to
wait for the SweetAlert confirmation button `.swal2-confirm` after submitting,
click it, and only then continue or wait for page loading so settings
persistence is confirmed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: fa0d31f5-5cd1-4595-ac8f-d7e2e591da4f
📒 Files selected for processing (22)
.github/workflows/ci-e2e.ymlREADME.mdapp/Controllers/ProfileController.phpapp/Controllers/RegistrationController.phpapp/Controllers/SettingsController.phpapp/Controllers/UsersController.phpapp/Routes/web.phpapp/Support/ConfigStore.phpapp/Support/RegistrationFields.phpapp/Views/auth/register.phpapp/Views/profile/index.phpapp/Views/settings/index.phpapp/Views/utenti/modifica_utente.phpinstaller/database/migrations/migrate_0.7.37.sqlinstaller/database/schema.sqllocale/de_DE.jsonlocale/en_US.jsonlocale/fr_FR.jsonlocale/it_IT.jsontests/issue-255-registration.spec.jstests/migration-0.7.37.unit.phpversion.json
…+ 25-check E2E Address the review round and move the registration settings where they belong: - New Settings → Registrazione tab with its own form and POST endpoint (/admin/settings/registration): admin-approval flag, the three built-in requirement toggles and the custom-field editor move out of the email form. Email settings save is registration-free again. - Validation hardening: RegistrationFields::validate() rejects non-scalar payloads (custom_field[id][]=x would cast to "Array", pass required checks and tick checkboxes); the requirement toggles now also govern phone and address in the profile (view + server) and address in the admin create/edit forms, so a member can't blank a field the library requires. - Atomic persistence: the custom-field definitions save runs in a transaction with per-statement checks and a visible error flash on rollback; the profile UPDATE and the user's custom values commit or roll back together. - View escaping: htmlspecialchars(..., ENT_QUOTES, 'UTF-8') replaces HtmlHelper::e() in the new view code (project rule). - migration-0.7.37.unit.php now FAILS when no database is reachable — a silently-skipped migration test is a false green in CI (both workflows provision MySQL). The redundant extra ci-e2e step was skipped on purpose: ci-quality already runs every tests/*.unit.php against MySQL. - issue-255-registration.spec.js grows from 4 to 25 real-browser checks: server-side enforcement of each built-in requirement via novalidate submits, all six custom types created and rendered with the right controls, the full 2^3 toggle matrix, a registration storing every type, invalid values (bad email, javascript: URL, non-numeric number, missing required, >1000 chars) rejected by the server with crafted POSTs, label tags neutralised on save, script-carrying values rendered escaped on the admin detail (never executed), and inactive fields disappearing from the form. Verified: PHPStan L5 = 0, unit 38/38, issue-255 E2E 25/25, full-test.spec.js 136 passed (one external scraping-provider 504 flake reran green).
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
app/Views/settings/index.php (1)
402-402: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winCorreggi la nota sul salvataggio.
Questo form invia solo le impostazioni di registrazione, non quelle email.
Correzione proposta
-<?= __("Le modifiche ai campi vengono salvate insieme alle impostazioni email.") ?> +<?= __("Le modifiche ai campi vengono salvate insieme alle impostazioni di registrazione.") ?>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/Views/settings/index.php` at line 402, Correggi il testo della nota nella vista delle impostazioni affinché indichi che le modifiche ai campi vengono salvate insieme alle impostazioni di registrazione, non a quelle email.app/Controllers/ProfileController.php (1)
174-183: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAggiungi
required_fieldsalla mappa degli errori del profilo.Entrambi i redirect producono questa chiave, ma
app/Views/profile/index.phpnon la gestisce e mostra soltanto l’errore generico.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/Controllers/ProfileController.php` around lines 174 - 183, Aggiorna la mappa degli errori in app/Views/profile/index.php per gestire la chiave required_fields prodotta dai redirect di ProfileController. Collega required_fields al messaggio dedicato ai campi obbligatori, mantenendo invariati gli altri errori e il fallback generico.app/Controllers/UsersController.php (1)
335-365: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winAllinea il form admin ai requisiti configurabili.
app/Views/utenti/modifica_utente.phpmantiene il cognome semprerequirede il JavaScript rende il telefono obbligatorio per ogni non-admin, anche quando questi toggle sono disattivati. L’indirizzo, invece, non riceverequiredquando il server lo pretende. Il browser blocca quindi modifiche valide o rinvia l’errore al server.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/Controllers/UsersController.php` around lines 335 - 365, Allinea i requisiti client-side del form admin con i controlli server basati su RegistrationFields::isRequired(). Aggiorna il campo cognome per usare required solo quando configurato, limita l’obbligatorietà del telefono ai non-admin quando il relativo toggle è attivo e aggiungi required all’indirizzo quando richiesto dal server; correggi anche la validazione JavaScript per mantenere lo stesso comportamento.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/Controllers/SettingsController.php`:
- Around line 285-306: Nel ciclo di aggiornamento dei campi personalizzati in
SettingsController, valida che ogni valore annidato usato da delete, etichetta,
tipo, obbligatorio e attivo sia scalare prima di applicare empty(), cast o
sanitizzazione; se un valore non è scalare, interrompi l’elaborazione e annulla
la transazione invece di ignorarlo o interpretarlo. Mantieni invariati i
controlli sui valori validi e il flusso di bind_param per i dati accettati.
- Around line 225-233: Convert the toggle values to actual booleans before
calling ConfigStore::set in the requireApproval assignment and the
BUILTIN_TOGGLES loop. Preserve the repository’s existing '1'/'0' string values,
but pass false when a setting is unchecked and true when checked.
In `@app/Views/settings/index.php`:
- Line 328: Sostituisci HtmlHelper::e() nel campo hidden csrf_token della view
con htmlspecialchars(Csrf::ensureToken(), ENT_QUOTES, 'UTF-8'), mantenendo
invariato il valore del token e la struttura dell’input.
In `@locale/it_IT.json`:
- Line 6536: Update the Italian translation value for “Le modifiche ai campi
vengono salvate insieme alle impostazioni email.” to reference registration
settings instead of email settings, matching the section where the field changes
are configured.
In `@tests/issue-255-registration.spec.js`:
- Around line 111-115: Update the test.afterAll cleanup to delete matching rows
from utenti_campi_valori before deleting registrazione_campi and utenti;
preserve the existing TOKEN-based filters and close the admin context afterward.
- Line 108: Remove the setToggles(true, true, true) initialization before tests
1–4 so they exercise the application’s actual default toggle values. Keep
explicit toggle setup only where later tests require non-default state.
---
Outside diff comments:
In `@app/Controllers/ProfileController.php`:
- Around line 174-183: Aggiorna la mappa degli errori in
app/Views/profile/index.php per gestire la chiave required_fields prodotta dai
redirect di ProfileController. Collega required_fields al messaggio dedicato ai
campi obbligatori, mantenendo invariati gli altri errori e il fallback generico.
In `@app/Controllers/UsersController.php`:
- Around line 335-365: Allinea i requisiti client-side del form admin con i
controlli server basati su RegistrationFields::isRequired(). Aggiorna il campo
cognome per usare required solo quando configurato, limita l’obbligatorietà del
telefono ai non-admin quando il relativo toggle è attivo e aggiungi required
all’indirizzo quando richiesto dal server; correggi anche la validazione
JavaScript per mantenere lo stesso comportamento.
In `@app/Views/settings/index.php`:
- Line 402: Correggi il testo della nota nella vista delle impostazioni affinché
indichi che le modifiche ai campi vengono salvate insieme alle impostazioni di
registrazione, non a quelle email.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b1d21cd0-7485-4051-89e1-7ea29b315e24
📒 Files selected for processing (14)
app/Controllers/ProfileController.phpapp/Controllers/SettingsController.phpapp/Controllers/UsersController.phpapp/Routes/web.phpapp/Support/RegistrationFields.phpapp/Views/profile/index.phpapp/Views/settings/index.phpapp/Views/utenti/modifica_utente.phplocale/de_DE.jsonlocale/en_US.jsonlocale/fr_FR.jsonlocale/it_IT.jsontests/issue-255-registration.spec.jstests/migration-0.7.37.unit.php
- schema-integrity.spec.js: register the two new tables in the hardcoded CORE_TABLES list (57 → 59) so the CI post-install verification passes. Note: PR #254 replaces this list with a schema.sql-derived one; on merge the dynamic version supersedes this entry. - SettingsController: a crafted nested payload (custom_fields[5][etichetta][]=x) aborts the batch — an array is truthy for empty() (phantom delete) and casts to "Array" (garbage label). - Registration-tab CSRF hidden input uses htmlspecialchars (view rule); the custom-fields note now says the changes save with the REGISTRATION settings (stale "email" wording swapped in all four locales). - Profile error map handles the required_fields code the controller actually emits (previously fell through to the generic message). - Admin create/edit user forms: the surname required attribute follows the require_cognome toggle like the server does. - issue-255-registration.spec.js: tests 1-4 now exercise the CODE default (toggle rows deleted, absent row = required) instead of a value the suite planted; teardown restores the pristine state. Skipped from the review with reasons: ConfigStore::set already receives booleans ((bool)'0' === false); the explicit utenti_campi_valori cleanup is unnecessary — both FKs are ON DELETE CASCADE and the migration test asserts exactly that. Verified: PHPStan L5 = 0, unit 38/38, issue-255 E2E 25/25, schema-integrity 18/18.
…leftovers
- crea/modifica utente: the JS that forced the phone required for every
non-admin now honours require_telefono, and the address gains the same
toggle-driven required the server enforces; the surname asterisk follows
its toggle like the input's required attribute already did.
- SettingsController: the non-scalar payload guard also covers the
new_custom_field_* inputs (an array would cast to "Array" and create a
garbage definition).
- E2E teardown deletes utenti_campi_valori rows before their parents:
FK-safe order per project test rules — teardown must not depend on the
cascades the suite itself verifies.
Already addressed in the previous commit (review overlap): registration-tab
note wording, required_fields in the profile error map, CSRF hidden escaping,
stale it_IT string, default-purity of tests 1-4, hardcoded CORE_TABLES.
Skipped with evidence: the ConfigStore boolean finding — (bool)'0' is FALSE
in PHP ('0' is falsy), so unchecked toggles already reach ConfigStore as
false.
Verified: PHPStan L5 = 0, issue-255 E2E 25/25.
Review — PR #256 (configurable registration fields, #255)Reviewed Confirmed (worth addressing)1.
( 2. A newly-required custom field retroactively blocks existing users' profile saves · 3. Format-invalid custom values collapse into the generic "fill required fields" banner · Reviewed and clearedF001 — admin create/edit user doesn't run custom-field validation/save. Not a defect: Below the gate (noted, not blocking)
Security, migration versioning, escaping, and locale parity all check out. None of the confirmed findings is a merge blocker; #1 and #3 are the two I'd fix before release. |
…s, custom-field UX) Self-review of this branch surfaced three confirmed issues plus a few lower-severity ones; all fixed here. Confirmed: - Optional surname left a trailing space. cognome='' (the new optional path) made 6 unchanged display sites render "Mario " via naive nome.' '.cognome. Added a reusable full_name() helper that collapses the gap and applied it at all six (utenti/index, admin/stats, settings/messages-tab, PrestitiController, two UsersController audit fields). - A custom field made required AFTER signup walled existing users out of unrelated profile edits. RegistrationFields::validate() gained an $enforceRequired flag; the profile passes false so required is enforced at registration (the field's contract) but a blank pre-existing value no longer blocks a profile save. Format checks still apply to anything typed. - Format-invalid custom values collapsed into the generic "fill required fields" banner. validate() now returns error_reason (missing|format) and both registration and profile map a format failure to a distinct custom_field_invalid code + message, so a user who DID fill the field isn't told to fill it. Lower-severity: - Un-nested the admin-form JS so addressField.required no longer depends on the phone field's DOM presence. - updateRegistrationSettings now persists the custom-field batch first and bails before writing the toggles on failure, so a field-batch error never leaves toggles applied under a "save failed" flash. saveRegistrationCustomFields returns bool instead of signalling through the session. - Deleting a custom field (cascades to every user's stored value) now asks for confirmation at submit. - Fixed a stale comment in resolveEmailSettings that still pointed at the email form. New i18n strings in all four locales (parity verified). Tests: migration unit grows to 21 checks (error_reason, enforceRequired=false, full_name); issue-255 E2E grows to 27 (distinct error code, no-trailing-space). PHPStan L5 = 0, unit 38/38, full-test.spec.js 136 passed.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/Views/admin/stats.php`:
- Line 251: Sostituisci l’uso di HtmlHelper::e() con htmlspecialchars(...,
ENT_QUOTES, 'UTF-8') mantenendo invariato il valore già passato: aggiorna
app/Views/admin/stats.php:251, app/Views/settings/messages-tab.php:55 e
app/Views/utenti/index.php:83, inclusi i riferimenti qualificati con namespace.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 096e72bc-a9c8-42e2-aee0-27ba5023f09e
📒 Files selected for processing (22)
app/Controllers/PrestitiController.phpapp/Controllers/ProfileController.phpapp/Controllers/RegistrationController.phpapp/Controllers/SettingsController.phpapp/Controllers/UsersController.phpapp/Support/RegistrationFields.phpapp/Views/admin/stats.phpapp/Views/auth/register.phpapp/Views/profile/index.phpapp/Views/settings/index.phpapp/Views/settings/messages-tab.phpapp/Views/utenti/crea_utente.phpapp/Views/utenti/index.phpapp/Views/utenti/modifica_utente.phpapp/helpers.phplocale/de_DE.jsonlocale/en_US.jsonlocale/fr_FR.jsonlocale/it_IT.jsontests/issue-255-registration.spec.jstests/migration-0.7.37.unit.phptests/schema-integrity.spec.js
…ouched views - issue-255-registration.spec.js dbQuery now prefers TCP host/port (CI's MySQL listens on 127.0.0.1, not the default socket) and falls back to the socket for local dev — mirrors schema-integrity.spec.js. The required CI step was red with "Can't connect through socket /var/run/mysqld/mysqld.sock". - The three display sites I touched for the full_name() fix now escape with htmlspecialchars(..., ENT_QUOTES, 'UTF-8') instead of HtmlHelper::e(), per the view-escaping rule.
…e API + hardening Extends the feature to the companion-app surface and hardens the admin editor. Mobile API (mobile-api 1.2.2 → 1.3.0, requires_app 0.7.37): - GET /api/v1/health advertises the built-in requirements (require_cognome/ telefono/indirizzo) and the active custom-field definitions, so the app can render the right registration form. - POST /api/v1/auth/register reaches web parity: built-in requirements follow the toggles, telefono/indirizzo are omitted (NULL) when optional and blank, custom fields are validated and saved in the same transaction as the account (1062 → email_exists), notifications run AFTER commit so a mail failure can't 500 a created account, and requires_approval reflects the real setting. - GET/PATCH /api/v1/me expose and update custom_fields with PATCH semantics (omitted ids preserved, an explicit empty value clears one field); also surface email_verificata + stato. OpenAPI schemas updated (incl. the tipo_utente enum fix: standard/premium/staff/admin). Web hardening: - Changing a custom field's TYPE is refused when it already has stored values (RegistrationFields::hasStoredValues + a DomainException whose message reaches the admin), preventing silent data corruption (e.g. text→number). - Profile custom-field validation runs only when the section was actually submitted (custom_fields_present marker), so a partial profile POST never wipes or spuriously requires custom values. - User detail page shows the stored custom values read-only. New RegistrationFields helpers reused across both surfaces: apiDefinitions(), editableFieldsForUser(), hasStoredValues(). Tests: new tests/registration-fields-255.unit.php (26 DB-backed checks over the API helpers + the validate() enforceRequired matrix + save/read round-trip); issue-255 E2E grows to 29 (no-block existing user, destructive type-change guard); mobile-api E2E adds register-with-custom-fields + PATCH /me cases. i18n parity across all four locales. PHPStan L5 = 0, unit 39/39, issue-255 29/29, mobile-api 76/76 + idempotency 41/41.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@storage/plugins/mobile-api/src/Controllers/AuthController.php`:
- Around line 299-304: Allinea lo stato creato dell’account al valore di
$requiresApproval: recupera questa impostazione prima dell’INSERT e assegna
stato='attivo' quando l’approvazione non è richiesta, mantenendo stato='sospeso'
quando è richiesta. Aggiorna il flusso di registrazione in AuthController senza
modificare il valore restituito di requires_approval.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9844c00c-4130-426d-b4bc-0c66591b87a4
📒 Files selected for processing (24)
README.mdapp/Controllers/ProfileController.phpapp/Controllers/SettingsController.phpapp/Controllers/UsersController.phpapp/Support/RegistrationFields.phpapp/Views/admin/stats.phpapp/Views/profile/index.phpapp/Views/settings/messages-tab.phpapp/Views/utenti/dettagli_utente.phpapp/Views/utenti/index.phplocale/de_DE.jsonlocale/en_US.jsonlocale/fr_FR.jsonlocale/it_IT.jsonstorage/plugins/mobile-api/MobileApiPlugin.phpstorage/plugins/mobile-api/plugin.jsonstorage/plugins/mobile-api/src/Controllers/ActionsController.phpstorage/plugins/mobile-api/src/Controllers/AuthController.phpstorage/plugins/mobile-api/src/Controllers/HealthController.phpstorage/plugins/mobile-api/src/Controllers/OpenApiController.phptests/issue-255-registration.spec.jstests/mobile-api-idempotency.spec.jstests/mobile-api.spec.jstests/registration-fields-255.unit.php
…l-plugins packaging - Settings: the #256 merge kept the email-test IIFE but dropped its HTML (the "Invia email di prova" input + button), leaving the script targeting elements that no longer existed. Restored the block in the Email panel; the /admin/settings/email/test route + sendTestEmail() were already present. - FrontendController: add social_bluesky to the sanitized Organization sameAs (the earlier fix covered only four of the five socials). - .rsync-filter: bundle every source plugin (`storage/plugins/*/**`) minus the premium scraping-pro, instead of a stale explicit list of five — this now matches BundledPlugins::LIST exactly (19), so the build verifier passes as the plugin set grows. Public uploads ship only the cover placeholder + directory sentinels, never installation data. - build-release.sh: the package verifier rejects more dev artifacts (.code-review-graph, .playwright-*, phpstan.neon(+baseline), .coderabbit.yaml, .semgrepignore, graphify-out, test-upgrade) and asserts the uploads contents. - Tests: new release-0.7.37-integration.spec.js guards the merged settings contract (email-test controls live only in the Email tab; the delete-confirm belongs to Registration; CSRF on the AJAX), wired into ci-e2e as required. social-links and issue-255 teardowns now snapshot+restore the exact pre-suite state (row absence included) instead of blanket-deleting.
…-rc.1; rename fabiodalez-dev#255 migration test to -rc.1 Bundling fabiodalez-dev#253+fabiodalez-dev#254+fabiodalez-dev#256 into 0.7.37-rc.1 moves version.json past fabiodalez-dev#237's migration, so issue-237-regression's statement-count + enum-drift checks (which are specific to the contributor migration) now pin to migrate_0.7.36-rc.1.sql instead of "the release migration". migration-0.7.37.unit.php is renamed to migration-0.7.37-rc.1.unit.php (matching the RC-named migration) and its internal file reference updated.
Closes #255.
What this does
1. Per-field requirement toggles. Surname, phone and address are no longer hardcoded as required at self-registration: each gets a checkbox under Settings → Email → Registrazione utenti. Defaults keep today's all-required behaviour, so existing installs change nothing until the admin opts out. The toggles flow end-to-end: public form (labels +
requiredattributes), registration controller, user profile and the admin create/edit user forms.2. Optional surname — deliberately stored as
'', not NULL. The issue asked for a nullablecognome; I kept the column NOT NULL on purpose: 70+ display paths (loan emails, member lists, exports, plugins) build the full name withCONCAT(nome, ' ', cognome), and a NULL there blanks the whole name in MySQL. An empty string delivers "surname not required" with zero migration risk and zero display sweep. Empty phone/address are stored as NULL (those columns were already nullable).3. Custom registration fields. The admin defines label / type (text, textarea, email, URL, number, checkbox) / required / active rows; they render on the registration form and in the user profile with server-side type validation and a 1000-char cap, and show read-only on the admin user detail — e.g. a Telegram username for a community that notifies through it. Definitions live in
registrazione_campi, per-user values inutenti_campi_valori(PKutente_id+campo_id, FK CASCADE both ways). The account row and the custom values insert atomically at registration. The bundled Mobile API exposes the requirement flags and custom definitions through/api/v1/health, accepts custom values on/auth/registerandPATCH /me, and returns them fromGET /me; the companion Android app consumes the same contract in its separate repository.Schema
migrate_0.7.37.sql— twoCREATE TABLE IF NOT EXISTS, idempotent; the same tables ship inschema.sqlfor fresh installs; every reader degrades gracefully during the pre-migration window.version.json→ 0.7.37 (this PR assumes the 0.7.36 line from #254 ships first; I'll rename the migration if the order changes).Verification
tests/migration-0.7.37.unit.php— the real migration against sandbox-renamed tables: both tables + PK/FKs, idempotent second run, both FK cascades, the''surname contract, and the full validation matrix (14 checks).tests/issue-255-registration.spec.js— real-browser E2E wired into ci-e2e as a required step: defaults are required → admin unticks the toggles and defines a Telegram field → a registration without surname/phone/address succeeds and stores the custom value (cognome='',telefono/indirizzo NULL) → restoring the toggles makes the fields required again.Summary by CodeRabbit
custom_fields.