refactor(settings): declutter account-access, group registration with user defaults

Three related cleanups in the same area:

- Account-access loses 4 info blockquotes that paraphrased their cards,
  4 redundant "allowed range" hints (the input min/max + DB descriptions
  already convey the bounds), and the fieldset wrappers around single
  checkboxes.
- The registration toggle moves out of account-access into general's
  user-creation card (renamed to "User onboarding"), so all "new user"
  settings live together while account-access stays focused on existing
  user sessions.
- Both feature toggles (allow registration, Microsoft auto-remember)
  switch to role="switch" with the description sitting outside the label
  as <small class="muted">, matching the tenant form pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 09:29:01 +02:00
parent 5d5cce7ad5
commit 5b972b3633
7 changed files with 39 additions and 61 deletions

View File

@@ -107,6 +107,7 @@
"App logo": "App-Logo",
"App logo and favicon": "App-Logo und Favicon",
"App title": "App-Titel",
"App title, language, registration and new-user defaults": "App-Titel, Sprache, Registrierung und Standardwerte für neue Benutzer",
"App title, language, user creation defaults": "App-Titel, Sprache, Standardwerte für neue Benutzer",
"APP_CRYPTO_KEY is missing or invalid": "APP_CRYPTO_KEY fehlt oder ist ungültig",
"Appearance": "Darstellung",
@@ -870,6 +871,8 @@
"Register": "Registrieren",
"Registration controls whether new users can create an account.": "Hier wird gesteuert, ob neue Benutzer ein Konto erstellen dürfen.",
"Registration is currently disabled": "Registrierung ist derzeit deaktiviert.",
"Registration off": "Registrierung aus",
"Registration on": "Registrierung an",
"Registration successful! Please check your email for the verification code.": "Registrierung erfolgreich! Bitte prüfe deine E-Mails für den Bestätigungscode.",
"Registration, sessions, login persistence": "Registrierung, Sitzungen, Login-Persistenz",
"Remember login after Microsoft sign-in": "Angemeldet bleiben nach Microsoft-Login",
@@ -1033,6 +1036,7 @@
"Session idle timeout is invalid": "Sitzungs-Inaktivitäts-Timeout ist ungültig",
"Session limits define how long logged-in users stay signed in.": "Hier werden Inaktivitäts- und Absolute-Timeout für aktive Sitzungen festgelegt.",
"Session policy": "Sitzungsrichtlinie",
"Sessions, login persistence, login tokens": "Sitzungen, Login-Persistenz, Login-Tokens",
"Sessions, registration, lifecycle, audit, telemetry": "Sitzungen, Registrierung, Lebenszyklus, Audit, Telemetrie",
"setting.api_cors_allowed_origins": "Erlaubte Browser-Origins für API-CORS (eine Origin pro Zeile)",
"setting.api_token_default_ttl_days": "Standard-Laufzeit für neue API-Tokens in Tagen (Default: 90)",
@@ -1298,6 +1302,7 @@
"User lifecycle policy": "Benutzer-Lifecycle-Regel",
"User lifecycle settings": "Benutzer-Lebenszyklus-Einstellungen",
"User not found": "Benutzer nicht gefunden",
"User onboarding": "Benutzeranlage",
"User override disabled": "Benutzer-Override deaktiviert",
"User override enabled": "Benutzer-Override aktiviert",
"User profile fields are global and affect all tenants of this user": "Benutzer-Profildaten sind global und wirken auf alle Mandanten dieses Benutzers.",

View File

@@ -107,6 +107,7 @@
"App logo": "App logo",
"App logo and favicon": "App logo and favicon",
"App title": "App title",
"App title, language, registration and new-user defaults": "App title, language, registration and new-user defaults",
"App title, language, user creation defaults": "App title, language, user creation defaults",
"APP_CRYPTO_KEY is missing or invalid": "APP_CRYPTO_KEY is missing or invalid",
"Appearance": "Appearance",
@@ -870,6 +871,8 @@
"Register": "Register",
"Registration controls whether new users can create an account.": "Registration controls whether new users can create an account.",
"Registration is currently disabled": "Registration is currently disabled.",
"Registration off": "Registration off",
"Registration on": "Registration on",
"Registration successful! Please check your email for the verification code.": "Registration successful! Please check your email for the verification code.",
"Registration, sessions, login persistence": "Registration, sessions, login persistence",
"Remember login after Microsoft sign-in": "Remember login after Microsoft sign-in",
@@ -1033,6 +1036,7 @@
"Session idle timeout is invalid": "Session idle timeout is invalid",
"Session limits define how long logged-in users stay signed in.": "Set idle and absolute limits for active sessions.",
"Session policy": "Session policy",
"Sessions, login persistence, login tokens": "Sessions, login persistence, login tokens",
"Sessions, registration, lifecycle, audit, telemetry": "Sessions, registration, lifecycle, audit, telemetry",
"setting.api_cors_allowed_origins": "Allowed browser origins for API CORS (one origin per line)",
"setting.api_token_default_ttl_days": "Default lifetime for newly created API tokens in days (default: 90)",
@@ -1298,6 +1302,7 @@
"User lifecycle policy": "User lifecycle policy",
"User lifecycle settings": "User lifecycle settings",
"User not found": "User not found",
"User onboarding": "User onboarding",
"User override disabled": "User override disabled",
"User override enabled": "User override enabled",
"User profile fields are global and affect all tenants of this user": "User profile fields are global and affect all tenants of this user",

View File

@@ -42,7 +42,6 @@ if ($request->isMethod('POST')) {
}
$sectionKeys = [
'app_registration',
'session_idle_timeout_minutes',
'session_absolute_timeout_hours',
'remember_token_lifetime_days',

View File

@@ -11,16 +11,13 @@ use MintyPHP\Session;
$values = $values ?? [];
$settings = $settings ?? [];
$appRegistration = !empty($values['app_registration']);
$sessionIdleTimeoutMinutes = (int) ($values['session_idle_timeout_minutes'] ?? 30);
$sessionAbsoluteTimeoutHours = (int) ($values['session_absolute_timeout_hours'] ?? 8);
$rememberTokenLifetimeDays = (int) ($values['remember_token_lifetime_days'] ?? 30);
$microsoftAutoRememberDefault = !empty($values['microsoft_auto_remember_default']);
$appRegistrationDesc = $settings['app_registration']['description'] ?? 'setting.app_registration';
$sessionIdleTimeoutMinutesDesc = $settings['session_idle_timeout_minutes']['description'] ?? 'setting.session_idle_timeout_minutes';
$sessionAbsoluteTimeoutHoursDesc = $settings['session_absolute_timeout_hours']['description'] ?? 'setting.session_absolute_timeout_hours';
$rememberTokenLifetimeDaysDesc = $settings['remember_token_lifetime_days']['description'] ?? 'setting.remember_token_lifetime_days';
$microsoftAutoRememberDefaultDesc = $settings['microsoft_auto_remember_default']['description'] ?? 'setting.microsoft_auto_remember_default';
$activeLoginTokens = (int) ($activeLoginTokens ?? 0);
$canUpdateSettings = (bool) ($canUpdateSettings ?? false);
$isReadOnly = !$canUpdateSettings;
@@ -56,30 +53,6 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
?>
<form id="settings-account-access-form" method="post" data-details-storage="settings-account-access-details-v1" data-standard-detail-form="1">
<details class="app-details-card" name="settings-account-access-registration" data-details-key="settings-account-access-registration">
<summary>
<span class="app-details-card-summary-title"><?php e(t('Allow registration')); ?></span>
<span class="app-details-card-summary-meta">
<span class="badge" data-variant="neutral"><?php e($appRegistration ? t('Enabled') : t('Disabled')); ?></span>
</span>
</summary>
<div class="app-details-card-container">
<blockquote data-variant="info">
<small><?php e(t('Registration controls whether new users can create an account.')); ?></small>
</blockquote>
<fieldset>
<legend>
<small><?php e(t($appRegistrationDesc)); ?></small>
</legend>
<label class="app-field">
<input type="checkbox" name="app_registration" value="1" <?php e($appRegistration ? 'checked' : ''); ?>
<?php e($disabledAttr); ?>>
<span><?php e(t('Allow registration')); ?> <span class="badge" data-variant="info"><?php e(t('Cached')); ?></span></span>
</label>
</fieldset>
</div>
</details>
<details class="app-details-card" name="settings-account-access-session" data-details-key="settings-account-access-session" open>
<summary>
<span class="app-details-card-summary-title"><?php e(t('Session policy')); ?></span>
@@ -89,9 +62,6 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
</span>
</summary>
<div class="app-details-card-container">
<blockquote data-variant="info">
<small><?php e(t('Session limits define how long logged-in users stay signed in.')); ?></small>
</blockquote>
<div class="grid">
<label class="app-field">
<span><?php e(t('Session idle timeout (minutes)')); ?></span>
@@ -99,7 +69,6 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
value="<?php e((string) $sessionIdleTimeoutMinutes); ?>" min="5" max="1440" step="1"
<?php e($readonlyAttr); ?>>
<small><?php e(t($sessionIdleTimeoutMinutesDesc)); ?></small>
<small class="muted"><?php e(t('Allowed range: 5-1440 minutes (default: 30)')); ?></small>
</label>
<label class="app-field">
<span><?php e(t('Session absolute timeout (hours)')); ?></span>
@@ -107,7 +76,6 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
value="<?php e((string) $sessionAbsoluteTimeoutHours); ?>" min="1" max="72" step="1"
<?php e($readonlyAttr); ?>>
<small><?php e(t($sessionAbsoluteTimeoutHoursDesc)); ?></small>
<small class="muted"><?php e(t('Allowed range: 1-72 hours (default: 8)')); ?></small>
</label>
</div>
</div>
@@ -121,31 +89,21 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
</span>
</summary>
<div class="app-details-card-container">
<blockquote data-variant="info">
<small><?php e(t('Controls remember-me token lifetime and Microsoft auto-remember behavior.')); ?></small>
</blockquote>
<div class="grid">
<label class="app-field">
<span><?php e(t('Remember token lifetime (days)')); ?></span>
<input type="number" name="remember_token_lifetime_days"
value="<?php e((string) $rememberTokenLifetimeDays); ?>" min="1" max="365" step="1"
<?php e($readonlyAttr); ?>>
<small><?php e(t($rememberTokenLifetimeDaysDesc)); ?></small>
<small class="muted"><?php e(t('Allowed range: 1-365 days (default: 30)')); ?></small>
</label>
<fieldset>
<legend>
<small><?php e(t($microsoftAutoRememberDefaultDesc)); ?></small>
</legend>
<label class="app-field">
<input type="checkbox" name="microsoft_auto_remember_default" value="1"
<?php e($microsoftAutoRememberDefault ? 'checked' : ''); ?>
<?php e($disabledAttr); ?>>
<span><?php e(t('Microsoft Auto-Remember default')); ?></span>
</label>
<small class="muted"><?php e(t('When enabled, successful Microsoft logins automatically persist a remember-me token (unless overridden per tenant).')); ?></small>
</fieldset>
</div>
<label class="app-field">
<span><?php e(t('Remember token lifetime (days)')); ?></span>
<input type="number" name="remember_token_lifetime_days"
value="<?php e((string) $rememberTokenLifetimeDays); ?>" min="1" max="365" step="1"
<?php e($readonlyAttr); ?>>
<small><?php e(t($rememberTokenLifetimeDaysDesc)); ?></small>
</label>
<hr>
<label class="app-field">
<input type="checkbox" role="switch" name="microsoft_auto_remember_default" value="1"
<?php e($microsoftAutoRememberDefault ? 'checked' : ''); ?>
<?php e($disabledAttr); ?>>
<span><?php e(t('Microsoft Auto-Remember default')); ?></span>
</label>
<small class="muted"><?php e(t('When enabled, successful Microsoft logins automatically persist a remember-me token (unless overridden per tenant).')); ?></small>
</div>
</details>

View File

@@ -49,6 +49,7 @@ if ($request->isMethod('POST')) {
'default_department_id',
'app_title',
'app_locale',
'app_registration',
];
$mergedPost = settingsSectionMergePost($values, $request->bodyAll(), $sectionKeys);

View File

@@ -18,8 +18,10 @@ $defaultRoleId = (int) ($values['default_role_id'] ?? 0);
$defaultDepartmentId = (int) ($values['default_department_id'] ?? 0);
$appTitle = (string) ($values['app_title'] ?? '');
$appLocale = (string) ($values['app_locale'] ?? '');
$appRegistration = !empty($values['app_registration']);
$appTitleDesc = $settings['app_title']['description'] ?? 'setting.app_title';
$appLocaleDesc = $settings['app_locale']['description'] ?? 'setting.app_locale';
$appRegistrationDesc = $settings['app_registration']['description'] ?? 'setting.app_registration';
$locales = defined('APP_LOCALES') ? APP_LOCALES : [];
$canUpdateSettings = (bool) ($canUpdateSettings ?? false);
$isReadOnly = !$canUpdateSettings;
@@ -97,12 +99,20 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
<?php $configuredDefaultsCount = (int) ($defaultTenantId > 0) + (int) ($defaultDepartmentId > 0) + (int) ($defaultRoleId > 0); ?>
<details class="app-details-card" name="user-creation" data-details-key="user-creation">
<summary>
<span class="app-details-card-summary-title"><?php e(t('User creation defaults')); ?></span>
<span class="app-details-card-summary-title"><?php e(t('User onboarding')); ?></span>
<span class="app-details-card-summary-meta">
<span class="badge" data-variant="neutral"><?php e($appRegistration ? t('Registration on') : t('Registration off')); ?></span>
<span class="badge" data-variant="neutral"><?php e(sprintf(t('%d defaults configured'), $configuredDefaultsCount)); ?></span>
</span>
</summary>
<div class="app-details-card-container">
<label class="app-field">
<input type="checkbox" role="switch" name="app_registration" value="1" <?php e($appRegistration ? 'checked' : ''); ?>
<?php e($disabledAttr); ?>>
<span><?php e(t('Allow registration')); ?> <span class="badge" data-variant="info"><?php e(t('Cached')); ?></span></span>
</label>
<small class="muted"><?php e(t($appRegistrationDesc)); ?></small>
<hr>
<blockquote data-variant="info">
<small><?php e(t('These defaults prefill tenant, department and role when new users are created.')); ?></small>
</blockquote>

View File

@@ -20,14 +20,14 @@
'label' => t('General'),
'icon' => 'bi bi-sliders',
'iconTone' => 'blue',
'tooltip' => t('App title, language, user creation defaults'),
'tooltip' => t('App title, language, registration and new-user defaults'),
]);
appTile([
'href' => 'admin/settings/account-access',
'label' => t('Account access'),
'icon' => 'bi bi-shield-lock',
'iconTone' => 'violet',
'tooltip' => t('Registration, sessions, login persistence'),
'tooltip' => t('Sessions, login persistence, login tokens'),
]);
appTile([
'href' => 'admin/settings/user-lifecycle',