diff --git a/modules/audit/i18n/default_de.json b/modules/audit/i18n/default_de.json index 29323da..165c72b 100644 --- a/modules/audit/i18n/default_de.json +++ b/modules/audit/i18n/default_de.json @@ -67,5 +67,6 @@ "Lifecycle runs (7d)": "Lifecycle-Läufe (7d)", "Lifecycle risks (7d)": "Lifecycle-Risiken (7d)", "Top lifecycle reason codes (7d)": "Top-Lifecycle-Fehlercodes (7d)", - "Recent lifecycle risks (7d)": "Aktuelle Lifecycle-Risiken (7d)" + "Recent lifecycle risks (7d)": "Aktuelle Lifecycle-Risiken (7d)", + "Audit & logs": "Audit & Protokolle" } diff --git a/modules/audit/i18n/default_en.json b/modules/audit/i18n/default_en.json index aaaf994..f6ce003 100644 --- a/modules/audit/i18n/default_en.json +++ b/modules/audit/i18n/default_en.json @@ -67,5 +67,6 @@ "Lifecycle runs (7d)": "Lifecycle runs (7d)", "Lifecycle risks (7d)": "Lifecycle risks (7d)", "Top lifecycle reason codes (7d)": "Top lifecycle reason codes (7d)", - "Recent lifecycle risks (7d)": "Recent lifecycle risks (7d)" + "Recent lifecycle risks (7d)": "Recent lifecycle risks (7d)", + "Audit & logs": "Audit & logs" } diff --git a/templates/partials/app-main-aside.phtml b/templates/partials/app-main-aside.phtml index a45279b..cac3a58 100644 --- a/templates/partials/app-main-aside.phtml +++ b/templates/partials/app-main-aside.phtml @@ -3,6 +3,9 @@ use MintyPHP\Service\Docs\DocsCatalogService; $layoutAuth = is_array($viewAuth['layout'] ?? null) ? $viewAuth['layout'] : []; +$moduleNavItemSlots = is_array(($layoutNav ?? [])['moduleSlots']['sidebar.admin_nav_item'] ?? null) + ? $layoutNav['moduleSlots']['sidebar.admin_nav_item'] + : []; $canViewTenants = (bool) ($layoutAuth['can_view_tenants'] ?? false); $canViewDepartments = (bool) ($layoutAuth['can_view_departments'] ?? false); $canViewUsers = (bool) ($layoutAuth['can_view_users'] ?? false); @@ -169,36 +172,66 @@ $adminNavGroups = [ ], ]; -// ── Merge module-contributed sidebar nav items into admin groups ── +// ── Append module-contributed admin nav groups (sidebar.admin_nav_item slot) ── +// Module items are grouped by their 'group' key. Each unique group becomes a +//
section appended after the core admin nav groups. if ($moduleNavItemSlots) { - // Index groups by key for fast lookup - $groupIndex = []; - foreach ($adminNavGroups as $idx => $group) { - $groupIndex[$group['key'] ?? ''] = $idx; - } - // Sort module items by order, then append to matching group + $moduleGroupsByKey = []; usort($moduleNavItemSlots, static fn ($a, $b) => ($a['order'] ?? 100) <=> ($b['order'] ?? 100)); foreach ($moduleNavItemSlots as $slot) { if (!is_array($slot)) { continue; } - $targetGroup = trim((string) ($slot['group'] ?? '')); + $groupKey = trim((string) ($slot['group'] ?? '')); $slotPath = trim((string) ($slot['path'] ?? '')); $slotLabel = trim((string) ($slot['label'] ?? '')); $slotPermission = trim((string) ($slot['permission'] ?? '')); - if ($targetGroup === '' || $slotPath === '' || $slotLabel === '') { continue; } - // Check permission via layoutAuth (module permissions are merged into layout capabilities) + if ($groupKey === '' || $slotPath === '' || $slotLabel === '') { continue; } $slotVisible = $slotPermission === '' || !empty($layoutAuth[$slotPermission]); - if (!isset($groupIndex[$targetGroup])) { continue; } - $gIdx = $groupIndex[$targetGroup]; - $adminNavGroups[$gIdx]['items'][] = [ + if (!isset($moduleGroupsByKey[$groupKey])) { + $moduleGroupsByKey[$groupKey] = []; + } + $moduleGroupsByKey[$groupKey][] = [ 'label' => t($slotLabel), 'path' => $slotPath, 'active' => navActive($slotPath, true), 'visible' => $slotVisible, 'withTenant' => false, ]; - if ($slotVisible) { - $adminNavGroups[$gIdx]['visible'] = true; + } + // Known group metadata — modules declare their group key, we map to label + icon. + $moduleGroupMeta = [ + 'admin-logs' => ['label' => t('Audit & logs'), 'icon' => 'bi-journal-text'], + ]; + foreach ($moduleGroupsByKey as $groupKey => $items) { + $hasVisible = false; + foreach ($items as $item) { + if (!empty($item['visible'])) { $hasVisible = true; break; } } + // Also add mail log to the logs group if it exists as a core item + if ($groupKey === 'admin-logs' && $canViewMailLog) { + array_unshift($items, [ + 'label' => t('Mail logs'), + 'path' => 'admin/mail-log', + 'active' => navActive('admin/mail-log', true), + 'visible' => $canViewMailLog, + 'withTenant' => false, + ]); + $hasVisible = true; + } + $meta = $moduleGroupMeta[$groupKey] ?? ['label' => $groupKey, 'icon' => 'bi-list']; + $adminNavGroups[] = [ + 'key' => $groupKey, + 'label' => $meta['label'], + 'icon' => $meta['icon'], + 'visible' => $hasVisible, + 'items' => $items, + ]; + } + // If modules contributed to admin-logs, remove the core-only logs group (which only had mail log) + if (isset($moduleGroupsByKey['admin-logs'])) { + $adminNavGroups = array_values(array_filter($adminNavGroups, static function ($g) { + // Remove the original core logs group (it's been merged into the module-contributed one) + return !(($g['key'] ?? '') === 'admin-logs' && count($g['items'] ?? []) === 1 && ($g['items'][0]['path'] ?? '') === 'admin/mail-log'); + })); } } @@ -266,7 +299,6 @@ $csrfToken = (string) ($layoutNav['csrfToken'] ?? ''); $moduleSlots = is_array($layoutNav['moduleSlots'] ?? null) ? $layoutNav['moduleSlots'] : []; $modulePanelSlots = is_array($moduleSlots['aside.tab_panel'] ?? null) ? $moduleSlots['aside.tab_panel'] : []; $moduleSearchSlots = is_array($moduleSlots['search.resource_item'] ?? null) ? $moduleSlots['search.resource_item'] : []; -$moduleNavItemSlots = is_array($moduleSlots['sidebar.admin_nav_item'] ?? null) ? $moduleSlots['sidebar.admin_nav_item'] : []; ?>