From 52bda7c94fcc22b61466a3be4bf1695f43060c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 6 Jul 2018 15:47:14 +0200 Subject: [PATCH] add confirmation page for task --- Controller/TaskController.php | 85 ++++++++++-- Event/UI/UIEvent.php | 130 ++++++++++++++++++ Resources/translations/messages.fr.yml | 13 +- Resources/views/SingleTask/_list.html.twig | 6 +- .../views/SingleTask/transition.html.twig | 34 +++++ Templating/UI/CountNotificationTask.php | 14 +- Workflow/Definition/DefaultTaskDefinition.php | 12 +- 7 files changed, 268 insertions(+), 26 deletions(-) create mode 100644 Event/UI/UIEvent.php create mode 100644 Resources/views/SingleTask/transition.html.twig diff --git a/Controller/TaskController.php b/Controller/TaskController.php index a24fa9ce9..a54964574 100644 --- a/Controller/TaskController.php +++ b/Controller/TaskController.php @@ -11,6 +11,11 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Chill\TaskBundle\Event\UI\UIEvent; +use Chill\TaskBundle\Entity\AbstractTask; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Workflow\Transition; class TaskController extends Controller @@ -41,7 +46,8 @@ class TaskController extends Controller Registry $registry, EntityManagerInterface $em, Request $request, - TranslatorInterface $translator + TranslatorInterface $translator, + EventDispatcherInterface $eventDispatcher ) { switch ($kind) { case 'single-task': @@ -49,9 +55,12 @@ class TaskController extends Controller ->find($taskId) ; $defaultReturnPath = $this->generateUrl( - 'chill_task_singletask_list', - [ 'person_id' => $task->getPerson() ] - ); + 'chill_task_single_task_show', + [ + 'id' => $task->getId(), + 'list_params' => $request->query->get('list_params', []) + ]); + $defaultTemplate = '@ChillTask/SingleTask/transition.html.twig'; break; default: return new Response("The type '$kind' is not implemented", @@ -59,27 +68,75 @@ class TaskController extends Controller } if (NULL === $task) { - $this->createNotFoundException("task with id '$taskId' and type " + throw $this->createNotFoundException("task with id '$taskId' and type " . "'$type' does not exists"); } + + $workflow = $registry->get($task); + + if (!$workflow->can($task, $transition)) { + throw $this->createAccessDeniedException('You are not allowed to apply this transition'); + } + $transitionInstance = \array_values( // array_values needed to reset keys (array_filter preserves keys) + \array_filter( + $workflow->getEnabledTransitions($task), + function(Transition $t) use ($transition) { + return $t->getName() === $transition; + } + ))[0]; // we simply check that the user can see the task. Other ACL checks // should be performed using `guard` events. $this->denyAccessUnlessGranted(TaskVoter::SHOW, $task); + + $form = $this->createTransitionForm($task); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { - $workflow = $registry->get($task); + if ($workflow->can($task, $transition)) { + $workflow->apply($task, $transition); - if ($workflow->can($task, $transition)) { - $workflow->apply($task, $transition); + $em->flush(); - $em->flush(); - - $this->addFlash('success', $translator->trans('The transition is successfully applied')); + $this->addFlash('success', $translator->trans('The transition is successfully applied')); + } else { + $this->addFlash('error', $translator->trans('The transition could not be applied')); + } + + return $this->redirect($defaultReturnPath); } else { - $this->addFlash('error', $translator->trans('The transition could not be applied')); + $event = (new UIEvent($kind, $task)) + ->setForm($form) + ->setTransition($transitionInstance) + ; + + $eventDispatcher->dispatch(UIEvent::SHOW_TRANSITION_PAGE, $event); + + if ($event->hasResponse()) { + return $event->getResponse(); + } else { + return $this->render($defaultTemplate, [ + 'task' => $task, + 'form' => $form->createView(), + 'transition' => $transitionInstance + ]); + } } - - return $this->redirect($request->query->get('return_path', $defaultReturnPath)); + } + + /** + * + * @param \Chill\TaskBundle\Controller\AbstractTask $task + * @return \Symfony\Component\Form\FormInterface + */ + protected function createTransitionForm(AbstractTask $task) + { + $builder = $this->createFormBuilder($task); + $builder->add('submit', SubmitType::class); + + return $builder->getForm(); } } diff --git a/Event/UI/UIEvent.php b/Event/UI/UIEvent.php new file mode 100644 index 000000000..201c3a265 --- /dev/null +++ b/Event/UI/UIEvent.php @@ -0,0 +1,130 @@ + + */ +class UIEvent extends Event +{ + const SHOW_TRANSITION_PAGE = 'chill_task.show_transition_page'; + + /** + * + * @var string + */ + protected $kind; + + /** + * + * @var AbstractTask + */ + protected $task; + + /** + * + * @var Response|null + */ + protected $response = null; + + /** + * + * @var FormInterface|null + */ + protected $form = null; + + /** + * @var Transition + */ + protected $transition = null; + + public function __construct($kind, AbstractTask $task) + { + $this->kind = $kind; + $this->task = $task; + } + + /** + * + * @return string + */ + public function getKind() + { + return $this->kind; + } + + /** + * + * @return AbstractTask + */ + public function getTask(): AbstractTask + { + return $this->task; + } + + /** + * + * @return FormInterface|null + */ + public function getForm() + { + return $this->form; + } + + public function setForm(FormInterface $form) + { + $this->form = $form; + + return $this; + } + + public function getTransition() + { + return $this->transition; + } + + public function setTransition(Transition $transition) + { + $this->transition = $transition; + + return $this; + } + + /** + * + * @return Response + */ + public function getResponse(): Response + { + return $this->response; + } + + /** + * + * @param Response $response + * @return $this + */ + public function setResponse(Response $response) + { + $this->response = $response; + + return $this; + } + + public function hasResponse() + { + return $this->response instanceof Response; + } + + +} diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml index e13e07bda..3ec1f10eb 100644 --- a/Resources/translations/messages.fr.yml +++ b/Resources/translations/messages.fr.yml @@ -60,7 +60,7 @@ Associated person: Personne associée Default task: Tâche par défaut -# transitions +# transitions - default task definition 'new': 'nouvelle' 'in_progress': 'en cours' 'closed': 'fermée' @@ -68,10 +68,16 @@ Default task: Tâche par défaut start: démarrer close: clotûrer cancel: annuler +Start_verb: Démarrer +Close_verb: Clotûrer +Set this task to cancel state: Marquer cette tâche comme annulée '%user% has closed the task': %user% a fermé la tâche '%user% has canceled the task': %user% a annulé la tâche '%user% has started the task': %user% a commencé la tâche '%user% has created the task': %user% a introduit la tâche +Are you sure you want to close this task ?: Êtes-vous sûrs de vouloir clotûrer cette tâche ? +Are you sure you want to cancel this task ?: Êtes-vous sûrs de vouloir annuler cette tâche ? +Are you sure you want to start this task ?: Êtes-vous sûrs de vouloir démarrer cette tâche ? #Flash messages 'The task is created': 'La tâche a été créée' @@ -88,4 +94,7 @@ cancel: annuler #title My tasks near deadline: Mes tâches à échéance proche -My tasks over deadline: Mes tâches à échéance dépassée \ No newline at end of file +My tasks over deadline: Mes tâches à échéance dépassée + +#transition page +Apply transition on task %title%: Appliquer la transition sur la tâche %title% diff --git a/Resources/views/SingleTask/_list.html.twig b/Resources/views/SingleTask/_list.html.twig index dadf6e248..28b3ddc8b 100644 --- a/Resources/views/SingleTask/_list.html.twig +++ b/Resources/views/SingleTask/_list.html.twig @@ -18,7 +18,7 @@ {% for task in tasks %} {{ task.title }} - {{ task_workflow_metadata(task, 'definition.name') }} + {{ task_workflow_metadata(task, 'definition.name')|trans }} {% if person is null %} {{ task.person}} {% endif %} @@ -58,7 +58,7 @@  
{% for transition in workflow_transitions(task) %} - {{ task_workflow_metadata(task, 'transition.verb', transition)|trans }} + {{ task_workflow_metadata(task, 'transition.verb', transition)|trans }} {% endfor %}
@@ -141,7 +141,7 @@ {% import _self as helper %} -

{{ app.request.query.get('title', null)|default('Task list'|trans) }}

+

{{ app.request.query.get('title', null)|escape('html')|default('Task list'|trans) }}

{% if false == app.request.query.boolean('hide_form', false) %}

{{ 'Filter the tasks'|trans }}

diff --git a/Resources/views/SingleTask/transition.html.twig b/Resources/views/SingleTask/transition.html.twig new file mode 100644 index 000000000..7ba341cd9 --- /dev/null +++ b/Resources/views/SingleTask/transition.html.twig @@ -0,0 +1,34 @@ +{% extends "ChillPersonBundle::layout.html.twig" %} + +{% set activeRouteKey = 'chill_task_task_list' %} +{% set person = task.person %} + +{% block title 'Remove task'|trans %} + +{% block personcontent %} + +

{{ 'Apply transition on task %title%'|trans({ '%title%': task.title } )|raw }}

+ + +{% if task_workflow_metadata(task, 'transition.sentence_confirmation', transition) is not empty %} +

{{ task_workflow_metadata(task, 'transition.sentence_confirmation', transition)|trans }}

+{% else %} +

{{ 'Are you sure to apply the transition %name% on this task ?'|trans({ '%name%': task_workflow_metadata(task, 'transition.name', transition)|default(transition.name)|trans }) }}

+{% endif %} + +{{ form_start(form) }} + + + +{{ form_end(form) }} + +{% endblock %} diff --git a/Templating/UI/CountNotificationTask.php b/Templating/UI/CountNotificationTask.php index a3a6377e2..e53b85fbe 100644 --- a/Templating/UI/CountNotificationTask.php +++ b/Templating/UI/CountNotificationTask.php @@ -108,10 +108,16 @@ class CountNotificationTask implements NotificationCounterInterface $task = $e->getSubject(); if (NULL !== $task->getAssignee()) { - $sumCache = $this->cachePool->getItem($this->getCacheKey($task->getAssignee())); - - if ($sumCache->isHit()) { - $this->cachePool->deleteItem($this->getCacheKey($task->getAssignee())); + foreach ([ + SingleTaskRepository::DATE_STATUS_ENDED, + SingleTaskRepository::DATE_STATUS_WARNING + ] as $status) { + $key = $this->getCacheKey($task->getAssignee(), $status); + $sumCache = $this->cachePool->getItem($key); + + if ($sumCache->isHit()) { + $this->cachePool->deleteItem($key); + } } } } diff --git a/Workflow/Definition/DefaultTaskDefinition.php b/Workflow/Definition/DefaultTaskDefinition.php index 3c9fd677e..292f783c6 100644 --- a/Workflow/Definition/DefaultTaskDefinition.php +++ b/Workflow/Definition/DefaultTaskDefinition.php @@ -32,17 +32,23 @@ class DefaultTaskDefinition implements \Chill\TaskBundle\Workflow\TaskWorkflowDe 'close' => [ 'verb' => 'close', 'class' => 'sc-button bt-task-label bt-task-close', - 'sentence' => '%user% has closed the task' + 'sentence' => '%user% has closed the task', + 'sentence_confirmation' => 'Are you sure you want to close this task ?', + 'apply_transition_submit_label' => 'Close_verb' ], 'cancel' => [ 'verb' => 'cancel', 'class' => 'sc-button bt-task-label bt-task-cancel', - 'sentence' => '%user% has canceled the task' + 'sentence' => '%user% has canceled the task', + 'sentence_confirmation' => 'Are you sure you want to cancel this task ?', + 'apply_transition_submit_label' => 'Set this task to cancel state' ], 'start' => [ 'verb' => 'start', 'class' => 'sc-button bt-task-label bt-task-start', - 'sentence' => '%user% has started the task' + 'sentence' => '%user% has started the task', + 'sentence_confirmation' => 'Are you sure you want to start this task ?', + 'apply_transition_submit_label' => 'Start_verb' ] ];