diff --git a/Controller/SingleTaskController.php b/Controller/SingleTaskController.php index c01723419..89540d22e 100644 --- a/Controller/SingleTaskController.php +++ b/Controller/SingleTaskController.php @@ -24,6 +24,7 @@ use Chill\MainBundle\Entity\UserRepository; use Chill\TaskBundle\Event\TaskEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Translation\TranslatorInterface; +use Chill\TaskBundle\Event\UI\UIEvent; class SingleTaskController extends Controller @@ -152,7 +153,8 @@ class SingleTaskController extends Controller public function editAction( Request $request, $id, - TranslatorInterface $translator + TranslatorInterface $translator, + EventDispatcherInterface $dispatcher ) { /* @var $taskRepository SingleTaskRepository */ $taskRepository = $this->get('chill_task.single_task_repository'); @@ -179,8 +181,14 @@ class SingleTaskController extends Controller if (!$task) { throw $this->createNotFoundException('Unable to find Task entity.'); } - - $form = $this->setCreateForm($task, new Role(TaskVoter::UPDATE)); + + $event = (new UIEvent('single-task', $task)) + ->setForm($this->setCreateForm($task, new Role(TaskVoter::UPDATE))) + ; + + $dispatcher->dispatch(UIEvent::EDIT_FORM, $event); + + $form = $event->getForm(); $form->handleRequest($request); @@ -203,7 +211,13 @@ class SingleTaskController extends Controller $this->addFlash('error', $translator->trans("This form contains errors")); } } - + + $dispatcher->dispatch(UIEvent::EDIT_PAGE, $event); + + if ($event->hasResponse()) { + return $event->getResponse(); + } + return $this->render('ChillTaskBundle:SingleTask:edit.html.twig', array( 'task' => $task, 'form' => $form->createView() @@ -357,6 +371,7 @@ class SingleTaskController extends Controller $params['user'] = null; $viewParams['center'] = null; $params['center'] = null; + $params['types'] = null; // Get parameters from url if (!empty($request->query->get('person_id', NULL))) { @@ -372,6 +387,13 @@ class SingleTaskController extends Controller $viewParams['person'] = $person; $params['person'] = $person; } + + if(!empty($request->query->get('types', []))) { + $types = $request->query->get('types', []); + if (count($types) > 0) { + $params['types'] = $types; + } + } if (!empty($request->query->get('user_id', null))) { if ($request->query->get('user_id') === '_unassigned') { @@ -467,11 +489,12 @@ class SingleTaskController extends Controller $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ 'person' => $viewParams['person'], 'method' => Request::METHOD_GET, - 'csrf_protection' => false + 'csrf_protection' => false, + 'add_type' => true ]); $form->handleRequest($request); - + return $this->render('ChillTaskBundle:SingleTask:index.html.twig', \array_merge($viewParams, [ 'form' => $form->createView() ])); } diff --git a/Event/UI/UIEvent.php b/Event/UI/UIEvent.php index 74e9154d9..e041da542 100644 --- a/Event/UI/UIEvent.php +++ b/Event/UI/UIEvent.php @@ -18,6 +18,8 @@ use Symfony\Component\Workflow\Transition; class UIEvent extends Event { const SHOW_TRANSITION_PAGE = 'chill_task.show_transition_page'; + const EDIT_FORM = 'chill_task.edit_form'; + const EDIT_PAGE = 'chill_task.edit_page'; /** * diff --git a/Form/SingleTaskListType.php b/Form/SingleTaskListType.php index 3f0a4aeed..f6b47e409 100644 --- a/Form/SingleTaskListType.php +++ b/Form/SingleTaskListType.php @@ -34,6 +34,7 @@ use Chill\PersonBundle\Form\Type\PickPersonType; use Chill\PersonBundle\Entity\Person; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Chill\PersonBundle\Form\DataTransformer\PersonToIdTransformer; +use Chill\TaskBundle\Workflow\TaskWorkflowManager; /** * @@ -60,14 +61,22 @@ class SingleTaskListType extends AbstractType */ protected $authorizationHelper; + /** + * + * @var TaskWorkflowManager + */ + protected $taskWorkflowManager; + public function __construct( EntityManagerInterface $em, TokenStorageInterface $tokenStorage, - AuthorizationHelper $authorizationHelper + AuthorizationHelper $authorizationHelper, + TaskWorkflowManager $taskWorkflowManager ) { $this->em = $em; $this->tokenStorage = $tokenStorage; $this->authorizationHelper = $authorizationHelper; + $this->taskWorkflowManager = $taskWorkflowManager; } @@ -90,7 +99,7 @@ class SingleTaskListType extends AbstractType ]) ; - if ($options['add_status'] === true) { + if ($options['add_status']) { $builder ->add('status', ChoiceType::class, [ 'choices' => $statuses, @@ -100,6 +109,21 @@ class SingleTaskListType extends AbstractType ]); } + if ($options['add_type']) { + $types = $this->getTaskTypesChoices($options); + + if (count($types) > 0) { + $builder->add('types', ChoiceType::class, [ + 'choices' => $types, + 'required' => false, + 'expanded' => true, + 'multiple' => true, + 'label' => 'Task types' + ]); + } + + } + if ($options['person'] === null) { $builder ->add('person_id', PickPersonType::class, [ @@ -136,6 +160,56 @@ class SingleTaskListType extends AbstractType return $choices; } + protected function getTaskTypesChoices($options) + { + $qb = $this->em->createQueryBuilder(); + $user = $this->tokenStorage->getToken()->getUser(); + $role = new Role(TaskVoter::SHOW); + $centers = $this->authorizationHelper->getReachableCenters($user, $role); + + $qb->select('DISTINCT task.type AS type') + ->from(SingleTask::class, 'task') + ->join('task.person', 'person') + ; + + $i = 0; + $orCenters = $qb->expr()->orX(); + foreach($centers as $center) { + $circles = $this->authorizationHelper->getReachableCircles($user, $role, $center); + + if (count($circles) > 0) { + $andX = $qb->expr()->andX(); + $andX + ->add($qb->expr()->eq('person.center', ':center_'.$i)) + ->add($qb->expr()->in('task.circle', ':circles_'.$i)) + ; + $orCenters->add($andX); + + $qb + ->setParameter('center_'.$i, $center) + ->setParameter('circles_'.$i, $circles) + ; + $i++; + } + } + + if ($i > 0) { + $qb->where($orCenters); + } + + $types = $qb->getQuery()->getResult(); + + $choices = []; + + foreach ($types as $row) { + $fake = (new SingleTask())->setType($row['type']); + $label = $this->taskWorkflowManager->getWorkflowMetadata($fake, 'definition.name'); + $choices[$label] = $row['type']; + } + + return $choices; + } + /** * Return a list of user having a task assigned. * @@ -191,6 +265,9 @@ class SingleTaskListType extends AbstractType ->setDefined('add_status') ->setDefault('add_status', false) ->setAllowedTypes('add_status', ['bool']) + ->setDefined('add_type') + ->setDefault('add_type', false) + ->setAllowedTypes('add_type', ['bool']) ; } } diff --git a/Menu/SectionMenuBuilder.php b/Menu/SectionMenuBuilder.php new file mode 100644 index 000000000..7ed84dbf5 --- /dev/null +++ b/Menu/SectionMenuBuilder.php @@ -0,0 +1,78 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\TaskBundle\Menu; + +use Chill\MainBundle\Routing\LocalMenuBuilderInterface; +use Knp\Menu\MenuItem; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Chill\TaskBundle\Security\Authorization\TaskVoter; +use Symfony\Component\Translation\TranslatorInterface; + + +/** + * + * + */ +class SectionMenuBuilder implements LocalMenuBuilderInterface +{ + /** + * + * @var AuthorizationCheckerInterface + */ + public $authorizationChecker; + + /** + * + * @var TranslatorInterface + */ + public $translator; + + public function __construct( + AuthorizationCheckerInterface $authorizationChecker, + TranslatorInterface $translator + ) { + $this->authorizationChecker = $authorizationChecker; + $this->translator = $translator; + } + + public function buildMenu($menuId, MenuItem $menu, array $parameters) + { + if (FALSE === $this->authorizationChecker->isGranted(TaskVoter::SHOW)) { + return; + } + + $menu->addChild( + $this->translator->trans("Tasks"), + [ + 'route' => 'chill_task_singletask_list', [ + 'routeParameters' => [ + 'hide_form' => false + ] + ]]) + ->setExtras([ + 'order'=> 50, + 'icon' => 'exclamation-triangle', + 'entryclass' => 'user_menu__entry--warning-entry' + ]); + } + + public static function getMenuIds(): array + { + return [ 'section' ]; + } +} diff --git a/Repository/SingleTaskRepository.php b/Repository/SingleTaskRepository.php index 02a5bda51..6d4048371 100644 --- a/Repository/SingleTaskRepository.php +++ b/Repository/SingleTaskRepository.php @@ -127,6 +127,11 @@ class SingleTaskRepository extends \Doctrine\ORM\EntityRepository $qb->andWhere($qb->expr()->eq('st.circle', ':scope')); $qb->setParameter('scope', $params['scope']); } + + if (\array_key_exists('types', $params)) { + $qb->andWhere($qb->expr()->in('st.type', ':types')); + $qb->setParameter('types', $params['types']); + } if (\array_key_exists('date_status', $params) and !empty($params['date_status'])) { $this->addTypeFilter($qb, $params); @@ -135,10 +140,6 @@ class SingleTaskRepository extends \Doctrine\ORM\EntityRepository if (\array_key_exists('is_closed', $params)) { $qb->andWhere($this->buildIsClosed($qb, !$params['is_closed'])); } - - if (\array_key_exists('types', $params) && count($params['types']) > 0) { - $qb->andWhere($qb->expr()->in('st.type', $params['types'])); - } } diff --git a/Resources/config/services/form.yml b/Resources/config/services/form.yml index a284ad541..f82e4a562 100644 --- a/Resources/config/services/form.yml +++ b/Resources/config/services/form.yml @@ -4,5 +4,6 @@ services: $em: '@Doctrine\ORM\EntityManagerInterface' $authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper' $tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface' + $taskWorkflowManager: '@Chill\TaskBundle\Workflow\TaskWorkflowManager' tags: - { name: form.type } diff --git a/Resources/config/services/menu.yml b/Resources/config/services/menu.yml index 3c74fe498..b4ff7a421 100644 --- a/Resources/config/services/menu.yml +++ b/Resources/config/services/menu.yml @@ -14,3 +14,10 @@ services: $translator: '@Symfony\Component\Translation\TranslatorInterface' tags: - { name: 'chill.menu_builder' } + + Chill\TaskBundle\Menu\SectionMenuBuilder: + arguments: + $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' + $translator: '@Symfony\Component\Translation\TranslatorInterface' + tags: + - { name: 'chill.menu_builder' } diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml index 1e250a763..bea9f527e 100644 --- a/Resources/translations/messages.fr.yml +++ b/Resources/translations/messages.fr.yml @@ -48,6 +48,7 @@ User: Utilisateur 'No description': 'Pas de description' 'No dates specified': 'Dates non spécifiées' 'No one assignee': 'Aucune personne assignée' +'Task types': Types de tâches Days: Jour(s) Weeks: Semaine(s) Months: Mois diff --git a/Resources/views/SingleTask/_list.html.twig b/Resources/views/SingleTask/_list.html.twig index 28b3ddc8b..0f465e17b 100644 --- a/Resources/views/SingleTask/_list.html.twig +++ b/Resources/views/SingleTask/_list.html.twig @@ -152,6 +152,10 @@ {{ form_row(form.status) }} {% endif %} + {% if form.types is defined %} + {{ form_row(form.types) }} + {% endif %} + {% if form.person_id is defined %} {{ form_row(form.person_id) }} {% endif %} diff --git a/Workflow/Definition/DefaultTaskDefinition.php b/Workflow/Definition/DefaultTaskDefinition.php index 292f783c6..64b753875 100644 --- a/Workflow/Definition/DefaultTaskDefinition.php +++ b/Workflow/Definition/DefaultTaskDefinition.php @@ -69,6 +69,7 @@ class DefaultTaskDefinition implements \Chill\TaskBundle\Workflow\TaskWorkflowDe } public function getWorkflowMetadata( + AbstractTask $task, string $key, $metadataSubject = null ) { diff --git a/Workflow/TaskWorkflowManager.php b/Workflow/TaskWorkflowManager.php index 1b74da21c..e5a111dad 100644 --- a/Workflow/TaskWorkflowManager.php +++ b/Workflow/TaskWorkflowManager.php @@ -79,7 +79,7 @@ class TaskWorkflowManager implements SupportStrategyInterface public function getWorkflowMetadata(AbstractTask $task, string $key, $metadataSubject = null, string $name = null) { return $this->getTaskWorkflowDefinition($task) - ->getWorkflowMetadata($key, $metadataSubject); + ->getWorkflowMetadata($task, $key, $metadataSubject); } public function onTaskStateEntered(Event $e)