<?php
declare(strict_types=1);
namespace App\Nolimit\Task\Controller;
use App\Entity\Project;
use App\Entity\ProjectStatus;
use App\Entity\ProjectType;
use App\Entity\Task;
use App\Entity\TaskStatus;
use App\Entity\TaskType;
use App\Entity\User;
use App\Nolimit\Task\Manager\TaskManager;
use App\Nolimit\Task\Persister\TaskLogPersister;
use App\Nolimit\Task\Persister\TaskPersister;
use App\Nolimit\Task\Provider\TaskDatatableProvider;
use App\Nolimit\Task\Service\TaskTimeImportService;
use App\Repository\ProjectRepository;
use App\Repository\ProjectTypeRepository;
use App\Repository\TaskLogRepository;
use App\Repository\TaskRepository;
use App\Repository\TaskStatusRepository;
use App\Repository\TaskTypeRepository;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class TaskController extends CRUDController{
private EntityManagerInterface $entity;
private TaskDatatableProvider $datatableProvider;
private TranslatorInterface $translator;
private TaskManager $taskManager;
private TaskLogRepository $taskLogRepository;
private TaskRepository $taskRepository;
private TaskLogPersister $taskLogPersister;
private TaskPersister $taskPersister;
private TaskDatatableProvider $taskDatatableProvider;
private ProjectRepository $projectRepository;
private ProjectTypeRepository $projectTypeRepository;
private TaskTypeRepository $taskTypeRepository;
private TaskStatusRepository $taskStatusRepository;
private UserRepository $userRepository;
private TaskTimeImportService $taskTimeImportService;
/**
* @var TokenStorageInterface
*/
private $tokenStorage;
public function __construct(EntityManagerInterface $entity,
TaskDatatableProvider $datatableProvider,
TranslatorInterface $translator,
TaskManager $taskManager,TaskLogRepository $taskLogRepository,TaskRepository $taskRepository,TaskLogPersister $taskLogPersister,TaskPersister $taskPersister,TaskDatatableProvider $taskDatatableProvider,ProjectRepository $projectRepository,ProjectTypeRepository $projectTypeRepository,TaskTypeRepository $taskTypeRepository,TaskStatusRepository $taskStatusRepository,UserRepository $userRepository,TokenStorageInterface $tokenStorage, TaskTimeImportService $taskTimeImportService)
{
$this->entity = $entity;
$this->datatableProvider = $datatableProvider;
$this->translator = $translator;
$this->taskManager = $taskManager;
$this->taskLogRepository = $taskLogRepository;
$this->taskRepository = $taskRepository;
$this->taskLogPersister = $taskLogPersister;
$this->taskPersister = $taskPersister;
$this->taskDatatableProvider = $taskDatatableProvider;
$this->projectRepository = $projectRepository;
$this->projectTypeRepository = $projectTypeRepository;
$this->taskTypeRepository = $taskTypeRepository;
$this->taskStatusRepository = $taskStatusRepository;
$this->userRepository = $userRepository;
$this->tokenStorage = $tokenStorage;
$this->taskTimeImportService = $taskTimeImportService;
}
public function listAction(Request $request): Response
{
// $notActiveProjetStatu=[ProjectStatus::PROJECT_STATUS_BEFEJEZETT, ProjectStatus::PROJECT_STATUS_FELFUGESZTETT, ProjectStatus::PROJECT_STATUS_VISSZAUTASITOTT];
// $projects=$this->projectRepository->getProjectByStatusNotIn($notActiveProjetStatu);
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$projects=$this->projectRepository->findAll();
$projectTypes=$this->projectTypeRepository->findAll();
$taskTypes=$this->taskTypeRepository->findAll();
$taskStatus=$this->taskStatusRepository->findAll();
$user=$this->tokenStorage->getToken()->getUser();
$user_id=$user->getId();
$isSuperAdmin = in_array("ROLE_SUPER_ADMIN", $user->getRoles());
$selectedStatus = $isSuperAdmin
? [TaskStatus::TASK_STATUS_LETREHOZOTT, TaskStatus::TASK_STATUS_FEJLESZTESALATT, TaskStatus::TASK_STATUS_BEFEJEZETT]
: [TaskStatus::TASK_STATUS_LETREHOZOTT, TaskStatus::TASK_STATUS_FEJLESZTESALATT];
$users=$this->userRepository->findAll();
return $this->renderWithExtraParams('/task/task_view.html.twig', array(
'projects'=>$projects,
'projectTypes'=>$projectTypes,
'taskTypes'=>$taskTypes,
'taskStatus'=>$taskStatus,
'selectedStatus'=>$selectedStatus,
'user_id'=>$user_id,
'current_user_id'=>$user_id,
'is_super_admin'=>$isSuperAdmin,
'users'=>$users
));
}
/**
* @Route("/admin/app/task/task_list", name="task_list", methods={"POST"} )
*/
public function taskListAction()
{
$user = $this->tokenStorage->getToken()->getUser();
$user_id = $user->getId();
$isSuperAdmin = in_array("ROLE_SUPER_ADMIN", $user->getRoles());
$selectedStatus = $isSuperAdmin
? [TaskStatus::TASK_STATUS_LETREHOZOTT, TaskStatus::TASK_STATUS_FEJLESZTESALATT, TaskStatus::TASK_STATUS_BEFEJEZETT]
: [TaskStatus::TASK_STATUS_LETREHOZOTT, TaskStatus::TASK_STATUS_FEJLESZTESALATT];
$adata['data'] = $this->datatableProvider->datatableRow($selectedStatus, $user_id);
return $this->json($adata);
}
/**
* @Route("/admin/app/apv/task_filtered", name="task_filtered", methods={"POST"} )
*/
public function taskFilteredAction(Request $request): Response
{
$adata['adata'] = $this->taskDatatableProvider->getFilteredData($request);
return $this->json($adata);
}
/**
* @Route("/admin/app/task/task_new", name="task_new", methods={"POST"} )
*/
public function taskNewAction(Request $request): Response
{
if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) {
// AJAX-kérés esetén 401-es válasz küldése
if ($request->isXmlHttpRequest()) {
return new JsonResponse(['error' => 'Unauthorized'], 401);
}
// Nem AJAX-kérés esetén hozzáférés megtagadása
throw new AccessDeniedException('Hozzáférés megtagadva');
}
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$currentUser = $this->tokenStorage->getToken()->getUser();
$isSuperAdmin = in_array("ROLE_SUPER_ADMIN", $currentUser->getRoles());
$project = $this->projectRepository->getProjectsForTaskAdd($isSuperAdmin);
$user = $this->entity->getRepository(User::class)->findAll();
$taskType = $this->entity->getRepository(TaskType::class)->findAll();
$taskStatus = $this->entity->getRepository(TaskStatus::class)->findAll();
$lastWorkedDay = $this->taskManager->getLastWorkedDay();
$defaultProject = !empty($project) ? reset($project) : null;
$nextOrderTask = $defaultProject ? $this->taskRepository->getNextOrderTaskByProjectId($defaultProject->getId()) : 1;
return $this->renderWithExtraParams('/task/task_new_edit.html.twig', array(
'project'=>$project,
'user'=>$user,
'taskType'=>$taskType,
'title'=>$this->translator->trans('New task'),
'edited'=>false,
'lastWorkedDay'=>$lastWorkedDay,
'taskStatus'=>$taskStatus,
'nextOrderTask'=>$nextOrderTask
));
}
/**
* @Route("/admin/app/task/task_edit", name="task_edit", methods={"POST"} )
*/
public function taskEditAction(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$id = $request->get('id');
$task = $this->entity->getRepository(Task::class)->find($id);
// Ellenőrzés: Ha befejezett task, karbantartási projekt és van tranche_id, csak super admin módosíthatja
$currentUser = $this->tokenStorage->getToken()->getUser();
$isSuperAdmin = in_array("ROLE_SUPER_ADMIN", $currentUser->getRoles());
if ($task->getTaskStatus()->getId() == TaskStatus::TASK_STATUS_BEFEJEZETT &&
in_array($task->getProject()->getProjectType()->getId(), [ProjectType::PROJECT_TYPE_KARBANTARTAS, ProjectType::PROJECT_TYPE_HOSTING]) &&
$task->getTranche() !== null &&
!$isSuperAdmin) {
return new JsonResponse(['error' => 'Ez a feladat nem módosítható. Csak Super Admin módosíthatja a lezárt karbantartási vagy hosting feladatokat, amelyekhez tartozik tranche.'], 403);
}
// Átadott vagy befejezett projekt + befejezett feladat esetén csak super admin módosíthat
if ($task->getTaskStatus()->getId() == TaskStatus::TASK_STATUS_BEFEJEZETT &&
$task->getProject()->getStatus() &&
in_array($task->getProject()->getStatus()->getId(), [ProjectStatus::PROJECT_STATUS_ATADOTT, ProjectStatus::PROJECT_STATUS_BEFEJEZETT]) &&
!$isSuperAdmin) {
return new JsonResponse(['error' => 'Ez a feladat nem módosítható, mert a projekt átadott vagy befejezett és a feladat be van fejezve. Csak Super Admin módosíthatja.'], 403);
}
// ERP / WEB fejlesztés + befejezett feladat esetén csak super admin módosíthat
if ($task->getTaskStatus()->getId() == TaskStatus::TASK_STATUS_BEFEJEZETT &&
$task->getProject()->getProjectType() &&
in_array($task->getProject()->getProjectType()->getId(), [ProjectType::PROJECT_TYPE_ERP_FEJLESZTES, ProjectType::PROJECT_TYPE_WEB_FEJLESZTES]) &&
!$isSuperAdmin) {
return new JsonResponse(['error' => 'Ez a feladat nem módosítható, mert ERP vagy WEB fejlesztési projekt befejezett feladata. Csak Super Admin módosíthatja.'], 403);
}
$project = $this->projectRepository->getProjectsForTaskAdd($isSuperAdmin);
// Szerkesztéskor a feladat jelenlegi projektje mindig szerepeljen a listában (ha nem Super Admin)
$currentProject = $task->getProject();
if ($currentProject && !in_array($currentProject, $project, true)) {
$project = array_merge([$currentProject], $project);
}
$taskStatus = $this->entity->getRepository(TaskStatus::class)->findAll();
$user = $this->entity->getRepository(User::class)->findAll();
$taskType = $this->entity->getRepository(TaskType::class)->findAll();
return $this->renderWithExtraParams('/task/task_new_edit.html.twig', array(
'project'=>$project,
'user'=>$user,
'task'=>$task,
'title'=>$this->translator->trans('Edit task'),
'taskStatus'=>$taskStatus,
'taskType'=>$taskType,
'edited'=>true
));
}
/**
* @Route("/admin/app/task/task_save", name="task_save", methods={"POST"} )
*/
public function taskSaveAction(Request $request): Response
{
$task = $this->taskPersister->insertUpdateTask($request);
$data[] = $this->taskDatatableProvider->getRow($task);
return new JsonResponse(array('msg' => true,'datatableRow' =>$data[0]), 200);
}
/**
* @Route("/admin/app/task/task_next_order", name="task_next_order", methods={"POST"} )
*/
public function taskNextOrderAction(Request $request): JsonResponse
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$projectId = (int) $request->get('projectId');
if ($projectId <= 0) {
return new JsonResponse(['nextOrderTask' => 1]);
}
return new JsonResponse([
'nextOrderTask' => $this->taskRepository->getNextOrderTaskByProjectId($projectId),
]);
}
/**
* @Route("/admin/app/task/task_new_log", name="task_new_log", methods={"POST"} )
*/
public function taskNewLogAction(Request $request): Response
{
$id = $request->get('id');
$task = $this->entity->getRepository(Task::class)->find($id);
$lastWorkedDay = $this->taskManager->getLastWorkedDay();
return $this->renderWithExtraParams('/task/task_new_edit_log.html.twig', array(
'task'=>$task,
'title'=>$this->translator->trans('New log'),
'edited'=>false,
'lastWorkedDay'=>$lastWorkedDay
));
}
/**
* @Route("/admin/app/task/task_edit_log", name="task_edit_log", methods={"POST"} )
*/
public function taskEditLogAction(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$id = (int)$request->get('id');
$taskLog = $this->taskLogRepository->find($id);
if (!$taskLog) {
return new JsonResponse(['error' => 'A log nem található.'], 404);
}
$currentUser = $this->tokenStorage->getToken()->getUser();
$isSuperAdmin = in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles());
if (!$isSuperAdmin && $taskLog->getUser()->getId() !== $currentUser->getId()) {
return new JsonResponse(['error' => 'Nincs jogosultsága a log módosításához.'], 403);
}
return $this->renderWithExtraParams('/task/task_new_edit_log.html.twig', array(
'task'=>$taskLog->getTask(),
'title'=>$this->translator->trans('Edit log'),
'taskLog'=>$taskLog,
'workedHourMin'=>$this->taskManager->convertMinInHourMin($taskLog->getWorkedMinutes()),
'descriptionValue'=>$this->taskDatatableProvider->sanitizeTextForDatatable($taskLog->getDescription()),
'edited'=>true
));
}
/**
* @Route("/admin/app/task/task_save_log", name="task_save_log", methods={"POST"} )
*/
public function taskSaveLogAction(Request $request): Response
{
$this->taskLogPersister->insertUpdateTaskLog($request);
$task = $this->taskRepository->find($request->get('taskId'));
$data[] = $this->datatableProvider->getRow($task);
return new JsonResponse(array('msg' => true,'datatableRow' =>$data[0]), 200);
}
/**
* @Route("/admin/app/task/task_delete_log", name="task_delete_log", methods={"POST"} )
*/
public function taskDeleteLogAction(Request $request): Response
{
$logId = (int)$request->get('logId');
$taskId = (int)$request->get('taskId');
$taskLog = $this->taskLogRepository->find($logId);
$this->entity->remove($taskLog);
$this->entity->flush();
$task = $this->taskRepository->find($taskId);
$data[] = $this->taskDatatableProvider->getRow($task);
return new JsonResponse(array('msg' => true,'datatableRow' =>$data[0]), 200);
}
/**
* @Route("/admin/app/task/task_move_log", name="task_move_log", methods={"POST"} )
*/
public function taskMoveLogAction(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$currentUser = $this->tokenStorage->getToken()->getUser();
if (!in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles())) {
return new JsonResponse(['error' => 'Csak Super Admin helyezheti át a logot.'], 403);
}
$logId = (int)$request->get('logId');
$taskLog = $this->taskLogRepository->find($logId);
if (!$taskLog) {
return new JsonResponse(['error' => 'A log nem található.'], 404);
}
$task = $taskLog->getTask();
$projects = $this->projectRepository->findBy([], ['name' => 'ASC']);
$taskItems = $this->formatTasksForSelect(
$task->getProject()->getId(),
$task->getId()
);
return $this->renderWithExtraParams('/task/task_move_log.html.twig', [
'taskLog' => $taskLog,
'task' => $task,
'taskName' => $this->taskDatatableProvider->sanitizeTextForDatatable($task->getName()),
'projects' => $projects,
'tasks' => $taskItems,
'title' => $this->translator->trans('Move log'),
]);
}
/**
* @Route("/admin/app/task/task_move_log_tasks", name="task_move_log_tasks", methods={"POST"} )
*/
public function taskMoveLogTasksAction(Request $request): JsonResponse
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$currentUser = $this->tokenStorage->getToken()->getUser();
if (!in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles())) {
return new JsonResponse(['error' => 'Csak Super Admin.'], 403);
}
$projectId = (int)$request->get('project_id');
$sourceTaskId = (int)$request->get('source_task_id');
return new JsonResponse([
'tasks' => $this->formatTasksForSelect($projectId, $sourceTaskId),
]);
}
/**
* @Route("/admin/app/task/task_move_log_save", name="task_move_log_save", methods={"POST"} )
*/
public function taskMoveLogSaveAction(Request $request): JsonResponse
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$currentUser = $this->tokenStorage->getToken()->getUser();
if (!in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles())) {
return new JsonResponse(['error' => 'Csak Super Admin helyezheti át a logot.'], 403);
}
try {
$result = $this->taskLogPersister->moveTaskLog($request);
$rows = [];
$taskIds = [];
foreach ([$result['sourceTask'], $result['targetTask']] as $t) {
$id = $t->getId();
if (!in_array($id, $taskIds, true)) {
$taskIds[] = $id;
$rows[] = $this->taskDatatableProvider->getRow($t);
}
}
return new JsonResponse([
'msg' => true,
'datatableRows' => $rows,
'sourceTaskId' => $result['sourceTask']->getId(),
]);
} catch (\Exception $e) {
return new JsonResponse(['error' => $e->getMessage()], 400);
}
}
/**
* @Route("/admin/app/task/task_delete", name="task_delete", methods={"POST"} )
*/
public function taskDeleteAction(Request $request): Response
{
$id = (int)$request->get('id');
$task = $this->taskRepository->find($id);
$this->entity->remove($task);
$this->entity->flush();
return new JsonResponse(array('msg' => true), 200);
}
/**
* @Route("/admin/app/task/task_import", name="task_import", methods={"POST"} )
*/
public function taskImportAction(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$currentUser = $this->tokenStorage->getToken()->getUser();
return $this->renderWithExtraParams('/task/task_import_modal.html.twig', $this->importModalParams(
$currentUser->getName(),
'Import time from PDF',
'Select PDF file',
'.pdf,application/pdf',
'pdf_file',
'task_import_parse',
'',
'Time import select pdf',
'Time import no rows'
));
}
/**
* @Route("/admin/app/task/task_import_toptracker", name="task_import_toptracker", methods={"POST"} )
*/
public function taskImportToptrackerAction(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$currentUser = $this->tokenStorage->getToken()->getUser();
return $this->renderWithExtraParams('/task/task_import_modal.html.twig', $this->importModalParams(
$currentUser->getName(),
'Import time from TopTracker',
'Select TopTracker CSV file',
'.csv,text/csv',
'toptracker_file',
'task_import_toptracker_parse',
'Time import toptracker upload hint',
'Time import select toptracker',
'Time import toptracker no rows'
));
}
/**
* @Route("/admin/app/task/task_import_toptracker_parse", name="task_import_toptracker_parse", methods={"POST"} )
*/
public function taskImportToptrackerParseAction(Request $request): JsonResponse
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
/** @var UploadedFile|null $file */
$file = $request->files->get('toptracker_file');
if ($file === null || !$file->isValid()) {
return new JsonResponse(['error' => 'Érvénytelen vagy hiányzó TopTracker CSV fájl.'], 400);
}
$extension = strtolower((string) $file->getClientOriginalExtension());
if (!in_array($extension, ['csv', 'txt'], true)) {
return new JsonResponse(['error' => 'A TopTracker kivonat CSV fájl legyen.'], 400);
}
$content = file_get_contents($file->getPathname());
if ($content === false) {
return new JsonResponse(['error' => 'A fájl nem olvasható.'], 400);
}
$currentUser = $this->tokenStorage->getToken()->getUser();
$isSuperAdmin = in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles(), true);
try {
$result = $this->taskTimeImportService->parseAndMatchTopTracker($content, $isSuperAdmin);
} catch (\InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], 400);
}
return new JsonResponse($result, 200);
}
/**
* @return array<string, string>
*/
private function importModalParams(
string $importUserName,
string $importTitle,
string $fileLabel,
string $fileAccept,
string $fileField,
string $parseRoute,
string $uploadHint,
string $missingFileMessage,
string $noRowsMessage
): array {
return [
'importUserName' => $importUserName,
'importTitle' => $importTitle,
'fileLabel' => $fileLabel,
'fileAccept' => $fileAccept,
'fileField' => $fileField,
'parseUrl' => $this->admin->generateUrl($parseRoute),
'uploadHint' => $uploadHint,
'missingFileMessage' => $missingFileMessage,
'noRowsMessage' => $noRowsMessage,
];
}
/**
* @Route("/admin/app/task/task_import_parse", name="task_import_parse", methods={"POST"} )
*/
public function taskImportParseAction(Request $request): JsonResponse
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
/** @var UploadedFile|null $file */
$file = $request->files->get('pdf_file');
if ($file === null || !$file->isValid()) {
return new JsonResponse(['error' => 'Érvénytelen vagy hiányzó PDF fájl.'], 400);
}
$content = file_get_contents($file->getPathname());
if ($content === false) {
return new JsonResponse(['error' => 'A fájl nem olvasható.'], 400);
}
$currentUser = $this->tokenStorage->getToken()->getUser();
$isSuperAdmin = in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles(), true);
$result = $this->taskTimeImportService->parseAndMatch($content, $isSuperAdmin);
return new JsonResponse($result, 200);
}
/**
* @Route("/admin/app/task/task_import_tasks", name="task_import_tasks", methods={"POST"} )
*/
public function taskImportTasksAction(Request $request): JsonResponse
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$projectId = (int) $request->get('project_id');
$currentUser = $this->tokenStorage->getToken()->getUser();
$isSuperAdmin = in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles(), true);
return new JsonResponse([
'tasks' => $this->taskTimeImportService->formatTasks($projectId, $isSuperAdmin),
]);
}
/**
* @Route("/admin/app/task/task_import_save", name="task_import_save", methods={"POST"} )
*/
public function taskImportSaveAction(Request $request): JsonResponse
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$data = json_decode($request->getContent(), true);
$rows = $data['rows'] ?? [];
if (!is_array($rows) || $rows === []) {
return new JsonResponse(['error' => 'Nincs mentendő sor.'], 400);
}
try {
$result = $this->taskTimeImportService->saveRows($rows);
return new JsonResponse($result, 200);
} catch (\Exception $e) {
return new JsonResponse(['error' => $e->getMessage()], 400);
}
}
private function formatTasksForSelect(int $projectId, int $excludeTaskId): array
{
$items = [];
foreach ($this->taskRepository->getTasksByProjectId($projectId) as $t) {
if ($t->getId() === $excludeTaskId) {
continue;
}
$items[] = [
'id' => $t->getId(),
'name' => $this->taskDatatableProvider->sanitizeTextForDatatable($t->getName()),
];
}
return $items;
}
}