From 136e2d68a25fad661142022feb280f58e8ae39c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Sat, 10 Jan 2026 12:13:15 +0100 Subject: [PATCH] feat: integrate with easy admin bundle --- src/Command/ReadXmlDumpCommand.php | 107 ------------------ .../Admin/BookingEditDraftCrudController.php | 2 +- src/Controller/Admin/DashboardController.php | 12 +- .../Admin/LogEntryCrudController.php | 14 ++- src/Controller/Admin/UserCrudController.php | 4 +- src/Controller/Admin/XmlDumpController.php | 80 +++++++++++++ src/Entity/LogEntry.php | 5 + src/Service/XmlDumpService.php | 77 +++++++++++++ templates/account/index.html.twig | 8 ++ templates/admin/xml_dump/list.html.twig | 62 ++++++++++ 10 files changed, 256 insertions(+), 115 deletions(-) delete mode 100644 src/Command/ReadXmlDumpCommand.php create mode 100644 src/Controller/Admin/XmlDumpController.php create mode 100644 src/Service/XmlDumpService.php create mode 100644 templates/admin/xml_dump/list.html.twig diff --git a/src/Command/ReadXmlDumpCommand.php b/src/Command/ReadXmlDumpCommand.php deleted file mode 100644 index 9950104..0000000 --- a/src/Command/ReadXmlDumpCommand.php +++ /dev/null @@ -1,107 +0,0 @@ -addArgument('requestId', InputArgument::REQUIRED, 'The request ID to look up') - ->addOption('type', 't', InputOption::VALUE_REQUIRED, 'Dump type: request or response', 'request'); - } - - /** - * @see \Symfony\Component\Console\Command\Command::execute() - */ - protected function execute(InputInterface $input, OutputInterface $output): int - { - $io = new SymfonyStyle($input, $output); - - $requestId = $input->getArgument('requestId'); - $type = $input->getOption('type'); - - if (false === in_array($type, ['request', 'response'], true)) { - $io->error('Invalid type. Must be "request" or "response".'); - - return Command::FAILURE; - } - - try { - $files = $this->findMatchingFiles($requestId, $type); - - if (0 === count($files)) { - $io->warning(sprintf('No dump files found for request ID "%s". They may have been cleaned up.', $requestId)); - - return Command::FAILURE; - } - - foreach ($files as $filename) { - if (count($files) > 1) { - $io->section($filename); - } - $content = $this->xmlDump->read($filename); - $output->writeln($content); - } - - return Command::SUCCESS; - } catch (FilesystemException $e) { - $io->error(sprintf('Failed to read dump file: %s', $e->getMessage())); - - return Command::FAILURE; - } - } - - /** - * Finds dump files matching the given request ID and type. - * - * Supports both exact matches (e.g., "abc123_1") and base ID matches (e.g., "abc123") - * which will return all files with counter suffixes. - * - * @return string[] - * - * @throws FilesystemException - */ - private function findMatchingFiles(string $requestId, string $type): array - { - $exactMatch = $requestId.'_'.$type.'.xml'; - if ($this->xmlDump->fileExists($exactMatch)) { - return [$exactMatch]; - } - - $pattern = '/^'.preg_quote($requestId, '/').'_\d+_'.$type.'\.xml$/'; - $matches = []; - - foreach ($this->xmlDump->listContents('.') as $item) { - if ($item->isFile() && 1 === preg_match($pattern, $item->path())) { - $matches[] = $item->path(); - } - } - - sort($matches); - - return $matches; - } -} diff --git a/src/Controller/Admin/BookingEditDraftCrudController.php b/src/Controller/Admin/BookingEditDraftCrudController.php index 441f66c..5ac68c4 100644 --- a/src/Controller/Admin/BookingEditDraftCrudController.php +++ b/src/Controller/Admin/BookingEditDraftCrudController.php @@ -45,7 +45,7 @@ class BookingEditDraftCrudController extends AbstractCrudController public function configureFields(string $pageName): iterable { return [ - IntegerField::new('bookingId', 'Buchungs ID'), + IntegerField::new('bookingNumber', 'Vorgang'), AssociationField::new('user', 'Kundenaccount')->formatValue(fn (User $user) => $user->getEmail()), DateTimeField::new('createdAt', 'erstellt am'), DateTimeField::new('updatedAt', 'aktualisiert am'), diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index a075c8a..b26d6f1 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -8,8 +8,10 @@ use App\Entity\User; use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard; use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard; use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem; +use EasyCorp\Bundle\EasyAdminBundle\Config\UserMenu; use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\User\UserInterface; #[AdminDashboard(routePath: '/admin', routeName: 'admin')] class DashboardController extends AbstractDashboardController @@ -28,11 +30,19 @@ class DashboardController extends AbstractDashboardController ; } + public function configureUserMenu(UserInterface $user): UserMenu + { + return parent::configureUserMenu($user) + ->addMenuItems([ + MenuItem::linkToRoute('MyE&P', 'fa fa-user', 'app_account') + ]); + } + public function configureMenuItems(): iterable { yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home'); yield MenuItem::linkToCrud('Log', 'fa fa-list', LogEntry::class); yield MenuItem::linkToCrud('Buchungsentwürfe', 'fa fa-pen-to-square', BookingEditDraft::class); - yield MenuItem::linkToCrud('Kundenaccounts', 'fa fa-users', User::class); + yield MenuItem::linkToCrud('Benutzeraccounts', 'fa fa-users', User::class); } } diff --git a/src/Controller/Admin/LogEntryCrudController.php b/src/Controller/Admin/LogEntryCrudController.php index 105a350..654df9b 100644 --- a/src/Controller/Admin/LogEntryCrudController.php +++ b/src/Controller/Admin/LogEntryCrudController.php @@ -1,5 +1,7 @@ linkToRoute('admin_xml_dump_list', static fn (LogEntry $entity): array => ['id' => $entity->getId()]) + ->displayIf(static fn (LogEntry $entity): bool => 'bpn' === $entity->getChannel()); + return $actions ->add(Crud::PAGE_INDEX, Action::DETAIL) + ->add(Crud::PAGE_INDEX, $viewDumps) + ->add(Crud::PAGE_DETAIL, $viewDumps) ->remove(Crud::PAGE_INDEX, Action::NEW) ->remove(Crud::PAGE_INDEX, Action::EDIT) ->remove(Crud::PAGE_INDEX, Action::DELETE) @@ -45,8 +50,9 @@ class LogEntryCrudController extends AbstractCrudController return [ DateTimeField::new('createdAt', 'Zeitstempel'), TextField::new('username', 'Benutzer'), - TextField::new('requestId', 'Request ID'), TextField::new('message', 'Aktion'), + TextField::new('requestId', 'Request ID'), + TextField::new('uri', 'URI'), ]; } } diff --git a/src/Controller/Admin/UserCrudController.php b/src/Controller/Admin/UserCrudController.php index fe01565..e5fae35 100644 --- a/src/Controller/Admin/UserCrudController.php +++ b/src/Controller/Admin/UserCrudController.php @@ -31,8 +31,8 @@ class UserCrudController extends AbstractCrudController public function configureCrud(Crud $crud): Crud { return $crud - ->setEntityLabelInSingular('Kundenaccount') - ->setEntityLabelInPlural('Kundenaccounts') + ->setEntityLabelInSingular('Benutzeraccount') + ->setEntityLabelInPlural('Benutzeraccounts') ->setDefaultSort(['lastLoginAt' => 'DESC']); } diff --git a/src/Controller/Admin/XmlDumpController.php b/src/Controller/Admin/XmlDumpController.php new file mode 100644 index 0000000..25347f9 --- /dev/null +++ b/src/Controller/Admin/XmlDumpController.php @@ -0,0 +1,80 @@ +logEntryRepository->find($id); + + if (null === $logEntry) { + throw new NotFoundHttpException('Log entry not found.'); + } + + if ('bpn' !== $logEntry->getChannel()) { + throw new NotFoundHttpException('XML dumps are only available for BPN channel entries.'); + } + + $requestId = $logEntry->getRequestId(); + $dumps = []; + + try { + $dumps = $this->xmlDumpService->findDumpsForRequestId($requestId); + } catch (FilesystemException) { + // Dumps directory may not exist or be inaccessible + } + + return $this->render('admin/xml_dump/list.html.twig', [ + 'logEntry' => $logEntry, + 'requestId' => $requestId, + 'dumps' => $dumps, + ]); + } + + #[Route('/download/{filename}', name: 'admin_xml_dump_download', methods: ['GET'], requirements: ['filename' => '.+'])] + public function download(string $filename): Response + { + try { + if (false === $this->xmlDumpService->fileExists($filename)) { + throw new NotFoundHttpException('Dump file not found. It may have been cleaned up.'); + } + + $content = $this->xmlDumpService->getContent($filename); + } catch (FilesystemException $e) { + throw new NotFoundHttpException('Failed to read dump file: '.$e->getMessage()); + } + + $response = new Response($content); + $response->headers->set('Content-Type', 'application/xml'); + + $disposition = $response->headers->makeDisposition( + ResponseHeaderBag::DISPOSITION_ATTACHMENT, + basename($filename) + ); + $response->headers->set('Content-Disposition', $disposition); + + return $response; + } +} diff --git a/src/Entity/LogEntry.php b/src/Entity/LogEntry.php index 6aeed53..a766fcc 100644 --- a/src/Entity/LogEntry.php +++ b/src/Entity/LogEntry.php @@ -111,4 +111,9 @@ class LogEntry { return $this->getExtra()['request_id'] ?? '-'; } + + public function getUri(): string + { + return $this->getExtra()['uri'] ?? ''; + } } diff --git a/src/Service/XmlDumpService.php b/src/Service/XmlDumpService.php new file mode 100644 index 0000000..9c4f31e --- /dev/null +++ b/src/Service/XmlDumpService.php @@ -0,0 +1,77 @@ + + * + * @throws FilesystemException + */ + public function findDumpsForRequestId(string $requestId): array + { + if ('' === $requestId || '-' === $requestId) { + return []; + } + + $pattern = '/^'.preg_quote($requestId, '/').'(_\d+)?_(request|response)\.xml$/'; + $matches = []; + + foreach ($this->xmlDump->listContents('.') as $item) { + if ($item->isFile() && 1 === preg_match($pattern, $item->path(), $typeMatch)) { + $matches[] = [ + 'filename' => $item->path(), + 'type' => $typeMatch[2], + 'size' => $this->xmlDump->fileSize($item->path()), + ]; + } + } + + usort($matches, static fn (array $a, array $b): int => strcmp($a['filename'], $b['filename'])); + + return $matches; + } + + /** + * Reads the content of a dump file. + * + * @throws FilesystemException + */ + public function getContent(string $filename): string + { + return $this->xmlDump->read($filename); + } + + /** + * Checks if a dump file exists. + * + * @throws FilesystemException + */ + public function fileExists(string $filename): bool + { + return $this->xmlDump->fileExists($filename); + } +} diff --git a/templates/account/index.html.twig b/templates/account/index.html.twig index c20e63c..133b78f 100644 --- a/templates/account/index.html.twig +++ b/templates/account/index.html.twig @@ -21,6 +21,14 @@ Meine Daten + {% if is_granted('ROLE_ADMIN') %} +
  • + + + Admin + +
  • + {% endif %}
  • diff --git a/templates/admin/xml_dump/list.html.twig b/templates/admin/xml_dump/list.html.twig new file mode 100644 index 0000000..4e4676b --- /dev/null +++ b/templates/admin/xml_dump/list.html.twig @@ -0,0 +1,62 @@ +{% extends '@EasyAdmin/layout.html.twig' %} + +{% block content_title %} + XML Dumps +{% endblock %} + +{% block main %} + +{% endblock %}