Compare commits
2 Commits
77ce2c3043
...
6246f29135
| Author | SHA1 | Date | |
|---|---|---|---|
| 6246f29135 | |||
| 00b42e4a4c |
18
CLAUDE.md
18
CLAUDE.md
@ -80,6 +80,24 @@ php bin/console cache:clear
|
||||
APP_ENV=prod php bin/console cache:warmup
|
||||
```
|
||||
|
||||
### Module Management (Plugin System)
|
||||
```bash
|
||||
# List all installed modules with license status
|
||||
php bin/console app:module:list
|
||||
|
||||
# Register/manage module license
|
||||
php bin/console app:module:license billing # Interactive
|
||||
php bin/console app:module:license billing <license-key> # Direct
|
||||
php bin/console app:module:license billing --validate # Validate
|
||||
php bin/console app:module:license billing --revoke # Revoke
|
||||
|
||||
# Remove a module safely
|
||||
php bin/console app:module:remove billing # Interactive
|
||||
php bin/console app:module:remove billing --force # No confirmation
|
||||
php bin/console app:module:remove billing --keep-license # Keep license
|
||||
php bin/console app:module:remove billing --keep-db-entry # Keep DB entry
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Security System (6 Layers)
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
"knpuniversity/oauth2-client-bundle": "*",
|
||||
"league/oauth2-client": "*",
|
||||
"mycrm/billing-module": "^1.0",
|
||||
"mycrm/test-module": "*",
|
||||
"nelmio/cors-bundle": "^2.6",
|
||||
"phpdocumentor/reflection-docblock": "^5.6",
|
||||
"phpstan/phpdoc-parser": "^2.3",
|
||||
|
||||
120
composer.lock
generated
120
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ac7d8a38f7c8dd8a8f2760177304dcb4",
|
||||
"content-hash": "f89ce01cc8bcb7810af77472d2437dd0",
|
||||
"packages": [
|
||||
{
|
||||
"name": "api-platform/core",
|
||||
@ -3259,42 +3259,6 @@
|
||||
"description": "Ausgangsrechnungsverwaltung für myCRM",
|
||||
"time": "2025-12-05T10:24:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mycrm/test-module",
|
||||
"version": "v1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://git.osdata-home.de/mycrm/mycrm-test-module",
|
||||
"reference": "0630a840ffdca475208b51f7807196468e44c27c"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/framework-bundle": "^7.0"
|
||||
},
|
||||
"type": "symfony-bundle",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.0-dev",
|
||||
"stable": "v1.0.0"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MyCRM\\TestModule\\": "src/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"proprietary"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Your Name",
|
||||
"email": "your.email@example.com"
|
||||
}
|
||||
],
|
||||
"description": "Test Module for myCRM - Demonstrates the plugin system",
|
||||
"time": "2025-12-03T15:57:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nelmio/cors-bundle",
|
||||
"version": "2.6.0",
|
||||
@ -6578,16 +6542,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/monolog-bundle",
|
||||
"version": "v3.11.0",
|
||||
"version": "v3.11.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/monolog-bundle.git",
|
||||
"reference": "e12eb92655b234cd50c21cda648088847a7ec777"
|
||||
"reference": "0e675a6e08f791ef960dc9c7e392787111a3f0c1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/e12eb92655b234cd50c21cda648088847a7ec777",
|
||||
"reference": "e12eb92655b234cd50c21cda648088847a7ec777",
|
||||
"url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/0e675a6e08f791ef960dc9c7e392787111a3f0c1",
|
||||
"reference": "0e675a6e08f791ef960dc9c7e392787111a3f0c1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -6634,7 +6598,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/symfony/monolog-bundle/issues",
|
||||
"source": "https://github.com/symfony/monolog-bundle/tree/v3.11.0"
|
||||
"source": "https://github.com/symfony/monolog-bundle/tree/v3.11.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -6654,7 +6618,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-11-27T09:16:19+00:00"
|
||||
"time": "2025-12-08T07:58:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/notifier",
|
||||
@ -9797,16 +9761,16 @@
|
||||
},
|
||||
{
|
||||
"name": "twig/extra-bundle",
|
||||
"version": "v3.22.1",
|
||||
"version": "v3.22.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/twig-extra-bundle.git",
|
||||
"reference": "b6534bc925bec930004facca92fccebd0c809247"
|
||||
"reference": "09de9be7f6c0d19ede7b5a1dbfcfb2e9d1e0ea9e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/b6534bc925bec930004facca92fccebd0c809247",
|
||||
"reference": "b6534bc925bec930004facca92fccebd0c809247",
|
||||
"url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/09de9be7f6c0d19ede7b5a1dbfcfb2e9d1e0ea9e",
|
||||
"reference": "09de9be7f6c0d19ede7b5a1dbfcfb2e9d1e0ea9e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -9855,7 +9819,7 @@
|
||||
"twig"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.22.1"
|
||||
"source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.22.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -9867,7 +9831,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-11-02T11:00:49+00:00"
|
||||
"time": "2025-12-05T08:51:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
@ -10362,16 +10326,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.6.2",
|
||||
"version": "v5.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "3a454ca033b9e06b63282ce19562e892747449bb"
|
||||
"reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
|
||||
"reference": "3a454ca033b9e06b63282ce19562e892747449bb",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
|
||||
"reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -10414,9 +10378,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
|
||||
},
|
||||
"time": "2025-10-21T19:32:17+00:00"
|
||||
"time": "2025-12-06T11:56:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -10538,23 +10502,23 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "12.5.0",
|
||||
"version": "12.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "bca180c050dd3ae15f87c26d25cabb34fe1a0a5a"
|
||||
"reference": "c467c59a4f6e04b942be422844e7a6352fa01b57"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bca180c050dd3ae15f87c26d25cabb34fe1a0a5a",
|
||||
"reference": "bca180c050dd3ae15f87c26d25cabb34fe1a0a5a",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c467c59a4f6e04b942be422844e7a6352fa01b57",
|
||||
"reference": "c467c59a4f6e04b942be422844e7a6352fa01b57",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^5.6.2",
|
||||
"nikic/php-parser": "^5.7.0",
|
||||
"php": ">=8.3",
|
||||
"phpunit/php-file-iterator": "^6.0",
|
||||
"phpunit/php-text-template": "^5.0",
|
||||
@ -10562,10 +10526,10 @@
|
||||
"sebastian/environment": "^8.0.3",
|
||||
"sebastian/lines-of-code": "^4.0",
|
||||
"sebastian/version": "^6.0",
|
||||
"theseer/tokenizer": "^1.3.1"
|
||||
"theseer/tokenizer": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^12.4.4"
|
||||
"phpunit/phpunit": "^12.5.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pcov": "PHP extension that provides line coverage",
|
||||
@ -10603,7 +10567,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.0"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -10623,7 +10587,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-11-29T07:15:54+00:00"
|
||||
"time": "2025-12-08T07:17:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -10872,16 +10836,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "12.5.0",
|
||||
"version": "12.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "fef037fe50d20ce826cdbd741b7a2afcdec5f45b"
|
||||
"reference": "6dc2e076d09960efbb0c1272aa9bc156fc80955e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fef037fe50d20ce826cdbd741b7a2afcdec5f45b",
|
||||
"reference": "fef037fe50d20ce826cdbd741b7a2afcdec5f45b",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6dc2e076d09960efbb0c1272aa9bc156fc80955e",
|
||||
"reference": "6dc2e076d09960efbb0c1272aa9bc156fc80955e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -10895,7 +10859,7 @@
|
||||
"phar-io/manifest": "^2.0.4",
|
||||
"phar-io/version": "^3.2.1",
|
||||
"php": ">=8.3",
|
||||
"phpunit/php-code-coverage": "^12.5.0",
|
||||
"phpunit/php-code-coverage": "^12.5.1",
|
||||
"phpunit/php-file-iterator": "^6.0.0",
|
||||
"phpunit/php-invoker": "^6.0.0",
|
||||
"phpunit/php-text-template": "^5.0.0",
|
||||
@ -10949,7 +10913,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.0"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -10973,7 +10937,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-05T04:59:40+00:00"
|
||||
"time": "2025-12-11T08:52:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@ -12379,23 +12343,23 @@
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
"version": "1.3.1",
|
||||
"version": "2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/theseer/tokenizer.git",
|
||||
"reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
|
||||
"reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
|
||||
"reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
|
||||
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4",
|
||||
"reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"php": "^7.2 || ^8.0"
|
||||
"php": "^8.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -12417,7 +12381,7 @@
|
||||
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
||||
"support": {
|
||||
"issues": "https://github.com/theseer/tokenizer/issues",
|
||||
"source": "https://github.com/theseer/tokenizer/tree/1.3.1"
|
||||
"source": "https://github.com/theseer/tokenizer/tree/2.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -12425,7 +12389,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-11-17T20:03:58+00:00"
|
||||
"time": "2025-12-08T11:19:18+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
||||
@ -18,6 +18,5 @@ return [
|
||||
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||
KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true],
|
||||
MyCRM\TestModule\TestModuleBundle::class => ['all' => true],
|
||||
MyCRM\BillingModule\BillingModuleBundle::class => ['all' => true],
|
||||
];
|
||||
|
||||
@ -223,6 +223,132 @@ Navigiere zu: `http://localhost:8000/admin/modules`
|
||||
|
||||
---
|
||||
|
||||
## 🗑️ Modul entfernen
|
||||
|
||||
### Option A: CLI (empfohlen)
|
||||
|
||||
```bash
|
||||
# Interaktiv mit Bestätigung
|
||||
php bin/console app:module:remove billing
|
||||
|
||||
# Ohne Bestätigung (force)
|
||||
php bin/console app:module:remove billing --force
|
||||
|
||||
# Lizenz behalten
|
||||
php bin/console app:module:remove billing --keep-license
|
||||
|
||||
# Permission-Datenbank-Eintrag behalten
|
||||
php bin/console app:module:remove billing --keep-db-entry
|
||||
```
|
||||
|
||||
Der Command führt folgende Schritte durch:
|
||||
|
||||
1. ✓ **Modul-Informationen anzeigen** (Plugin + Datenbank)
|
||||
2. ✓ **Bestätigung einholen** (außer mit `--force`)
|
||||
3. ✓ **Lizenz widerrufen** (außer mit `--keep-license`)
|
||||
4. ✓ **Datenbank-Eintrag löschen** (außer mit `--keep-db-entry`)
|
||||
5. → **Composer-Command vorschlagen**
|
||||
|
||||
### Manuelle Schritte nach app:module:remove
|
||||
|
||||
Der Command zeigt dir die nötigen manuellen Schritte:
|
||||
|
||||
```bash
|
||||
# 1. Composer-Package entfernen
|
||||
composer remove mycrm/billing-module
|
||||
|
||||
# 2. Bundle aus config/bundles.php entfernen (falls vorhanden)
|
||||
# Öffne config/bundles.php und entferne die Zeile:
|
||||
# MyCRM\BillingModule\BillingBundle::class => ['all' => true],
|
||||
|
||||
# 3. Optional: Migrationen zurückrollen (falls benötigt)
|
||||
php bin/console doctrine:migrations:migrate prev
|
||||
|
||||
# 4. Cache leeren
|
||||
php bin/console cache:clear
|
||||
```
|
||||
|
||||
### Option B: Vollständig manuell
|
||||
|
||||
Falls du den Command nicht nutzen möchtest:
|
||||
|
||||
```bash
|
||||
# 1. Lizenz widerrufen
|
||||
php bin/console app:module:license billing --revoke
|
||||
|
||||
# 2. Composer-Package entfernen
|
||||
composer remove mycrm/billing-module
|
||||
|
||||
# 3. Bundle aus config/bundles.php entfernen (manuell editieren)
|
||||
|
||||
# 4. Optional: Module-Entity aus DB löschen
|
||||
# Nur nötig, wenn das Modul auch im Permission-System registriert ist
|
||||
# VORSICHT: Löscht auch verknüpfte RolePermissions!
|
||||
|
||||
# 5. Cache leeren
|
||||
php bin/console cache:clear
|
||||
```
|
||||
|
||||
### Beispiel-Ausgabe
|
||||
|
||||
```
|
||||
Modul "billing" entfernen
|
||||
=========================
|
||||
|
||||
Modul-Informationen
|
||||
-------------------
|
||||
|
||||
Eigenschaft Wert
|
||||
Plugin-Informationen
|
||||
Name Rechnungsmodul
|
||||
Version 1.0.0
|
||||
Beschreibung Rechnungsverwaltung für myCRM
|
||||
Lizenziert ✓ Ja
|
||||
Aktiv ✓ Ja
|
||||
|
||||
Datenbank-Informationen (Permission-System)
|
||||
Name Rechnungen
|
||||
Code billing
|
||||
Aktiv ✓ Ja
|
||||
Permissions 3
|
||||
|
||||
! [NOTE] Möchten Sie dieses Modul wirklich entfernen? (yes/no) [no]:
|
||||
> yes
|
||||
|
||||
Entferne Modul
|
||||
--------------
|
||||
|
||||
⏳ Widerrufe Lizenz...
|
||||
[OK] ✓ Lizenz erfolgreich widerrufen
|
||||
|
||||
⏳ Entferne Datenbank-Eintrag (Permission-System)...
|
||||
! [WARNING] ACHTUNG: Dieses Modul hat 3 verknüpfte Permission(s). Diese werden ebenfalls gelöscht!
|
||||
! [NOTE] Trotzdem fortfahren? (yes/no) [no]:
|
||||
> yes
|
||||
[OK] ✓ Datenbank-Eintrag erfolgreich entfernt
|
||||
|
||||
Zusammenfassung
|
||||
---------------
|
||||
|
||||
* ✓ Lizenz widerrufen
|
||||
* ✓ Datenbank-Eintrag gelöscht
|
||||
|
||||
Nächste Schritte (manuell)
|
||||
--------------------------
|
||||
|
||||
* 1. Composer-Package entfernen: composer remove mycrm/billing-module
|
||||
* 2. Bundle aus config/bundles.php entfernen (falls vorhanden)
|
||||
* 3. Optional: Migrationen zurückrollen (falls benötigt)
|
||||
php bin/console doctrine:migrations:migrate prev
|
||||
* 4. Cache leeren: php bin/console cache:clear
|
||||
|
||||
[OK] Modul "billing" wurde vorbereitet zum Entfernen. Führen Sie die obigen Schritte manuell aus.
|
||||
```
|
||||
|
||||
✅ **Modul sicher entfernt!**
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Problem: "Modul wird nicht erkannt"
|
||||
|
||||
203
src/Command/ModuleRemoveCommand.php
Normal file
203
src/Command/ModuleRemoveCommand.php
Normal file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Entity\Module;
|
||||
use App\Plugin\LicenseValidatorInterface;
|
||||
use App\Plugin\ModuleRegistry;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* CLI-Command zum sicheren Entfernen von Modulen
|
||||
*/
|
||||
#[AsCommand(
|
||||
name: 'app:module:remove',
|
||||
description: 'Entfernt ein Modul sicher aus dem System',
|
||||
)]
|
||||
class ModuleRemoveCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ModuleRegistry $moduleRegistry,
|
||||
private readonly LicenseValidatorInterface $licenseValidator,
|
||||
private readonly EntityManagerInterface $entityManager
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('module', InputArgument::REQUIRED, 'Modul-Identifier (z.B. "billing")')
|
||||
->addOption('keep-license', null, InputOption::VALUE_NONE, 'Lizenz behalten (nicht widerrufen)')
|
||||
->addOption('keep-db-entry', null, InputOption::VALUE_NONE, 'Datenbank-Eintrag behalten (Permission-System)')
|
||||
->addOption('force', 'f', InputOption::VALUE_NONE, 'Keine Bestätigung erforderlich')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$moduleIdentifier = $input->getArgument('module');
|
||||
$keepLicense = $input->getOption('keep-license');
|
||||
$keepDbEntry = $input->getOption('keep-db-entry');
|
||||
$force = $input->getOption('force');
|
||||
|
||||
$io->title(sprintf('Modul "%s" entfernen', $moduleIdentifier));
|
||||
|
||||
// 1. Modul-Informationen sammeln
|
||||
$modulePlugin = $this->moduleRegistry->getModule($moduleIdentifier);
|
||||
$moduleEntity = $this->findModuleEntity($moduleIdentifier);
|
||||
|
||||
if (!$modulePlugin && !$moduleEntity) {
|
||||
$io->error(sprintf('Modul "%s" wurde nicht gefunden (weder als Plugin noch in der Datenbank).', $moduleIdentifier));
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// 2. Informationen anzeigen
|
||||
$this->displayModuleInfo($io, $modulePlugin, $moduleEntity);
|
||||
|
||||
// 3. Bestätigung einholen
|
||||
if (!$force && !$io->confirm('Möchten Sie dieses Modul wirklich entfernen?', false)) {
|
||||
$io->info('Abgebrochen.');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$io->section('Entferne Modul');
|
||||
|
||||
$steps = [];
|
||||
|
||||
// 4. Lizenz widerrufen
|
||||
if (!$keepLicense && $modulePlugin && $modulePlugin->isLicensed()) {
|
||||
$io->text('⏳ Widerrufe Lizenz...');
|
||||
try {
|
||||
$this->licenseValidator->revokeLicense($moduleIdentifier);
|
||||
$io->success('✓ Lizenz erfolgreich widerrufen');
|
||||
$steps[] = '✓ Lizenz widerrufen';
|
||||
} catch (\Exception $e) {
|
||||
$io->warning(sprintf('Lizenz konnte nicht widerrufen werden: %s', $e->getMessage()));
|
||||
$steps[] = '⚠ Lizenz-Widerruf fehlgeschlagen';
|
||||
}
|
||||
} elseif ($keepLicense) {
|
||||
$io->text('⏭ Lizenz wird behalten (--keep-license)');
|
||||
$steps[] = '⏭ Lizenz behalten';
|
||||
} else {
|
||||
$io->text('⏭ Keine Lizenz vorhanden');
|
||||
$steps[] = '⏭ Keine Lizenz';
|
||||
}
|
||||
|
||||
// 5. Datenbank-Eintrag entfernen
|
||||
if (!$keepDbEntry && $moduleEntity) {
|
||||
$io->text('⏳ Entferne Datenbank-Eintrag (Permission-System)...');
|
||||
|
||||
// Warnung, wenn Permissions existieren
|
||||
$permissionCount = $moduleEntity->getPermissions()->count();
|
||||
if ($permissionCount > 0) {
|
||||
$io->warning(sprintf(
|
||||
'ACHTUNG: Dieses Modul hat %d verknüpfte Permission(s). Diese werden ebenfalls gelöscht!',
|
||||
$permissionCount
|
||||
));
|
||||
|
||||
if (!$force && !$io->confirm('Trotzdem fortfahren?', false)) {
|
||||
$io->info('Abgebrochen.');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$this->entityManager->remove($moduleEntity);
|
||||
$this->entityManager->flush();
|
||||
$io->success('✓ Datenbank-Eintrag erfolgreich entfernt');
|
||||
$steps[] = '✓ Datenbank-Eintrag gelöscht';
|
||||
} catch (\Exception $e) {
|
||||
$io->error(sprintf('Datenbank-Eintrag konnte nicht entfernt werden: %s', $e->getMessage()));
|
||||
$steps[] = '✗ Datenbank-Eintrag Fehler';
|
||||
return Command::FAILURE;
|
||||
}
|
||||
} elseif ($keepDbEntry) {
|
||||
$io->text('⏭ Datenbank-Eintrag wird behalten (--keep-db-entry)');
|
||||
$steps[] = '⏭ DB-Eintrag behalten';
|
||||
} else {
|
||||
$io->text('⏭ Kein Datenbank-Eintrag vorhanden');
|
||||
$steps[] = '⏭ Kein DB-Eintrag';
|
||||
}
|
||||
|
||||
// 6. Nächste Schritte anzeigen
|
||||
$io->section('Zusammenfassung');
|
||||
$io->listing($steps);
|
||||
|
||||
$io->section('Nächste Schritte (manuell)');
|
||||
|
||||
$nextSteps = [];
|
||||
|
||||
// Composer-Package entfernen
|
||||
if ($modulePlugin) {
|
||||
$packageName = $this->guessPackageName($moduleIdentifier);
|
||||
$nextSteps[] = sprintf('1. Composer-Package entfernen: <comment>composer remove %s</comment>', $packageName);
|
||||
}
|
||||
|
||||
// Bundle aus config/bundles.php entfernen
|
||||
$nextSteps[] = '2. Bundle aus <comment>config/bundles.php</comment> entfernen (falls vorhanden)';
|
||||
|
||||
// Migrationen
|
||||
$nextSteps[] = '3. Optional: Migrationen zurückrollen (falls benötigt)';
|
||||
$nextSteps[] = ' <comment>php bin/console doctrine:migrations:migrate prev</comment>';
|
||||
|
||||
// Cache leeren
|
||||
$nextSteps[] = '4. Cache leeren: <comment>php bin/console cache:clear</comment>';
|
||||
|
||||
$io->listing($nextSteps);
|
||||
|
||||
$io->success(sprintf('Modul "%s" wurde vorbereitet zum Entfernen. Führen Sie die obigen Schritte manuell aus.', $moduleIdentifier));
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function displayModuleInfo(SymfonyStyle $io, $modulePlugin, ?Module $moduleEntity): void
|
||||
{
|
||||
$io->section('Modul-Informationen');
|
||||
|
||||
$rows = [];
|
||||
|
||||
if ($modulePlugin) {
|
||||
$rows[] = ['<info>Plugin-Informationen</info>', ''];
|
||||
$rows[] = [' Name', $modulePlugin->getDisplayName()];
|
||||
$rows[] = [' Version', $modulePlugin->getVersion()];
|
||||
$rows[] = [' Beschreibung', $modulePlugin->getDescription()];
|
||||
$rows[] = [' Lizenziert', $modulePlugin->isLicensed() ? '✓ Ja' : '✗ Nein'];
|
||||
$rows[] = [' Aktiv', $this->moduleRegistry->isModuleActive($modulePlugin->getIdentifier()) ? '✓ Ja' : '✗ Nein'];
|
||||
}
|
||||
|
||||
if ($moduleEntity) {
|
||||
if (!empty($rows)) {
|
||||
$rows[] = ['', ''];
|
||||
}
|
||||
$rows[] = ['<info>Datenbank-Informationen (Permission-System)</info>', ''];
|
||||
$rows[] = [' Name', $moduleEntity->getName()];
|
||||
$rows[] = [' Code', $moduleEntity->getCode()];
|
||||
$rows[] = [' Aktiv', $moduleEntity->isActive() ? '✓ Ja' : '✗ Nein'];
|
||||
$rows[] = [' Permissions', (string)$moduleEntity->getPermissions()->count()];
|
||||
}
|
||||
|
||||
$io->table(['Eigenschaft', 'Wert'], $rows);
|
||||
}
|
||||
|
||||
private function findModuleEntity(string $identifier): ?Module
|
||||
{
|
||||
return $this->entityManager
|
||||
->getRepository(Module::class)
|
||||
->findOneBy(['code' => $identifier]);
|
||||
}
|
||||
|
||||
private function guessPackageName(string $identifier): string
|
||||
{
|
||||
// Versuche Package-Name zu erraten (z.B. billing -> mycrm/billing-module)
|
||||
return sprintf('mycrm/%s-module', $identifier);
|
||||
}
|
||||
}
|
||||
@ -90,9 +90,6 @@
|
||||
"mycrm/billing-module": {
|
||||
"version": "dev-main"
|
||||
},
|
||||
"mycrm/test-module": {
|
||||
"version": "dev-main"
|
||||
},
|
||||
"nelmio/cors-bundle": {
|
||||
"version": "2.6",
|
||||
"recipe": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user