This commit is contained in:
@@ -19,6 +19,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind l
|
||||
- Das Profilmenü oben rechts enthält nur `Profil`, für berechtigte Nutzer zusätzlich `Admin-Einstellungen`, sowie `Abmelden`.
|
||||
- Der Profilbereich enthält persönliche Angaben, Kinder, eigene Events, eigene Orte und Veranstaltungen, Cookie-/Kontoeinstellungen sowie Community-Informationen.
|
||||
- Der Admin-Bereich ist getrennt vom Profilbereich und enthält abhängig von der Berechtigung Profil-Levels, Kategorien, System-Einstellungen sowie Community-Moderation und Listing-Freigaben.
|
||||
- Die Admin-Unterbereiche bleiben im selben Dashboard-Layout. Listing-Freigaben zeigen ausschließlich noch nicht veröffentlichte Orte und Veranstaltungen inklusive direkter Bearbeitung; Community-Moderation zeigt nur offene Bewerbungen und Meldungen; Rollen und System-Levels liegen im User Management, die Community-Migration liegt unter System.
|
||||
- Bestehende Direktlinks zu `Kategorien`, `Profil-Levels` und `System` werden automatisch in den Admin-Bereich eingeordnet.
|
||||
- Kinder können angelegt, bearbeitet und gelöscht werden.
|
||||
- Wenn ein Geburtsdatum gesetzt ist, wird das Alter automatisch berechnet und später bei Bedarf aktualisiert.
|
||||
|
||||
@@ -17,6 +17,7 @@ Stand: 2026-08-10
|
||||
- Avatar-Verwaltung: `src/App/Avatar/AvatarManager.php`
|
||||
- aktive Styles: `src/App/Avatar/Lorelei.php`, `src/App/Avatar/Croodles.php`
|
||||
- Profil- und Admin-Bereiche mit getrennten Seitennavigationen: `partials/landing/account/dashboard.php`, Zugriffskontext in `src/App/AccountPages.php`, Einstieg über `partials/structure/nav.php`
|
||||
- Admin-Freigaben, Community-Moderation, User Management und Migration werden im Dashboard durch `src/App/AccountPages.php` verarbeitet und in `partials/landing/account/dashboard.php` dargestellt.
|
||||
- Event-Quellen aus Datenbank plus Speicherung der Event-Location-Herkunft: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `Internal/db/schema.sql`
|
||||
- Consent- und rechtliche Texte: `public/page/datenschutz.php`
|
||||
- System-Einstellungen: `src/App/SystemSettings.php`
|
||||
@@ -25,11 +26,11 @@ Stand: 2026-08-10
|
||||
- Kalender-Export und abonnierbarer Feed: `src/App/CalendarSync.php` plus `public/page/calendar/export.php` und `public/page/calendar/feed.php`
|
||||
- UI für getrennte Bereiche `Events` und `Orte & Veranstaltungen` im Mitgliederbereich: `partials/landing/account/dashboard.php`
|
||||
- eigener Mitgliederbereichspunkt `Kategorien` für neue Kategorien und deren Zusammenführung per Vorschlagsfeld: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `src/App/ListingCatalog.php`
|
||||
- Community-Level-Rechte für Kategorien und Orts-/Veranstaltungsfreigaben: `config/community.php`, `src/App/Community.php`, `src/App/CommunityAccess.php`, `partials/landing/account/community-admin.php`
|
||||
- Community-Level-Rechte für Kategorien und Orts-/Veranstaltungsfreigaben: `config/community.php`, `src/App/Community.php`, `src/App/CommunityAccess.php`, `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`
|
||||
- Preis-, Bild- und Ortseingabe für Orte und Veranstaltungen sowie reduzierte Event-Maske: `partials/landing/account/dashboard.php` plus Speicherung in `src/App/ListingCatalog.php`
|
||||
- Freigaben sowie Änderungs- und Löschanfragen für Orte und Veranstaltungen: `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`, `partials/landing/account/community-admin.php`
|
||||
- Freigaben sowie Änderungs- und Löschanfragen für Orte und Veranstaltungen: `src/App/ListingCatalog.php`, `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`
|
||||
- Direktbearbeitung noch nicht veröffentlichter Orts- und Veranstaltungsvorschläge sowie Rechteauflösung für deren Freigabe: `src/App/CommunityAccess.php`, `src/App/AccountPages.php`, `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`
|
||||
- Rollenvergabe per Benutzersuche im Community-Admin: `partials/landing/account/community-admin.php`, `src/App/CommunityAccess.php`
|
||||
- Rollenvergabe per Benutzersuche im User Management: `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`, `src/App/CommunityAccess.php`
|
||||
- Legacy-Eigen-Events wurden erweitert in `src/App/AccountPages.php` und `Internal/db/schema.sql` um Kategorie- und Bildfelder
|
||||
- Kategorien-Prüfung und Zusammenführung für Seiten-Admins liegen ebenfalls in `partials/landing/account/dashboard.php` mit Logik in `src/App/ListingCatalog.php`
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf lokale
|
||||
- Profilmenü oben rechts mit `Profil` sowie für Berechtigte `Admin-Einstellungen`
|
||||
- Profilbereich für persönliche Angaben, Kinder, eigene Events, eigene Orte und Veranstaltungen, Einstellungen und Community-Informationen
|
||||
- Separater Admin-Bereich für Profil-Levels, Kategorien, System-Einstellungen sowie Moderation und Freigaben von Orten und Veranstaltungen
|
||||
- Admin-Unterbereiche bleiben im selben Dashboard-Layout getrennt: Freigaben zeigen nur noch nicht veröffentlichte Orte und Veranstaltungen, Community-Moderation nur offene Bewerbungen und Meldungen, User Management enthält Rollen und System-Levels, System enthält die Community-Migration.
|
||||
- Konto-E-Mails sowie sensible Profilfelder werden app-seitig verschlüsselt gespeichert
|
||||
- Kinder können angelegt, bearbeitet und gelöscht werden
|
||||
- Profiladresse mit verschlüsselter Straße/Hausnummer, Adresssuche, Browser-Übernahme und Validierung
|
||||
|
||||
@@ -19,6 +19,7 @@ Papa-Kind-Treff is a PHP-based platform for fathers. Core areas are local events
|
||||
- The top-right profile menu only contains `Profile`, `Admin Settings` for authorized users, and `Logout`.
|
||||
- The profile area contains personal information, children, own events, own places and event-style entries, cookie/account settings, and community information.
|
||||
- The admin area is separated from the profile area and contains, depending on permissions, profile levels, categories, system settings, community moderation, and listing approvals.
|
||||
- Admin subsections stay within the same dashboard layout. Listing approvals only show not-yet-published places and event-style entries and allow direct editing; community moderation only shows open applications and reports; roles and system levels live in User Management, while the community migration lives under System.
|
||||
- Existing direct links to `Categories`, `Profile Levels`, and `System` are automatically assigned to the admin area.
|
||||
- Children can be created, edited, and deleted.
|
||||
- If a birth date is set, age is calculated automatically and updated later when needed.
|
||||
|
||||
@@ -17,6 +17,7 @@ Updated: 2026-08-10
|
||||
- avatar management: `src/App/Avatar/AvatarManager.php`
|
||||
- active avatar styles: `src/App/Avatar/Lorelei.php`, `src/App/Avatar/Croodles.php`
|
||||
- separated profile and admin navigation inside the member area: `partials/landing/account/dashboard.php`, access context in `src/App/AccountPages.php`, entry through `partials/structure/nav.php`
|
||||
- admin approvals, community moderation, user management, and migration are processed in `src/App/AccountPages.php` and rendered within `partials/landing/account/dashboard.php`.
|
||||
- database-backed event location sources plus persisted event location origin: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `Internal/db/schema.sql`
|
||||
- consent and legal texts: `public/page/datenschutz.php`
|
||||
- system settings: `src/App/SystemSettings.php`
|
||||
@@ -25,11 +26,11 @@ Updated: 2026-08-10
|
||||
- calendar export and subscribable feed: `src/App/CalendarSync.php` plus `public/page/calendar/export.php` and `public/page/calendar/feed.php`
|
||||
- separated `Events` and `Places & Events` UI in member area: `partials/landing/account/dashboard.php`
|
||||
- dedicated member-area section `Categories` for new categories and merging through a suggestion field: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `src/App/ListingCatalog.php`
|
||||
- community-level rights for categories and place/event approvals: `config/community.php`, `src/App/Community.php`, `src/App/CommunityAccess.php`, `partials/landing/account/community-admin.php`
|
||||
- community-level rights for categories and place/event approvals: `config/community.php`, `src/App/Community.php`, `src/App/CommunityAccess.php`, `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`
|
||||
- pricing, image upload, and place/address input for places and event-style entries plus a reduced own-event form: `partials/landing/account/dashboard.php` with persistence in `src/App/ListingCatalog.php`
|
||||
- approvals as well as change and deletion requests for places and event-style entries: `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`, `partials/landing/account/community-admin.php`
|
||||
- approvals as well as change and deletion requests for places and event-style entries: `src/App/ListingCatalog.php`, `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`
|
||||
- direct editing for not-yet-published place and event-style suggestions plus approval-right resolution: `src/App/CommunityAccess.php`, `src/App/AccountPages.php`, `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`
|
||||
- role assignment via user search in community admin: `partials/landing/account/community-admin.php`, `src/App/CommunityAccess.php`
|
||||
- role assignment via user search in User Management: `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`, `src/App/CommunityAccess.php`
|
||||
- legacy own events were extended in `src/App/AccountPages.php` and `Internal/db/schema.sql` with category and image fields
|
||||
|
||||
## Documentation Rule
|
||||
|
||||
@@ -19,6 +19,7 @@ Papa-Kind-Treff is a PHP-based platform for fathers focused on local events, app
|
||||
- top-right profile menu with `Profile` and, for authorized users, `Admin Settings`
|
||||
- profile area for personal information, children, own events, own places and event-style entries, settings, and community information
|
||||
- separate admin area for profile levels, categories, system settings, and moderation and approvals for places and event-style entries
|
||||
- Admin subsections stay inside the same dashboard layout: approvals only show not-yet-published places and event-style entries, community moderation only shows open applications and reports, User Management contains roles and system levels, and System contains the community migration.
|
||||
- account emails and sensitive profile fields are stored encrypted application-side
|
||||
- children can be created, edited, and deleted
|
||||
- profile address supports encrypted street/house number, address search, browser import, and validation
|
||||
|
||||
@@ -39,6 +39,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf:
|
||||
- Das Profilmenü oben rechts trennt klar zwischen `Profil` und dem nur für Berechtigte sichtbaren Punkt `Admin-Einstellungen`.
|
||||
- Der Profilbereich bündelt persönliche Angaben, Kinder, eigene Events, eigene Orte und Veranstaltungen, Cookie-/Kontoeinstellungen sowie Community-Informationen.
|
||||
- Der Admin-Bereich bündelt getrennt Profil-Levels, Kategorien, System-Einstellungen sowie die Moderation und Freigabe von Orten und Veranstaltungen.
|
||||
- Innerhalb des Admin-Bereichs bleiben Freigaben, Community-Moderation, User Management, Profil-Levels und System im selben Dashboard-Layout getrennt; Rollen und System-Levels liegen im User Management, die Community-Migration im System.
|
||||
- In `Profil-Levels` kann der `SiteOwner` Community-Level pflegen, Benutzer suchen und Community-Punkte manuell mit Begründung erhöhen
|
||||
- In `Profil-Levels` werden zusätzlich Nutzer angezeigt, die in den letzten 30 Tagen mindestens den Rang `Säule der Väter-Community` erreicht haben
|
||||
- Im Bereich `Community-Admin` läuft die Rollenvergabe jetzt über eine Benutzersuche statt über rohe Benutzer-IDs; `Site-Admins` bleiben dabei auf die vorhandene Rollenlogik begrenzt
|
||||
|
||||
@@ -147,7 +147,19 @@ $sectionLinks = $isAdminArea
|
||||
'settings' => 'Einstellungen',
|
||||
];
|
||||
$showEventTypeChoice = !$editing && (($section ?? 'events') === 'places') && $requestedCreateListingKind === null;
|
||||
$modalSectionTarget = ($editingListing || ($section ?? '') === 'places') ? 'places' : 'events';
|
||||
$modalSectionTarget = $isAdminArea && $editingListing
|
||||
? 'listing-review'
|
||||
: (($editingListing || ($section ?? '') === 'places') ? 'places' : 'events');
|
||||
$modalActionArea = $isAdminArea && $editingListing ? 'area=admin&' : '';
|
||||
if ($isAdminArea && !empty($canReviewListings)) {
|
||||
$sectionLinks['listing-review'] = 'Orte & Veranstaltungen freigeben';
|
||||
}
|
||||
if ($isAdminArea && (!empty($canModerateForum) || !empty($canManageSystemSettings))) {
|
||||
$sectionLinks['community-moderation'] = 'Community-Moderation';
|
||||
}
|
||||
if ($isAdminArea && !empty($canManageUserManagement)) {
|
||||
$sectionLinks['user-management'] = 'User Management';
|
||||
}
|
||||
if ($isAdminArea && !empty($canManageCategories)) {
|
||||
$sectionLinks['categories'] = 'Kategorien';
|
||||
}
|
||||
@@ -188,10 +200,6 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
|
||||
<span><?= htmlspecialchars($sectionLabel, ENT_QUOTES) ?></span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
<?php if ($isAdminArea && !empty($canAccessCommunityAdmin)): ?>
|
||||
<a href="/community-admin#listing-requests"><span>Orte & Veranstaltungen freigeben</span></a>
|
||||
<a href="/community-admin"><span>Community-Moderation</span></a>
|
||||
<?php endif; ?>
|
||||
<?php if ($isAdminArea): ?>
|
||||
<a class="account-sidebar__back" href="/dashboard?section=profile"><span>Zum Profil</span></a>
|
||||
<?php endif; ?>
|
||||
@@ -208,16 +216,25 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
|
||||
</div>
|
||||
<div class="account-panel__body">
|
||||
<div class="profile-summary-grid">
|
||||
<?php if (!empty($canAccessCommunityAdmin)): ?>
|
||||
<a class="profile-summary-card profile-summary-card--link" href="/community-admin#listing-requests">
|
||||
<?php if (!empty($canReviewListings)): ?>
|
||||
<a class="profile-summary-card profile-summary-card--link" href="/dashboard?area=admin&section=listing-review">
|
||||
<span class="profile-summary-card__eyebrow">Freigaben</span>
|
||||
<strong>Orte & Veranstaltungen</strong>
|
||||
<p class="muted small">Neue Einträge sowie Änderungs- und Löschanfragen prüfen.</p>
|
||||
<p class="muted small">Noch nicht veröffentlichte Orte und Veranstaltungen prüfen.</p>
|
||||
</a>
|
||||
<a class="profile-summary-card profile-summary-card--link" href="/community-admin">
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($canModerateForum) || !empty($canManageSystemSettings)): ?>
|
||||
<a class="profile-summary-card profile-summary-card--link" href="/dashboard?area=admin&section=community-moderation">
|
||||
<span class="profile-summary-card__eyebrow">Moderation</span>
|
||||
<strong>Community</strong>
|
||||
<p class="muted small">Meldungen, Bewerbungen und Rollen verwalten.</p>
|
||||
<p class="muted small">Offene Bewerbungen und Meldungen bearbeiten.</p>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($canManageUserManagement)): ?>
|
||||
<a class="profile-summary-card profile-summary-card--link" href="/dashboard?area=admin&section=user-management">
|
||||
<span class="profile-summary-card__eyebrow">Zugriffe</span>
|
||||
<strong>User Management</strong>
|
||||
<p class="muted small">Systemrollen und Benutzerzugriffe verwalten.</p>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($canManageCategories)): ?>
|
||||
@@ -231,7 +248,7 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
|
||||
<a class="profile-summary-card profile-summary-card--link" href="/dashboard?area=admin&section=profile-levels">
|
||||
<span class="profile-summary-card__eyebrow">Community</span>
|
||||
<strong>Profil-Levels</strong>
|
||||
<p class="muted small">Level, Punkte und Systemrollen verwalten.</p>
|
||||
<p class="muted small">Community-Level und Community-Punkte verwalten.</p>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($canManageSystemSettings)): ?>
|
||||
@@ -245,6 +262,45 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
<?php if ($section === 'listing-review' && !empty($canReviewListings)): ?>
|
||||
<section class="account-panel">
|
||||
<div class="account-panel__head"><h2>Orte & Veranstaltungen freigeben</h2><p class="muted">Nur noch nicht freigegebene Orte und Veranstaltungen prüfen und bei Bedarf vor der Freigabe bearbeiten.</p></div>
|
||||
<div class="account-panel__body">
|
||||
<?php if (!$listingReviewRequests): ?><p class="muted">Keine offenen Orte oder Veranstaltungen zur Freigabe.</p><?php endif; ?>
|
||||
<div class="stack gap-12">
|
||||
<?php foreach ($listingReviewRequests as $request): ?>
|
||||
<article class="card" style="padding:18px;">
|
||||
<strong><?= htmlspecialchars((string)$request['title'], ENT_QUOTES) ?></strong>
|
||||
<span class="badge"><?= (string)($request['listing_type'] ?? '') === 'place' ? 'Ort' : 'Veranstaltung' ?></span>
|
||||
<p class="muted small"><?= htmlspecialchars(trim(implode(', ', array_filter([(string)($request['street'] ?? ''), trim((string)($request['zip'] ?? '') . ' ' . (string)($request['city'] ?? '')), (string)($request['region'] ?? '')]))), ENT_QUOTES) ?></p>
|
||||
<p class="muted small">Eingereicht von <?= htmlspecialchars((string)($request['requested_by_name'] ?: 'Mitglied'), ENT_QUOTES) ?>.</p>
|
||||
<div class="flex gap-12" style="flex-wrap:wrap; margin:14px 0;"><a class="btn ghost" href="/dashboard?area=admin&section=listing-review&edit_listing=<?= (int)$request['listing_id'] ?>">Bearbeiten</a></div>
|
||||
<form method="post" class="stack gap-8">
|
||||
<input type="hidden" name="action" value="listing_request_decide"><input type="hidden" name="request_id" value="<?= (int)$request['id'] ?>">
|
||||
<textarea name="review_note" class="textarea" rows="2" placeholder="Hinweis für die Entscheidung"></textarea>
|
||||
<div class="flex gap-12"><button class="btn" type="submit" name="decision" value="approved">Freigeben</button><button class="btn ghost" type="submit" name="decision" value="rejected">Ablehnen</button></div>
|
||||
</form>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
<?php if ($section === 'community-moderation' && (!empty($canModerateForum) || !empty($canManageSystemSettings))): ?>
|
||||
<section class="account-panel"><div class="account-panel__head"><h2>Community-Moderation</h2><p class="muted">Offene Bewerbungen und gemeldete Inhalte bearbeiten.</p></div><div class="account-panel__body stack gap-18">
|
||||
<?php if (!empty($canManageSystemSettings)): ?><div class="card"><strong>Offene Bewerbungen</strong><?php if (!$communityApplications): ?><p class="muted small">Keine offenen Bewerbungen.</p><?php endif; ?><?php foreach ($communityApplications as $application): ?><div class="card" style="margin-top:12px; padding:14px;"><strong><?= htmlspecialchars((string)($application['display_name'] ?: $application['email']), ENT_QUOTES) ?></strong><p><?= nl2br(htmlspecialchars((string)$application['motivation'], ENT_QUOTES)) ?></p><form method="post" class="stack gap-8"><input type="hidden" name="action" value="application_decide"><input type="hidden" name="application_id" value="<?= (int)$application['id'] ?>"><textarea name="decision_reason" class="textarea" rows="2" placeholder="Begründung"></textarea><div class="flex gap-12"><button class="btn" type="submit" name="decision" value="approved">Annehmen</button><button class="btn ghost" type="submit" name="decision" value="rejected">Ablehnen</button></div></form></div><?php endforeach; ?></div><?php endif; ?>
|
||||
<?php if (!empty($canModerateForum)): ?><div class="card"><strong>Offene Meldungen</strong><?php if (!$communityReports): ?><p class="muted small">Keine offenen Meldungen.</p><?php endif; ?><?php foreach ($communityReports as $report): ?><div class="card" style="margin-top:12px; padding:14px;"><strong><?= htmlspecialchars((string)$report['target_type'], ENT_QUOTES) ?> #<?= (int)$report['target_id'] ?></strong><p><?= nl2br(htmlspecialchars((string)$report['reason'], ENT_QUOTES)) ?></p><form method="post" class="stack gap-8"><input type="hidden" name="action" value="report_resolve"><input type="hidden" name="report_id" value="<?= (int)$report['id'] ?>"><textarea name="moderator_note" class="textarea" rows="2" placeholder="Moderationsnotiz"></textarea><button class="btn" type="submit">Als erledigt markieren</button></form></div><?php endforeach; ?></div><?php endif; ?>
|
||||
</div></section>
|
||||
<?php endif; ?>
|
||||
<?php if ($section === 'user-management' && !empty($canManageUserManagement)): ?>
|
||||
<section class="account-panel"><div class="account-panel__head"><h2>User Management</h2><p class="muted">Benutzer suchen und Systemrollen verwalten.</p></div><div class="account-panel__body">
|
||||
<form method="get" class="form-grid"><input type="hidden" name="area" value="admin"><input type="hidden" name="section" value="user-management"><div class="stack gap-6"><label class="label" for="userManagementQuery">Name oder E-Mail</label><input id="userManagementQuery" name="user_management_query" class="input" value="<?= htmlspecialchars((string)$userManagementSearchQuery, ENT_QUOTES) ?>"></div><div class="stack gap-6" style="justify-content:end;"><span class="label"> </span><button class="btn" type="submit">Suchen</button></div></form>
|
||||
<?php foreach ($userManagementSearchResults as $candidate): ?><?php $candidateRoles = $communityAccess->getUserRoles((int)$candidate['id']); $assignableRoles = !empty($canManageProfileLevels) ? ['forum_admin', 'site_admin', 'owner'] : ['forum_admin']; ?><div class="card" style="margin-top:14px; padding:14px;"><strong><?= htmlspecialchars((string)($candidate['display_name'] ?: trim(((string)($candidate['first_name'] ?? '')) . ' ' . ((string)($candidate['last_name'] ?? ''))) ?: 'Mitglied'), ENT_QUOTES) ?></strong><p class="muted small">Aktuelle Rollen: <?= htmlspecialchars($candidateRoles ? implode(', ', $candidateRoles) : 'Keine', ENT_QUOTES) ?></p><div class="flex gap-8" style="flex-wrap:wrap;"><?php foreach ($assignableRoles as $roleKey): ?><?php if (!in_array($roleKey, $candidateRoles, true)): ?><form method="post"><input type="hidden" name="action" value="role_assign"><input type="hidden" name="target_user_id" value="<?= (int)$candidate['id'] ?>"><input type="hidden" name="role" value="<?= $roleKey ?>"><button class="btn ghost" type="submit"><?= htmlspecialchars($roleKey, ENT_QUOTES) ?> vergeben</button></form><?php elseif (!empty($canManageProfileLevels)): ?><form method="post"><input type="hidden" name="action" value="role_revoke"><input type="hidden" name="target_user_id" value="<?= (int)$candidate['id'] ?>"><input type="hidden" name="role" value="<?= $roleKey ?>"><button class="btn ghost" type="submit"><?= htmlspecialchars($roleKey, ENT_QUOTES) ?> entziehen</button></form><?php endif; ?><?php endforeach; ?></div></div><?php endforeach; ?>
|
||||
<?php if ($userManagementSearchQuery !== '' && !$userManagementSearchResults): ?><p class="muted small" style="margin-top:14px;">Keine passenden Benutzer gefunden.</p><?php endif; ?>
|
||||
<?php if (!empty($canManageProfileLevels)): ?><div class="card" style="margin-top:18px;"><strong>Vergebene Systemrollen</strong><?php if (!$systemRoleAssignments): ?><p class="muted small">Keine Rollen vergeben.</p><?php endif; ?><?php foreach ($systemRoleAssignments as $assignment): ?><div class="flex between center-y" style="gap:12px; flex-wrap:wrap; margin-top:12px;"><span><?= htmlspecialchars((string)($assignment['display_name'] ?: 'Mitglied'), ENT_QUOTES) ?> · <?= htmlspecialchars((string)$assignment['role'], ENT_QUOTES) ?></span><form method="post"><input type="hidden" name="action" value="role_revoke"><input type="hidden" name="target_user_id" value="<?= (int)$assignment['user_id'] ?>"><input type="hidden" name="role" value="<?= htmlspecialchars((string)$assignment['role'], ENT_QUOTES) ?>"><button class="btn ghost" type="submit">Entziehen</button></form></div><?php endforeach; ?></div><?php endif; ?>
|
||||
<div class="card" style="margin-top:18px;"><strong>System-Levels</strong><div class="stack gap-12" style="margin-top:14px;"><?php foreach (($systemLevelDefinitions ?? []) as $systemLevel): ?><div><strong><?= htmlspecialchars((string)$systemLevel['label'], ENT_QUOTES) ?></strong><ul class="dash-list"><?php foreach (($systemLevel['rights'] ?? []) as $right): ?><li><?= htmlspecialchars((string)$right, ENT_QUOTES) ?></li><?php endforeach; ?></ul></div><?php endforeach; ?></div></div>
|
||||
</div></section>
|
||||
<?php endif; ?>
|
||||
<?php if ($section === 'profile'): ?>
|
||||
<section class="account-panel">
|
||||
<div class="account-panel__head">
|
||||
@@ -384,16 +440,6 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($section === 'profile-levels' && !empty($canManageProfileLevels)): ?>
|
||||
<?php
|
||||
$systemRoleAssignmentsByRole = [];
|
||||
foreach (($systemRoleAssignments ?? []) as $assignment) {
|
||||
$roleKey = (string)($assignment['role'] ?? '');
|
||||
if ($roleKey === '') {
|
||||
continue;
|
||||
}
|
||||
$systemRoleAssignmentsByRole[$roleKey][] = $assignment;
|
||||
}
|
||||
?>
|
||||
<section class="account-panel">
|
||||
<div class="account-panel__head">
|
||||
<h2>Profil-Levels</h2>
|
||||
@@ -451,9 +497,6 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
|
||||
Punkte: <?= number_format((float)($searchedUser['community_points'] ?? 0), 1, ',', '.') ?> ·
|
||||
Level: <?= htmlspecialchars(trim(((string)(($searchedUser['community_level']['icon'] ?? ''))) . ' ' . ((string)(($searchedUser['community_level']['label'] ?? '')))), ENT_QUOTES) ?>
|
||||
</div>
|
||||
<?php if (!empty($searchedUser['roles'])): ?>
|
||||
<div class="muted small" style="margin-top:6px;">System-Level: <?= htmlspecialchars(implode(', ', array_map('strval', (array)$searchedUser['roles'])), ENT_QUOTES) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="stack gap-12" style="margin-top:14px;">
|
||||
@@ -569,33 +612,6 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top:18px;">
|
||||
<strong>System-Level</strong>
|
||||
<div class="stack gap-12" style="margin-top:14px;">
|
||||
<?php foreach (($systemLevelDefinitions ?? []) as $systemLevel): ?>
|
||||
<div class="card" style="padding:14px;">
|
||||
<strong><?= htmlspecialchars((string)($systemLevel['label'] ?? ''), ENT_QUOTES) ?></strong>
|
||||
<?php if (!empty($systemRoleAssignmentsByRole[(string)($systemLevel['key'] ?? '')])): ?>
|
||||
<div class="muted small" style="margin-top:8px;">
|
||||
Aktuell:
|
||||
<?php
|
||||
$assignmentLabels = [];
|
||||
foreach ($systemRoleAssignmentsByRole[(string)$systemLevel['key']] as $assignment) {
|
||||
$assignmentLabels[] = (string)($assignment['display_name'] ?? 'Mitglied');
|
||||
}
|
||||
echo htmlspecialchars(implode(', ', $assignmentLabels), ENT_QUOTES);
|
||||
?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<ul class="dash-list" style="margin-top:12px;">
|
||||
<?php foreach (($systemLevel['rights'] ?? []) as $systemRight): ?>
|
||||
<li><?= htmlspecialchars((string)$systemRight, ENT_QUOTES) ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
@@ -1015,6 +1031,15 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
|
||||
<li>Fehlende Tabellen: <?= !empty($listingCatalogStatus['missing']) ? htmlspecialchars(implode(', ', $listingCatalogStatus['missing']), ENT_QUOTES) : 'Keine' ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php if ($communityMigrationStatus): ?>
|
||||
<div class="card" style="margin-top:18px;">
|
||||
<strong>Community-Migration</strong>
|
||||
<p class="muted small">Status: <?= !empty($communityMigrationStatus['complete']) ? 'Vollständig eingerichtet' : 'Migration noch offen' ?></p>
|
||||
<p class="muted small">Fehlende Bausteine: <?= !empty($communityMigrationStatus['missing']) ? htmlspecialchars(implode(', ', $communityMigrationStatus['missing']), ENT_QUOTES) : 'Keine' ?></p>
|
||||
<form method="post" style="margin-top:14px;"><input type="hidden" name="action" value="community_migrate"><button class="btn" type="submit">Community-Migration ausführen</button></form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
@@ -1530,7 +1555,7 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
|
||||
<h3><?= htmlspecialchars($eventTitle, ENT_QUOTES) ?></h3>
|
||||
<button class="btn ghost" type="button" data-modal-close>✕</button>
|
||||
</div>
|
||||
<form id="eventEditorForm" class="stack gap-12" style="margin-top: 10px;" method="post" action="/dashboard?section=<?= htmlspecialchars($modalSectionTarget, ENT_QUOTES) ?>#<?= htmlspecialchars($modalSectionTarget, ENT_QUOTES) ?>" enctype="multipart/form-data">
|
||||
<form id="eventEditorForm" class="stack gap-12" style="margin-top: 10px;" method="post" action="/dashboard?<?= $modalActionArea ?>section=<?= htmlspecialchars($modalSectionTarget, ENT_QUOTES) ?>#<?= htmlspecialchars($modalSectionTarget, ENT_QUOTES) ?>" enctype="multipart/form-data">
|
||||
<input type="hidden" name="action" value="<?= htmlspecialchars($actionEvent, ENT_QUOTES) ?>">
|
||||
<input type="hidden" name="entry_kind" id="entryKind" value="<?= htmlspecialchars($currentEntryKind, ENT_QUOTES) ?>">
|
||||
<?php if ($editingEvent && $editEvent): ?>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
tpl('community-admin', 'landing', 'account');
|
||||
redirect('/dashboard?area=admin§ion=community-moderation');
|
||||
|
||||
@@ -184,7 +184,9 @@ final class AccountPages
|
||||
$communityPointsForAccess = $community ? $community->computePoints($userId) : 0.0;
|
||||
$canManageCategories = $communityAccess ? $communityAccess->canManageCategories($userId, $communityPointsForAccess) : false;
|
||||
$canReviewListings = $communityAccess ? $communityAccess->canReviewListings($userId, $communityPointsForAccess) : false;
|
||||
$canModerateForum = $communityAccess ? $communityAccess->canModerateForum($userId) : false;
|
||||
$canAccessCommunityAdmin = $communityAccess ? $communityAccess->canAccessCommunityAdmin($userId, $communityPointsForAccess) : false;
|
||||
$canManageUserManagement = $canManageProfileLevels || $canManageSystemSettings;
|
||||
$canAccessAdminArea = $canAccessCommunityAdmin || $canManageCategories || $canManageSystemSettings || $canManageProfileLevels;
|
||||
|
||||
// Alte Deep Links zu Verwaltungssektionen bleiben gültig, landen aber im getrennten Admin-Bereich.
|
||||
@@ -211,6 +213,15 @@ final class AccountPages
|
||||
if ($area === 'admin' && $canManageProfileLevels) {
|
||||
$allowedSections[] = 'profile-levels';
|
||||
}
|
||||
if ($area === 'admin' && $canReviewListings) {
|
||||
$allowedSections[] = 'listing-review';
|
||||
}
|
||||
if ($area === 'admin' && ($canModerateForum || $canManageSystemSettings)) {
|
||||
$allowedSections[] = 'community-moderation';
|
||||
}
|
||||
if ($area === 'admin' && $canManageUserManagement) {
|
||||
$allowedSections[] = 'user-management';
|
||||
}
|
||||
|
||||
if ($systemSettings) {
|
||||
$systemSettings->ensureSchema();
|
||||
@@ -705,6 +716,42 @@ final class AccountPages
|
||||
}
|
||||
$communityAccess->submitApplication($userId, (string)($_POST['motivation'] ?? ''));
|
||||
$info = 'Deine Bewerbung wurde eingereicht.';
|
||||
} elseif ($action === 'listing_request_decide') {
|
||||
if (!$canReviewListings || !$listingCatalog) {
|
||||
throw new \RuntimeException('Keine Berechtigung für Listing-Freigaben.');
|
||||
}
|
||||
$listingCatalog->decideModerationRequest($userId, (int)($_POST['request_id'] ?? 0), (string)($_POST['decision'] ?? ''), (string)($_POST['review_note'] ?? ''));
|
||||
$info = 'Ort oder Veranstaltung wurde bearbeitet.';
|
||||
} elseif ($action === 'application_decide') {
|
||||
if (!$canManageSystemSettings || !$communityAccess) {
|
||||
throw new \RuntimeException('Keine Berechtigung für Bewerbungen.');
|
||||
}
|
||||
$communityAccess->decideApplication($userId, (int)($_POST['application_id'] ?? 0), (string)($_POST['decision'] ?? ''), (string)($_POST['decision_reason'] ?? ''));
|
||||
$info = 'Bewerbung wurde bearbeitet.';
|
||||
} elseif ($action === 'report_resolve') {
|
||||
if (!$canModerateForum || !$communityAccess) {
|
||||
throw new \RuntimeException('Keine Berechtigung für Meldungen.');
|
||||
}
|
||||
$communityAccess->resolveReport($userId, (int)($_POST['report_id'] ?? 0), (string)($_POST['moderator_note'] ?? ''));
|
||||
$info = 'Meldung wurde abgeschlossen.';
|
||||
} elseif ($action === 'role_assign') {
|
||||
if (!$canManageUserManagement || !$communityAccess) {
|
||||
throw new \RuntimeException('Keine Berechtigung für die Benutzerverwaltung.');
|
||||
}
|
||||
$communityAccess->assignRole($userId, (int)($_POST['target_user_id'] ?? 0), (string)($_POST['role'] ?? ''));
|
||||
$info = 'Rolle wurde vergeben.';
|
||||
} elseif ($action === 'role_revoke') {
|
||||
if (!$canManageUserManagement || !$communityAccess) {
|
||||
throw new \RuntimeException('Keine Berechtigung für die Benutzerverwaltung.');
|
||||
}
|
||||
$communityAccess->revokeRole($userId, (int)($_POST['target_user_id'] ?? 0), (string)($_POST['role'] ?? ''));
|
||||
$info = 'Rolle wurde entzogen.';
|
||||
} elseif ($action === 'community_migrate') {
|
||||
if (!$canManageSystemSettings || !$communityMigration) {
|
||||
throw new \RuntimeException('Keine Berechtigung für die Community-Migration.');
|
||||
}
|
||||
$communityMigration->apply();
|
||||
$info = 'Die Community-Migration wurde ausgeführt.';
|
||||
} elseif ($action === 'community_level_save') {
|
||||
if (!$canManageProfileLevels || !$community) {
|
||||
throw new \RuntimeException('Keine Berechtigung für Profil-Levels.');
|
||||
@@ -1010,7 +1057,17 @@ final class AccountPages
|
||||
$communityLevelDefinitions = $community ? $community->listMembershipLevels() : [];
|
||||
$communityLevelRightDefinitions = Community::membershipRightDefinitions();
|
||||
$communityRoles = $communityAccess ? $communityAccess->getUserRoles($userId) : [];
|
||||
$systemRoleAssignments = $communityAccess && $canManageProfileLevels ? $communityAccess->listRoleAssignments() : [];
|
||||
$systemRoleAssignments = $communityAccess && $canManageUserManagement ? $communityAccess->listRoleAssignments() : [];
|
||||
$listingReviewRequests = $canReviewListings && $listingCatalog
|
||||
? array_values(array_filter($listingCatalog->listOpenModerationRequests(), static fn(array $request): bool => (string)($request['request_type'] ?? '') === 'create'))
|
||||
: [];
|
||||
$communityApplications = $canManageSystemSettings && $communityAccess ? $communityAccess->listApplications('open') : [];
|
||||
$communityReports = $canModerateForum && $communityAccess && $communityAccess->supportsReports() ? $communityAccess->listOpenReports() : [];
|
||||
$userManagementSearchQuery = trim((string)($_GET['user_management_query'] ?? ''));
|
||||
$userManagementSearchResults = $canManageUserManagement && $communityAccess && $userManagementSearchQuery !== ''
|
||||
? $communityAccess->searchUsers($userManagementSearchQuery, 12)
|
||||
: [];
|
||||
$communityMigrationStatus = $communityMigration && $canManageSystemSettings ? $communityMigration->status() : null;
|
||||
$recentHighLevelUsers = [];
|
||||
$recentHighLevelThresholdLabel = '';
|
||||
$profileLevelUserSearchQuery = '';
|
||||
@@ -1122,9 +1179,17 @@ final class AccountPages
|
||||
'communityLevelRightDefinitions',
|
||||
'communityRoles',
|
||||
'canReviewListings',
|
||||
'canModerateForum',
|
||||
'canAccessCommunityAdmin',
|
||||
'canAccessAdminArea',
|
||||
'canManageUserManagement',
|
||||
'systemRoleAssignments',
|
||||
'listingReviewRequests',
|
||||
'communityApplications',
|
||||
'communityReports',
|
||||
'userManagementSearchQuery',
|
||||
'userManagementSearchResults',
|
||||
'communityMigrationStatus',
|
||||
'recentHighLevelUsers',
|
||||
'recentHighLevelThresholdLabel',
|
||||
'profileLevelUserSearchQuery',
|
||||
|
||||
Reference in New Issue
Block a user