Admin panels
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Game\Controller;
|
||||
|
||||
use App\Game\Entity\Session;
|
||||
use App\Game\Enum\SessionStatus;
|
||||
use App\Game\Repository\GameRepository;
|
||||
use App\Game\Repository\SessionRepository;
|
||||
use App\Tech\Repository\UserRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
@@ -25,14 +28,23 @@ final class GameAdminController extends AbstractController
|
||||
#[Route('', name: 'game_admin_dashboard', methods: ['GET'])]
|
||||
public function index(
|
||||
UserRepository $userRepository,
|
||||
SessionRepository $sessionRepository
|
||||
SessionRepository $sessionRepository,
|
||||
GameRepository $gameRepository,
|
||||
): Response {
|
||||
$players = $userRepository->findByRole('ROLE_PLAYER');
|
||||
$sessions = $sessionRepository->findAll();
|
||||
$allUsers = $userRepository->findAll();
|
||||
$allSessions = $sessionRepository->findAll();
|
||||
$activeSessions = array_filter(
|
||||
$allSessions,
|
||||
fn(Session $s) => in_array($s->getStatus(), [SessionStatus::CREATED, SessionStatus::READY, SessionStatus::PLAYING])
|
||||
);
|
||||
|
||||
return $this->render('game/admin/index.html.twig', [
|
||||
'players' => $players,
|
||||
'sessions' => $sessions,
|
||||
'totalUsers' => count($allUsers),
|
||||
'totalPlayers' => count($userRepository->findByRole('ROLE_PLAYER')),
|
||||
'totalAdmins' => count($userRepository->findByRole('ROLE_ADMIN')),
|
||||
'totalGames' => count($gameRepository->findAll()),
|
||||
'totalSessions' => count($allSessions),
|
||||
'activeSessions' => count($activeSessions),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -44,18 +56,13 @@ final class GameAdminController extends AbstractController
|
||||
$username = $player->getUser()->getUsername();
|
||||
$logFile = $this->projectDir . '/var/log/sessions/' . $session->getId() . '/' . $username . '.txt';
|
||||
|
||||
$logs = '';
|
||||
if (file_exists($logFile)) {
|
||||
$logs = file_get_contents($logFile);
|
||||
}
|
||||
|
||||
$playersLogs[] = [
|
||||
'username' => $username,
|
||||
'logs' => $logs,
|
||||
'logs' => file_exists($logFile) ? file_get_contents($logFile) : '',
|
||||
];
|
||||
}
|
||||
|
||||
return $this->render('game/admin/session.html.twig', [
|
||||
return $this->render('game/admin/sessions/view.html.twig', [
|
||||
'session' => $session,
|
||||
'playersLogs' => $playersLogs,
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Game\Controller;
|
||||
|
||||
use App\Tech\Repository\EmailLogRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[Route('/admin/email-log')]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
final class GameAdminEmailLogController extends AbstractController
|
||||
{
|
||||
#[Route('', name: 'game_admin_email_log', methods: ['GET'])]
|
||||
public function index(EmailLogRepository $emailLogRepository): Response
|
||||
{
|
||||
return $this->render('game/admin/email_log/index.html.twig', [
|
||||
'logs' => $emailLogRepository->findBy([], ['sentAt' => 'DESC']),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Game\Controller;
|
||||
|
||||
use App\Game\Entity\GameSetting;
|
||||
use App\Game\Enum\GameSettingType;
|
||||
use App\Game\Form\AdminGameType;
|
||||
use App\Game\Entity\Game;
|
||||
use App\Game\Repository\GameRepository;
|
||||
use App\Game\Repository\GameSettingRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[Route('/admin/games')]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
final class GameAdminGameController extends AbstractController
|
||||
{
|
||||
#[Route('', name: 'game_admin_games', methods: ['GET'])]
|
||||
public function index(GameRepository $gameRepository): Response
|
||||
{
|
||||
return $this->render('game/admin/games/index.html.twig', [
|
||||
'games' => $gameRepository->findBy([], ['id' => 'ASC']),
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{id}/edit', name: 'game_admin_game_edit', methods: ['GET', 'POST'])]
|
||||
public function edit(
|
||||
Game $game,
|
||||
Request $request,
|
||||
EntityManagerInterface $em,
|
||||
GameSettingRepository $gameSettingRepository,
|
||||
): Response {
|
||||
$totalTimeSetting = $gameSettingRepository->getSetting($game, GameSettingType::TOTAL_TIME);
|
||||
|
||||
$form = $this->createForm(AdminGameType::class, $game, [
|
||||
'total_time' => $totalTimeSetting?->getValue(),
|
||||
]);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$totalTime = $form->get('totalTime')->getData();
|
||||
|
||||
if ($totalTime !== null) {
|
||||
if (!$totalTimeSetting) {
|
||||
$totalTimeSetting = new GameSetting();
|
||||
$totalTimeSetting->setGame($game);
|
||||
$totalTimeSetting->setName(GameSettingType::TOTAL_TIME);
|
||||
}
|
||||
$totalTimeSetting->setValue((string) $totalTime);
|
||||
$em->persist($totalTimeSetting);
|
||||
}
|
||||
|
||||
$em->flush();
|
||||
$this->addFlash('success', sprintf('Game "%s" updated.', $game->getName()));
|
||||
|
||||
return $this->redirectToRoute('game_admin_games');
|
||||
}
|
||||
|
||||
return $this->render('game/admin/games/edit.html.twig', [
|
||||
'game' => $game,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Game\Controller;
|
||||
|
||||
use App\Game\Entity\Session;
|
||||
use App\Game\Enum\SessionStatus;
|
||||
use App\Game\Repository\SessionRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[Route('/admin/sessions')]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
final class GameAdminSessionController extends AbstractController
|
||||
{
|
||||
#[Route('', name: 'game_admin_sessions', methods: ['GET'])]
|
||||
public function index(SessionRepository $sessionRepository): Response
|
||||
{
|
||||
return $this->render('game/admin/sessions/index.html.twig', [
|
||||
'sessions' => $sessionRepository->findBy([], ['created' => 'DESC']),
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{id}/close', name: 'game_admin_session_close', methods: ['POST'])]
|
||||
public function close(Session $session, Request $request, EntityManagerInterface $em): Response
|
||||
{
|
||||
if (!$this->isCsrfTokenValid('close_session_' . $session->getId(), $request->request->get('_token'))) {
|
||||
$this->addFlash('danger', 'Invalid CSRF token.');
|
||||
return $this->redirectToRoute('game_admin_sessions');
|
||||
}
|
||||
|
||||
$session->setStatus(SessionStatus::LOST);
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success', sprintf('Session #%d closed.', $session->getId()));
|
||||
|
||||
return $this->redirectToRoute('game_admin_sessions');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Game\Controller;
|
||||
|
||||
use App\Tech\Entity\User;
|
||||
use App\Tech\Form\AdminUserType;
|
||||
use App\Tech\Repository\UserRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[Route('/admin/users')]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
final class GameAdminUserController extends AbstractController
|
||||
{
|
||||
#[Route('', name: 'game_admin_users', methods: ['GET'])]
|
||||
public function index(UserRepository $userRepository): Response
|
||||
{
|
||||
return $this->render('game/admin/users/index.html.twig', [
|
||||
'users' => $userRepository->findBy([], ['id' => 'ASC']),
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{id}/edit', name: 'game_admin_user_edit', methods: ['GET', 'POST'])]
|
||||
public function edit(
|
||||
User $user,
|
||||
Request $request,
|
||||
EntityManagerInterface $em,
|
||||
UserPasswordHasherInterface $passwordHasher,
|
||||
): Response {
|
||||
$form = $this->createForm(AdminUserType::class, $user);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$plainPassword = $form->get('plainPassword')->getData();
|
||||
if ($plainPassword) {
|
||||
$user->setPassword($passwordHasher->hashPassword($user, $plainPassword));
|
||||
}
|
||||
|
||||
$em->flush();
|
||||
$this->addFlash('success', sprintf('User "%s" updated.', $user->getUsername()));
|
||||
|
||||
return $this->redirectToRoute('game_admin_users');
|
||||
}
|
||||
|
||||
return $this->render('game/admin/users/edit.html.twig', [
|
||||
'user' => $user,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{id}/delete', name: 'game_admin_user_delete', methods: ['POST'])]
|
||||
public function delete(User $user, Request $request, EntityManagerInterface $em): Response
|
||||
{
|
||||
if (!$this->isCsrfTokenValid('delete_user_' . $user->getId(), $request->request->get('_token'))) {
|
||||
$this->addFlash('danger', 'Invalid CSRF token.');
|
||||
return $this->redirectToRoute('game_admin_users');
|
||||
}
|
||||
|
||||
$username = $user->getUsername();
|
||||
$em->remove($user);
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success', sprintf('User "%s" deleted.', $username));
|
||||
|
||||
return $this->redirectToRoute('game_admin_users');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Game\Form;
|
||||
|
||||
use App\Game\Entity\Game;
|
||||
use App\Game\Enum\GameStatus;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Constraints\Positive;
|
||||
|
||||
class AdminGameType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('name', TextType::class, [
|
||||
'constraints' => [new NotBlank()],
|
||||
])
|
||||
->add('numberOfPlayers', IntegerType::class, [
|
||||
'label' => 'Number of players',
|
||||
'constraints' => [new NotBlank(), new Positive()],
|
||||
])
|
||||
->add('status', ChoiceType::class, [
|
||||
'choices' => [
|
||||
'In development' => GameStatus::IN_DEVELOPMENT,
|
||||
'Locked' => GameStatus::LOCKED,
|
||||
'Open' => GameStatus::OPEN,
|
||||
],
|
||||
])
|
||||
->add('totalTime', IntegerType::class, [
|
||||
'mapped' => false,
|
||||
'required' => false,
|
||||
'label' => 'Total time (seconds)',
|
||||
'data' => $options['total_time'],
|
||||
'attr' => ['placeholder' => 'e.g. 3600 for 1 hour'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Game::class,
|
||||
'total_time' => null,
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('total_time', ['null', 'string', 'int']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tech\Form;
|
||||
|
||||
use App\Tech\Entity\User;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Email;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
class AdminUserType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('email', EmailType::class, [
|
||||
'constraints' => [new NotBlank(), new Email()],
|
||||
])
|
||||
->add('username', TextType::class, [
|
||||
'constraints' => [new NotBlank(), new Length(min: 2, max: 180)],
|
||||
])
|
||||
->add('plainPassword', PasswordType::class, [
|
||||
'mapped' => false,
|
||||
'required' => false,
|
||||
'label' => 'New password',
|
||||
'attr' => ['autocomplete' => 'new-password', 'placeholder' => 'Leave blank to keep current'],
|
||||
])
|
||||
->add('roles', ChoiceType::class, [
|
||||
'choices' => [
|
||||
'Player' => 'ROLE_PLAYER',
|
||||
'Admin' => 'ROLE_ADMIN',
|
||||
],
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'label' => 'Roles',
|
||||
])
|
||||
->add('isVerified', CheckboxType::class, [
|
||||
'required' => false,
|
||||
'label' => 'Email verified',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => User::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user