Initial commit

This commit is contained in:
2025-07-15 23:39:10 +02:00
parent 1d3986a901
commit fb14214612
181 changed files with 31070 additions and 50 deletions

0
src/Controller/.gitignore vendored Normal file
View File

View File

@ -0,0 +1,19 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DefaultController extends AbstractController {
#[Route('/dashboard', name: 'bo_dashboard', methods: ['GET'])]
public function dashboard(): Response {
return $this->render('_dashboard/base.html.twig');
}
// #[Route('/sandbox', name: 'sandbox', methods: 'GET')]
// public function sandbox(): Response {
// return $this->render('sandbox/sandbox.html.twig');
// }
}

View File

@ -0,0 +1,128 @@
<?php
namespace App\Controller;
use App\Entity\Grid;
use App\Entity\Region;
use App\Kernel;
use App\Repository\GridRepository;
use App\Repository\MapRepository;
use App\Service\FileManager;
use Doctrine\ORM\EntityManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/dashboard/regions/region-{regionId}/grid')]
#[ParamConverter(data: 'region', class: Region::class, options: ['id' => 'regionId'])]
class GridController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private FileManager $fileManager,
private GridRepository $gridRepository,
private MapRepository $mapRepository) {}
#[Route('', name: 'bo_region_grid_index', methods: ['GET'])]
public function index(Request $request, Region $region): Response {
$version = $request->query->get('v');
$version = !$version || !in_array(floatval($version), Kernel::SUPPORTED_GAME_VERSION) ? Kernel::GAME_VERSION : floatval($version);
$cells = $this->gridRepository->getGridCells($region);
$maps = $this->mapRepository->getCellsMap($cells, $version);
$grid = $this->gridRepository->buildWorldmap($version, $cells, $maps, true);
return $this->render('_dashboard/grid/index.html.twig', array(
'region' => $region,
'grid' => $grid,
'version' => $version,
));
}
#[Route('/edit', name: 'bo_region_grid_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Region $region): Response {
$logs=array();
$form = $this->gridRepository->getGridForm('bo_region_grid_edit', $region);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
if(is_array($form->get('positions')->getData())) {
if(in_array('row_before', $form->get('positions')->getData())) {
$this->addRow($region, $this->gridRepository->getGridCells($region), true);
$logs[]='• Added one row before.';
}
if(in_array('row_after', $form->get('positions')->getData())) {
$this->addRow($region, $this->gridRepository->getGridCells($region), false);
$logs[]='• Added one row after.';
}
if(in_array('column_before', $form->get('positions')->getData())) {
$this->addColumn($region, $this->gridRepository->getGridCells($region), true);
$logs[]='• Added one column before.';
}
if(in_array('column_after', $form->get('positions')->getData())) {
$this->addColumn($region, $this->gridRepository->getGridCells($region), false);
$logs[]='• Added one column after.';
}
$form = $this->gridRepository->getGridForm('bo_region_grid_edit', $region);
}
}
return $this->renderForm('_dashboard/grid/edit.html.twig', array(
'region' => $region,
'form' => $form,
'logs' => $logs,
));
}
private function addColumn(Region $region, array $cells, bool $isBefore) {
if($isBefore) {
foreach($cells as $_cell) {
$_cell->setCol($_cell->getCol() + 1);
}
}
for($n = 1; $n <= $region->getGridHeight(); $n++) {
$cell = new Grid();
$cell->setRegion($region)
->setCol($isBefore ? 1 : $region->getGridWidth() + 1)
->setRow($n);
$this->_em->persist($cell);
}
$region->setGridWidth($region->getGridWidth() + 1);
$this->_em->flush();
}
/**
* @param Region $region
* @param Grid[] $cells
* @param bool $isBefore
* @return void
*/
private function addRow(Region $region, array $cells, bool $isBefore) {
if($isBefore) {
foreach($cells as $_cell) {
$_cell->setRow($_cell->getRow() + 1);
}
}
for($n = 1; $n <= $region->getGridWidth(); $n++) {
$cell = new Grid();
$cell->setRegion($region)
->setCol($n)
->setRow($isBefore ? 1 : $region->getGridHeight() + 1);
$this->_em->persist($cell);
}
$region->setGridHeight($region->getGridHeight() + 1);
$this->_em->flush();
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace App\Controller;
use App\Entity\ItemCategory;
use App\Form\ItemCategoryType;
use App\Repository\ItemCategoryRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/dashboard/items-categories')]
class ItemCategoryController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private ItemCategoryRepository $itemCategoryRepository) {}
#[Route('', name: 'bo_item_category_index', methods: ['GET'])]
public function index(): Response {
$itemsCategories = $this->itemCategoryRepository->findBy(array(), array('sortOrder' => 'ASC'));
return $this->render('_dashboard/item_category/index.html.twig', array(
'itemsCategories' => $itemsCategories,
));
}
//ToDo set form to XHR for errors
#[Route('/new', name: 'bo_item_category_new', methods: ['GET', 'POST'])]
public function new(Request $request): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$itemCategory = new ItemCategory();
$form = $this->itemCategoryRepository->getForm('bo_item_category_new', $itemCategory);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$this->_em->persist($itemCategory);
$this->_em->flush();
return $this->redirectToRoute('bo_item_category_index', array(), Response::HTTP_SEE_OTHER);
}
$itemsCategories=$this->itemCategoryRepository->findBy(array(), array('sortOrder'=>'ASC'));
return $this->renderForm('_dashboard/item_category/new.html.twig', array(
'itemsCategories'=>$itemsCategories,
'itemCategory'=>$itemCategory,
'form'=>$form,
));
}
//ToDo set form to XHR for errors
#[Route('/category-{id}', name: 'bo_item_category_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, ItemCategory $itemCategory): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$form = $this->itemCategoryRepository->getForm('bo_item_category_edit', $itemCategory);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$this->_em->flush();
return $this->redirectToRoute('bo_item_category_index', array(), Response::HTTP_SEE_OTHER);
}
$itemsCategories=$this->itemCategoryRepository->findBy(array(), array('sortOrder'=>'ASC'));
return $this->renderForm('_dashboard/item_category/edit.html.twig', [
'itemsCategories'=>$itemsCategories,
'itemCategory'=>$itemCategory,
'form'=>$form,
]);
}
#[Route('/delete-{id}', name: 'bo_item_category_delete', methods: ['POST'])]
public function delete(Request $request, ItemCategory $itemCategory): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
if($this->isCsrfTokenValid('delete'.$itemCategory->getId(), $request->request->get('_token'))) {
$this->_em->remove($itemCategory);
$this->_em->flush();
}
return $this->redirectToRoute('bo_item_category_index', array(), Response::HTTP_SEE_OTHER);
}
}

View File

@ -0,0 +1,239 @@
<?php
namespace App\Controller;
use App\Entity\Item;
use App\Entity\ItemCategory;
use App\Repository\ItemCategoryRepository;
use App\Repository\ItemRepository;
use App\Repository\WorldmarkRepository;
use App\Service\FileManager;
use Doctrine\ORM\EntityManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/dashboard/items-categories/category-{itemCategoryId}/items')]
#[ParamConverter(data: 'itemCategory', class: ItemCategory::class, options: ['id' => 'itemCategoryId'])]
class ItemController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private FileManager $fileManager,
private ItemCategoryRepository $itemCategoryRepository,
private ItemRepository $itemRepository) {}
#[Route('', name: 'bo_item_index', methods: ['GET'])]
public function index(ItemCategory $itemCategory): Response {
$itemsCategories=$this->itemCategoryRepository->findBy(array(), array('sortOrder'=>'ASC'));
$items=$this->itemRepository->findBy(array('category'=>$itemCategory), array('name'=>'ASC'));
return $this->render('_dashboard/item/index.html.twig', array(
'itemsCategories'=>$itemsCategories,
'itemCategory'=>$itemCategory,
'items'=>$items,
));
}
#[Route('/new', name: 'bo_item_new', methods: ['GET', 'POST'])]
public function new(Request $request, ItemCategory $itemCategory): Response {
$this->denyAccessUnlessGranted('ROLE_SENIOR');
if($request->isMethod('POST') && !$request->isXmlHttpRequest()) {
throw new NotFoundHttpException("Page not found");
}
$item=new Item();
$item->setCategory($itemCategory);
$form=$this->itemRepository->getForm('bo_item_new', $itemCategory, $item);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
if($item->getIcon() instanceof UploadedFile) {
$uploadedFile=$this->fileManager->uploadItemIcon($item->getIcon());
if($uploadedFile['error']) {
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $uploadedFile['message'],
),
),
),
));
}
$item->setIcon($uploadedFile['filename']);
}
$this->_em->persist($item);
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name' => 'redirect',
'params' => array(
'url' => $this->generateUrl('bo_item_index', array('itemCategoryId' => $itemCategory->getId())),
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
$itemsCategories=$this->itemCategoryRepository->findBy(array(), array('sortOrder'=>'ASC'));
return $this->renderForm('_dashboard/item/new.html.twig', array(
'itemsCategories'=>$itemsCategories,
'itemCategory'=>$itemCategory,
'item'=>$item,
'form'=>$form,
));
}
#[Route('/item-{id}', name: 'bo_item_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, WorldmarkRepository $worldmarkRepository, ItemCategory $itemCategory, Item $item): Response {
$this->denyAccessUnlessGranted('ROLE_SENIOR');
if($request->isMethod('POST') && !$request->isXmlHttpRequest()) {
throw new NotFoundHttpException("Page not found");
}
$icon=$item->getIcon();
$form=$this->itemRepository->getForm('bo_item_edit', $itemCategory, $item);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
if(!$form->get('removeFile')->getData()) {
if($item->getIcon() instanceof UploadedFile) {
$uploadedFile=$this->fileManager->uploadItemIcon($item->getIcon());
if($uploadedFile['error']) {
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $uploadedFile['message'],
),
),
),
));
}
if($icon) {
$this->fileManager->removeItemIcon($icon);
}
$item->setIcon($uploadedFile['filename']);
$worldmark = $worldmarkRepository->findOneBy(array('item'=>$item));
if($worldmark) {
$uploadedFile=$this->fileManager->getWorldmarkIconFromEntity($item);
if($uploadedFile['error']) {
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $uploadedFile['message'],
),
),
),
));
}
if($worldmark->getIcon()) {
$this->fileManager->removeWorldmarkIcon($icon);
}
$worldmark->setIcon($uploadedFile['filename']);
}
} else {
$item->setIcon($icon);
}
} else {
$this->fileManager->removeItemIcon($icon);
$item->setIcon(null);
}
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name' => 'redirect',
'params' => array(
'url' => $this->generateUrl('bo_item_index', array('itemCategoryId' => $itemCategory->getId())),
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
$itemsCategories=$this->itemCategoryRepository->findBy(array(), array('sortOrder'=>'ASC'));
return $this->renderForm('_dashboard/item/edit.html.twig', [
'itemsCategories'=>$itemsCategories,
'itemCategory'=>$itemCategory,
'item'=>$item,
'form'=>$form,
]);
}
#[Route('/delete-{id}', name: 'bo_item_delete', methods: ['POST'])]
public function delete(Request $request, ItemCategory $itemCategory, Item $item): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_SENIOR');
if($this->isCsrfTokenValid('delete'.$item->getId(), $request->request->get('_token'))) {
if($item->getIcon()) {
$this->fileManager->removeItemIcon($item->getIcon());
}
$this->_em->remove($item);
$this->_em->flush();
}
return $this->redirectToRoute('bo_item_index', array('itemCategoryId'=>$itemCategory->getId()), Response::HTTP_SEE_OTHER);
}
}

View File

@ -0,0 +1,198 @@
<?php
namespace App\Controller;
use App\Entity\Grid;
use App\Entity\Map;
use App\Repository\MapRepository;
use App\Service\FileManager;
use Doctrine\ORM\EntityManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/dashboard/grid/cell-{cellId}/maps')]
#[ParamConverter(data: 'cell', class: Grid::class, options: ['id' => 'cellId'])]
class MapController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private FileManager $fileManager,
private MapRepository $mapRepository) {}
#[Route('/new', name: 'bo_map_new', methods: ['POST'])]
public function new(Request $request, Grid $cell): JsonResponse {
if($request->isXmlHttpRequest()) {
$map = new Map();
$map->setGrid($cell);
$form = $this->mapRepository->getForm('bo_map_new', $cell, $map);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
if($map->getFile() instanceof UploadedFile) {
$uploadedFile = $this->fileManager->uploadMapFile($map->getFile());
if($uploadedFile['error']) {
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $uploadedFile['message'],
),
),
),
));
}
$map->setFile($uploadedFile['filename']);
$this->_em->persist($map);
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'success',
'message' => 'Map updated',
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'No file found or uploaded',
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'No form submitted',
),
),
),
));
}
throw $this->createNotFoundException();
}
#[Route('/map-{id}', name: 'bo_map_edit', methods: ['POST'])]
public function edit(Request $request, Grid $cell, Map $map): JsonResponse {
if($request->isXmlHttpRequest()) {
$file = $map->getFile();
$form = $this->mapRepository->getForm('bo_map_edit', $cell, $map);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
if($map->getFile() instanceof UploadedFile) {
$uploadedFile=$this->fileManager->uploadMapFile($map->getFile());
if($uploadedFile['error']) {
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $uploadedFile['message'],
),
),
),
));
}
$map->setFile($uploadedFile['filename']);
$this->fileManager->removeMapFile($file);
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'success',
'message' => 'Map updated',
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'No file found or uploaded',
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'No form submitted',
),
),
),
));
}
throw $this->createNotFoundException();
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\Controller;
use App\Entity\MonsterCategory;
use App\Repository\MonsterCategoryRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/dashboard/monsters-categories')]
class MonsterCategoryController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private MonsterCategoryRepository $monsterCategoryRepository) {}
#[Route('', name: 'bo_monster_category_index', methods: ['GET'])]
public function index(): Response {
$monstersCategories = $this->monsterCategoryRepository->findBy(array(), array('name' => 'ASC'));
return $this->render('_dashboard/monster_category/index.html.twig', array(
'monstersCategories' => $monstersCategories,
));
}
#[Route('/new', name: 'bo_monster_category_new', methods: ['GET', 'POST'])]
public function new(Request $request): RedirectResponse|Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$monstersCategories = $this->monsterCategoryRepository->findBy(array(), array('name' => 'ASC'));
$monsterCategory = new MonsterCategory();
$form = $this->monsterCategoryRepository->getForm('bo_monster_category_new', $monsterCategory);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$this->_em->persist($monsterCategory);
$this->_em->flush();
return $this->redirectToRoute('bo_monster_category_index', array(), Response::HTTP_SEE_OTHER);
}
return $this->renderForm('_dashboard/monster_category/new.html.twig', array(
'monstersCategories' => $monstersCategories,
'monsterCategory' => $monsterCategory,
'form' => $form,
));
}
#[Route('/category-{id}', name: 'bo_monster_category_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, MonsterCategory $monsterCategory): RedirectResponse|Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$monstersCategories = $this->monsterCategoryRepository->findBy(array(), array('name' => 'ASC'));
$form = $this->monsterCategoryRepository->getForm('bo_monster_category_edit', $monsterCategory);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$this->_em->flush();
return $this->redirectToRoute('bo_monster_category_index', array(), Response::HTTP_SEE_OTHER);
}
return $this->renderForm('_dashboard/monster_category/edit.html.twig', array(
'monstersCategories' => $monstersCategories,
'monsterCategory' => $monsterCategory,
'form' => $form,
));
}
#[Route('/delete-{id}', name: 'bo_monster_category_delete', methods: ['POST'])]
public function delete(Request $request, MonsterCategory $monsterCategory): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
if($this->isCsrfTokenValid('delete'.$monsterCategory->getId(), $request->request->get('_token'))) {
$this->_em->remove($monsterCategory);
$this->_em->flush();
}
return $this->redirectToRoute('bo_monster_category_index', [], Response::HTTP_SEE_OTHER);
}
}

View File

@ -0,0 +1,214 @@
<?php
namespace App\Controller;
use App\Entity\Monster;
use App\Entity\MonsterCategory;
use App\Repository\MonsterCategoryRepository;
use App\Repository\MonsterRepository;
use App\Service\FileManager;
use Doctrine\ORM\EntityManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/dashboard/monsters-categories/category-{monsterCategoryId}/monsters')]
#[ParamConverter(data: 'monsterCategory', class: MonsterCategory::class, options: ['id' => 'monsterCategoryId'])]
class MonsterController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private FileManager $fileManager,
private MonsterCategoryRepository $monsterCategoryRepository,
private MonsterRepository $monsterRepository) {}
#[Route('', name: 'bo_monster_index', methods: ['GET'])]
public function index(MonsterCategory $monsterCategory): Response {
$monstersCategories = $this->monsterCategoryRepository->findBy(array(), array('name' => 'ASC'));
$monsters = $this->monsterRepository->findBy(array('category' => $monsterCategory), array('name' => 'ASC'));
return $this->render('_dashboard/monster/index.html.twig', array(
'monstersCategories' => $monstersCategories,
'monsterCategory' => $monsterCategory,
'monsters' => $monsters,
));
}
#[Route('/new', name: 'bo_monster_new', methods: ['GET', 'POST'])]
public function new(Request $request, MonsterCategory $monsterCategory): JsonResponse|Response {
$this->denyAccessUnlessGranted('ROLE_SENIOR');
if($request->isMethod('POST') && !$request->isXmlHttpRequest()) {
throw new NotFoundHttpException("Page not found");
}
$monstersCategories = $this->monsterCategoryRepository->findBy(array(), array('name' => 'ASC'));
$monster = new Monster();
$monster->setCategory($monsterCategory);
$form = $this->monsterRepository->getForm('bo_monster_new', $monsterCategory, $monster);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
if($monster->getIcon() instanceof UploadedFile) {
$uploadedFile = $this->fileManager->uploadMonsterIcon($monster->getIcon());
if($uploadedFile['error']) {
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $uploadedFile['message'],
),
),
),
));
}
$monster->setIcon($uploadedFile['filename']);
}
$this->_em->persist($monster);
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name' => 'redirect',
'params' => array(
'url' => $this->generateUrl('bo_monster_index', array('monsterCategoryId' => $monsterCategory->getId())),
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
return $this->renderForm('_dashboard/monster/new.html.twig', array(
'monstersCategories' => $monstersCategories,
'monsterCategory' => $monsterCategory,
'monster' => $monster,
'form' => $form,
));
}
#[Route('/monster-{id}', name: 'bo_monster_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, MonsterCategory $monsterCategory, Monster $monster): JsonResponse|Response {
$this->denyAccessUnlessGranted('ROLE_SENIOR');
if($request->isMethod('POST') && !$request->isXmlHttpRequest()) {
throw new NotFoundHttpException("Page not found");
}
$monstersCategories = $this->monsterCategoryRepository->findBy(array(), array('name' => 'ASC'));
$icon = $monster->getIcon();
$form = $this->monsterRepository->getForm('bo_monster_edit', $monsterCategory, $monster);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
if(!$form->get('removeFile')->getData()) {
if($monster->getIcon() instanceof UploadedFile) {
$uploadedFile = $this->fileManager->uploadMonsterIcon($monster->getIcon());
if($uploadedFile['error']) {
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $uploadedFile['message'],
),
),
),
));
}
if($icon) {
$this->fileManager->removeMonsterIcon($icon);
}
$monster->setIcon($uploadedFile['filename']);
} else {
$monster->setIcon($icon);
}
} else {
$this->fileManager->removeMonsterIcon($icon);
$monster->setIcon(null);
}
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name' => 'redirect',
'params' => array(
'url' => $this->generateUrl('bo_monster_index', array('monsterCategoryId' => $monsterCategory->getId())),
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
return $this->renderForm('_dashboard/monster/edit.html.twig', array(
'monstersCategories' => $monstersCategories,
'monsterCategory' => $monsterCategory,
'monster' => $monster,
'form' => $form,
));
}
#[Route('/delete-{id}', name: 'bo_monster_delete', methods: ['POST'])]
public function delete(Request $request, MonsterCategory $monsterCategory, Monster $monster): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_SENIOR');
if($this->isCsrfTokenValid('delete'.$monster->getId(), $request->request->get('_token'))) {
if($monster->getIcon()) {
$this->fileManager->removeMonsterIcon($monster->getIcon());
}
$this->_em->remove($monster);
$this->_em->flush();
}
return $this->redirectToRoute('bo_monster_index', array('monsterCategoryId' => $monsterCategory->getId()), Response::HTTP_SEE_OTHER);
}
}

View File

@ -0,0 +1,426 @@
<?php
namespace App\Controller;
use App\Entity\Grid;
use App\Entity\Node;
use App\Entity\Worldmark;
use App\Repository\NodeRepository;
use App\Service\FileManager;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/dashboard/grid-{gridId}/worldmark-{worldmarkId}/nodes')]
#[ParamConverter(data: 'grid', class: Grid::class, options: ['id' => 'gridId'])]
#[ParamConverter(data: 'worldmark', class: Worldmark::class, options: ['id' => 'worldmarkId'])]
class NodeController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private FileManager $fileManager,
private NodeRepository $nodeRepository) {}
#[Route('/new', name: 'bo_node_new', methods: ['POST'])]
public function new(Request $request, Grid $grid, Worldmark $worldmark): JsonResponse {
if($request->isXmlHttpRequest()) {
if($this->isGranted('ROLE_CONTRIBUTOR')) {
$node = new Node();
$node->setGrid($grid)
->setWorldmark($worldmark);
$form = $this->nodeRepository->getForm('bo_node_new', $grid, $worldmark, $node);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
$errors = array();
$uploadedFiles = array();
$screenshotSetMethods = array('setScreenshotA', 'setScreenshotB', 'setScreenshotC', 'setScreenshotD', 'setScreenshotE');
$screenshotGetMethods = array('getScreenshotA', 'getScreenshotB', 'getScreenshotC', 'getScreenshotD', 'getScreenshotE');
foreach($screenshotSetMethods as $key => $method) {
if(is_callable(array($node, $method)) && is_callable(array($node, $screenshotGetMethods[$key]))) {
$uploadedFiles[$method] = ($node->{$screenshotGetMethods[$key]}() instanceof UploadedFile)
? $this->fileManager->uploadScreenshot($node->{$screenshotGetMethods[$key]}())
: null;
}
}
foreach($uploadedFiles as $method => $uploadedFile) {
if($uploadedFile) {
if($uploadedFile['error']) {
$errors[] = $uploadedFile['message'];
} elseif(is_callable(array($node, $method))) {
$node->$method($uploadedFile['filename']);
}
}
}
if(count($errors)) {
$messages = array();
foreach($screenshotGetMethods as $method) {
if(is_callable(array($node, $method)) && is_string($node->$method())) {
$this->fileManager->removeScreenshot($node->$method());
}
}
foreach($errors as $error) {
$messages[] = array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $error,
),
);
}
return $this->json(array('::function' => $messages));
}
$this->_em->persist($node);
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name'=>'paq_event',
'params' => array(
'type' => 'trackEvent',
'name' => "Contribution: Nodes",
'value' => "Nodes created by {$this->getUser()->getUserIdentifier()}",
),
),
array(
'name' => 'paq_event',
'params' => array(
'type' => 'trackEvent',
'name' => "Contribution: {$node->getWorldmark()->getCategory()->getName()} nodes category",
'value' => "Nodes created by {$this->getUser()->getUserIdentifier()}",
),
),
array(
'name' => 'paq_event',
'params' => array(
'type' => 'trackEvent',
'name' => "Contribution: {$node->getGrid()->getRegion()->getName()} nodes log",
'value' => "[{$node->getModifiedAt()->format('Y-m-d H:i:s')}] Node {$node->getId()} was created by {$this->getUser()->getUserIdentifier()}",
),
),
array(
'name' => 'node_validateForm',
'params' => array(
'gridId' => $grid->getId(),
'htmlString' => $this->renderView('woldmap/_node.html.twig', array(
'grid' => $grid,
'worldmark' => $worldmark,
'node' => $node,
'directRender'=>true,
)),
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'node_loadForm',
'params' => array(
'gridId' => $grid->getId(),
'coordinate' => array('x' => '50', 'y' => '50'),
'htmlString' => $this->renderView('_dashboard/node/_form.html.twig', array(
'grid' => $grid,
'worldmark' => $worldmark,
'node' => $node,
'form' => $form->createView(),
)),
'nodeId' => null,
),
),
),
));
}
return $this->json(array('error' => 'Not found'), 404);
}
throw $this->createNotFoundException();
}
#[Route('/node-{id}', name: 'bo_node_edit', methods: ['POST'])]
public function edit(Request $request, Grid $grid, Worldmark $worldmark, Node $node): JsonResponse {
if($request->isXmlHttpRequest()) {
if($this->isGranted('ROLE_CONTRIBUTOR')) {
$screenshots = array(
'ScreenshotA' => $node->getScreenshotA(),
'ScreenshotB' => $node->getScreenshotB(),
'ScreenshotC' => $node->getScreenshotC(),
'ScreenshotD' => $node->getScreenshotD(),
'ScreenshotE' => $node->getScreenshotE(),
);
$form = $this->nodeRepository->getForm('bo_node_edit', $grid, $worldmark, $node);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
$errors = array();
$uploadedFiles = array();
$screenshotSetMethods = array('setScreenshotA', 'setScreenshotB', 'setScreenshotC', 'setScreenshotD', 'setScreenshotE');
$screenshotGetMethods = array('getScreenshotA', 'getScreenshotB', 'getScreenshotC', 'getScreenshotD', 'getScreenshotE');
foreach($screenshotSetMethods as $key => $method) {
if(is_callable(array($node, $method)) && is_callable(array($node, $screenshotGetMethods[$key]))) {
$uploadedFiles[$method] = ($node->{$screenshotGetMethods[$key]}() instanceof UploadedFile)
? $this->fileManager->uploadScreenshot($node->{$screenshotGetMethods[$key]}())
: null;
}
}
foreach($uploadedFiles as $method => $uploadedFile) {
if($uploadedFile) {
if($uploadedFile['error']) {
$errors[] = $uploadedFile['message'];
} elseif(is_callable(array($node, $method))) {
$node->$method($uploadedFile['filename']);
}
}
}
if(count($errors)) {
$messages = array();
foreach($screenshotGetMethods as $method) {
if(is_callable(array($node, $method)) && is_string($node->$method())) {
$this->fileManager->removeScreenshot($node->$method());
}
}
foreach($errors as $error) {
$messages[] = array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $error,
),
);
}
return $this->json(array('::function' => $messages));
}
foreach($screenshotGetMethods as $key => $method) {
if(is_callable(array($node, $method))
&& is_callable(array($node, $screenshotSetMethods[$key]))
&& $node->$method() === null) {
$node->{$screenshotSetMethods[$key]}($screenshots[substr($method, 3)]);
}
}
$this->_em->persist($node);
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name' => 'paq_event',
'params' => array(
'type' => 'trackEvent',
'name' => "Contribution: Nodes",
'value' => "Nodes edited by {$this->getUser()->getUserIdentifier()}",
),
),
array(
'name'=>'paq_event',
'params' => array(
'type' => 'trackEvent',
'name' => "Contribution: {$node->getWorldmark()->getCategory()->getName()} nodes category",
'value' => "Nodes edited by {$this->getUser()->getUserIdentifier()}",
),
),
array(
'name' => 'paq_event',
'params' => array(
'type' => 'trackEvent',
'name' => "Contribution: {$node->getGrid()->getRegion()->getName()} nodes log",
'value' => "[{$node->getModifiedAt()->format('Y-m-d H:i:s')}] Node {$node->getId()} was edited by {$this->getUser()->getUserIdentifier()}",
),
),
array(
'name' => 'node_validateForm',
'params' => array(
'gridId' => $grid->getId(),
'htmlString' => $this->renderView('woldmap/_node.html.twig', array(
'grid' => $grid,
'worldmark' => $worldmark,
'node' => $node,
'directRender'=>true,
)),
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'node_loadForm',
'params' => array(
'gridId' => $grid->getId(),
'coordinate' => array('x' => '50', 'y' => '50'),
'htmlString' => $this->renderView('_dashboard/node/_form.html.twig', array(
'grid' => $grid,
'worldmark' => $worldmark,
'node' => $node,
'form' => $form->createView(),
)),
'nodeId' => $node->getId(),
),
),
),
));
}
return $this->json(array('error' => 'Not found'), 404);
}
throw $this->createNotFoundException();
}
#[Route('/delete-{id}', name: 'bo_node_delete', methods: ['POST'])]
public function delete(Request $request, Grid $grid, Worldmark $worldmark, Node $node): JsonResponse {
if($request->isXmlHttpRequest()) {
if($this->isGranted('ROLE_CONTRIBUTOR')) {
$form = $this->nodeRepository->getForm('bo_node_delete', $grid, $worldmark, $node);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
$nodeId = $node->getId();
if($this->isGranted('ROLE_ADMIN')) {
$screenshotGetMethods = array('getScreenshotA', 'getScreenshotB', 'getScreenshotC', 'getScreenshotD', 'getScreenshotE');
foreach($screenshotGetMethods as $method) {
if(is_callable(array($node, $method)) && $node->$method() !== null) {
$this->fileManager->removeScreenshot($node->$method());
}
}
$this->_em->remove($node);
}
$this->_em->flush();
$deleteDate=new DateTime();
return $this->json(array(
'::function' => array(
array(
'name'=>'paq_event',
'params' => array(
'type' => 'trackEvent',
'name' => "Contribution: Nodes",
'value' => "Nodes deleted by {$this->getUser()->getUserIdentifier()}",
),
),
array(
'name' => 'paq_event',
'params' => array(
'type' => 'trackEvent',
'name' => "Contribution: {$node->getWorldmark()->getCategory()->getName()} nodes category",
'value' => "Nodes deleted by {$this->getUser()->getUserIdentifier()}",
),
),
array(
'name' => 'paq_event',
'params' => array(
'type' => 'trackEvent',
'name' => "Contribution: {$node->getGrid()->getRegion()->getName()} nodes log",
'value' => "[{$deleteDate->format('Y-m-d H:i:s')}] Node {$node->getId()} was deleted by {$this->getUser()->getUserIdentifier()}",
),
),
array(
'name' => 'node_removeElement',
'params' => array(
'gridId' => $grid->getId(),
'nodeId' => $nodeId,
),
),
array(
'name' => 'modal_unload',
'params' => null,
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'modal_load',
'params' => array(
'htmlString' => $this->renderView('_dashboard/node/_delete_form.html.twig', array(
'grid' => $grid,
'worldmark' => $worldmark,
'node' => $node,
'form' => $form->createView(),
)),
),
),
),
));
}
return $this->json(array('error' => 'Not found'), 404);
}
throw $this->createNotFoundException();
}
}

View File

@ -0,0 +1,179 @@
<?php
namespace App\Controller;
use App\Entity\Grid;
use App\Entity\Region;
use App\Kernel;
use App\Repository\GridRepository;
use App\Repository\MapRepository;
use App\Repository\NodeRepository;
use App\Repository\RegionRepository;
use App\Repository\WorldmarkCategoryRepository;
use App\Repository\WorldmarkRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class RegionController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private RegionRepository $regionRepository,
private GridRepository $gridRepository,
private MapRepository $mapRepository,
private WorldmarkCategoryRepository $worldmarkCategoryRepository,
private WorldmarkRepository $worldmarkRepository,
private NodeRepository $nodeRepository) {}
#[Route('/worldmap', name: 'fo_worldmap_redirect_a', methods: ['GET'])]
public function redirectA(): RedirectResponse {
return $this->redirectToRoute('fo_region_show', array('slug'=>'mondstadt'), Response::HTTP_MOVED_PERMANENTLY);
}
#[Route('/worldmap/{slug}', name: 'fo_worldmap_redirect_b', methods: ['GET'])]
public function redirectB(Region $region): RedirectResponse {
return $this->redirectToRoute('fo_region_show', array('slug'=>$region->getSlug()), Response::HTTP_MOVED_PERMANENTLY);
}
#[Route('/{slug}', name: 'fo_region_show', methods: ['GET'])]
public function show(Request $request, ?Region $region): Response {
$version = $request->query->get('v');
if(!$version
|| !in_array(floatval($version), Kernel::SUPPORTED_GAME_VERSION)
|| (floatval($version) < Kernel::GAME_VERSION && !$this->isGranted('ROLE_ADMIN'))
|| (floatval($version) > Kernel::GAME_VERSION && !$this->isGranted('ROLE_CONTRIBUTOR'))) {
$version = Kernel::GAME_VERSION;
} else {
$version = floatval($version);
}
if(!$region
|| $region->getVersion() > $version
|| !$region->getIsActive() && !$this->isGranted('ROLE_CONTRIBUTOR')) {
throw $this->createNotFoundException();
}
$_region = $region->getIsAlias() ? $region->getParentRegion() : $region;
$cells = $this->gridRepository->getGridCells($_region);
$maps = $this->mapRepository->getCellsMap($cells, $version);
$grid = $this->gridRepository->buildWorldmap($version, $cells, $maps, true);
$worldmarks = $this->worldmarkRepository->getRegionWorldmarks($_region, $version);
$nodes = $this->nodeRepository->getGridNodes($cells, $worldmarks, $version, $this->isGranted('ROLE_ADMIN'));
$worldmarksData = array();
foreach($worldmarks as $worldmark) {
if(!array_key_exists($worldmark->getCategory()->getSlug(), $worldmarksData)) {
$worldmarksData[$worldmark->getCategory()->getSlug()]['_data'] = $worldmark->getCategory();
$worldmarksData[$worldmark->getCategory()->getSlug()]['_worldmarks'] = array();
}
$worldmarksData[$worldmark->getCategory()->getSlug()]['_worldmarks'][] = $worldmark;
}
$nodesData = array();
foreach($nodes as $node) {
$nodesData["grid_{$node->getGrid()->getId()}"][] = $node;
}
return $this->render('woldmap/show.html.twig', array(
// 'title' => "Genshin Impact interactive map of {$_region->getName()}",
'title' => "{$_region->getName()} interactive map - Genshin Impact - Genshin World",
'region' => $_region,
'regionSlug' => $region->getSlug(),
'version' => $version,
'grid' => $grid,
'worldmarksData' => $worldmarksData,
'nodes' => $nodesData,
'anchor' => $region->getIsAlias() ? $region->getAnchor() : null,
'filter'=>$request->get('filter'),
));
}
#[Route('/dashboard/regions', name: 'bo_region_index', methods: ['GET'])]
public function index(): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
return $this->render('_dashboard/region/index.html.twig', array(
'title' => "Dashboard: Regions index",
));
}
#[Route('/dashboard/regions/new', name: 'bo_region_new', methods: ['GET', 'POST'])]
public function new(Request $request): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$region = new Region();
$form = $this->regionRepository->getForm('bo_region_new', $region);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$this->_em->persist($region);
if($region->getIsAlias()) {
$region->setGridHeight(0);
$region->setGridWidth(0);
}
if($region->getGridHeight() > 0 && $region->getGridWidth() > 0) {
for($row = 1; $row <= $region->getGridHeight(); $row++) {
for($col = 1; $col <= $region->getGridWidth(); $col++) {
$grid = new Grid();
$grid->setRegion($region)
->setRow($row)
->setCol($col);
$this->_em->persist($grid);
}
}
}
$this->_em->flush();
return $this->redirectToRoute('bo_region_index', array(), Response::HTTP_SEE_OTHER);
}
return $this->renderForm('_dashboard/region/new.html.twig', array(
'title' => "Dashboard: Create region",
'region' => $region,
'form' => $form,
));
}
#[Route('/dashboard/regions/region-{id}', name: 'bo_region_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Region $region): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$form = $this->regionRepository->getForm('bo_region_edit', $region);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$this->_em->flush();
return $this->redirectToRoute('bo_region_index', array(), Response::HTTP_SEE_OTHER);
}
return $this->renderForm('_dashboard/region/edit.html.twig', array(
'title' => "Dashboard: Edit region",
'region' => $region,
'form' => $form,
));
}
#[Route('/dashboard/regions/delete-{id}', name: 'bo_region_delete', methods: ['POST'])]
public function delete(Request $request, Region $region): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
if($this->isCsrfTokenValid('delete'.$region->getId(), $request->request->get('_token'))) {
$this->_em->remove($region);
$this->_em->flush();
}
return $this->redirectToRoute('bo_region_index', array(), Response::HTTP_SEE_OTHER);
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace App\Controller;
use App\Entity\User;
use App\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\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController {
public function __construct(
private readonly EntityManagerInterface $_em,
private readonly UserRepository $userRepository,
private readonly UserPasswordHasherInterface $userPasswordHasher) {
}
// #[Route('/035c7785-2c2f-11ec-aaf0-ac1f6b44f230/register', name: 'security_register')]
public function register(Request $request): Response {
$user = new User();
$form = $this->userRepository->getForm('security_register', $user);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
// encode the plain password
$user->setPassword(
$this->userPasswordHasher->hashPassword(
$user,
$form->get('plainPassword')->getData()
)
);
$this->_em->persist($user);
$this->_em->flush();
// do anything else you need here, like send an email
return $this->redirectToRoute('security_login', array(), Response::HTTP_SEE_OTHER);
}
return $this->renderForm('security/register.html.twig', array(
'form' => $form,
));
}
#[Route(path: '/035c7785-2c2f-11ec-aaf0-ac1f6b44f230/login', name: 'security_login')]
public function login(AuthenticationUtils $authenticationUtils): Response {
// if ($this->getUser()) {
// return $this->redirectToRoute('target_path');
// }
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', array(
'last_username' => $lastUsername,
'error' => $error,
));
}
#[Route(path: '/035c7785-2c2f-11ec-aaf0-ac1f6b44f230/logout', name: 'security_logout')]
public function logout(): void {
// throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace App\Controller;
use App\Entity\WorldmarkCategory;
use App\Repository\WorldmarkCategoryRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/dashboard/worldmarks-categories')]
class WorldmarkCategoryController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private WorldmarkCategoryRepository $worldmarkCategoryRepository) {}
#[Route('', name: 'bo_worldmark_category_index', methods: ['GET'])]
public function index(): Response {
return $this->render('_dashboard/worldmark_category/index.html.twig', array(
'worldmarksCategories' => $this->worldmarkCategoryRepository->findBy(array(), array('sortOrder' => 'ASC'))
));
}
//ToDo set foirm to XHR for errors
#[Route('/new', name: 'bo_worldmark_category_new', methods: ['GET', 'POST'])]
public function new(Request $request): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$category=new WorldmarkCategory();
$form=$this->worldmarkCategoryRepository->getForm('bo_worldmark_category_new', $category);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$this->_em->persist($category);
$this->_em->flush();
return $this->redirectToRoute('bo_worldmark_category_index', array(), Response::HTTP_SEE_OTHER);
}
return $this->renderForm('_dashboard/worldmark_category/new.html.twig', array(
'worldmarksCategories' => $this->worldmarkCategoryRepository->findBy(array(), array('sortOrder' => 'ASC')),
'worldmarkCategory'=>$category,
'form'=>$form,
));
}
//ToDo set foirm to XHR for errors
#[Route('/category-{id}', name: 'bo_worldmark_category_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, WorldmarkCategory $worldmarkCategory): Response {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$form=$this->worldmarkCategoryRepository->getForm('bo_worldmark_category_edit', $worldmarkCategory);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$this->_em->flush();
return $this->redirectToRoute('bo_worldmark_category_index', array(), Response::HTTP_SEE_OTHER);
}
return $this->renderForm('_dashboard/worldmark_category/edit.html.twig', array(
'worldmarksCategories' => $this->worldmarkCategoryRepository->findBy(array(), array('sortOrder' => 'ASC')),
'worldmarkCategory'=>$worldmarkCategory,
'form'=>$form,
));
}
#[Route('/delete-{id}', name: 'bo_worldmark_category_delete', methods: ['POST'])]
public function delete(Request $request, WorldmarkCategory $worldmarkCategory): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
if($this->isCsrfTokenValid('delete'.$worldmarkCategory->getId(), $request->request->get('_token'))) {
$this->_em->remove($worldmarkCategory);
$this->_em->flush();
}
return $this->redirectToRoute('bo_worldmark_category_index', array(), Response::HTTP_SEE_OTHER);
}
}

View File

@ -0,0 +1,240 @@
<?php
namespace App\Controller;
use App\Entity\Worldmark;
use App\Entity\WorldmarkCategory;
use App\Repository\ItemCategoryRepository;
use App\Repository\MonsterCategoryRepository;
use App\Repository\WorldmarkCategoryRepository;
use App\Repository\WorldmarkRepository;
use App\Service\FileManager;
use Doctrine\ORM\EntityManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/dashboard/worldmarks-categories/category-{worldmarkCategoryId}/worldmarks')]
#[ParamConverter(data: 'worldmarkCategory', class: WorldmarkCategory::class, options: ['id' => 'worldmarkCategoryId'])]
class WorldmarkController extends AbstractController {
public function __construct(private EntityManagerInterface $_em,
private FileManager $fileManager,
private WorldmarkCategoryRepository $worldmarkCategoryRepository,
private ItemCategoryRepository $itemCategoryRepository,
private MonsterCategoryRepository $monsterCategoryRepository,
private WorldmarkRepository $worldmarkRepository) {}
#[Route('', name: 'bo_worldmark_index', methods: ['GET'])]
public function index(WorldmarkCategory $worldmarkCategory): Response {
$worldmarksCategories = $this->worldmarkCategoryRepository->findBy(array(), array('sortOrder' => 'ASC'));
$worldmarks = $this->worldmarkRepository->findBy(array('category' => $worldmarkCategory), array('sortOrder' => 'ASC'));
return $this->render('_dashboard/worldmark/index.html.twig', array(
'worldmarksCategories' => $worldmarksCategories,
'worldmarkCategory' => $worldmarkCategory,
'worldmarks' => $worldmarks,
));
}
#[Route('/new', name: 'bo_worldmark_new', methods: ['GET', 'POST'])]
public function new(Request $request, WorldmarkCategory $worldmarkCategory): JsonResponse|Response {
$this->denyAccessUnlessGranted('ROLE_SENIOR');
if($request->isMethod('POST') && !$request->isXmlHttpRequest()) {
throw new NotFoundHttpException("Page not found");
}
$worldmark = new Worldmark();
$worldmark->setCategory($worldmarkCategory);
$form = $this->worldmarkRepository->getForm('bo_worldmark_new', $worldmarkCategory, $worldmark);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
if($worldmark->getItem() || $worldmark->getMonster() || $worldmark->getIcon() instanceof UploadedFile) {
if($worldmark->getItem()) {
$uploadedFile = $this->fileManager->getWorldmarkIconFromEntity($worldmark->getItem());
} elseif($worldmark->getMonster()) {
$uploadedFile = $this->fileManager->getWorldmarkIconFromEntity($worldmark->getMonster());
} elseif($worldmark->getIcon() instanceof UploadedFile) {
$uploadedFile = $this->fileManager->uploadWorldmarkIcon($worldmark->getIcon());
} else {
$uploadedFile = array(
'error' => true,
'filename' => null,
'message' => 'An internal error occurred',
);
}
if($uploadedFile['error']) {
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $uploadedFile['message'],
),
),
),
));
}
$worldmark->setIcon($uploadedFile['filename']);
}
$this->_em->persist($worldmark);
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name' => 'redirect',
'params' => array(
'url' => $this->generateUrl('bo_worldmark_index', array('worldmarkCategoryId' => $worldmarkCategory->getId())),
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
$worldmarksCategories = $this->worldmarkCategoryRepository->findBy(array(), array('sortOrder' => 'ASC'));
return $this->renderForm('_dashboard/worldmark/new.html.twig', array(
'worldmarksCategories' => $worldmarksCategories,
'worldmarkCategory' => $worldmarkCategory,
'worldmark' => $worldmark,
'form' => $form,
));
}
#[Route('/worldmark-{id}', name: 'bo_worldmark_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, WorldmarkCategory $worldmarkCategory, Worldmark $worldmark): JsonResponse|Response {
$this->denyAccessUnlessGranted('ROLE_SENIOR');
if($request->isMethod('POST') && !$request->isXmlHttpRequest()) {
throw new NotFoundHttpException("Page not found");
}
$icon = $worldmark->getIcon();
$item = $worldmark->getItem();
$monster = $worldmark->getMonster();
$form = $this->worldmarkRepository->getForm('bo_worldmark_edit', $worldmarkCategory, $worldmark);
$form->handleRequest($request);
if($form->isSubmitted()) {
if($form->isValid()) {
if(($worldmark->getItem() && $worldmark->getItem()->getId() !== $item->getId())
|| ($worldmark->getMonster() && $worldmark->getMonster()->getId() !== $monster->getId())
|| $worldmark->getIcon() instanceof UploadedFile) {
if($worldmark->getItem()) {
$uploadedFile = $this->fileManager->getWorldmarkIconFromEntity($worldmark->getItem());
} elseif($worldmark->getMonster()) {
$uploadedFile = $this->fileManager->getWorldmarkIconFromEntity($worldmark->getMonster());
} elseif($worldmark->getIcon() instanceof UploadedFile) {
$uploadedFile = $this->fileManager->uploadWorldmarkIcon($worldmark->getIcon());
} else {
$uploadedFile = array(
'error' => true,
'filename' => null,
'message' => 'An internal error occurred',
);
}
if($uploadedFile['error']) {
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => $uploadedFile['message'],
),
),
),
));
}
if($icon) {
$this->fileManager->removeWorldmarkIcon($icon);
}
$worldmark->setIcon($uploadedFile['filename']);
} else {
$worldmark->setIcon($icon);
}
$this->_em->flush();
return $this->json(array(
'::function' => array(
array(
'name' => 'redirect',
'params' => array(
'url' => $this->generateUrl('bo_worldmark_index', array('worldmarkCategoryId' => $worldmarkCategory->getId())),
),
),
),
));
}
return $this->json(array(
'::function' => array(
array(
'name' => 'messageInfo',
'params' => array(
'type' => 'error',
'message' => 'Invalid form',
),
),
),
));
}
$worldmarksCategories = $this->worldmarkCategoryRepository->findBy(array(), array('sortOrder' => 'ASC'));
return $this->renderForm('_dashboard/worldmark/edit.html.twig', array(
'worldmarksCategories' => $worldmarksCategories,
'worldmarkCategory' => $worldmarkCategory,
'worldmark' => $worldmark,
'form' => $form,
));
}
#[Route('/delete-{id}', name: 'bo_worldmark_delete', methods: ['POST'])]
public function delete(Request $request, WorldmarkCategory $worldmarkCategory, Worldmark $worldmark): RedirectResponse {
$this->denyAccessUnlessGranted('ROLE_SENIOR');
if($this->isCsrfTokenValid('delete'.$worldmark->getId(), $request->request->get('_token'))) {
if($worldmark->getIcon()) {
$this->fileManager->removeWorldmarkIcon($worldmark->getIcon());
}
$this->_em->remove($worldmark);
$this->_em->flush();
}
return $this->redirectToRoute('bo_worldmark_index', array('worldmarkCategoryId' => $worldmarkCategory->getId()), Response::HTTP_SEE_OTHER);
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Doctrine\DBAL\Types;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
class TinyintType extends Type {
private const TINYINT='tinyint';
/**
* @return string
*/
public function getName(): string {
return self::TINYINT;
}
/**
* @param array $column
* @param AbstractPlatform $platform
* @return string
*/
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string {
$declaration="TINYINT(1)";
if(array_key_exists('unsigned', $column) && $column['unsigned'] === true) {
$declaration.=" UNSIGNED";
}
$declaration.=" COMMENT '(DC2Type:tinyint)'";
return $declaration;
}
/**
* @return bool
*/
public function canRequireSQLConversion(): bool {
return false;
}
/**
* @param $value
* @param AbstractPlatform $platform
* @return int|null
*/
public function convertToPHPValue($value, AbstractPlatform $platform): ?int {
return $value === null ? null : (int)$value;
}
/**
* @param $value
* @param AbstractPlatform $platform
* @return int|null
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?int {
return $value === null ? null : (int)$value;
}
/**
* @return int
*/
public function getBindingType(): int {
return ParameterType::INTEGER;
}
}

0
src/Entity/.gitignore vendored Normal file
View File

58
src/Entity/Grid.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace App\Entity;
use App\Repository\GridRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: GridRepository::class)]
class Grid {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private $id;
#[ORM\ManyToOne(targetEntity: Region::class)]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
private $region;
#[ORM\Column(name: '`row`', type: 'tinyint')]
private $row;
#[ORM\Column(type: 'tinyint')]
private $col;
public function getId(): ?int {
return $this->id;
}
public function getRegion(): ?Region {
return $this->region;
}
public function setRegion(?Region $region): self {
$this->region=$region;
return $this;
}
public function getRow() {
return $this->row;
}
public function setRow($row): self {
$this->row=$row;
return $this;
}
public function getCol() {
return $this->col;
}
public function setCol($col): self {
$this->col=$col;
return $this;
}
}

106
src/Entity/Item.php Normal file
View File

@ -0,0 +1,106 @@
<?php
namespace App\Entity;
use App\Repository\ItemRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\HttpFoundation\File\UploadedFile;
#[ORM\Entity(repositoryClass: ItemRepository::class)]
#[ORM\Index(columns: ['slug'])]
#[UniqueEntity(fields: ['slug'], message: 'There is already an item with this slug')]
class Item {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private $id;
#[ORM\ManyToOne(targetEntity: ItemCategory::class)]
#[ORM\JoinColumn(nullable: false)]
private $category;
#[ORM\Column(type: 'string', length: 60)]
private $name;
#[ORM\Column(type: 'string', length: 60, unique: true)]
private $slug;
#[ORM\Column(type: 'text', nullable: true)]
private $description;
#[ORM\Column(type: 'string', length: 50, nullable: true)]
private $icon;
#[ORM\Column(type: 'float')]
private $version;
public function __toString(): string {
return $this->getName();
}
public function getId(): ?int {
return $this->id;
}
public function getCategory(): ItemCategory {
return $this->category;
}
public function setCategory(ItemCategory $category): self {
$this->category = $category;
return $this;
}
public function getName(): ?string {
return $this->name;
}
public function setName(string $name): self {
$this->name = $name;
return $this;
}
public function getSlug(): ?string {
return $this->slug;
}
public function setSlug(string $slug): self {
$this->slug = $slug;
return $this;
}
public function getDescription(): ?string {
return $this->description;
}
public function setDescription(?string $description): self {
$this->description = $description;
return $this;
}
public function getIcon(): string|UploadedFile|null {
return $this->icon;
}
public function setIcon(string|UploadedFile|null $icon): self {
$this->icon = $icon;
return $this;
}
public function getVersion(): ?float {
return $this->version;
}
public function setVersion(float $version): self {
$this->version = $version;
return $this;
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace App\Entity;
use App\Repository\ItemCategoryRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity(repositoryClass: ItemCategoryRepository::class)]
#[ORM\Index(columns: ['slug'])]
#[UniqueEntity(fields: ['name'], message: 'There is already an item category with this name')]
#[UniqueEntity(fields: ['slug'], message: 'There is already an item category with this slug')]
class ItemCategory {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private $id;
#[ORM\Column(type: 'string', length: 50, unique: true)]
private $name;
#[ORM\Column(type: 'string', length: 50, unique: true)]
private $slug;
#[ORM\Column(type: 'smallint', options: ['default' => 65535, 'unsigned' => true])]
private $sortOrder;
public function __toString(): string {
return $this->getName();
}
public function getId(): ?int {
return $this->id;
}
public function getName(): ?string {
return $this->name;
}
public function setName(string $name): self {
$this->name = $name;
return $this;
}
public function getSlug(): ?string {
return $this->slug;
}
public function setSlug(string $slug): self {
$this->slug = $slug;
return $this;
}
public function getSortOrder(): ?int {
return $this->sortOrder;
}
public function setSortOrder(int $sortOrder): self {
$this->sortOrder = $sortOrder;
return $this;
}
}

89
src/Entity/Map.php Normal file
View File

@ -0,0 +1,89 @@
<?php
namespace App\Entity;
use App\Repository\MapRepository;
use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\HttpFoundation\File\UploadedFile;
#[ORM\Entity(repositoryClass: MapRepository::class)]
#[ORM\HasLifecycleCallbacks]
#[UniqueEntity(fields: ['file'], message: 'There is already a file with this name')]
class Map {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private $id;
#[ORM\ManyToOne(targetEntity: Grid::class)]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
private $grid;
#[ORM\Column(type: 'string', length: 50, unique: true)]
private $file;
#[ORM\Column(type: 'datetime')]
private $modifiedAt;
#[ORM\Column(type: 'float')]
private $version;
public function __toString(): string {
return $this->getFile();
}
#[ORM\PrePersist]
#[ORM\PreUpdate]
public function updateLifecycle() {
$this->setModifiedAt(new DateTime());
}
public function getId(): ?int {
return $this->id;
}
public function getGrid(): ?Grid
{
return $this->grid;
}
public function setGrid(?Grid $grid): self
{
$this->grid = $grid;
return $this;
}
public function getFile(): string|UploadedFile|null {
return $this->file;
}
public function setFile(string|UploadedFile|null $file): self {
$this->file=$file;
return $this;
}
public function getModifiedAt(): ?DateTimeInterface {
return $this->modifiedAt;
}
public function setModifiedAt(DateTimeInterface $modifiedAt): self {
$this->modifiedAt=$modifiedAt;
return $this;
}
public function getVersion(): ?float {
return $this->version;
}
public function setVersion(float $version): self {
$this->version=$version;
return $this;
}
}

105
src/Entity/Monster.php Normal file
View File

@ -0,0 +1,105 @@
<?php
namespace App\Entity;
use App\Repository\MonsterRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\HttpFoundation\File\UploadedFile;
#[ORM\Entity(repositoryClass: MonsterRepository::class)]
#[ORM\Index(columns: ['slug'])]
#[UniqueEntity(fields: ['slug'], message: 'There is already a monster with this slug')]
class Monster {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private $id;
#[ORM\ManyToOne(targetEntity: MonsterCategory::class)]
#[ORM\JoinColumn(nullable: false)]
private $category;
#[ORM\Column(type: 'string', length: 60)]
private $name;
#[ORM\Column(type: 'string', length: 60, unique: true)]
private $slug;
#[ORM\Column(type: 'text', nullable: true)]
private $description;
#[ORM\Column(type: 'string', length: 50, nullable: true)]
private $icon;
#[ORM\Column(type: 'float')]
private $version;
public function __toString(): string {
return $this->getName();
}
public function getId(): ?int {
return $this->id;
}
public function getCategory(): MonsterCategory {
return $this->category;
}
public function setCategory(MonsterCategory $category): self {
$this->category=$category;
return $this;
}
public function getName(): ?string {
return $this->name;
}
public function setName(string $name): self {
$this->name=$name;
return $this;
}
public function getSlug(): ?string {
return $this->slug;
}
public function setSlug(string $slug): self {
$this->slug=$slug;
return $this;
}
public function getDescription(): ?string {
return $this->description;
}
public function setDescription(?string $description): self {
$this->description=$description;
return $this;
}
public function getIcon(): string|UploadedFile|null {
return $this->icon;
}
public function setIcon(string|UploadedFile|null $icon): self {
$this->icon=$icon;
return $this;
}
public function getVersion(): ?float {
return $this->version;
}
public function setVersion(float $version): self {
$this->version=$version;
return $this;
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Entity;
use App\Repository\MonsterCategoryRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity(repositoryClass: MonsterCategoryRepository::class)]
#[ORM\Index(columns: ['slug'])]
#[UniqueEntity(fields: ['name'], message: 'There is already a monster category with this name')]
#[UniqueEntity(fields: ['slug'], message: 'There is already a monster category with this slug')]
class MonsterCategory {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private $id;
#[ORM\Column(type: 'string', length: 50, unique: true)]
private $name;
#[ORM\Column(type: 'string', length: 50, unique: true)]
private $slug;
public function __toString(): string {
return $this->getName();
}
public function getId(): ?int {
return $this->id;
}
public function getName(): ?string {
return $this->name;
}
public function setName(string $name): self {
$this->name=$name;
return $this;
}
public function getSlug(): ?string {
return $this->slug;
}
public function setSlug(string $slug): self {
$this->slug=$slug;
return $this;
}
}

230
src/Entity/Node.php Normal file
View File

@ -0,0 +1,230 @@
<?php
namespace App\Entity;
use App\Repository\NodeRepository;
use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
#[ORM\Entity(repositoryClass: NodeRepository::class)]
#[ORM\HasLifecycleCallbacks]
class Node {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private ?int $id;
#[ORM\ManyToOne(targetEntity: Grid::class)]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
private ?Grid $grid;
#[ORM\ManyToOne(targetEntity: Worldmark::class)]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
private ?Worldmark $worldmark;
#[ORM\Column(type: 'text', nullable: true)]
private ?string $description;
#[ORM\Column(type: 'tinyint', options: ['default' => 1])]
private int $quantity;
#[ORM\Column(type: 'smallint', options: ['default' => 0, 'unsigned' => true])]
private ?int $primogem;
#[ORM\Column(type: 'json')]
private array $coordinate;
#[ORM\Column(type: 'string', length: 50, nullable: true)]
private string|UploadedFile|null $screenshotA;
#[ORM\Column(type: 'string', length: 50, nullable: true)]
private string|UploadedFile|null $screenshotB;
#[ORM\Column(type: 'string', length: 50, nullable: true)]
private string|UploadedFile|null $screenshotC;
#[ORM\Column(type: 'string', length: 50, nullable: true)]
private string|UploadedFile|null $screenshotD;
#[ORM\Column(type: 'string', length: 50, nullable: true)]
private string|UploadedFile|null $screenshotE;
#[ORM\Column(type: 'datetime')]
private ?DateTimeInterface $modifiedAt;
#[ORM\Column(type: 'float')]
private ?float $version;
#[ORM\Column(type: 'boolean')]
private bool $isDeleted;
public function __construct() {
$this->id = null;
$this->description = null;
$this->quantity = 1;
$this->primogem = 0;
$this->coordinate = array(
'x' => 50,
'y' => 50,
);
$this->screenshotA = null;
$this->screenshotB = null;
$this->screenshotC = null;
$this->screenshotD = null;
$this->screenshotE = null;
$this->version = null;
$this->isDeleted = false;
}
#[ORM\PrePersist]
#[ORM\PreUpdate]
public function updateLifecycle() {
$this->setModifiedAt(new DateTime());
}
public function getId(): ?int {
return $this->id;
}
public function getGrid(): ?Grid {
return $this->grid;
}
public function setGrid(?Grid $grid): self {
$this->grid = $grid;
return $this;
}
public function getWorldmark(): ?Worldmark {
return $this->worldmark;
}
public function setWorldmark(?Worldmark $worldmark): self {
$this->worldmark = $worldmark;
return $this;
}
public function getDescription(): ?string {
return $this->description;
}
public function setDescription(?string $description): self {
$this->description = $description;
return $this;
}
public function getQuantity(): int {
return $this->quantity;
}
public function setQuantity($quantity): self {
$this->quantity = $quantity;
return $this;
}
public function getPrimogem(): int {
return $this->primogem;
}
public function setPrimogem($primogem): self {
$this->primogem = $primogem;
return $this;
}
public function getCoordinate(): array {
return $this->coordinate;
}
public function setCoordinate(array $coordinate): self {
$this->coordinate = $coordinate;
return $this;
}
public function getScreenshotA(): string|UploadedFile|null {
return $this->screenshotA;
}
public function setScreenshotA(string|UploadedFile|null $screenshotA): self {
$this->screenshotA = $screenshotA;
return $this;
}
public function getScreenshotB(): string|UploadedFile|null {
return $this->screenshotB;
}
public function setScreenshotB(string|UploadedFile|null $screenshotB): self {
$this->screenshotB = $screenshotB;
return $this;
}
public function getScreenshotC(): string|UploadedFile|null {
return $this->screenshotC;
}
public function setScreenshotC(string|UploadedFile|null $screenshotC): self {
$this->screenshotC = $screenshotC;
return $this;
}
public function getScreenshotD(): string|UploadedFile|null {
return $this->screenshotD;
}
public function setScreenshotD(string|UploadedFile|null $screenshotD): self {
$this->screenshotD = $screenshotD;
return $this;
}
public function getScreenshotE(): string|UploadedFile|null {
return $this->screenshotE;
}
public function setScreenshotE(string|UploadedFile|null $screenshotE): self {
$this->screenshotE = $screenshotE;
return $this;
}
public function getModifiedAt(): ?DateTimeInterface {
return $this->modifiedAt;
}
public function setModifiedAt(DateTimeInterface $modifiedAt): self {
$this->modifiedAt = $modifiedAt;
return $this;
}
public function getVersion(): ?float {
return $this->version;
}
public function setVersion(float $version): self {
$this->version = $version;
return $this;
}
public function getIsDeleted(): ?bool {
return $this->isDeleted;
}
public function setIsDeleted(bool $isDeleted): self {
$this->isDeleted = $isDeleted;
return $this;
}
}

234
src/Entity/Region.php Normal file
View File

@ -0,0 +1,234 @@
<?php
namespace App\Entity;
use App\Repository\RegionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity(repositoryClass: RegionRepository::class)]
#[ORM\Index(columns: ['slug'])]
#[UniqueEntity(fields: ['name'], message: 'There is already an region with this name')]
#[UniqueEntity(fields: ['slug'], message: 'There is already an region with this slug')]
class Region {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private ?int $id;
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'subRegions')]
private ?Region $parentRegion;
#[ORM\OneToMany(mappedBy: 'parentRegion', targetEntity: self::class)]
private Collection $subRegions;
#[ORM\Column(type: 'string', length: 50, unique: true)]
private ?string $name;
#[ORM\Column(type: 'string', length: 50, unique: true)]
private ?string $slug;
#[ORM\Column(type: 'boolean', options: ['default' => 0])]
private ?bool $isAlias;
#[ORM\Column(type: 'json', length: 20, nullable: true)]
private ?string $anchor;
#[ORM\Column(type: 'text', nullable: true)]
private ?string $description;
#[ORM\Column(type: 'string', length: 50)]
private ?string $icon;
#[ORM\Column(type: 'tinyint', options: ['default' => 0, 'unsigned' => true])]
private ?int $gridHeight;
#[ORM\Column(type: 'tinyint', options: ['default' => 0, 'unsigned' => true])]
private ?int $gridWidth;
#[ORM\Column(type: 'string', length: 50)]
private ?string $mapBackground;
#[ORM\Column(type: 'float')]
private ?float $version;
#[ORM\Column(type: 'smallint', options: ['default' => 65535, 'unsigned' => true])]
private ?int $sortOrder;
#[ORM\Column(type: 'boolean', options: ['default' => 1])]
private int $isActive;
#[ORM\ManyToMany(targetEntity: Worldmark::class, mappedBy: 'regions')]
private Collection $worldmarks;
public function __construct() {
$this->id = null;
$this->mapBackground = null;
$this->worldmarks = new ArrayCollection();
$this->subRegions = new ArrayCollection();
$this->isAlias = 0;
$this->isActive = 1;
}
public function __toString(): string {
return $this->getName();
}
public function getId(): ?int {
return $this->id;
}
public function getParentRegion(): ?self {
return $this->parentRegion;
}
public function setParentRegion(?self $parentRegion): self {
$this->parentRegion = $parentRegion;
return $this;
}
public function getSubRegions(): Collection {
return $this->subRegions;
}
public function getName(): ?string {
return $this->name;
}
public function setName(string $name): self {
$this->name=$name;
return $this;
}
public function getSlug(): ?string {
return $this->slug;
}
public function setSlug(string $slug): self {
$this->slug=$slug;
return $this;
}
public function getIsAlias(): ?bool {
return $this->isAlias;
}
public function setIsAlias(bool $isAlias): self {
$this->isAlias = $isAlias;
return $this;
}
public function getAnchor(): ?string {
return $this->anchor;
}
public function setAnchor(?string $anchor): self {
$this->anchor = $anchor;
return $this;
}
public function getDescription(): ?string {
return $this->description;
}
public function setDescription(?string $description): self {
$this->description = $description;
return $this;
}
public function getIcon(): ?string {
return $this->icon;
}
public function setIcon(string $icon): self {
$this->icon=$icon;
return $this;
}
public function getGridHeight(): ?int {
return $this->gridHeight;
}
public function setGridHeight(int $gridHeight): self {
$this->gridHeight=$gridHeight;
return $this;
}
public function getGridWidth(): ?int {
return $this->gridWidth;
}
public function setGridWidth(int $gridWidth): self {
$this->gridWidth=$gridWidth;
return $this;
}
public function getMapBackground(): ?string {
return $this->mapBackground;
}
public function setMapBackground(string $mapBackground): self {
$this->mapBackground=$mapBackground;
return $this;
}
public function getVersion(): ?float {
return $this->version;
}
public function setVersion(float $version): self {
$this->version=$version;
return $this;
}
public function getSortOrder(): ?int {
return $this->sortOrder;
}
public function setSortOrder(int $sortOrder): self {
$this->sortOrder=$sortOrder;
return $this;
}
public function getIsActive(): bool {
return $this->isActive;
}
public function setIsActive(bool $isActive): self {
$this->isActive=$isActive;
return $this;
}
public function getWorldmarks(): Collection {
return $this->worldmarks;
}
public function addWorldmark(Worldmark $worldmark): self {
if(!$this->worldmarks->contains($worldmark)) {
$this->worldmarks[]=$worldmark;
}
return $this;
}
public function removeWorldmark(Worldmark $worldmark): self {
$this->worldmarks->removeElement($worldmark);
return $this;
}
}

138
src/Entity/User.php Normal file
View File

@ -0,0 +1,138 @@
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\HasLifecycleCallbacks]
#[UniqueEntity(fields: ['username'], message: 'There is already an account with this username')]
#[UniqueEntity(fields: ['email'], message: 'There is already an account with this email')]
class User implements UserInterface, PasswordAuthenticatedUserInterface {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private $id;
#[ORM\Column(type: 'string', length: 180, unique: true)]
private $username;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $email;
#[ORM\Column(type: 'integer', nullable: true, options: ["unsigned" => true])]
private $uid;
#[ORM\Column(type: 'string')]
private $password;
#[ORM\Column(type: 'json')]
private $roles = [];
public function __toString(): string {
return $this->getUserIdentifier();
}
#[ORM\PrePersist]
public function setFirstRole() {
$this->setRoles(array());
}
public function getId(): ?int {
return $this->id;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string {
return (string)$this->username;
}
/**
* @deprecated since Symfony 5.3, use getUserIdentifier instead
*/
public function getUsername(): string {
return (string)$this->username;
}
public function setUsername(string $username): self {
$this->username = $username;
return $this;
}
public function getEmail(): ?string {
return $this->email;
}
public function setEmail(?string $email): self {
$this->email = $email;
return $this;
}
public function getUid(): ?int {
return $this->uid;
}
public function setUid(?int $uid): self {
$this->uid = $uid;
return $this;
}
/**
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string {
return $this->password;
}
public function setPassword(string $password): self {
$this->password = $password;
return $this;
}
/**
* @see UserInterface
*/
public function getRoles(): array {
return $this->roles;
}
public function setRoles(array $roles): self {
if(!in_array('ROLE_USER', $roles)) {
array_unshift($roles, 'ROLE_USER');
}
$this->roles = array_unique($roles);
return $this;
}
/**
* Returning a salt is only needed, if you are not using a modern
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
*
* @see UserInterface
*/
public function getSalt(): ?string {
return null;
}
/**
* @see UserInterface
*/
public function eraseCredentials() {
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}

217
src/Entity/Worldmark.php Normal file
View File

@ -0,0 +1,217 @@
<?php
namespace App\Entity;
use App\Repository\WorldmarkRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\HttpFoundation\File\UploadedFile;
#[ORM\Entity(repositoryClass: WorldmarkRepository::class)]
#[ORM\Index(columns: ['slug'])]
#[UniqueEntity(fields: ['slug'], message: 'There is already a worldmark with this slug')]
class Worldmark {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private $id;
#[ORM\ManyToOne(targetEntity: WorldmarkCategory::class)]
#[ORM\JoinColumn(nullable: false)]
private $category;
#[ORM\ManyToOne(targetEntity: Item::class)]
private $item;
#[ORM\ManyToOne(targetEntity: Monster::class)]
private $monster;
#[ORM\Column(type: 'string', length: 60)]
private $name;
#[ORM\Column(type: 'string', length: 60, unique: true)]
private $slug;
#[ORM\Column(type: 'text', nullable: true)]
private $description;
#[ORM\Column(type: 'tinyint', options: ['default' => 1, 'unsigned' => true])]
private $defaultQuantityValue;
#[ORM\Column(type: 'tinyint', options: ['default' => 0, 'unsigned' => true])]
private $defaultPrimogemValue;
#[ORM\Column(type: 'string', length: 50, nullable: true)]
private $icon;
#[ORM\Column(type: 'boolean', options: ['default' => 1])]
private $canBeHidden;
#[ORM\Column(type: 'float')]
private $version;
#[ORM\Column(type: 'smallint', options: ['default' => 65535, 'unsigned' => true])]
private $sortOrder;
#[ORM\ManyToMany(targetEntity: Region::class, inversedBy: 'worldmarks')]
#[ORM\JoinTable(name: 'region_worldmark')]
private $regions;
public function __construct() {
$this->defaultQuantityValue = 1;
$this->defaultPrimogemValue = 0;
$this->canBeHidden = 1;
$this->regions=new ArrayCollection();
}
public function __toString(): string {
return $this->getName();
}
public function getId(): ?int {
return $this->id;
}
public function getCategory(): WorldmarkCategory {
return $this->category;
}
public function setCategory(WorldmarkCategory $category): self {
$this->category=$category;
return $this;
}
public function getItem(): ?Item {
return $this->item;
}
public function setItem(?Item $item): self {
$this->item=$item;
return $this;
}
public function getMonster(): ?Monster {
return $this->monster;
}
public function setMonster(?Monster $monster): self {
$this->monster=$monster;
return $this;
}
public function getName(): ?string {
return $this->name;
}
public function setName(string $name): self {
$this->name=$name;
return $this;
}
public function getSlug(): ?string {
return $this->slug;
}
public function setSlug(string $slug): self {
$this->slug=$slug;
return $this;
}
public function getDescription(): ?string {
return $this->description;
}
public function setDescription(?string $description): self {
$this->description=$description;
return $this;
}
public function getDefaultQuantityValue() {
return $this->defaultQuantityValue;
}
public function setDefaultQuantityValue($defaultQuantityValue): self {
$this->defaultQuantityValue = $defaultQuantityValue;
return $this;
}
public function getDefaultPrimogemValue() {
return $this->defaultPrimogemValue;
}
public function setDefaultPrimogemValue($defaultPrimogemValue): self {
$this->defaultPrimogemValue = $defaultPrimogemValue;
return $this;
}
public function getIcon(): string|UploadedFile|null {
return $this->icon;
}
public function setIcon(string|UploadedFile|null $icon): self {
$this->icon=$icon;
return $this;
}
public function getCanBeHidden(): ?bool {
return $this->canBeHidden;
}
public function setCanBeHidden(bool $canBeHidden): self {
$this->canBeHidden=$canBeHidden;
return $this;
}
public function getVersion(): ?float {
return $this->version;
}
public function setVersion(float $version): self {
$this->version=$version;
return $this;
}
public function getSortOrder(): ?int {
return $this->sortOrder;
}
public function setSortOrder(int $sortOrder): self {
$this->sortOrder=$sortOrder;
return $this;
}
public function getRegions(): Collection {
return $this->regions;
}
public function addRegion(Region $region): self {
if(!$this->regions->contains($region)) {
$this->regions[]=$region;
$region->addWorldmark($this);
}
return $this;
}
public function removeRegion(Region $region): self {
if($this->regions->removeElement($region)) {
$region->removeWorldmark($this);
}
return $this;
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace App\Entity;
use App\Repository\WorldmarkCategoryRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity(repositoryClass: WorldmarkCategoryRepository::class)]
#[ORM\Index(columns: ['slug'])]
#[UniqueEntity(fields: ['name'], message: 'There is already a worldmark category with this name')]
#[UniqueEntity(fields: ['slug'], message: 'There is already a worldmark category with this slug')]
class WorldmarkCategory {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer', options: ['unsigned' => true])]
private $id;
#[ORM\Column(type: 'string', length: 50, unique: true)]
private $name;
#[ORM\Column(type: 'string', length: 50, unique: true)]
private $slug;
#[ORM\Column(type: 'smallint', options: ['default' => 65535, 'unsigned' => true])]
private $sortOrder;
public function __toString(): string {
return $this->getName();
}
public function getId(): ?int {
return $this->id;
}
public function getName(): ?string {
return $this->name;
}
public function setName(string $name): self {
$this->name=$name;
return $this;
}
public function getSlug(): ?string {
return $this->slug;
}
public function setSlug(string $slug): self {
$this->slug=$slug;
return $this;
}
public function getSortOrder(): ?int {
return $this->sortOrder;
}
public function setSortOrder(int $sortOrder): self {
$this->sortOrder=$sortOrder;
return $this;
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace App\EventListener;
use App\Extension\PhpExtension;
use App\Kernel;
use App\Repository\RegionRepository;
use App\Repository\WorldmarkCategoryRepository;
use Detection\MobileDetect;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
use Twig\Environment as TwigEnvironment;
class KernelListener {
public function __construct(private Security $security,
private RouterInterface $router,
private TwigEnvironment $twigEnvironment,
private PhpExtension $phpExtension,
// private MobileDetect $mobileDetect,
private RegionRepository $regionRepository,
private WorldmarkCategoryRepository $worldmarkCategoryRepository) {}
public function onKernelRequest(RequestEvent $requestEvent) {
$request=$requestEvent->getRequest();
if(!$requestEvent->isMainRequest() || $request->isXmlHttpRequest()) {
return;
}
if(($this->phpExtension->strStartWith($request->attributes->get('_route'), 'bo_')
&& !$this->security->isGranted('ROLE_CONTRIBUTOR'))
|| ($this->phpExtension->strStartWith($request->getRequestUri(), '/dev')
&& (!$this->security->getUser() || !$this->security->isGranted('ROLE_ADMIN')))) {
throw new NotFoundHttpException("Page not found");
}
}
public function onKernelController(ControllerEvent $controllerEvent) {
if(!$controllerEvent->isMainRequest()) {
return;
}
$request = $controllerEvent->getRequest();
$route = $request->attributes->get('_route');
$this->twigEnvironment->addGlobal('user', $this->security->getUser());
$this->twigEnvironment->addGlobal('route', $route);
$this->twigEnvironment->addGlobal('locale', $request->getLocale());
$this->twigEnvironment->addGlobal('gameVersion', Kernel::GAME_VERSION);
$this->twigEnvironment->addGlobal('supportedGameVersion', Kernel::SUPPORTED_GAME_VERSION);
$this->twigEnvironment->addGlobal('isTouchDevice', $this->isTouchDevice());
$regions = $this->regionRepository->getRegions();
$this->twigEnvironment->addGlobal('regions', $regions);
}
public function onKernelResponse(ResponseEvent $responseEvent) {
}
public function onKernelException(ExceptionEvent $exceptionEvent) {
}
private function isTouchDevice(): array {
$detect = new MobileDetect();
return array(
'isTablet' => $detect->isTablet(),
'isMobile' => $detect->isMobile(),
);
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace App\Extension;
class PhpExtension {
public function strStartWith(string $str, string $needle): bool {
if($str === '' || $needle === '' || strlen($needle) > strlen($str) || substr_compare($str, $needle, 0, strlen($needle)) !== 0) {
return false;
} else {
return true;
}
}
public function strEndWith(string $str, string $needle): bool {
if($str === '' || $needle === '' || strlen($needle) > strlen($str) || substr_compare($str, $needle, strlen($str)-strlen($needle), strlen($needle)) !== 0) {
return false;
} else {
return true;
}
}
public function strLike(string $str, string $needle, bool $ignoreCase = false): bool {
if($ignoreCase) {
return str_contains(strtolower($str), strtolower($needle));
} else {
return str_contains($str, $needle);
}
}
public function strPad(string $str, int $padLength, string $padString, string $direction='left'): string {
$strPad=null;
if($direction == 'left') {
$strPad=STR_PAD_LEFT;
} elseif($direction == 'right') {
$strPad=STR_PAD_RIGHT;
} elseif($direction == 'both') {
$strPad=STR_PAD_BOTH;
}
if($strPad !== null) {
return str_pad($str, $padLength, $padString, $strPad);
} else {
return $str;
}
}
public function trim($input): array|string {
return !is_array($input) ? trim($input) : array_map(array($this, 'trim',), $input);
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace App\Extension;
use Ramsey\Uuid\Uuid;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
class TwigExtension extends AbstractExtension {
public function getFunctions(): array {
return array(
new TwigFunction('getUuid', array($this, 'getUuidFunction')),
);
}
public function getFilters(): array {
return array(
new TwigFilter('json_decode', array($this, 'jsonDecodeFilter')),
new TwigFilter('start_with', array($this, 'strStartWithFilter')),
new TwigFilter('end_with', array($this, 'strEndWithFilter')),
new TwigFilter('like', array($this, 'strLikeFilter')),
new TwigFilter('pad', array($this, 'strPadFilter')),
new TwigFilter('trim', array($this, 'trimFilter')),
new TwigFilter('shuffle_array', array($this, 'shuffleArrayFilter')),
);
}
//<editor-fold desc="TWIG FUNCTIONS">
public function getUuidFunction(): string {
return Uuid::uuid1()->toString();
}
//</editor-fold>
//<editor-fold desc="TWIG FILTERS">
public function jsonDecodeFilter($json): mixed {
if($this->isJson($json)) {
return json_decode($json);
}
return false;
}
public function strStartWithFilter(string $str, string $needle): bool {
if($str === '' || $needle === '' || strlen($needle) > strlen($str) || substr_compare($str, $needle, 0, strlen($needle)) !== 0) {
return false;
} else {
return true;
}
}
public function strEndWithFilter(string $str, string $needle): bool {
if($str === '' || $needle === '' || strlen($needle) > strlen($str) || substr_compare($str, $needle, strlen($str) - strlen($needle), strlen($needle)) !== 0) {
return false;
} else {
return true;
}
}
public function strLikeFilter(string $str, string $needle, bool $ignoreCase = false): bool {
if($ignoreCase) {
return str_contains(strtolower($str), strtolower($needle));
} else {
return str_contains($str, $needle);
}
}
public function strPadFilter(string $str, int $padLength, string $padString, string $direction = 'left'): string {
$strPad = null;
if($direction == 'left') {
$strPad = STR_PAD_LEFT;
} elseif($direction == 'right') {
$strPad = STR_PAD_RIGHT;
} elseif($direction == 'both') {
$strPad = STR_PAD_BOTH;
}
if($strPad !== null) {
return str_pad($str, $padLength, $padString, $strPad);
} else {
return $str;
}
}
public function trimFilter($input): array|string {
return !is_array($input) ? trim($input) : array_map(array($this, 'trim',), $input);
}
/**
* @param array $array
* @return array
*/
public function shuffleArrayFilter(array $array): array {
shuffle($array);
return $array;
}
//</editor-fold>
private function isJson($string): bool {
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
}

37
src/Form/GridType.php Normal file
View File

@ -0,0 +1,37 @@
<?php
namespace App\Form;
use App\Entity\Grid;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class GridType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options): void {
// $builder->add('row')
// ->add('col')
// ->add('region');
$builder->add('positions', ChoiceType::class, array(
'choices' => array(
'Add row before' => 'row_before',
'Add row after' => 'row_after',
'Add column before' => 'column_before',
'Add column after' => 'column_after',
),
'data'=>null,
'mapped'=>false,
'expanded'=>true,
'multiple'=>true,
'required'=>false,
));
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class' => Grid::class,
'data_route' => null,
));
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace App\Form;
use App\Entity\ItemCategory;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ItemCategoryType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options): void {
$builder->add('name', TextType::class, array(
'attr' => array('data-slug-target' => 'item_category_name')
))
->add('slug', TextType::class, array(
'attr' => array(
'data-slug-source' => 'item_category_name',
'readonly' => 'readonly',
),
))
->add('sortOrder', TextType::class, array(
// 'attr' => array('pattern'=>'^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$'),
));
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class' => ItemCategory::class,
'data_route' => null,
));
}
}

56
src/Form/ItemType.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace App\Form;
use App\Entity\Item;
use App\Extension\PhpExtension;
use App\Kernel;
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\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ItemType extends AbstractType {
public function __construct(private PhpExtension $phpExtension) {}
public function buildForm(FormBuilderInterface $builder, array $options): void {
$builder->add('name', TextType::class, array(
'attr'=>array('data-slug-target'=>'item_name')
))
->add('slug', TextType::class, array(
'attr'=>array(
'data-slug-source'=>'item_name',
'readonly'=>'readonly'
)
))
->add('description', TextareaType::class, array(
'required'=>false
))
->add('icon', FileType::class, array(
'data'=>null,
'required'=>false,
))
->add('version', ChoiceType::class, array(
'choices'=>array_combine(Kernel::SUPPORTED_GAME_VERSION, Kernel::SUPPORTED_GAME_VERSION),
'data'=>$this->phpExtension->strEndWith($options['data_route'], '_new') ? Kernel::GAME_VERSION : $options['data']->getVersion(),
));
if($this->phpExtension->strEndWith($options['data_route'], '_edit')) {
$builder->add('removeFile', CheckboxType::class, array(
'mapped'=>false,
'required'=>false,
));
}
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class' => Item::class,
'data_route' => null,
));
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Form;
use App\Entity\Map;
use App\Extension\PhpExtension;
use App\Kernel;
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\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class MapFragmentType extends AbstractType {
public function __construct(private PhpExtension $phpExtension) {}
public function buildForm(FormBuilderInterface $builder, array $options): void {
$builder->add('row', TextType::class, array(
'attr'=>array('pattern'=>'[0-9]'),
))
->add('col', TextType::class, array(
'attr'=>array('pattern'=>'[0-9]'),
))
->add('fragment', FileType::class, array(
'data_class'=>null,
'required'=>false,
))
->add('version', ChoiceType::class, array(
'choices'=>array_combine(Kernel::SUPPORTED_GAME_VERSION, Kernel::SUPPORTED_GAME_VERSION),
'data'=>$this->phpExtension->strEndWith($options['data_route'], '_new') ? Kernel::GAME_VERSION : $options['data']->getVersion(),
));
// ->add('modifiedAt')
// ->add('region');
if($this->phpExtension->strEndWith($options['data_route'], '_edit')) {
$builder->add('blank', CheckboxType::class, array(
'mapped'=>false,
'required'=>false,
));
}
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults([
'data_class'=>Map::class,
'data_route'=>null,
]);
}
}

33
src/Form/MapType.php Normal file
View File

@ -0,0 +1,33 @@
<?php
namespace App\Form;
use App\Entity\Map;
use App\Extension\PhpExtension;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class MapType extends AbstractType {
public function __construct(private PhpExtension $phpExtension) {}
public function buildForm(FormBuilderInterface $builder, array $options): void {
$builder->add('file', FileType::class, array(
'data_class'=>null,
'required'=>false,
));
if($this->phpExtension->strEndWith($options['data_route'], '_new')) {
$builder->add('version', HiddenType::class);
}
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class'=>Map::class,
'data_route'=>null,
));
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Form;
use App\Entity\MonsterCategory;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class MonsterCategoryType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options): void {
$builder->add('name', TextType::class, array(
'attr'=>array('data-slug-target'=>'monster_category_name')
))
->add('slug', TextType::class, array(
'attr'=>array(
'data-slug-source'=>'monster_category_name',
'readonly'=>'readonly'
)
));
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class'=>MonsterCategory::class,
'data_route'=>null,
));
}
}

58
src/Form/MonsterType.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace App\Form;
use App\Entity\Monster;
use App\Extension\PhpExtension;
use App\Kernel;
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\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class MonsterType extends AbstractType {
public function __construct(private PhpExtension $phpExtension) {}
public function buildForm(FormBuilderInterface $builder, array $options): void {
$builder->add('name', TextType::class, array(
'attr'=>array(
'data-slug-target'=>'monster_name'
)
))
->add('slug', TextType::class, array(
'attr'=>array(
'data-slug-source'=>'monster_name',
'readonly'=>'readonly'
)
))
->add('description', TextareaType::class, array(
'required'=>false
))
->add('icon', FileType::class, array(
'data'=>null,
'required'=>false,
))
->add('version', ChoiceType::class, array(
'choices'=>array_combine(Kernel::SUPPORTED_GAME_VERSION, Kernel::SUPPORTED_GAME_VERSION),
'data'=>$this->phpExtension->strEndWith($options['data_route'], '_new') ? Kernel::GAME_VERSION : $options['data']->getVersion(),
));
if($this->phpExtension->strEndWith($options['data_route'], '_edit')) {
$builder->add('removeFile', CheckboxType::class, array(
'mapped'=>false,
'required'=>false,
));
}
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class'=>Monster::class,
'data_route'=>null,
));
}
}

126
src/Form/NodeType.php Normal file
View File

@ -0,0 +1,126 @@
<?php
namespace App\Form;
use App\Entity\Node;
use App\Extension\PhpExtension;
use App\Kernel;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class NodeType extends AbstractType {
public function __construct(private PhpExtension $phpExtension) {}
public function buildForm(FormBuilderInterface $builder, array $options): void {
/** @var Node $node */
$node =$options['data'];
if($options['data_route'] == 'bo_node_delete') {
$builder->add('isDeleted', HiddenType::class, array(
'data'=>true,
'required'=>true
));
} else {
$coordinate = $node->getCoordinate();
$versions=Kernel::SUPPORTED_GAME_VERSION;
foreach($versions as $key=>$version) {
if($version < Kernel::GAME_VERSION) {
unset($versions[$key]);
}
}
$builder->add('quantity', TextType::class, array(
'attr'=>array(
'pattern'=>'^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$',
),
'data' => $this->phpExtension->strEndWith($options['data_route'], '_new') ? $node->getWorldmark()->getDefaultQuantityValue() : $node->getQuantity()
))
->add('primogem', TextType::class, array(
'attr'=>array(
'pattern'=>'^([0-9]|[1-9][0-9]{1-2})$',
),
'data' => $this->phpExtension->strEndWith($options['data_route'], '_new') ? $node->getWorldmark()->getDefaultPrimogemValue() : $node->getPrimogem()
))
->add('coordinate', HiddenType::class)
->add('coordX', NumberType::class, array(
'attr'=>array(
'readonly'=>true,
'disabled'=>true,
),
'data'=>$coordinate['x'],
'mapped'=>false,
))
->add('coordY', NumberType::class, array(
'attr'=>array(
'readonly'=>true,
'disabled'=>true,
),
'data'=>$coordinate['y'],
'mapped'=>false,
))
->add('description', TextareaType::class, array(
'required'=>false
))
->add('screenshotA', FileType::class, array(
'data'=>null,
'required'=>false,
))
->add('screenshotB', FileType::class, array(
'data'=>null,
'required'=>false,
))
->add('screenshotC', FileType::class, array(
'data'=>null,
'required'=>false,
))
->add('screenshotD', FileType::class, array(
'data'=>null,
'required'=>false,
))
->add('screenshotE', FileType::class, array(
'data'=>null,
'required'=>false,
))
->add('version', ChoiceType::class, array(
'choices' => array_combine($versions, $versions),
'data' => $this->phpExtension->strEndWith($options['data_route'], '_new') ? Kernel::GAME_VERSION : ($options['data']->getVersion() < Kernel::GAME_VERSION ? Kernel::GAME_VERSION : $options['data']->getVersion()),
'expanded' => true,
'multiple' => false,
'required' => true,
));
// ->add('createAt')
// ->add('modifiedAt')
// ->add('isDeleted')
// ->add('grid')
// ->add('worldmark')
$builder->get('coordinate')
->addModelTransformer(new CallbackTransformer(
function($array) use ($builder) {
return json_encode($array);
},
function($string) use ($builder) {
return json_decode($string, true);
}
));
}
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class'=>Node::class,
'data_route'=>null,
));
}
}

107
src/Form/RegionType.php Normal file
View File

@ -0,0 +1,107 @@
<?php
namespace App\Form;
use App\Entity\Region;
use App\Extension\PhpExtension;
use App\Kernel;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class RegionType extends AbstractType {
private array $parameters;
public function __construct(ParameterBagInterface $parameterBag,
private PhpExtension $phpExtension) {
$this->parameters=array('assets'=>$parameterBag->get('assets'));
}
public function buildForm(FormBuilderInterface $builder, array $options): void {
$icons=array();
$finder=new Finder();
$finder->files()->in($this->parameters['assets']['img']['region'])
->name('*.png')
->sortByName();
foreach($finder as $fileInfo) {
$icons[$fileInfo->getBasename()]=$fileInfo->getBasename();
}
$builder->add('parentRegion')
->add('name', TextType::class, array(
'attr'=>array(
'data-slug-target'=>'region_name'
)
))
->add('slug', TextType::class, array(
'attr'=>array(
'data-slug-source'=>'region_name',
'readonly'=>'readonly'
)
))
->add('isAlias', ChoiceType::class, array(
'choices'=>array('No'=>0, 'Yes'=>1),
'data'=>$this->phpExtension->strEndWith($options['data_route'], '_new') ? 0 : $options['data']->getIsAlias(),
'expanded' => true,
'multiple' => false,
'required' => true,
))
->add('anchor', TextType::class, array('required' => false))
->add('description', TextareaType::class, array(
'required'=>false
))
->add('icon', ChoiceType::class, array(
'choices'=>$icons,
'expanded'=>true,
'required'=>true,
))
->add('mapBackground', ChoiceType::class, array(
'choices' => array(
'Sea' => 'sea',
'Sand' => 'sand',
),
'expanded'=>true,
'required' => true,
))
->add('version', ChoiceType::class, array(
'choices'=>array_combine(Kernel::SUPPORTED_GAME_VERSION, Kernel::SUPPORTED_GAME_VERSION),
'data'=>$this->phpExtension->strEndWith($options['data_route'], '_new') ? Kernel::GAME_VERSION : $options['data']->getVersion(),
))
->add('sortOrder', TextType::class, array(
// 'attr'=>array('pattern'=>'^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$'),
))
->add('isActive', ChoiceType::class, array(
'choices'=>array('No'=>0, 'Yes'=>1),
'data'=>$this->phpExtension->strEndWith($options['data_route'], '_new') ? 1 : $options['data']->getIsActive(),
'expanded' => true,
'multiple' => false,
'required' => true,
));
if($this->phpExtension->strEndWith($options['data_route'], '_new')) {
$builder->add('gridHeight', TextType::class, array(
'attr'=>array(
'pattern'=>'^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$',
),
))
->add('gridWidth', TextType::class, array(
'attr'=>array(
'pattern'=>'^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$',
),
));
}
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class'=>Region::class,
'data_route'=>null,
));
}
}

48
src/Form/SecurityType.php Normal file
View File

@ -0,0 +1,48 @@
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class SecurityType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options): void {
$builder->add('username')
->add('plainPassword', PasswordType::class, array(
// instead of being set onto the object directly,
// this is read and encoded in the controller
'mapped' => false,
'attr' => array('autocomplete' => 'new-password'),
'constraints' => array(
new NotBlank(array('message' => 'Please enter a password')),
new Length(array(
'min' => 6,
'minMessage' => 'Your password should be at least {{ limit }} characters',
// max length allowed by Symfony for security reasons
'max' => 4096,
)),
),
))
->add('agreeTerms', CheckboxType::class, array(
'mapped' => false,
'constraints' => array(
new IsTrue(array('message' => 'You should agree to our terms.')),
),
'label' => 'Agree non-existing terms',
));
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class' => User::class,
'data_route' => null,
));
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace App\Form;
use App\Entity\WorldmarkCategory;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class WorldmarkCategoryType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options): void {
$builder->add('name', TextType::class, array(
'attr' => array('data-slug-target' => 'worldmark_category_name'),
))
->add('slug', TextType::class, array(
'attr' => array(
'data-slug-source' => 'worldmark_category_name',
'readonly' => 'readonly',
),
))
->add('sortOrder', TextType::class, array(
// 'attr' => array('pattern'=>'^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$'),
));
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class' => WorldmarkCategory::class,
'data_route' => null,
));
}
}

103
src/Form/WorldmarkType.php Normal file
View File

@ -0,0 +1,103 @@
<?php
namespace App\Form;
use App\Entity\Item;
use App\Entity\Monster;
use App\Entity\Region;
use App\Entity\Worldmark;
use App\Extension\PhpExtension;
use App\Kernel;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class WorldmarkType extends AbstractType {
public function __construct(private PhpExtension $phpExtension) {}
public function buildForm(FormBuilderInterface $builder, array $options): void {
$builder->add('name', TextType::class, array(
'attr' => array('data-slug-target' => 'worldmark_name'),
))
->add('slug', TextType::class, array(
'attr' => array(
'data-slug-source' => 'worldmark_name',
// 'readonly' => 'readonly',
),
))
->add('description', TextareaType::class, array(
'required' => false,
))
->add('defaultQuantityValue', TextType::class, array(
'attr' => array(
'pattern' => '^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$',
),
))
->add('defaultPrimogemValue', TextType::class, array(
'attr' => array(
'pattern' => '^([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$',
),
))
->add('icon', FileType::class, array(
'data' => null,
'required' => false,
))
->add('canBeHidden')
->add('item', EntityType::class, array(
'class' => Item::class,
'query_builder' => function(EntityRepository $er) {
$qb = $er->createQueryBuilder("item");
return $qb->addSelect("category")
->join("item.category", "category")
->orderBy("category.name")
->addOrderBy("item.name");
},
'group_by' => 'category',
'required' => false,
))
->add('monster', EntityType::class, array(
'class' => Monster::class,
'query_builder' => function(EntityRepository $er) {
$qb = $er->createQueryBuilder("monster");
return $qb->addSelect("category")
->join("monster.category", "category")
->orderBy("category.name")
->addOrderBy("monster.name");
},
'group_by' => 'category',
'required' => false,
))
->add('regions', EntityType::class, array(
'class' => Region::class,
'query_builder' => function(EntityRepository $er) {
$qb = $er->createQueryBuilder("regions");
return $qb->andWhere($qb->expr()->eq("regions.isActive", 1));
},
'expanded' => true,
'multiple' => true,
))
->add('version', ChoiceType::class, array(
'choices' => array_combine(Kernel::SUPPORTED_GAME_VERSION, Kernel::SUPPORTED_GAME_VERSION),
'data' => $this->phpExtension->strEndWith($options['data_route'], '_new') ? Kernel::GAME_VERSION : $options['data']->getVersion(),
))
->add('sortOrder', TextType::class, array(
// 'attr'=>array('pattern'=>'^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$'),
));
}
public function configureOptions(OptionsResolver $resolver): void {
$resolver->setDefaults(array(
'data_class'=>Worldmark::class,
'data_route'=>null,
));
}
}

17
src/Kernel.php Normal file
View File

@ -0,0 +1,17 @@
<?php
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel {
use MicroKernelTrait;
public const GAME_VERSION = 3.6;
public const SUPPORTED_GAME_VERSION=array(
1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6,
2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8,
3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6
);
}

0
src/Repository/.gitignore vendored Normal file
View File

View File

@ -0,0 +1,116 @@
<?php
namespace App\Repository;
use App\Entity\Grid;
use App\Entity\Map;
use App\Entity\Region;
use App\Form\GridType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\Persistence\ManagerRegistry;
use LogicException;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method Grid|null find($id, $lockMode=null, $lockVersion=null)
* @method Grid|null findOneBy(array $criteria, array $orderBy=null)
* @method Grid[] findAll()
* @method Grid[] findBy(array $criteria, array $orderBy=null, $limit=null, $offset=null)
*/
class GridRepository extends ServiceEntityRepository {
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory,
private MapRepository $mapRepository) {
parent::__construct($registry, Grid::class);
}
public function getForm(string $route, Grid $grid): FormInterface {
$params=$grid->getId() !== null ? array('id'=>$grid->getId()) : array();
return $this->formFactory->create(GridType::class, $grid, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
public function getGridForm(string $route, Region $region): FormInterface {
return $this->formFactory->create(GridType::class, null, array(
'action'=>$this->router->generate($route, array('regionId'=>$region->getId())),
'method'=>'post',
'data_route'=>$route,
));
}
/**
* @param Region $region
* @return Grid[]
*/
public function getGridCells(Region $region): array {
$qb=$this->_em->createQueryBuilder();
$qb->select("grid")
->from(Grid::class, "grid")
->andWhere($qb->expr()->eq("grid.region", ":param_region"))
->orderBy("grid.row")
->addOrderBy("grid.col");
$qb->setParameter('param_region', $region->getId(), Types::INTEGER);
return $qb->getQuery()->getResult();
}
public function buildWorldmap(float $version, array $cells, array $maps, bool $withForm=false): array {
$grid=array();
/** @var Map $map */
foreach($maps as $key=>$map) {
if(!($map instanceof Map)) {
throw new LogicException(sprintf(
'Variable passed must be an instance of "%s". "%s" given',
Map::class,
gettype($map)
));
}
$maps["cell_{$map->getGrid()->getId()}"]=array(
'map'=>$map,
'form'=>$withForm ? $this->mapRepository->getForm('bo_map_edit', $map->getGrid(), $map)->createView() : null
);
unset($maps[$key]);
}
/** @var Grid $cell */
foreach($cells as $cell) {
if(!($cell instanceof Grid)) {
throw new LogicException(sprintf(
'Variable passed must be an instance of "%s". "%s" given',
Grid::class,
gettype($map)
));
}
if(!array_key_exists("cell_{$cell->getId()}", $maps)) {
$_map=new Map();
$_map->setVersion($version);
$maps["cell_{$cell->getId()}"]=array(
'map'=>$_map,
'form'=>$withForm ? $this->mapRepository->getForm('bo_map_new', $cell, $_map)->createView() : null
);
}
$grid[$cell->getRow()][$cell->getCol()]=array(
'id'=>$cell->getId(),
'map_data'=>$maps["cell_{$cell->getId()}"],
);
}
return $grid;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Repository;
use App\Entity\ItemCategory;
use App\Form\ItemCategoryType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method ItemCategory|null find($id, $lockMode = null, $lockVersion = null)
* @method ItemCategory|null findOneBy(array $criteria, array $orderBy = null)
* @method ItemCategory[] findAll()
* @method ItemCategory[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ItemCategoryRepository extends ServiceEntityRepository {
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory) {
parent::__construct($registry, ItemCategory::class);
}
public function getForm(string $route, ItemCategory $itemCategory): FormInterface {
$params=$itemCategory->getId() !== null ? array('id'=>$itemCategory->getId()) : array();
return $this->formFactory->create(ItemCategoryType::class, $itemCategory, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Repository;
use App\Entity\Item;
use App\Entity\ItemCategory;
use App\Form\ItemType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method Item|null find($id, $lockMode = null, $lockVersion = null)
* @method Item|null findOneBy(array $criteria, array $orderBy = null)
* @method Item[] findAll()
* @method Item[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ItemRepository extends ServiceEntityRepository {
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory) {
parent::__construct($registry, Item::class);
}
public function getForm(string $route, ItemCategory $itemCategory, Item $item): FormInterface {
$params=$item->getId() !== null ? array('itemCategoryId'=>$itemCategory->getId(), 'id'=>$item->getId()) : array('itemCategoryId'=>$itemCategory->getId());
return $this->formFactory->create(ItemType::class, $item, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace App\Repository;
use App\Entity\Grid;
use App\Entity\Map;
use App\Form\MapType;
use App\Kernel;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method Map|null find($id, $lockMode=null, $lockVersion=null)
* @method Map|null findOneBy(array $criteria, array $orderBy=null)
* @method Map[] findAll()
* @method Map[] findBy(array $criteria, array $orderBy=null, $limit=null, $offset=null)
*/
class MapRepository extends ServiceEntityRepository {
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory) {
parent::__construct($registry, Map::class);
}
public function getForm(string $route, Grid $cell, Map $map): FormInterface {
$params=$map->getId() !== null ? array('cellId'=>$cell->getId(), 'id'=>$map->getId()) : array('cellId'=>$cell->getId());
return $this->formFactory->create(MapType::class, $map, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
/**
* @param array $cells
* @param float $version
* @return Map[]
*/
public function getCellsMap(array $cells, float $version=Kernel::GAME_VERSION): array {
$qb=$this->_em->createQueryBuilder();
$qb->select("map")
->from(Map::class, "map")
->andWhere($qb->expr()->eq("map.version", ":param_version"))
->andWhere($qb->expr()->in("map.grid", ":param_in"));
$qb->setParameter('param_in', $cells)
->setParameter("param_version", $version, Types::FLOAT);
return $qb->getQuery()->getResult();
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Repository;
use App\Entity\MonsterCategory;
use App\Form\MonsterCategoryType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method MonsterCategory|null find($id, $lockMode=null, $lockVersion=null)
* @method MonsterCategory|null findOneBy(array $criteria, array $orderBy=null)
* @method MonsterCategory[] findAll()
* @method MonsterCategory[] findBy(array $criteria, array $orderBy=null, $limit=null, $offset=null)
*/
class MonsterCategoryRepository extends ServiceEntityRepository {
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory) {
parent::__construct($registry, MonsterCategory::class);
}
public function getForm(string $route, MonsterCategory $monsterCategory): FormInterface {
$params=$monsterCategory->getId() !== null ? array('id'=>$monsterCategory->getId()) : array();
return $this->formFactory->create(MonsterCategoryType::class, $monsterCategory, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Repository;
use App\Entity\Monster;
use App\Entity\MonsterCategory;
use App\Form\MonsterType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method Monster|null find($id, $lockMode=null, $lockVersion=null)
* @method Monster|null findOneBy(array $criteria, array $orderBy=null)
* @method Monster[] findAll()
* @method Monster[] findBy(array $criteria, array $orderBy=null, $limit=null, $offset=null)
*/
class MonsterRepository extends ServiceEntityRepository {
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory) {
parent::__construct($registry, Monster::class);
}
public function getForm(string $route, MonsterCategory $monsterCategory, Monster $monster): FormInterface {
$params=$monster->getId() !== null ? array('monsterCategoryId'=>$monsterCategory->getId(), 'id'=>$monster->getId()) : array('monsterCategoryId'=>$monsterCategory->getId());
return $this->formFactory->create(MonsterType::class, $monster, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace App\Repository;
use App\Entity\Grid;
use App\Entity\Node;
use App\Entity\Worldmark;
use App\Form\NodeType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method Node|null find($id, $lockMode=null, $lockVersion=null)
* @method Node|null findOneBy(array $criteria, array $orderBy=null)
* @method Node[] findAll()
* @method Node[] findBy(array $criteria, array $orderBy=null, $limit=null, $offset=null)
*/
class NodeRepository extends ServiceEntityRepository {
private RouterInterface $router;
private FormFactoryInterface $formFactory;
public function __construct(ManagerRegistry $registry, RouterInterface $router, FormFactoryInterface $formFactory) {
parent::__construct($registry, Node::class);
$this->router=$router;
$this->formFactory=$formFactory;
}
/**
* @param Grid[] $grid
* @param Worldmark[] $worldmarks
* @param float $version
* @param bool $includeDeleted
* @return Node[]
*/
public function getGridNodes(array $grid, array $worldmarks, float $version, bool $includeDeleted = false): array {
$qb=$this->_em->createQueryBuilder();
$qb->select("node")
->from(Node::class, "node")
->andWhere($qb->expr()->in("node.grid", ":param_inGridId"))
->andWhere($qb->expr()->in("node.worldmark", ":param_inWorldmarkId"))
->andWhere($qb->expr()->lte("node.version", ":param_version"))
->orderBy("node.grid");
$qb->setParameters(array(
'param_inGridId' => $grid,
'param_inWorldmarkId' => $worldmarks,
'param_version' => $version,
));
if(!$includeDeleted) {
$qb->andWhere($qb->expr()->eq("node.isDeleted", 0));
}
return $qb->getQuery()->getResult();
}
public function getForm(string $route, Grid $grid, Worldmark $worldmark, Node $node): FormInterface {
$params=$node->getId() !== null ? array(
'gridId'=>$grid->getId(),
'worldmarkId'=>$worldmark->getId(),
'id'=>$node->getId(),
) : array(
'gridId'=>$grid->getId(),
'worldmarkId'=>$worldmark->getId(),
);
return $this->formFactory->create(NodeType::class, $node, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace App\Repository;
use App\Entity\Region;
use App\Form\RegionType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method Region|null find($id, $lockMode=null, $lockVersion=null)
* @method Region|null findOneBy(array $criteria, array $orderBy=null)
* @method Region[] findAll()
* @method Region[] findBy(array $criteria, array $orderBy=null, $limit=null, $offset=null)
*/
class RegionRepository extends ServiceEntityRepository {
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory) {
parent::__construct($registry, Region::class);
}
public function getRegions() {
$qb=$this->_em->createQueryBuilder();
$qb->addSelect("region")
->addSelect("subRegions")
->from(Region::class, "region")
->leftJoin("region.subRegions", "subRegions")
->andWhere($qb->expr()->isNull("region.parentRegion"))
->orderBy("region.sortOrder");
return $qb->getQuery()->getResult();
}
public function getForm(string $route, Region $region): FormInterface {
$params=$region->getId() !== null ? array('id'=>$region->getId()) : array();
return $this->formFactory->create(RegionType::class, $region, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Repository;
use App\Entity\User;
use App\Form\SecurityType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use function get_class;
/**
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User|null findOneBy(array $criteria, array $orderBy = null)
* @method User[] findAll()
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface {
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory) {
parent::__construct($registry, User::class);
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void {
if(!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
$user->setPassword($newHashedPassword);
$this->_em->persist($user);
$this->_em->flush();
}
public function getForm(string $route, User $user): FormInterface {
$params=$user->getId() !== null ? array('id'=>$user->getId(),) : array();
return $this->formFactory->create(SecurityType::class, $user, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Repository;
use App\Entity\WorldmarkCategory;
use App\Form\WorldmarkCategoryType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method WorldmarkCategory|null find($id, $lockMode=null, $lockVersion=null)
* @method WorldmarkCategory|null findOneBy(array $criteria, array $orderBy=null)
* @method WorldmarkCategory[] findAll()
* @method WorldmarkCategory[] findBy(array $criteria, array $orderBy=null, $limit=null, $offset=null)
*/
class WorldmarkCategoryRepository extends ServiceEntityRepository {
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory) {
parent::__construct($registry, WorldmarkCategory::class);
}
public function getForm(string $route, WorldmarkCategory $worldmarkCategory): FormInterface {
$params=$worldmarkCategory->getId() !== null ? array('id'=>$worldmarkCategory->getId()) : array();
return $this->formFactory->create(WorldmarkCategoryType::class, $worldmarkCategory, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace App\Repository;
use App\Entity\Region;
use App\Entity\Worldmark;
use App\Entity\WorldmarkCategory;
use App\Form\WorldmarkType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @method Worldmark|null find($id, $lockMode=null, $lockVersion=null)
* @method Worldmark|null findOneBy(array $criteria, array $orderBy=null)
* @method Worldmark[] findAll()
* @method Worldmark[] findBy(array $criteria, array $orderBy=null, $limit=null, $offset=null)
*/
class WorldmarkRepository extends ServiceEntityRepository {
/**
* @param ManagerRegistry $registry
* @param RouterInterface $router
* @param FormFactoryInterface $formFactory
*/
public function __construct(ManagerRegistry $registry,
private RouterInterface $router,
private FormFactoryInterface $formFactory) {
parent::__construct($registry, Worldmark::class);
}
/**
* @param Region $region
* @param float $version
* @return Worldmark[]
*/
public function getRegionWorldmarks(Region $region, float $version): array {
$qb=$this->_em->createQueryBuilder();
$qb->select("worldmark")
->addSelect("category")
->from(Worldmark::class, "worldmark")
->join("worldmark.regions", "regions")
->join("worldmark.category", "category")
->andWhere($qb->expr()->eq("regions.id", ":param_regionId"))
->andWhere($qb->expr()->lte("worldmark.version", ":param_version"))
->orderBy("category.sortOrder")
->addOrderBy("worldmark.sortOrder");
$qb->setParameters(array(
'param_regionId'=>$region->getId(),
'param_version'=>$version,
));
return $qb->getQuery()->getResult();
}
public function getForm(string $route, WorldmarkCategory $worldmarkCategory, Worldmark $worldmark): FormInterface {
$params=$worldmark->getId() !== null ? array('worldmarkCategoryId'=>$worldmarkCategory->getId(), 'id'=>$worldmark->getId()) : array('worldmarkCategoryId'=>$worldmarkCategory->getId());
return $this->formFactory->create(WorldmarkType::class, $worldmark, array(
'action'=>$this->router->generate($route, $params),
'method'=>'post',
'data_route'=>$route,
));
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class Authenticator extends AbstractLoginFormAuthenticator {
use TargetPathTrait;
public const LOGIN_ROUTE = 'security_login';
private UrlGeneratorInterface $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator) {
$this->urlGenerator = $urlGenerator;
}
public function authenticate(Request $request): Passport {
$username = $request->request->get('username', '');
$request->getSession()->set(Security::LAST_USERNAME, $username);
return new Passport(
new UserBadge($username),
new PasswordCredentials($request->request->get('password', '')),
array(
new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')),
new RememberMeBadge(),
),
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response {
if($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
// For example:
//return new RedirectResponse($this->urlGenerator->generate('some_route'));
// throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
return new RedirectResponse($this->urlGenerator->generate('fo_region_show', array('slug'=>'mondstadt')));
}
protected function getLoginUrl(Request $request): string {
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}

401
src/Service/FileManager.php Normal file
View File

@ -0,0 +1,401 @@
<?php
namespace App\Service;
use App\Entity\Item;
use App\Entity\Monster;
use Exception;
use Imagick;
use ImagickPixel;
use Ramsey\Uuid\Uuid;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileManager {
private Filesystem $filesystem;
private array $parameters;
public function __construct(ParameterBagInterface $parameterBag) {
$this->filesystem=new Filesystem();
$this->parameters=array(
'assets'=>$parameterBag->get('assets'),
'env'=>$parameterBag->get('kernel.environment')
);
}
private function generateUuid(?string $directory): bool|string {
if($directory) {
$scan=scandir($directory);
if($scan) {
do {
$filename=Uuid::uuid1()->toString();
} while(in_array($filename, $scan));
return $filename;
} else {
return false;
}
} else {
return Uuid::uuid1()->toString();
}
}
private function resizeJpeg(string $sourceFile, string $targetPath, string $filename, int $width, int $height, int $quality = 100): array {
try {
$img = new Imagick();
$img->setResolution(72, 72);
$img->setBackgroundColor((new ImagickPixel('#1B283B')));
$img->setInterlaceScheme(Imagick::INTERLACE_LINE);
$img->readImage($sourceFile);
$img->setImageUnits(Imagick::RESOLUTION_PIXELSPERINCH);
$img->setImageResolution(72, 72);
$img->setImageFormat('jpeg');
$img->setImageCompression(Imagick::COMPRESSION_JPEG);
$img->setImageCompressionQuality($quality);
if($img->getImageWidth() > $width) {
$img->scaleImage($width, $height, true);
}
$img->extentImage($width, $height, (($img->getImageWidth() - $width) / 2), (($img->getImageHeight() - $height) / 2));
$img->stripImage();
$img->writeImage("$targetPath/$filename.jpeg");
$img->clear();
$img->destroy();
return array(
'error'=>false,
'message'=>null,
);
} catch(Exception) {
return array(
'error'=>true,
'message'=>'Could not write file to disk',
);
}
}
private function resizePng(string $sourceFile, string $targetPath, string $filename, int $width, int $height): array {
try {
$img=new Imagick();
$img->setResolution(72, 72);
$img->setBackgroundColor((new ImagickPixel('transparent')));
$img->readImage($sourceFile);
$img->setImageUnits(Imagick::RESOLUTION_PIXELSPERINCH);
$img->setImageResolution(72, 72);
$img->setImageFormat('png');
$img->scaleImage($width, $height, true);
$img->extentImage($width, $height, (($img->getImageWidth() - $width) / 2), (($img->getImageHeight() - $height) / 2));
$img->stripImage();
$img->writeImage("$targetPath/$filename.png");
$img->clear();
$img->destroy();
return array(
'error'=>false,
'message'=>null,
);
} catch(Exception) {
return array(
'error'=>true,
'message'=>'Could not write file to disk',
);
}
}
private function uploadFile(UploadedFile $uploadedFile, string $type): array {
if(!in_array($type, array('map', 'screenshot', 'item', 'monster', 'worldmark'))) {
return array(
'error'=>true,
'filename'=>null,
'message'=>'File could not be processed',
);
}
$ext=$uploadedFile->guessExtension();
if(!$ext) {
return array(
'error'=>true,
'filename'=>null,
'message'=>'Cannot guess file extension from mimetype',
);
}
if(!in_array($ext, $this->parameters['assets'][$type]['ext'])) {
return array(
'error'=>true,
'filename'=>null,
'message'=>sprintf("File type not supported. Supported files types are [%s]", implode(', ', $this->parameters['assets'][$type]['ext'])),
);
}
$this->filesystem->mkdir($this->parameters['assets'][$type]['upload_path'], 0775);
$filename=$this->generateUuid($this->parameters['assets'][$type]['upload_path']);
$uploadedFile->move($this->parameters['assets'][$type]['upload_path'], "$filename.$ext");
if(in_array($type, array('item', 'monster', 'worldmark'))) {
$imgWidth = match ($type) {
'item', 'monster' => $imgHeight = 160,
'worldmark' => $imgHeight = 48,
default => $imgHeight = null,
};
if($imgWidth === null) {
return array(
'error'=>true,
'filename'=>null,
'message'=>'Could not define image dimensions',
);
}
$process=$this->resizePng("{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext",
$this->parameters['assets'][$type]['upload_path'], $filename, $imgWidth, $imgHeight);
if($process['error']) {
if($type == 'item') {
$this->removeItemIcon("$filename.$ext");
} elseif($type == 'monster') {
$this->removeMonsterIcon("$filename.$ext");
} elseif($type == 'worldmark') {
$this->removeWorldmarkIcon("$filename.$ext");
}
return array(
'error'=>true,
'filename'=>null,
'message'=>'Could not write item file to disk',
);
}
} else if($type == 'screenshot') {
$imgSizes=getimagesize("{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext");
if($imgSizes[0] < 1280 || $imgSizes[1] < 720) {
$this->filesystem->remove("{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext");
return array(
'error'=>true,
'filename'=>null,
'message'=>'Screenshot must be at least 1280*720 pixels',
);
}
$process=$this->resizeJpeg("{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext",
$this->parameters['assets'][$type]['upload_path'], $filename,
1280, 720);
if($process['error']) {
$this->removeScreenshot("$filename.$ext");
return array(
'error'=>true,
'filename'=>null,
'message'=>'Could not write screenshot file to disk',
);
}
if(in_array($ext, ['jpg', 'png'])) {
$this->filesystem->remove("{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext");
$ext = 'jpeg';
}
$process=$this->resizeJpeg("{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext",
$this->parameters['assets'][$type]['thumbnail_path'], $filename, 320, 180, 65);
if($process['error']) {
$this->removeScreenshot("$filename.$ext");
return array(
'error'=>true,
'filename'=>null,
'message'=>'Could not write screenshot file to disk',
);
}
if($this->parameters['env'] == 'dev') {
$this->filesystem->copy("{$this->parameters['assets'][$type]['thumbnail_path']}/$filename.$ext",
str_replace("dev.genshin-world.com",
"genshin-world.com",
"{$this->parameters['assets'][$type]['thumbnail_path']}/$filename.$ext"));
} elseif($this->parameters['env'] == 'prod') {
$this->filesystem->copy("{$this->parameters['assets'][$type]['thumbnail_path']}/$filename.$ext",
str_replace("genshin-world.com",
"dev.genshin-world.com",
"{$this->parameters['assets'][$type]['thumbnail_path']}/$filename.$ext"));
}
}
if($this->parameters['env'] == 'dev') {
$this->filesystem->copy("{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext",
str_replace("dev.genshin-world.com",
"genshin-world.com",
"{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext"));
} elseif($this->parameters['env'] == 'prod') {
$this->filesystem->copy("{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext",
str_replace("genshin-world.com",
"dev.genshin-world.com",
"{$this->parameters['assets'][$type]['upload_path']}/$filename.$ext"));
}
return array(
'error'=>false,
'filename'=>"$filename.$ext",
'message'=>null,
);
}
private function removeLocalFile(string $filePath) {
$this->filesystem->remove($filePath);
}
public function uploadMapFile(UploadedFile $uploadedFile): array {
return $this->uploadFile($uploadedFile, 'map');
}
public function removeMapFile(string $filename) {
$this->removeLocalFile("{$this->parameters['assets']['map']['upload_path']}/$filename");
if($this->parameters['env'] == 'dev') {
$this->removeLocalFile(str_replace("dev.genshin-world.com",
"genshin-world.com",
"{$this->parameters['assets']['map']['upload_path']}/$filename"));
} elseif($this->parameters['env'] == 'prod') {
$this->removeLocalFile(str_replace("genshin-world.com",
"dev.genshin-world.com",
"{$this->parameters['assets']['map']['upload_path']}/$filename"));
}
}
public function uploadItemIcon(UploadedFile $uploadedFile): array {
return $this->uploadFile($uploadedFile, 'item');
}
public function removeItemIcon(string $filename) {
$this->removeLocalFile("{$this->parameters['assets']['item']['upload_path']}/$filename");
if($this->parameters['env'] == 'dev') {
$this->removeLocalFile(str_replace("dev.genshin-world.com",
"genshin-world.com",
"{$this->parameters['assets']['item']['upload_path']}/$filename"));
} elseif($this->parameters['env'] == 'prod') {
$this->removeLocalFile(str_replace("genshin-world.com",
"dev.genshin-world.com",
"{$this->parameters['assets']['item']['upload_path']}/$filename"));
}
}
public function uploadMonsterIcon(UploadedFile $uploadedFile): array {
return $this->uploadFile($uploadedFile, 'monster');
}
public function removeMonsterIcon(string $filename) {
$this->removeLocalFile("{$this->parameters['assets']['monster']['upload_path']}/$filename");
if($this->parameters['env'] == 'dev') {
$this->removeLocalFile(str_replace("dev.genshin-world.com",
"genshin-world.com",
"{$this->parameters['assets']['monster']['upload_path']}/$filename"));
} elseif($this->parameters['env'] == 'prod') {
$this->removeLocalFile(str_replace("genshin-world.com",
"dev.genshin-world.com",
"{$this->parameters['assets']['monster']['upload_path']}/$filename"));
}
}
public function uploadWorldmarkIcon(UploadedFile $uploadedFile): array {
return $this->uploadFile($uploadedFile, 'worldmark');
}
public function getWorldmarkIconFromEntity(Item|Monster $entity): array {
$type = $entity instanceof Item ? 'item' : 'monster';
if(!$this->filesystem->exists("{$this->parameters['assets'][$type]['upload_path']}/{$entity->getIcon()}")) {
return array(
'error'=>true,
'filename'=>null,
'message'=>'Could not find any file to copy',
);
}
$itemFile=new File("{$this->parameters['assets'][$type]['upload_path']}/{$entity->getIcon()}");
$ext=$itemFile->guessExtension();
$filename=$this->generateUuid($this->parameters['assets']['worldmark']['upload_path']);
$this->filesystem->mkdir($this->parameters['assets']['worldmark']['upload_path'], 0775);
$this->filesystem->copy("{$this->parameters['assets'][$type]['upload_path']}/{$entity->getIcon()}",
"{$this->parameters['assets']['worldmark']['upload_path']}/$filename.$ext");
$process=$this->resizePng("{$this->parameters['assets']['worldmark']['upload_path']}/$filename.$ext",
$this->parameters['assets']['worldmark']['upload_path'], $filename, 48, 48);
if($this->parameters['env'] == 'dev') {
$this->filesystem->copy("{$this->parameters['assets']['worldmark']['upload_path']}/$filename.$ext",
str_replace("dev.genshin-world.com",
"genshin-world.com",
"{$this->parameters['assets']['worldmark']['upload_path']}/$filename.$ext"));
} elseif($this->parameters['env'] == 'prod') {
$this->filesystem->copy("{$this->parameters['assets']['worldmark']['upload_path']}/$filename.$ext",
str_replace("genshin-world.com",
"dev.genshin-world.com",
"{$this->parameters['assets']['worldmark']['upload_path']}/$filename.$ext"));
}
if($process['error']) {
$this->removeItemIcon($filename);
return array(
'error'=>true,
'filename'=>null,
'message'=>'Could not write item icon file to disk',
);
}
return array(
'error'=>false,
'filename'=>"$filename.$ext",
'message'=>null,
);
}
public function removeWorldmarkIcon(string $filename) {
$this->removeLocalFile("{$this->parameters['assets']['worldmark']['upload_path']}/$filename");
}
public function uploadScreenshot(UploadedFile $uploadedFile): array {
return $this->uploadFile($uploadedFile, 'screenshot');
}
public function removeScreenshot(string $filename) {
$this->removeLocalFile("{$this->parameters['assets']['screenshot']['upload_path']}/$filename");
$this->removeLocalFile("{$this->parameters['assets']['screenshot']['thumbnail_path']}/$filename");
if($this->parameters['env'] == 'dev') {
$this->removeLocalFile(str_replace("dev.genshin-world.com",
"genshin-world.com",
"{$this->parameters['assets']['screenshot']['upload_path']}/$filename"));
$this->removeLocalFile(str_replace("dev.genshin-world.com",
"genshin-world.com",
"{$this->parameters['assets']['screenshot']['thumbnail_path']}/$filename"));
} elseif($this->parameters['env'] == 'prod') {
$this->removeLocalFile(str_replace("genshin-world.com",
"dev.genshin-world.com",
"{$this->parameters['assets']['screenshot']['upload_path']}/$filename"));
$this->removeLocalFile(str_replace("genshin-world.com",
"dev.genshin-world.com",
"{$this->parameters['assets']['screenshot']['thumbnail_path']}/$filename"));
}
}
}