feat: implement recursive menu item conversion with permission checks
This commit is contained in:
parent
b4974b93ef
commit
ed2199096d
@ -79,6 +79,44 @@ const adminMenu = {
|
||||
// Dynamisches Menü (wird geladen)
|
||||
const model = ref([...coreMenu]);
|
||||
|
||||
// Hilfsfunktion: Konvertiert ein Menü-Item und alle Sub-Items
|
||||
const convertMenuItem = (item) => {
|
||||
// Erstelle Permission-Prüfung
|
||||
let visibleFn;
|
||||
|
||||
if (item.permission) {
|
||||
// Explizite Permission aus Plugin (z.B. "billing.view")
|
||||
visibleFn = () => authStore.hasPermission(item.permission);
|
||||
} else if (item.module) {
|
||||
// Fallback: Modulname + ".view" (z.B. "billing" -> "billing.view")
|
||||
visibleFn = () => authStore.hasPermission(`${item.module}.view`);
|
||||
} else if (item.source) {
|
||||
// Fallback: Plugin-Identifier + ".view" (z.B. "billing" -> "billing.view")
|
||||
visibleFn = () => authStore.hasPermission(`${item.source}.view`);
|
||||
}
|
||||
|
||||
const converted = {
|
||||
label: item.label,
|
||||
icon: item.icon ? `pi pi-fw ${item.icon}` : 'pi pi-fw pi-circle',
|
||||
to: item.to,
|
||||
...(visibleFn && { visible: visibleFn })
|
||||
};
|
||||
|
||||
// Rekursiv Sub-Items verarbeiten
|
||||
if (item.items && Array.isArray(item.items)) {
|
||||
converted.items = item.items
|
||||
.filter(subItem => !subItem.separator) // Separators filtern
|
||||
.map(subItem => convertMenuItem(subItem));
|
||||
}
|
||||
|
||||
// Separators beibehalten
|
||||
if (item.separator) {
|
||||
return { separator: true };
|
||||
}
|
||||
|
||||
return converted;
|
||||
};
|
||||
|
||||
// Plugin-Menüs laden
|
||||
const loadPluginMenus = async () => {
|
||||
try {
|
||||
@ -91,29 +129,7 @@ const loadPluginMenus = async () => {
|
||||
// Konvertiere zu PrimeVue Menü-Format
|
||||
const menuGroup = {
|
||||
label: groupLabel,
|
||||
items: items.map(item => {
|
||||
// Erstelle Permission-Prüfung
|
||||
let visibleFn;
|
||||
|
||||
if (item.permission) {
|
||||
// Explizite Permission aus Plugin
|
||||
visibleFn = () => authStore.hasPermission(item.permission);
|
||||
} else if (item.module) {
|
||||
// Fallback: Leite von Modulname ab (z.B. 'billing')
|
||||
visibleFn = () => permissionStore.canView(item.module);
|
||||
} else if (item.source) {
|
||||
// Fallback: Nutze Plugin-Identifier als Modulname
|
||||
visibleFn = () => permissionStore.canView(item.source);
|
||||
}
|
||||
|
||||
return {
|
||||
label: item.label,
|
||||
icon: item.icon ? `pi pi-fw ${item.icon}` : 'pi pi-fw pi-circle',
|
||||
to: item.to,
|
||||
...(item.items && { items: item.items }),
|
||||
...(visibleFn && { visible: visibleFn })
|
||||
};
|
||||
})
|
||||
items: items.map(item => convertMenuItem(item))
|
||||
};
|
||||
|
||||
model.value.push(menuGroup);
|
||||
|
||||
8
composer.lock
generated
8
composer.lock
generated
@ -2052,12 +2052,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://git.osdata-home.de/mycrm/mycrm-billing-module",
|
||||
"reference": "a70043acd597e4ff5064a51e639a5f28227e5cd4"
|
||||
"reference": "8c2544569381333f45ae013b6d0a4ac61d4eb0e0"
|
||||
},
|
||||
"require": {
|
||||
"doctrine/orm": "^3.0",
|
||||
"php": ">=8.2",
|
||||
"symfony/framework-bundle": "^7.1"
|
||||
"symfony/framework-bundle": "^7.3"
|
||||
},
|
||||
"suggest": {
|
||||
"api-platform/symfony": "Required for API Platform integration (>=4.0)"
|
||||
@ -2066,7 +2066,7 @@
|
||||
"type": "symfony-bundle",
|
||||
"extra": {
|
||||
"symfony": {
|
||||
"require": "7.1.*"
|
||||
"require": "7.3.*"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2078,7 +2078,7 @@
|
||||
"proprietary"
|
||||
],
|
||||
"description": "Ausgangsrechnungsverwaltung für myCRM",
|
||||
"time": "2025-12-22T08:27:54+00:00"
|
||||
"time": "2025-12-28T09:14:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nelmio/cors-bundle",
|
||||
|
||||
@ -22,21 +22,22 @@ public function getMenuItems(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'label' => 'Rechnungen', // Menü-Titel
|
||||
'label' => 'Rechnungen', // Menü-Titel (Container)
|
||||
'icon' => 'pi-file-pdf', // PrimeIcons Icon (ohne pi pi-fw prefix)
|
||||
'group' => 'Finanzen', // Gruppierung im Menü
|
||||
// WICHTIG: Container-Items brauchen KEINE permission Property
|
||||
'items' => [
|
||||
[
|
||||
'label' => 'Alle Rechnungen',
|
||||
'icon' => 'pi-list',
|
||||
'to' => '/billing/invoices', // Vue Router path
|
||||
'permission' => 'billing.view' // Optional: Permission-Check
|
||||
'permission' => 'billing.view' // WICHTIG: Jedes Sub-Item braucht permission
|
||||
],
|
||||
[
|
||||
'label' => 'Neue Rechnung',
|
||||
'icon' => 'pi-plus',
|
||||
'to' => '/billing/invoices/create',
|
||||
'permission' => 'billing.create'
|
||||
'permission' => 'billing.create' // Explizite Permission
|
||||
],
|
||||
[
|
||||
'separator' => true // Optional: Trennlinie
|
||||
@ -45,7 +46,7 @@ public function getMenuItems(): array
|
||||
'label' => 'Einstellungen',
|
||||
'icon' => 'pi-cog',
|
||||
'to' => '/billing/settings',
|
||||
'permission' => 'billing.manage'
|
||||
'permission' => 'billing.manage' // Explizite Permission
|
||||
]
|
||||
]
|
||||
]
|
||||
@ -75,11 +76,14 @@ public function getMenuItems(): array
|
||||
```php
|
||||
public function getPermissionModules(): array
|
||||
{
|
||||
return ['billing', 'invoicing'];
|
||||
// Best Practice: Ein einziges Modul pro Plugin
|
||||
return ['billing'];
|
||||
}
|
||||
```
|
||||
|
||||
Das System erstellt automatisch folgende Permissions:
|
||||
**WICHTIG:** Verwende nur **ein** Permission-Modul pro Plugin, nicht mehrere! Dies vereinfacht die Administration massiv.
|
||||
|
||||
Das System erstellt automatisch folgende Permissions für das Modul `billing`:
|
||||
- `billing.view`
|
||||
- `billing.create`
|
||||
- `billing.edit`
|
||||
@ -276,7 +280,8 @@ class BillingModulePlugin implements ModulePluginInterface
|
||||
|
||||
public function getPermissionModules(): array
|
||||
{
|
||||
return ['billing', 'invoicing', 'payments'];
|
||||
// Best Practice: Ein einziges Modul für das gesamte Plugin
|
||||
return ['billing'];
|
||||
}
|
||||
|
||||
public function getMenuItems(): array
|
||||
@ -286,12 +291,13 @@ class BillingModulePlugin implements ModulePluginInterface
|
||||
'label' => 'Rechnungen',
|
||||
'icon' => 'pi-file-pdf',
|
||||
'group' => 'Finanzen',
|
||||
// Wichtig: Container-Item hat KEINE permission Property
|
||||
'items' => [
|
||||
[
|
||||
'label' => 'Dashboard',
|
||||
'icon' => 'pi-chart-line',
|
||||
'to' => '/billing/dashboard',
|
||||
'permission' => 'billing.view'
|
||||
'permission' => 'billing.view' // Explizite Permission
|
||||
],
|
||||
[
|
||||
'label' => 'Alle Rechnungen',
|
||||
@ -312,7 +318,7 @@ class BillingModulePlugin implements ModulePluginInterface
|
||||
'label' => 'Zahlungen',
|
||||
'icon' => 'pi-money-bill',
|
||||
'to' => '/billing/payments',
|
||||
'permission' => 'payments.view'
|
||||
'permission' => 'billing.view' // Einheitliches Modul!
|
||||
],
|
||||
[
|
||||
'label' => 'Einstellungen',
|
||||
@ -438,6 +444,78 @@ tail -f var/log/dev.log | grep -E "(Plugin|Menu|Permission)"
|
||||
- **Keine Core-Gruppen überschreiben** - "Home", "CRM", "Administration" sind reserviert
|
||||
- **Keine tiefen Verschachtelungen** - Max. 2 Ebenen (Gruppe → Items)
|
||||
- **Keine externen URLs ohne Warnung** - User erwarten interne Navigation
|
||||
- **Nicht mehrere Permission-Module für ein Plugin** - Erschwert die Administration unnötig
|
||||
|
||||
### 🎯 WICHTIG: Permission-Format und Sub-Items (Stand: 2025-12-28)
|
||||
|
||||
**Ein Permission-Modul pro Plugin:**
|
||||
```php
|
||||
// ✅ RICHTIG: Ein einziges Modul
|
||||
public function getPermissionModules(): array
|
||||
{
|
||||
return ['billing'];
|
||||
}
|
||||
|
||||
// ❌ FALSCH: Unnötig aufgeteilt
|
||||
public function getPermissionModules(): array
|
||||
{
|
||||
return ['billing', 'invoices', 'payments'];
|
||||
}
|
||||
```
|
||||
|
||||
**Explizite Permissions für ALLE Sub-Items:**
|
||||
```php
|
||||
// ✅ RICHTIG: Jedes anklickbare Item hat permission
|
||||
public function getMenuItems(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'label' => 'Rechnungen', // Container - KEINE Permission nötig
|
||||
'icon' => 'pi-file-pdf',
|
||||
'group' => 'Finanzen',
|
||||
'items' => [
|
||||
[
|
||||
'label' => 'Alle Rechnungen',
|
||||
'to' => '/billing/invoices',
|
||||
'permission' => 'billing.view' // ✅ Explizite Permission
|
||||
],
|
||||
[
|
||||
'label' => 'Neue Rechnung',
|
||||
'to' => '/billing/invoices/create',
|
||||
'permission' => 'billing.create' // ✅ Explizite Permission
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// ❌ FALSCH: Sub-Items ohne Permission
|
||||
public function getMenuItems(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'label' => 'Rechnungen',
|
||||
'permission' => 'billing.view', // ❌ Container braucht keine Permission
|
||||
'items' => [
|
||||
[
|
||||
'label' => 'Alle Rechnungen',
|
||||
'to' => '/billing/invoices'
|
||||
// ❌ FEHLT: permission Property!
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
**Warum ist das wichtig?**
|
||||
|
||||
Das Frontend (`AppMenu.vue`) verwendet eine **rekursive `convertMenuItem()` Funktion**, die:
|
||||
1. Alle Sub-Items durchläuft und deren `permission` Properties in `visible` Funktionen umwandelt
|
||||
2. Items mit `source` Property automatisch zu `${source}.view` Permission konvertiert
|
||||
3. Nur Items mit erfüllter Permission anzeigt
|
||||
|
||||
**Container-Items** (mit `items` Array) brauchen **KEINE eigene Permission**, da sie nur dann angezeigt werden, wenn mindestens ein Sub-Item sichtbar ist.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@
|
||||
]
|
||||
},
|
||||
"mycrm/billing-module": {
|
||||
"version": "v1.0.0"
|
||||
"version": "dev-main"
|
||||
},
|
||||
"nelmio/cors-bundle": {
|
||||
"version": "2.6",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user