feat: remove easy admin bundle, adopt admin theme
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Admin\Field;
|
||||
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait;
|
||||
|
||||
class JsonDataField implements FieldInterface
|
||||
{
|
||||
use FieldTrait;
|
||||
|
||||
public static function new(string $propertyName, ?string $label = null): self
|
||||
{
|
||||
return (new self())
|
||||
->setProperty($propertyName)
|
||||
->setLabel($label)
|
||||
->setTemplatePath('admin/field/json.html.twig');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Controller\Traits\ReturnUrlTrait;
|
||||
use App\Entity\BookingEditDraft;
|
||||
use App\Htmx\HxRedirectResponse;
|
||||
use App\Repository\BookingEditDraftRepository;
|
||||
use App\Service\BookingExporter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Knp\Component\Pager\PaginatorInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class BookingEditDraftController extends AbstractController
|
||||
{
|
||||
use ReturnUrlTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly BookingEditDraftRepository $bookingEditDraftRepository,
|
||||
private readonly PaginatorInterface $paginator,
|
||||
private readonly BookingExporter $bookingExporter,
|
||||
private readonly LoggerInterface $adminLogger,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/admin/booking-edit-draft', name: 'app_admin_bookingeditdraft')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$qb = $this
|
||||
->bookingEditDraftRepository
|
||||
->createQueryBuilder('booking_edit_draft')
|
||||
->leftJoin('booking_edit_draft.user', 'user')
|
||||
;
|
||||
|
||||
$pagination = $this->paginator->paginate(
|
||||
$qb,
|
||||
$request->query->getInt('page', 1),
|
||||
$request->query->getInt('limit', 20),
|
||||
[
|
||||
'defaultSortFieldName' => 'booking_edit_draft.createdAt',
|
||||
'defaultSortDirection' => 'DESC',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->render('admin/booking_edit_draft/index.html.twig', [
|
||||
'pagination' => $pagination,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/admin/booking-edit-draft/{id}/show', name: 'app_admin_bookingeditdraft_show')]
|
||||
public function show(BookingEditDraft $draft, Request $request): Response
|
||||
{
|
||||
$returnUrl = $this->getReturnUrl($request, 'app_admin_bookingeditdraft');
|
||||
|
||||
return $this->render('admin/booking_edit_draft/show.html.twig', [
|
||||
'draft' => $draft,
|
||||
'returnUrl' => $returnUrl,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/admin/booking-edit-draft/{id}/delete', name: 'app_admin_bookingeditdraft_delete')]
|
||||
public function delete(BookingEditDraft $draft, Request $request): Response
|
||||
{
|
||||
if (true === $request->isMethod(Request::METHOD_POST)) {
|
||||
$this->entityManager->remove($draft);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->adminLogger->info('Delete booking edit draft', [
|
||||
'booking_number' => $draft->getBookingNumber(),
|
||||
]);
|
||||
$this->addFlash('success', 'Der Buchungsentwurf wurde gelöscht');
|
||||
|
||||
$returnUrl = $this->getReturnUrl($request, 'app_admin_bookingeditdraft');
|
||||
|
||||
return new HxRedirectResponse($returnUrl);
|
||||
}
|
||||
|
||||
return $this->render('admin/booking_edit_draft/modal_delete.html.twig', [
|
||||
'draft' => $draft,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/admin/booking-edit-draft/{id}/export', name: 'app_admin_bookingeditdraft_export')]
|
||||
public function export(BookingEditDraft $draft): Response
|
||||
{
|
||||
if (false === $draft->hasExportData()) {
|
||||
$this->addFlash('warning', 'Der Export ist fehlgeschlagen');
|
||||
|
||||
return $this->redirectToRoute('app_admin_bookingeditdraft');
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->bookingExporter->createExportResponse($draft);
|
||||
} catch (\RuntimeException $e) {
|
||||
$this->addFlash('warning', 'Der Export ist fehlgeschlagen: '.$e->getMessage());
|
||||
|
||||
return $this->redirectToRoute('app_admin_bookingeditdraft');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Admin\Field\JsonDataField;
|
||||
use App\Entity\BookingEditDraft;
|
||||
use App\Entity\User;
|
||||
use App\Service\BookingExporter;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
/** @extends AbstractCrudController<BookingEditDraft> */
|
||||
class BookingEditDraftCrudController extends AbstractCrudController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly BookingExporter $exportService,
|
||||
private readonly AdminUrlGenerator $adminUrlGenerator,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return BookingEditDraft::class;
|
||||
}
|
||||
|
||||
public function configureActions(Actions $actions): Actions
|
||||
{
|
||||
$exportAction = Action::new('exportExcel', 'Excel Export', 'fa fa-file-excel')
|
||||
->linkToRoute(
|
||||
'admin_booking_draft_export',
|
||||
static fn (BookingEditDraft $entity): array => ['id' => $entity->getId()]
|
||||
)
|
||||
->displayIf(static fn (BookingEditDraft $entity): bool => $entity->hasExportData());
|
||||
|
||||
return $actions
|
||||
->add(Crud::PAGE_INDEX, Action::DETAIL)
|
||||
->add(Crud::PAGE_INDEX, $exportAction)
|
||||
->add(Crud::PAGE_DETAIL, $exportAction)
|
||||
->remove(Crud::PAGE_INDEX, Action::NEW)
|
||||
->remove(Crud::PAGE_INDEX, Action::EDIT)
|
||||
->remove(Crud::PAGE_DETAIL, Action::EDIT)
|
||||
;
|
||||
}
|
||||
|
||||
#[Route('/admin/booking-draft/{id}/export', name: 'admin_booking_draft_export', requirements: ['id' => '\d+'])]
|
||||
public function export(BookingEditDraft $draft): Response
|
||||
{
|
||||
if (false === $draft->hasExportData()) {
|
||||
$this->addFlash('danger', 'Export nicht möglich: Reisedaten fehlen');
|
||||
|
||||
$url = $this->adminUrlGenerator
|
||||
->setController(self::class)
|
||||
->setAction(Action::INDEX)
|
||||
->generateUrl();
|
||||
|
||||
return $this->redirect($url);
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->exportService->createExportResponse($draft);
|
||||
} catch (\RuntimeException $e) {
|
||||
$this->addFlash('danger', 'Export fehlgeschlagen: '.$e->getMessage());
|
||||
|
||||
$url = $this->adminUrlGenerator
|
||||
->setController(self::class)
|
||||
->setAction(Action::INDEX)
|
||||
->generateUrl();
|
||||
|
||||
return $this->redirect($url);
|
||||
}
|
||||
}
|
||||
|
||||
public function configureCrud(Crud $crud): Crud
|
||||
{
|
||||
return $crud
|
||||
->setEntityLabelInSingular('Buchungsentwurf')
|
||||
->setEntityLabelInPlural('Buchungsentwürfe')
|
||||
->setDefaultSort(['createdAt' => 'DESC']);
|
||||
}
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
IntegerField::new('bookingNumber', 'Vorgang'),
|
||||
AssociationField::new('user', 'Kundenaccount')->formatValue(fn (User $user) => $user->getEmail()),
|
||||
DateTimeField::new('createdAt', 'erstellt am'),
|
||||
DateTimeField::new('updatedAt', 'aktualisiert am'),
|
||||
JsonDataField::new('formData', 'Daten')->onlyOnDetail(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\BookingEditDraft;
|
||||
use App\Entity\LogEntry;
|
||||
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\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
#[AdminDashboard(routePath: '/admin', routeName: 'admin')]
|
||||
class DashboardController extends AbstractDashboardController
|
||||
class DashboardController extends AbstractController
|
||||
{
|
||||
#[Route('/admin/dashboard', name: 'app_admin_dashboard')]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('admin/dashboard/index.html.twig');
|
||||
}
|
||||
|
||||
public function configureDashboard(): Dashboard
|
||||
{
|
||||
return Dashboard::new()
|
||||
->setTitle('MyE&P')
|
||||
->setLocales(['de'])
|
||||
->setFaviconPath('build/favicon/icon.svg')
|
||||
;
|
||||
}
|
||||
|
||||
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('Benutzeraccounts', 'fa fa-users', User::class);
|
||||
return $this->render('admin/dashboard.html.twig');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Log;
|
||||
use App\Entity\LogEntry;
|
||||
use App\Repository\LogEntryRepository;
|
||||
use App\Service\XmlDumpReader;
|
||||
use Knp\Component\Pager\PaginatorInterface;
|
||||
use League\Flysystem\FilesystemException;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class LogController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly LogEntryRepository $logEntryRepository,
|
||||
private readonly PaginatorInterface $paginator,
|
||||
private readonly XmlDumpReader $xmlDumpReader,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/admin/log', name: 'app_admin_log')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$qb = $this
|
||||
->logEntryRepository
|
||||
->createQueryBuilder('log_entry')
|
||||
;
|
||||
|
||||
$pagination = $this->paginator->paginate(
|
||||
$qb,
|
||||
$request->query->getInt('page', 1),
|
||||
$request->query->getInt('limit', 50),
|
||||
[
|
||||
'defaultSortFieldName' => 'log_entry.createdAt',
|
||||
'defaultSortDirection' => 'DESC',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->render('admin/log/index.html.twig', [
|
||||
'pagination' => $pagination,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/admin/log/{id}/xml-dumps', name: 'app_admin_log_xmldumps')]
|
||||
public function xmlDumps(LogEntry $logEntry): Response
|
||||
{
|
||||
$dumps = [];
|
||||
try {
|
||||
$dumps = $this->xmlDumpReader->findDumpsForRequestId($logEntry->getRequestId());
|
||||
} catch (FilesystemException) {
|
||||
}
|
||||
|
||||
return $this->render('admin/log/xml_dumps.html.twig', [
|
||||
'logEntry' => $logEntry,
|
||||
'dumps' => $dumps,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/admin/log/download/{filename}', name: 'app_admin_log_xmldump_download', requirements: ['filename' => '.+'])]
|
||||
public function downloadXmlDump(string $filename): Response
|
||||
{
|
||||
try {
|
||||
if (false === $this->xmlDumpReader->fileExists($filename)) {
|
||||
throw $this->createNotFoundException('Dump file not found. It may have been cleaned up.');
|
||||
}
|
||||
|
||||
$content = $this->xmlDumpReader->getContent($filename);
|
||||
} catch (FilesystemException $e) {
|
||||
throw $this->createNotFoundException('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;
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Admin\Field\JsonDataField;
|
||||
use App\Entity\LogEntry;
|
||||
use App\Repository\LogEntryRepository;
|
||||
use App\Service\XmlDumpReader;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
use League\Flysystem\FilesystemException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/** @extends AbstractCrudController<LogEntry> */
|
||||
class LogEntryCrudController extends AbstractCrudController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly LogEntryRepository $logEntryRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return LogEntry::class;
|
||||
}
|
||||
|
||||
public function configureActions(Actions $actions): Actions
|
||||
{
|
||||
$viewDumps = Action::new('viewDumps', 'XML Dumps', 'fa fa-file-code')
|
||||
->linkToCrudAction('xmlDumps')
|
||||
->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)
|
||||
->remove(Crud::PAGE_DETAIL, Action::EDIT)
|
||||
->remove(Crud::PAGE_DETAIL, Action::DELETE)
|
||||
;
|
||||
}
|
||||
|
||||
public function configureCrud(Crud $crud): Crud
|
||||
{
|
||||
return $crud
|
||||
->setEntityLabelInSingular('Logeintrag')
|
||||
->setEntityLabelInPlural('Logeinträge')
|
||||
->setDefaultSort(['createdAt' => 'DESC'])
|
||||
->setSearchFields(['errorCode', 'message', 'channel', 'extra']);
|
||||
}
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
yield DateTimeField::new('createdAt', 'Zeitstempel');
|
||||
yield TextField::new('errorCode', 'Fehlercode')
|
||||
->formatValue(static fn (?string $value): string => $value ?? '-');
|
||||
yield TextField::new('username', 'Benutzer');
|
||||
yield TextField::new('message', 'Aktion');
|
||||
yield TextField::new('requestId', 'Request ID');
|
||||
yield TextField::new('uri', 'URI');
|
||||
yield JsonDataField::new('context', 'Kontext')->onlyOnDetail();
|
||||
yield JsonDataField::new('extra', 'Extra')->onlyOnDetail();
|
||||
}
|
||||
|
||||
public function xmlDumps(Request $request, XmlDumpReader $xmlDumpService): Response
|
||||
{
|
||||
$entityId = $request->query->getInt('entityId');
|
||||
if ($entityId <= 0) {
|
||||
throw $this->createNotFoundException('Log entry not found.');
|
||||
}
|
||||
|
||||
$entity = $this->logEntryRepository->find($entityId);
|
||||
if (!$entity instanceof LogEntry) {
|
||||
throw $this->createNotFoundException('Log entry not found.');
|
||||
}
|
||||
|
||||
$dumps = [];
|
||||
try {
|
||||
$dumps = $xmlDumpService->findDumpsForRequestId($entity->getRequestId());
|
||||
} catch (FilesystemException) {
|
||||
}
|
||||
|
||||
return $this->render('admin/xml_dump/list.html.twig', [
|
||||
'logEntry' => $entity,
|
||||
'requestId' => $entity->getRequestId(),
|
||||
'dumps' => $dumps,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use Knp\Component\Pager\PaginatorInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class UserController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserRepository $userRepository,
|
||||
private readonly PaginatorInterface $paginator,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/admin/user', name: 'app_admin_user')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$qb = $this
|
||||
->userRepository
|
||||
->createQueryBuilder('user')
|
||||
;
|
||||
|
||||
$pagination = $this->paginator->paginate(
|
||||
$qb,
|
||||
$request->query->getInt('page', 1),
|
||||
$request->query->getInt('limit', 50),
|
||||
[
|
||||
'defaultSortFieldName' => 'user.lastLoginAt',
|
||||
'defaultSortDirection' => 'DESC',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->render('admin/user/index.html.twig', [
|
||||
'pagination' => $pagination,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\User;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
/** @extends AbstractCrudController<User> */
|
||||
class UserCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return User::class;
|
||||
}
|
||||
|
||||
public function configureActions(Actions $actions): Actions
|
||||
{
|
||||
return $actions
|
||||
->remove(Crud::PAGE_INDEX, Action::NEW)
|
||||
->remove(Crud::PAGE_INDEX, Action::EDIT)
|
||||
->remove(Crud::PAGE_INDEX, Action::DELETE)
|
||||
;
|
||||
}
|
||||
|
||||
public function configureCrud(Crud $crud): Crud
|
||||
{
|
||||
return $crud
|
||||
->setEntityLabelInSingular('Benutzeraccount')
|
||||
->setEntityLabelInPlural('Benutzeraccounts')
|
||||
->setDefaultSort(['lastLoginAt' => 'DESC']);
|
||||
}
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
TextField::new('email', 'E-Mail'),
|
||||
DateTimeField::new('lastLoginAt', 'letzter Login'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Repository\LogEntryRepository;
|
||||
use App\Service\XmlDumpReader;
|
||||
use League\Flysystem\FilesystemException;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[Route('/admin/xml-dump')]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
class XmlDumpController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly XmlDumpReader $xmlDumpService,
|
||||
private readonly LogEntryRepository $logEntryRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/{id}', name: 'admin_xml_dump_list', methods: ['GET'])]
|
||||
public function list(int $id): Response
|
||||
{
|
||||
$logEntry = $this->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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Traits;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
trait ReturnUrlTrait
|
||||
{
|
||||
public function getReturnUrl(Request $request, string $defaultRoute, array $parameters = []): string
|
||||
{
|
||||
$defaultUrl = $this->generateUrl($defaultRoute, $parameters);
|
||||
|
||||
return rawurldecode($request->query->get('r', $defaultUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove specified query parameters from a URL.
|
||||
*
|
||||
* @param string $url The URL to process
|
||||
* @param string[] $parametersKeys Array of query parameter keys to remove
|
||||
*
|
||||
* @return string The URL without the specified query parameters
|
||||
*/
|
||||
public function removeQueryParameters(string $url, array $parametersKeys): string
|
||||
{
|
||||
if ([] === $parametersKeys) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$urlParts = parse_url($url);
|
||||
|
||||
if (false === isset($urlParts['query'])) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
parse_str($urlParts['query'], $queryParams);
|
||||
|
||||
foreach ($parametersKeys as $key) {
|
||||
unset($queryParams[$key]);
|
||||
}
|
||||
|
||||
$urlParts['query'] = http_build_query($queryParams);
|
||||
|
||||
if ('' === $urlParts['query']) {
|
||||
unset($urlParts['query']);
|
||||
}
|
||||
|
||||
// Rebuild URL
|
||||
$result = '';
|
||||
if (isset($urlParts['scheme'])) {
|
||||
$result .= $urlParts['scheme'].'://';
|
||||
}
|
||||
if (isset($urlParts['host'])) {
|
||||
$result .= $urlParts['host'];
|
||||
}
|
||||
if (isset($urlParts['port'])) {
|
||||
$result .= ':'.$urlParts['port'];
|
||||
}
|
||||
if (isset($urlParts['path'])) {
|
||||
$result .= $urlParts['path'];
|
||||
}
|
||||
if (isset($urlParts['query'])) {
|
||||
$result .= '?'.$urlParts['query'];
|
||||
}
|
||||
if (isset($urlParts['fragment'])) {
|
||||
$result .= '#'.$urlParts['fragment'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -2,11 +2,12 @@
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
#[ORM\Entity]
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace App\Menu;
|
||||
|
||||
use Knp\Menu\FactoryInterface;
|
||||
use Knp\Menu\ItemInterface;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
abstract class AbstractMenuBuilder
|
||||
{
|
||||
public function __construct(
|
||||
protected FactoryInterface $factory,
|
||||
protected Security $security,
|
||||
protected RequestStack $requestStack,
|
||||
protected TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
protected function createRootElement(): ItemInterface
|
||||
{
|
||||
return $this->factory->createItem('root');
|
||||
}
|
||||
|
||||
protected function getDefaultRouteParameters(string $parameter = 'id', string $default = '0'): array
|
||||
{
|
||||
$request = $this->requestStack->getMainRequest();
|
||||
|
||||
return [
|
||||
$parameter => $request->get($parameter, $default),
|
||||
'r' => $request->get('r'),
|
||||
];
|
||||
}
|
||||
|
||||
protected function addAdminItem(ItemInterface $menu): void
|
||||
{
|
||||
if ($this->security->isGranted('ROLE_ADMIN')) {
|
||||
$this->addDivider($menu);
|
||||
$menu->addChild('zum Adminbereich', [
|
||||
'route' => 'app_admin_index',
|
||||
'linkAttributes' => [
|
||||
'title' => 'zum Adminbereich',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'user',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function addManagerItem(ItemInterface $menu): void
|
||||
{
|
||||
if ($this->security->isGranted('ROLE_MANAGER')) {
|
||||
$this->addDivider($menu);
|
||||
$menu->addChild('zum Managementbereich', [
|
||||
'route' => 'app_manager_index',
|
||||
'linkAttributes' => [
|
||||
'title' => 'zum Managementbereich',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'user',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function addTeamerItem(ItemInterface $menu): void
|
||||
{
|
||||
if ($this->security->isGranted('ROLE_TEAMER')) {
|
||||
$this->addDivider($menu);
|
||||
$menu->addChild('zum Teambereich', [
|
||||
'route' => 'app_teamer_index',
|
||||
'linkAttributes' => [
|
||||
'title' => 'zum Teambereich',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'user',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function addHouseManagerItem(ItemInterface $menu): void
|
||||
{
|
||||
if ($this->security->isGranted('ROLE_HOUSE_MANAGER')) {
|
||||
$this->addDivider($menu);
|
||||
$menu->addChild('zum Hausleitungsbereich', [
|
||||
'route' => 'app_house_manager_index',
|
||||
'linkAttributes' => [
|
||||
'title' => 'zum Hausleitungsbereich',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'user',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function addLogoutItem(ItemInterface $menu): void
|
||||
{
|
||||
$this->addDivider($menu);
|
||||
|
||||
$menu->addChild('MyE&P', [
|
||||
'route' => 'app_account',
|
||||
'linkAttributes' => [
|
||||
'title' => 'zu MyE&P',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'logout',
|
||||
],
|
||||
]);
|
||||
$menu->addChild('Logout', [
|
||||
'route' => 'app_logout',
|
||||
'linkAttributes' => [
|
||||
'title' => 'Logout',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'logout',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addDivider(ItemInterface $menu): void
|
||||
{
|
||||
$menu->addChild(Uuid::v4(), [
|
||||
'extras' => [
|
||||
'divider' => true,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Menu;
|
||||
|
||||
use Knp\Menu\ItemInterface;
|
||||
|
||||
class AdminMenuBuilder extends AbstractMenuBuilder
|
||||
{
|
||||
public function createMainMenu(array $options): ItemInterface
|
||||
{
|
||||
$menu = $this->createRootElement();
|
||||
|
||||
$menu->addChild('Dashboard', [
|
||||
'route' => 'app_admin_dashboard',
|
||||
'linkAttributes' => [
|
||||
'title' => 'Dashboard',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'chart',
|
||||
],
|
||||
]);
|
||||
$menu->addChild('Buchungsentwürfe', [
|
||||
'route' => 'app_admin_bookingeditdraft',
|
||||
'linkAttributes' => [
|
||||
'title' => 'Buchungsentwürfe',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'edit',
|
||||
],
|
||||
]);
|
||||
$menu->addChild('Benutzer', [
|
||||
'route' => 'app_admin_user',
|
||||
'linkAttributes' => [
|
||||
'title' => 'Benutzer',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'users',
|
||||
],
|
||||
]);
|
||||
$menu->addChild('Logs', [
|
||||
'route' => 'app_admin_log',
|
||||
'linkAttributes' => [
|
||||
'title' => 'Logs',
|
||||
],
|
||||
'extras' => [
|
||||
'icon' => 'list',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->addLogoutItem($menu);
|
||||
|
||||
return $menu;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\User;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<User>
|
||||
*/
|
||||
class UserRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, User::class);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ class AppExtension extends AbstractExtension
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('icon', [AppRuntime::class, 'renderIcon'], ['needs_environment' => true, 'is_safe' => ['html']]),
|
||||
new TwigFunction('is_participant_eligible', [AppRuntime::class, 'isParticipantEligible']),
|
||||
new TwigFunction('qa_attribute', [AppRuntime::class, 'renderQaAttribute'], ['is_safe' => ['html']]),
|
||||
new TwigFunction('is_static_text', [AppRuntime::class, 'isStaticText']),
|
||||
@@ -42,6 +43,7 @@ class AppExtension extends AbstractExtension
|
||||
new TwigFunction('booking_theme', [AppRuntime::class, 'getBookingTheme']),
|
||||
new TwigFunction('gtm_id', [AppRuntime::class, 'getGtmId']),
|
||||
new TwigFunction('cmp_url', [AppRuntime::class, 'getCmpUrl']),
|
||||
new TwigFunction('return_url', [AppRuntime::class, 'getEncodedReturnUrl']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Model\DomainConfig;
|
||||
use App\Service\ParticipantEligibilityChecker;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Twig\Environment;
|
||||
use Twig\Extension\RuntimeExtensionInterface;
|
||||
use Twig\Extra\Intl\IntlExtension;
|
||||
|
||||
@@ -148,6 +149,14 @@ class AppRuntime implements RuntimeExtensionInterface
|
||||
return sprintf(' data-qa-%s="%s"', strtolower($label), $value);
|
||||
}
|
||||
|
||||
public function renderIcon(Environment $environment, string $icon, string $classes = 'w-5 h-5'): string
|
||||
{
|
||||
return $environment->render('_partials/_icon.html.twig', [
|
||||
'icon' => $icon,
|
||||
'class' => $classes,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a form field should be rendered as static text.
|
||||
*
|
||||
@@ -268,6 +277,17 @@ class AppRuntime implements RuntimeExtensionInterface
|
||||
return $this->getDomainConfig()->cmpUrl;
|
||||
}
|
||||
|
||||
public function getEncodedReturnUrl(): string
|
||||
{
|
||||
$masterRequest = $this->requestStack->getMainRequest();
|
||||
|
||||
if (null === $masterRequest) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return rawurlencode($masterRequest->getRequestUri());
|
||||
}
|
||||
|
||||
private function getDomainConfig(): DomainConfig
|
||||
{
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
Reference in New Issue
Block a user