672 lines
22 KiB
PHP

<?php
namespace Chill\TaskBundle\Controller;
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Chill\TaskBundle\Entity\SingleTask;
use Chill\TaskBundle\Form\SingleTaskType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormFactoryInterface;
use Chill\TaskBundle\Security\Authorization\TaskVoter;
use Symfony\Component\Security\Core\Role\Role;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\TaskBundle\Event\TaskEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Chill\TaskBundle\Event\UI\UIEvent;
use Chill\MainBundle\Timeline\TimelineBuilder;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface as TranslationTranslatorInterface;
/**
* Class SingleTaskController
*
* @package Chill\TaskBundle\Controller
*/
final class SingleTaskController extends AbstractController
{
private EventDispatcherInterface $eventDispatcher;
private TimelineBuilder $timelineBuilder;
private LoggerInterface $logger;
private CenterResolverDispatcher $centerResolverDispatcher;
private TranslatorInterface $translator;
private PaginatorFactory $paginatorFactory;
private SingleTaskAclAwareRepositoryInterface $singleTaskAclAwareRepository;
private FilterOrderHelperFactoryInterface $filterOrderHelperFactory;
public function __construct(
CenterResolverDispatcher $centerResolverDispatcher,
PaginatorFactory $paginatorFactory,
SingleTaskAclAwareRepositoryInterface $singleTaskAclAwareRepository,
TranslatorInterface $translator,
EventDispatcherInterface $eventDispatcher,
TimelineBuilder $timelineBuilder,
LoggerInterface $logger,
FilterOrderHelperFactoryInterface $filterOrderHelperFactory
) {
$this->eventDispatcher = $eventDispatcher;
$this->timelineBuilder = $timelineBuilder;
$this->logger = $logger;
$this->translator = $translator;
$this->centerResolverDispatcher = $centerResolverDispatcher;
$this->paginatorFactory = $paginatorFactory;
$this->singleTaskAclAwareRepository = $singleTaskAclAwareRepository;
$this->filterOrderHelperFactory = $filterOrderHelperFactory;
}
private function getEntityContext(Request $request)
{
if ($request->query->has('person_id')) {
return 'person';
} else if ($request->query->has('course_id')) {
return 'course';
} else {
return null;
}
}
/**
* @Route(
* "/{_locale}/task/single-task/new",
* name="chill_task_single_task_new"
* )
*/
public function newAction(Request $request) {
$task = (new SingleTask())
->setAssignee($this->getUser())
->setType('task_default')
;
$entityType = $this->getEntityContext($request);
if (NULL === $entityType) {
throw new BadRequestHttpException("You must provide a entity_type");
}
$entityId = $request->query->getInt("{$entityType}_id", 0);
if ($entityId === null) {
return new BadRequestHttpException("You must provide a {$entityType}_id");
}
switch ($entityType) {
case 'person':
$person = $this->getDoctrine()->getManager()
->getRepository(Person::class)
->find($entityId);
if ($person === null) {
$this->createNotFoundException("Invalid person id");
}
$task->setPerson($person);
$role = TaskVoter::CREATE_PERSON;
break;
case 'course':
$course = $this->getDoctrine()->getManager()
->getRepository(AccompanyingPeriod::class)
->find($entityId);
if ($course === null) {
$this->createNotFoundException("Invalid accompanying course id");
}
$task->setCourse($course);
$role = TaskVoter::CREATE_COURSE;
break;
default:
return new BadRequestHttpException("context with {$entityType} is not supported");
}
$this->denyAccessUnlessGranted($role, $task, 'You are not '
. 'allowed to create this task');
$form = $this->setCreateForm($task, new Role($role));
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($task);
$this->eventDispatcher->dispatch(TaskEvent::PERSIST, new TaskEvent($task));
$em->flush();
$this->addFlash('success', $this->translator->trans("The task is created"));
if ($request->query->has('returnPath')) {
return $this->redirect($request->query->get('returnPath'));
}
if ($entityType === 'person') {
return $this->redirectToRoute('chill_task_singletask_by-person_list', [
'id' => $task->getPerson()->getId()
]);
} elseif ($entityType === 'course') {
return $this->redirectToRoute('chill_task_singletask_by-course_list', [
'id' => $task->getCourse()->getId()
]);
}
} else {
$this->addFlash('error', $this->translator->trans("This form contains errors"));
}
}
switch ($entityType) {
case 'person':
return $this->render('@ChillTask/SingleTask/Person/new.html.twig', array(
'form' => $form->createView(),
'task' => $task,
'person' => $task->getPerson(),
));
case 'course':
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/new.html.twig', array(
'form' => $form->createView(),
'task' => $task,
'accompanyingCourse' => $task->getCourse(),
));
default:
throw new \LogicException("entity context not supported");
}
}
/**
* @Route(
* "/{_locale}/task/single-task/{id}/show",
* name="chill_task_single_task_show"
* )
*/
public function showAction(SingleTask $task, Request $request)
{
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $task);
if ($person = $task->getContext() instanceof Person) {
$event = new PrivacyEvent($person, array(
'element_class' => SingleTask::class,
'element_id' => $task->getId(),
'action' => 'show'
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
}
$timeline = $this->timelineBuilder
->getTimelineHTML('task', array('task' => $task));
if ($task->getContext() instanceof Person) {
return $this->render('@ChillTask/SingleTask/Person/show.html.twig', array(
'task' => $task,
'timeline' => $timeline
));
} else {
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/show.html.twig', array(
'task' => $task,
'timeline' => $timeline
));
}
}
/**
* @Route(
* "/{_locale}/task/single-task/{id}/edit",
* name="chill_task_single_task_edit"
* )
*/
public function editAction(
SingleTask $task,
Request $request
) {
$this->denyAccessUnlessGranted(TaskVoter::UPDATE, $task, 'You are not '
. 'allowed to edit this task');
$event = (new UIEvent('single-task', $task))
->setForm($this->setCreateForm($task, new Role(TaskVoter::UPDATE)))
;
$this->eventDispatcher->dispatch(UIEvent::EDIT_FORM, $event);
$form = $event->getForm();
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($task);
$em->flush();
$this->addFlash('success', $this->translator
->trans("The task has been updated"));
if ($task->getContext() instanceof Person) {
$event = new PrivacyEvent($task->getPerson(), array(
'element_class' => SingleTask::class,
'element_id' => $task->getId(),
'action' => 'update'
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
if ($request->query->has('returnPath')) {
return $this->redirect($request->query->get('returnPath'));
}
return $this->redirectToRoute(
'chill_task_singletask_list',
);
} else {
if ($request->query->has('returnPath')) {
return $this->redirect($request->query->get('returnPath'));
}
return $this->redirectToRoute(
'chill_task_singletask_by-course_list', ['id' => $task->getCourse()->getId()]
);
}
} else {
$this->addFlash('error', $this->translator->trans("This form contains errors"));
}
}
$this->eventDispatcher->dispatch(UIEvent::EDIT_PAGE, $event);
if ($event->hasResponse()) {
return $event->getResponse();
}
if ($task->getContext() instanceof Person) {
$event = new PrivacyEvent($task->getPerson(), array(
'element_class' => SingleTask::class,
'element_id' => $task->getId(),
'action' => 'edit'
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->render('@ChillTask/SingleTask/Person/edit.html.twig', array(
'task' => $task,
'form' => $form->createView()
));
} else {
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/edit.html.twig', array(
'task' => $task,
'form' => $form->createView(),
'accompanyingCourse' => $task->getCourse()
));
}
}
/**
* @Route(
* "/{_locale}/task/single-task/{id}/delete",
* name="chill_task_single_task_delete"
* )
*/
public function deleteAction(
Request $request,
$id
) {
$em = $this->getDoctrine()->getManager();
$task = $em->getRepository(SingleTask::class)->find($id);
if (!$task) {
throw $this->createNotFoundException('Unable to find Task entity.');
}
if ($task->getPerson() !== null) {
$personId = $task->getPerson()->getId();
if ($personId === null) {
return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST);
}
$person = $this->getDoctrine()->getManager()
->getRepository(Person::class)
->find($personId);
if ($person === null) {
throw $this->createNotFoundException("Invalid person id");
}
} else {
$courseId = $task->getCourse()->getId();
if ($courseId === null){
return new Response("You must provide a course_id", Response::HTTP_BAD_REQUEST);
}
$course = $this->getDoctrine()->getManager()
->getRepository(AccompanyingPeriod::class)
->find($courseId);
if($course === null){
throw $this->createNotFoundException("Invalid accompanying period id");
}
}
// TODO: reactivate right to delete
// $this->denyAccessUnlessGranted(TaskVoter::DELETE, $task, 'You are not '
// . 'allowed to delete this task');
$form = $this->createDeleteForm($id);
if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request);
if ($form->isValid()) {
$this->logger->notice("A task has been removed", array(
'by_user' => $this->getUser()->getUsername(),
'task_id' => $task->getId(),
'description' => $task->getDescription(),
'assignee' => $task->getAssignee(),
// TODO reimplement scope
// 'scope_id' => $task->getScope()->getId(),
));
$em = $this->getDoctrine()->getManager();
$em->remove($task);
$em->flush();
$this->addFlash('success', $this->translator
->trans("The task has been successfully removed."));
if ($task->getContext() instanceof Person) {
return $this->redirect($this->generateUrl(
'chill_task_singletask_by-person_list',
[ 'id' => $task->getPerson()->getId() ]
));
} else {
return $this->redirect($this->generateUrl(
'chill_task_singletask_by-course_list',
['id' => $task->getCourse()->getId()]
));
}
}
}
if($task->getContext() instanceof Person){
return $this->render('@ChillTask/SingleTask/Person/confirm_delete.html.twig', array(
'task' => $task,
'delete_form' => $form->createView()
));
} else {
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/confirm_delete.html.twig', array(
'task' => $task,
'delete_form' => $form->createView(),
'accompanyingCourse' => $course
));
}
}
/**
*
* @param SingleTask $task
* @param Role $role
* @return \Symfony\Component\Form\FormInterface
*/
protected function setCreateForm(SingleTask $task, Role $role)
{
$form = $this->createForm(SingleTaskType::class, $task, [
'role' => $role,
]);
$form->add('submit', SubmitType::class);
return $form;
}
/**
*
* @return Response
* @Route(
* "/{_locale}/task/single-task/list/my",
* name="chill_task_singletask_my_tasks"
* )
*/
public function myTasksAction()
{
$this->denyAccessUnlessGranted('ROLE_USER');
$filterOrder = $this->buildFilterOrder();
$flags = \array_merge(
$filterOrder->getCheckboxData('status'),
\array_map(fn ($i) => 'state_'.$i, $filterOrder->getCheckboxData('states'))
);
$nb = $this->singleTaskAclAwareRepository->countByCurrentUsersTasks(
$filterOrder->getQueryString(),
$flags
);
$paginator = $this->paginatorFactory->create($nb);
$tasks = $this->singleTaskAclAwareRepository->findByCurrentUsersTasks(
$filterOrder->getQueryString(),
$flags,
$paginator->getCurrentPageFirstItemNumber(),
$paginator->getItemsPerPage(),
[
'startDate' => 'DESC',
'endDate' => 'DESC',
]
);
return $this->render('@ChillTask/SingleTask/List/index_my_tasks.html.twig', [
'tasks' => $tasks,
'paginator' => $paginator,
'filter_order' => $filterOrder,
]);
}
private function buildFilterOrder(): FilterOrderHelper
{
$statuses = ['no-alert', 'warning', 'alert'];
$statusTrans = [
'Tasks without alert',
'Tasks near deadline',
'Tasks over deadline',
];
$states = [
// todo: get a list of possible states dynamically
'new', 'in_progress', 'closed', 'canceled'
];
return $this->filterOrderHelperFactory
->create(self::class)
->addSearchBox()
->addCheckbox('status', $statuses, $statuses, $statusTrans)
->addCheckbox('states', $states, ['new', 'in_progress'])
->build()
;
}
/**
*
* Arguments:
* - user_id
* - scope_id
* - s
* - person_id
* - hide_form (hide the form to filter the tasks)
* - status: date state, amongst SingleTaskRepository::DATE_STATUSES, or 'closed'
*
* @Route(
* "/{_locale}/task/single-task/list",
* name="chill_task_singletask_list"
* )
*/
public function listAction(
Request $request
) {
$this->denyAccessUnlessGranted(TaskVoter::SHOW, null);
$filterOrder = $this->buildFilterOrder();
$flags = \array_merge(
$filterOrder->getCheckboxData('status'),
\array_map(fn ($i) => 'state_'.$i, $filterOrder->getCheckboxData('states'))
);
$nb = $this->singleTaskAclAwareRepository->countByAllViewable(
$filterOrder->getQueryString(),
$flags
);
$paginator = $this->paginatorFactory->create($nb);
if (0 < $nb) {
$tasks = $this->singleTaskAclAwareRepository->findByAllViewable(
$filterOrder->getQueryString(),
$flags,
$paginator->getCurrentPageFirstItemNumber(),
$paginator->getItemsPerPage(),
[
'startDate' => 'DESC',
'endDate' => 'DESC',
]
);
} else {
$tasks = [];
}
return $this->render('@ChillTask/SingleTask/List/index.html.twig', [
'tasks' => $tasks,
'paginator' => $paginator,
'filter_order' => $filterOrder
]);
}
/**
* Creates a form to delete a Task entity by id.
*
* @param mixed $id The entity id
*
* @return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm($id)
{
return $this->createFormBuilder()
->setAction($this->generateUrl(
'chill_task_single_task_delete',
array('id' => $id)))
->setMethod('DELETE')
->add('submit', SubmitType::class, array('label' => 'Delete'))
->getForm()
;
}
/**
* @Route(
* "/{_locale}/task/single-task/by-course/{id}",
* name="chill_task_singletask_by-course_list")
*/
public function listCourseTasks(
AccompanyingPeriod $course,
FormFactoryInterface $formFactory,
Request $request
): Response
{
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $course);
$filterOrder = $this->buildFilterOrder();
$flags = \array_merge(
$filterOrder->getCheckboxData('status'),
\array_map(fn ($i) => 'state_'.$i, $filterOrder->getCheckboxData('states'))
);
$nb = $this->singleTaskAclAwareRepository->countByCourse(
$course,
$filterOrder->getQueryString(),
$flags
);
$paginator = $this->paginatorFactory->create($nb);
if (0 < $nb) {
$tasks = $this->singleTaskAclAwareRepository->findByCourse(
$course,
$filterOrder->getQueryString(),
$flags,
$paginator->getCurrentPageFirstItemNumber(),
$paginator->getItemsPerPage(),
[
'startDate' => 'DESC',
'endDate' => 'DESC',
]
);
} else {
$tasks = [];
}
return $this->render(
'@ChillTask/SingleTask/AccompanyingCourse/list.html.twig',
[
'tasks' => $tasks,
'accompanyingCourse' => $course,
'paginator' => $paginator,
'filter_order' => $filterOrder
]);
}
/**
* @Route(
* "/{_locale}/task/single-task/by-person/{id}",
* name="chill_task_singletask_by-person_list")
*/
public function listPersonTasks(
Person $person
): Response {
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $person);
$filterOrder = $this->buildFilterOrder();
$flags = \array_merge(
$filterOrder->getCheckboxData('status'),
\array_map(fn ($i) => 'state_'.$i, $filterOrder->getCheckboxData('states'))
);
$nb = $this->singleTaskAclAwareRepository->countByPerson(
$person,
$filterOrder->getQueryString(),
$flags
);
$paginator = $this->paginatorFactory->create($nb);
if (0 < $nb) {
$tasks = $this->singleTaskAclAwareRepository->findByPerson(
$person,
$filterOrder->getQueryString(),
$flags,
$paginator->getCurrentPageFirstItemNumber(),
$paginator->getItemsPerPage(),
[
'startDate' => 'DESC',
'endDate' => 'DESC',
]
);
} else {
$tasks = [];
}
return $this->render(
'@ChillTask/SingleTask/Person/list.html.twig',
[
'tasks' => $tasks,
'person' => $person,
'paginator' => $paginator,
'filter_order' => $filterOrder
]);
}
}