Finalize init defaults and auth flow UX improvements
This commit is contained in:
22
README.md
22
README.md
@@ -88,6 +88,28 @@ Standard-Demo-User:
|
||||
- Abteilung: `Musterabteilung`
|
||||
- Rollen: `Admin`, `User`
|
||||
|
||||
Initiale DB-Settings (Seed):
|
||||
|
||||
- `app_title` -> `CoreCore`
|
||||
- `app_locale` -> `de`
|
||||
- `app_theme` -> `light`
|
||||
- `app_theme_user` -> `1`
|
||||
- `app_registration` -> `1` (aktiv)
|
||||
- `app_primary_color` -> `#105433`
|
||||
- `api_token_default_ttl_days` -> `90`
|
||||
- `api_token_max_ttl_days` -> `365`
|
||||
- `api_cors_allowed_origins` -> `http://localhost:8080`, `http://127.0.0.1:8080`
|
||||
- `session_idle_timeout_minutes` -> `30`
|
||||
- `session_absolute_timeout_hours` -> `8`
|
||||
- `user_inactivity_deactivate_days` -> `180`
|
||||
- `user_inactivity_delete_days` -> `365`
|
||||
- `default_tenant_id` -> `MusterMandant`
|
||||
- `default_department_id` -> Abteilungscode `MUSTER` (`Musterabteilung`)
|
||||
- `default_role_id` -> Rolle `USER` (`User`)
|
||||
- `frontend_telemetry_enabled` -> `1`
|
||||
- `frontend_telemetry_sample_rate` -> `0.2`
|
||||
- `frontend_telemetry_allowed_events` -> `ajax_error,warn_once`
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
- `/config` Konfiguration (Routen, Settings, Themes, Assets)
|
||||
|
||||
@@ -938,13 +938,56 @@ JOIN permissions p ON p.`key` IN ('users.self_update', 'address_book.view')
|
||||
WHERE r.code = 'USER' OR r.description = 'User'
|
||||
ON DUPLICATE KEY UPDATE role_id = role_id;
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `description`)
|
||||
SELECT 'default_tenant_id', CAST(t.id AS CHAR), 'setting.default_tenant'
|
||||
FROM tenants t
|
||||
WHERE t.description = 'MusterMandant'
|
||||
LIMIT 1
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`key` = `key`;
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `description`)
|
||||
SELECT 'default_department_id', CAST(d.id AS CHAR), 'setting.default_department'
|
||||
FROM departments d
|
||||
JOIN tenants t ON t.id = d.tenant_id
|
||||
WHERE d.code = 'MUSTER'
|
||||
AND t.description = 'MusterMandant'
|
||||
LIMIT 1
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`key` = `key`;
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `description`)
|
||||
SELECT 'default_role_id', CAST(r.id AS CHAR), 'setting.default_role'
|
||||
FROM roles r
|
||||
WHERE r.code = 'USER'
|
||||
LIMIT 1
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`key` = `key`;
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `description`)
|
||||
SELECT 'api_cors_allowed_origins', CONCAT('http://localhost:8080', CHAR(10), 'http://127.0.0.1:8080'), 'setting.api_cors_allowed_origins'
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`key` = `key`;
|
||||
|
||||
INSERT INTO `settings` (`key`, `value`, `description`)
|
||||
VALUES
|
||||
('app_title', 'CoreCore', 'setting.app_title'),
|
||||
('app_locale', 'de', 'setting.app_locale'),
|
||||
('app_theme', 'light', 'setting.app_theme'),
|
||||
('app_theme_user', '1', 'setting.app_theme_user'),
|
||||
('app_registration', '1', 'setting.app_registration'),
|
||||
('app_primary_color', '#105433', 'setting.app_primary_color'),
|
||||
('api_token_default_ttl_days', '90', 'setting.api_token_default_ttl_days'),
|
||||
('api_token_max_ttl_days', '365', 'setting.api_token_max_ttl_days'),
|
||||
('session_idle_timeout_minutes', '30', 'setting.session_idle_timeout_minutes'),
|
||||
('session_absolute_timeout_hours', '8', 'setting.session_absolute_timeout_hours'),
|
||||
('user_inactivity_deactivate_days', '180', 'setting.user_inactivity_deactivate_days'),
|
||||
('user_inactivity_delete_days', '365', 'setting.user_inactivity_delete_days'),
|
||||
('system_audit_enabled', '1', 'setting.system_audit_enabled'),
|
||||
('system_audit_retention_days', '365', 'setting.system_audit_retention_days'),
|
||||
('frontend_telemetry_enabled', '0', 'setting.frontend_telemetry_enabled'),
|
||||
('frontend_telemetry_enabled', '1', 'setting.frontend_telemetry_enabled'),
|
||||
('frontend_telemetry_sample_rate', '0.2', 'setting.frontend_telemetry_sample_rate'),
|
||||
('frontend_telemetry_allowed_events', 'warn_once,ajax_error', 'setting.frontend_telemetry_allowed_events'),
|
||||
('frontend_telemetry_allowed_events', 'ajax_error,warn_once', 'setting.frontend_telemetry_allowed_events'),
|
||||
('microsoft_auto_remember_default', '0', 'setting.microsoft_auto_remember_default'),
|
||||
('remember_token_lifetime_days', '30', 'setting.remember_token_lifetime_days')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
|
||||
@@ -90,6 +90,34 @@ Die Firewall begrenzt gleichzeitige Requests pro IP (Concurrency-Schutz).
|
||||
|
||||
---
|
||||
|
||||
## Initiale DB-Settings (Seed)
|
||||
|
||||
Beim Fresh-Setup über `db/init/init.sql` werden zusätzlich folgende Settings in der DB vorbelegt:
|
||||
|
||||
- `app_title` -> `CoreCore`
|
||||
- `default_tenant_id` -> Seed-Mandant `MusterMandant`
|
||||
- `default_department_id` -> Seed-Abteilung mit Code `MUSTER`
|
||||
- `default_role_id` -> Seed-Rolle mit Code `USER`
|
||||
- `app_locale` -> `de`
|
||||
- `app_theme` -> `light`
|
||||
- `app_theme_user` -> `1`
|
||||
- `app_registration` -> `1` (Self-Registration aktiviert)
|
||||
- `app_primary_color` -> `#105433`
|
||||
- `api_token_default_ttl_days` -> `90`
|
||||
- `api_token_max_ttl_days` -> `365`
|
||||
- `api_cors_allowed_origins` -> `http://localhost:8080`, `http://127.0.0.1:8080`
|
||||
- `session_idle_timeout_minutes` -> `30`
|
||||
- `session_absolute_timeout_hours` -> `8`
|
||||
- `user_inactivity_deactivate_days` -> `180`
|
||||
- `user_inactivity_delete_days` -> `365`
|
||||
- `frontend_telemetry_enabled` -> `1`
|
||||
- `frontend_telemetry_sample_rate` -> `0.2`
|
||||
- `frontend_telemetry_allowed_events` -> `ajax_error,warn_once`
|
||||
|
||||
Diese Defaults werden von Selbstregistrierung, Admin-Benutzeranlage und Import genutzt, wenn keine expliziten Zuordnungen gesetzt sind.
|
||||
|
||||
---
|
||||
|
||||
## Produktions-Checkliste
|
||||
|
||||
Folgende Variablen **müssen** vor Go-Live angepasst werden:
|
||||
|
||||
@@ -14,6 +14,7 @@ $authHelpContext = [
|
||||
'show_back_to_login' => true,
|
||||
'show_support' => true,
|
||||
];
|
||||
$authLogoHref = lurl('login');
|
||||
?>
|
||||
|
||||
<article class="login-card">
|
||||
|
||||
@@ -33,6 +33,7 @@ $selectedTenantInitial = strtoupper(substr($selectedTenantLabel, 0, 1));
|
||||
if ($selectedTenantInitial === '') {
|
||||
$selectedTenantInitial = '?';
|
||||
}
|
||||
$canSwitchTenant = count($tenantCandidates) > 1;
|
||||
$errors = is_array($errors ?? null) ? $errors : [];
|
||||
$errorNoticeId = $errors ? 'auth-login-error-notice' : '';
|
||||
$loginHeading = t('Login');
|
||||
@@ -53,6 +54,10 @@ if ($stage === 'resolve_email') {
|
||||
'show_support' => true,
|
||||
];
|
||||
}
|
||||
$authLogoHref = lurl('login');
|
||||
if ($tenantHint !== '') {
|
||||
$authLogoHref .= '?tenant=' . rawurlencode($tenantHint);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -140,25 +145,30 @@ if ($stage === 'resolve_email') {
|
||||
<?php Session::getCsrfInput(); ?>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<section class="login-tenant-context" role="status" aria-live="polite">
|
||||
<div class="login-tenant-context-main">
|
||||
<?php if ($selectedTenantAvatarUrl !== ''): ?>
|
||||
<img class="login-tenant-context-avatar" src="<?php e($selectedTenantAvatarUrl); ?>" alt="" loading="lazy">
|
||||
<?php else: ?>
|
||||
<span class="login-tenant-context-avatar-placeholder"><?php e($selectedTenantInitial); ?></span>
|
||||
<?php endif; ?>
|
||||
<div class="login-tenant-context-text">
|
||||
<p class="login-tenant-context-name"><?php e($selectedTenantLabel); ?></p>
|
||||
<?php $hasSelectedTenantAvatar = $selectedTenantAvatarUrl !== ''; ?>
|
||||
<?php if ($canSwitchTenant): ?>
|
||||
<section class="login-tenant-context" role="status" aria-live="polite">
|
||||
<div class="login-tenant-context-main">
|
||||
<?php if ($hasSelectedTenantAvatar): ?>
|
||||
<img class="login-tenant-context-avatar" src="<?php e($selectedTenantAvatarUrl); ?>" alt="" loading="lazy">
|
||||
<?php else: ?>
|
||||
<span class="login-tenant-context-avatar-placeholder login-tenant-context-avatar-placeholder--name"><?php e($selectedTenantLabel); ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if ($hasSelectedTenantAvatar): ?>
|
||||
<div class="login-tenant-context-text">
|
||||
<p class="login-tenant-context-name"><?php e($selectedTenantLabel); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="login-tenant-context-form">
|
||||
<input type="hidden" name="stage" value="resolve_email">
|
||||
<input type="hidden" name="email" value="<?php e($email); ?>">
|
||||
<input type="hidden" name="tenant_hint" value="<?php e($tenantHint); ?>">
|
||||
<button type="submit" class="secondary outline small"><?php e(t('Switch tenant')); ?></button>
|
||||
<?php Session::getCsrfInput(); ?>
|
||||
</form>
|
||||
</section>
|
||||
<form method="post" class="login-tenant-context-form">
|
||||
<input type="hidden" name="stage" value="resolve_email">
|
||||
<input type="hidden" name="email" value="<?php e($email); ?>">
|
||||
<input type="hidden" name="tenant_hint" value="<?php e($tenantHint); ?>">
|
||||
<button type="submit" class="secondary outline small"><?php e(t('Switch tenant')); ?></button>
|
||||
<?php Session::getCsrfInput(); ?>
|
||||
</form>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="login-methods-stack">
|
||||
<?php if (!empty($selectedTenantMethods['microsoft']) && $selectedTenantSlug !== ''): ?>
|
||||
|
||||
@@ -21,6 +21,7 @@ $authHelpContext = [
|
||||
'show_back_to_login' => true,
|
||||
'show_support' => true,
|
||||
];
|
||||
$authLogoHref = lurl('login');
|
||||
?>
|
||||
<article class="login-card">
|
||||
<?php require templatePath('partials/auth-logo.phtml'); ?>
|
||||
|
||||
@@ -16,6 +16,7 @@ $authHelpContext = [
|
||||
'show_back_to_login' => true,
|
||||
'show_support' => true,
|
||||
];
|
||||
$authLogoHref = lurl('login');
|
||||
?>
|
||||
|
||||
<article class="login-card">
|
||||
|
||||
@@ -15,6 +15,7 @@ $authHelpContext = [
|
||||
'show_back_to_login' => true,
|
||||
'show_support' => true,
|
||||
];
|
||||
$authLogoHref = lurl('login');
|
||||
?>
|
||||
|
||||
<article class="login-card">
|
||||
|
||||
@@ -15,6 +15,7 @@ $authHelpContext = [
|
||||
'show_back_to_login' => true,
|
||||
'show_support' => true,
|
||||
];
|
||||
$authLogoHref = lurl('login');
|
||||
?>
|
||||
|
||||
<article class="login-card">
|
||||
|
||||
@@ -267,7 +267,7 @@ $csrfToken = (string) ($layoutNav['csrfToken'] ?? '');
|
||||
title="<?php e($tenantName); ?>">
|
||||
<?php else: ?>
|
||||
<div class="app-sidebar-tenant-name">
|
||||
<i class="bi bi-buildings"></i> <?php e($tenantName); ?>
|
||||
<?php e($tenantName); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
|
||||
@@ -4,7 +4,14 @@ $authLogoUrl = appLogoUrl();
|
||||
if ($authLogoUrl === '') {
|
||||
return;
|
||||
}
|
||||
$authLogoHref = isset($authLogoHref) && is_string($authLogoHref) ? trim($authLogoHref) : '';
|
||||
?>
|
||||
<div class="login-logo">
|
||||
<img src="<?php e($authLogoUrl); ?>" alt="<?php e(appTitle()); ?>" class="login-logo-img">
|
||||
<?php if ($authLogoHref !== ''): ?>
|
||||
<a href="<?php e($authLogoHref); ?>" class="login-logo-link">
|
||||
<img src="<?php e($authLogoUrl); ?>" alt="<?php e(appTitle()); ?>" class="login-logo-img">
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<img src="<?php e($authLogoUrl); ?>" alt="<?php e(appTitle()); ?>" class="login-logo-img">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
border: 0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.app-sidebar .app-sidebar-tenant-logo img {
|
||||
@@ -88,17 +89,13 @@
|
||||
}
|
||||
|
||||
.app-sidebar-tenant-name {
|
||||
flex: 1;
|
||||
font-weight: 600;
|
||||
font-size: var(--app-sidebar-tenant-size);
|
||||
font-size: 17px;
|
||||
color: var(--app-contrast);
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--app-border-radius);
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
border: 1px solid var(--app-border);
|
||||
}
|
||||
|
||||
.app-sidebar-title {
|
||||
@@ -247,16 +244,26 @@
|
||||
gap: 0.125rem;
|
||||
}
|
||||
|
||||
.app-sidebar #aside-panel-admin .app-sidebar-admin-group {
|
||||
.app-sidebar #aside-panel-admin .app-sidebar-admin-group {
|
||||
margin-bottom: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.app-sidebar #aside-panel-admin .app-sidebar-admin-group details > ul li:first-child {
|
||||
.app-sidebar
|
||||
#aside-panel-admin
|
||||
.app-sidebar-admin-group
|
||||
details
|
||||
> ul
|
||||
li:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.app-sidebar #aside-panel-admin .app-sidebar-admin-group details > ul li:last-child {
|
||||
.app-sidebar
|
||||
#aside-panel-admin
|
||||
.app-sidebar-admin-group
|
||||
details
|
||||
> ul
|
||||
li:last-child {
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
@@ -312,8 +319,14 @@
|
||||
color: var(--app-contrast);
|
||||
}
|
||||
|
||||
.app-sidebar .app-sidebar-saved-filter-item:hover .app-sidebar-saved-filter-delete button,
|
||||
.app-sidebar .app-sidebar-saved-filter-item:focus-within .app-sidebar-saved-filter-delete button {
|
||||
.app-sidebar
|
||||
.app-sidebar-saved-filter-item:hover
|
||||
.app-sidebar-saved-filter-delete
|
||||
button,
|
||||
.app-sidebar
|
||||
.app-sidebar-saved-filter-item:focus-within
|
||||
.app-sidebar-saved-filter-delete
|
||||
button {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
|
||||
@@ -556,6 +556,13 @@
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.login-logo-link:focus-visible {
|
||||
outline: 2px solid var(--app-primary-focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.login-logo-img {
|
||||
max-height: 2.5rem;
|
||||
max-width: 10rem;
|
||||
@@ -798,6 +805,16 @@
|
||||
color: var(--app-primary);
|
||||
}
|
||||
|
||||
.login-tenant-context-avatar-placeholder--name {
|
||||
width: auto;
|
||||
max-width: 18rem;
|
||||
height: auto;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
font-weight: 700;
|
||||
color: var(--app-text);
|
||||
}
|
||||
|
||||
.login-tenant-context-text {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
@@ -20,5 +20,5 @@ import './components/app-tenant-switcher.js';
|
||||
import './components/app-color-default-toggle.js';
|
||||
import './components/app-custom-field-options-toggle.js';
|
||||
import './components/app-tenant-sso-toggle.js';
|
||||
import './components/app-standard-detail-page.js';
|
||||
import './components/app-settings-telemetry.js';
|
||||
import './components/app-standard-detail-page.js';
|
||||
|
||||
Reference in New Issue
Block a user