From 8a3871252583072e82012ee24b3c1533a2c9c378 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 9 Sep 2021 17:43:35 +0200 Subject: [PATCH 01/79] relation between task and accompanyingcourse created --- .../ChillTaskBundle/Entity/AbstractTask.php | 21 +++++++++- .../migrations/Version20210909153533.php | 38 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php diff --git a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php index 73e98c0dd..649de0422 100644 --- a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php +++ b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php @@ -10,6 +10,7 @@ use Chill\MainBundle\Entity\HasScopeInterface; use Chill\MainBundle\Entity\HasCenterInterface; use Symfony\Component\Validator\Constraints as Assert; use Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency; +use Chill\PersonBundle\Entity\AccompanyingPeriod; /** * AbstractTask @@ -67,9 +68,16 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface * @ORM\ManyToOne( * targetEntity="\Chill\PersonBundle\Entity\Person" * ) - * @Assert\NotNull() */ private $person; + + + /** + * @var AccompanyingPeriod + * @ORM\ManyToOne(targetEntity="\Chill\PersonBundle\Entity\AccompanyingPeriod") + */ + + private $course; /** * @@ -202,6 +210,11 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface return $this->person; } + public function getCourse(): ?AccompanyingPeriod + { + return $this->course; + } + public function getCircle(): ?Scope { return $this->circle; @@ -219,6 +232,12 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface return $this; } + public function setCourse(AccompanyingPeriod $course) + { + $this->course = $course; + return $this; + } + public function setCircle(Scope $circle) { $this->circle = $circle; diff --git a/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php b/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php new file mode 100644 index 000000000..e2f59621b --- /dev/null +++ b/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php @@ -0,0 +1,38 @@ +addSql('ALTER TABLE chill_task.recurring_task ADD course_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_task.recurring_task ADD CONSTRAINT FK_9F663B90591CC992 FOREIGN KEY (course_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_9F663B90591CC992 ON chill_task.recurring_task (course_id)'); + $this->addSql('ALTER TABLE chill_task.single_task ADD course_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_task.single_task ADD CONSTRAINT FK_194CB3D8591CC992 FOREIGN KEY (course_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_194CB3D8591CC992 ON chill_task.single_task (course_id)'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_task.recurring_task DROP CONSTRAINT FK_9F663B90591CC992'); + $this->addSql('ALTER TABLE chill_task.recurring_task DROP course_id'); + $this->addSql('ALTER TABLE chill_task.single_task DROP CONSTRAINT FK_194CB3D8591CC992'); + $this->addSql('ALTER TABLE chill_task.single_task DROP course_id'); + } +} From 27077c9d96f68b444b7807aa74f84fe3e8eb15d9 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 9 Sep 2021 18:21:41 +0200 Subject: [PATCH 02/79] adaptation of newTask() method in singleTaskController --- .../Controller/SingleTaskController.php | 64 +++++++++++++++---- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index bc76dacd4..8c742c627 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -86,24 +86,52 @@ class SingleTaskController extends AbstractController ->setType('task_default') ; - if ($request->query->has('person_id')) { + if($request->query->has('person_id')){ + $entityType = 'person'; + } else if ($request->query->has('course_id')) { + $entityType = 'course'; + } else { + $entityType = null; + } + + if ($entityType !== null) { - $personId = $request->query->getInt('person_id', 0); // sf4 check: + $entityId = $request->query->getInt("{$entityType}_id", 0); // sf4 check: // prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given` - if ($personId === null) { - return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST); + if ($entityId === null) { + return new Response("You must provide a {$entityType}_id", Response::HTTP_BAD_REQUEST); } - $person = $this->getDoctrine()->getManager() - ->getRepository(Person::class) - ->find($personId); + if($entityType === 'person') + { + $person = $this->getDoctrine()->getManager() + ->getRepository(Person::class) + ->find($entityId); - if ($person === null) { - $this->createNotFoundException("Invalid person id"); + if ($person === null) { + $this->createNotFoundException("Invalid person id"); + } + + $task->setPerson($person); } - $task->setPerson($person); + if($entityType === 'course') + { + + $course = $this->getDoctrine()->getManager() + ->getRepository(AccompanyingPeriod::class) + ->find($entityId); + + if($course === null) { + $this->createNotFoundException("Invalid accompanying course id"); + } + + $task->setCourse($course); + + } + + } $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' @@ -124,9 +152,19 @@ class SingleTaskController extends AbstractController $this->addFlash('success', $translator->trans("The task is created")); - return $this->redirectToRoute('chill_task_singletask_list', [ - 'person_id' => $task->getPerson()->getId() - ]); + if($entityType === 'person') + { + return $this->redirectToRoute('chill_task_singletask_list', [ + 'person_id' => $task->getPerson()->getId() + ]); + } + + if($entityType === 'course') + { + return $this->redirectToRoute('chill_task_singletask_list', [ + 'course_id' => $task->getCourse()->getId() + ]); + } } else { $this->addFlash('error', $translator->trans("This form contains errors")); From ead96f38361510856d12167fb12c4ec1c1c93a9e Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 10 Sep 2021 14:57:41 +0200 Subject: [PATCH 03/79] tasks added to accompanyingCourse menu --- .../Menu/AccompanyingCourseMenuBuilder.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php index 744e5dbbe..1ac0f8008 100644 --- a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php @@ -67,6 +67,13 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface 'id' => $period->getId() ]]) ->setExtras(['order' => 40]); + + $menu->addChild($this->translator->trans('Tasks'), [ + 'route' => 'chill_task_singletask_list', + 'routeParameters' => [ + 'course_id' => $period->getId() + ]]) + ->setExtras(['order' => 50]); } From b28b4e5fba42177fbc775870707913f6c69af1f0 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 10 Sep 2021 15:19:41 +0200 Subject: [PATCH 04/79] templates + controller further adapted to work with accompanyingCourse. new and show methods don't work yet due to authorization/voter issues templates adapted for use with accompanyingCourse tasks also --- .../Controller/SingleTaskController.php | 165 +++++-- .../views/SingleTask/_list.html.twig | 428 +++++++++--------- .../views/SingleTask/index.html.twig | 38 +- .../config/services/controller.yaml | 19 +- 4 files changed, 365 insertions(+), 285 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index 8c742c627..aed805bcf 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -29,6 +29,10 @@ use Symfony\Component\Translation\TranslatorInterface; use Chill\TaskBundle\Event\UI\UIEvent; use Chill\MainBundle\Repository\CenterRepository; use Chill\MainBundle\Timeline\TimelineBuilder; +use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; +use Symfony\Component\HttpFoundation\RequestStack; /** * Class SingleTaskController @@ -53,6 +57,11 @@ class SingleTaskController extends AbstractController * @var LoggerInterface */ protected $logger; + + /** + * @var RequestStack + */ + protected $request; /** * SingleTaskController constructor. @@ -62,11 +71,25 @@ class SingleTaskController extends AbstractController public function __construct( EventDispatcherInterface $eventDispatcher, TimelineBuilder $timelineBuilder, - LoggerInterface $logger + LoggerInterface $logger, + RequestStack $requestStack ) { $this->eventDispatcher = $eventDispatcher; $this->timelineBuilder = $timelineBuilder; $this->logger = $logger; + $this->request = $requestStack->getCurrentRequest(); + } + + + public function getEntity() + { + if($this->request->query->has('person_id')){ + return 'person'; + } else if ($this->request->query->has('course_id')) { + return 'course'; + } else { + return null; + } } @@ -77,7 +100,6 @@ class SingleTaskController extends AbstractController * ) */ public function newAction( - Request $request, TranslatorInterface $translator ) { @@ -85,18 +107,12 @@ class SingleTaskController extends AbstractController ->setAssignee($this->getUser()) ->setType('task_default') ; - - if($request->query->has('person_id')){ - $entityType = 'person'; - } else if ($request->query->has('course_id')) { - $entityType = 'course'; - } else { - $entityType = null; - } + + $entityType = $this->getEntity(); if ($entityType !== null) { - $entityId = $request->query->getInt("{$entityType}_id", 0); // sf4 check: + $entityId = $this->request->query->getInt("{$entityType}_id", 0); // sf4 check: // prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given` if ($entityId === null) { @@ -134,12 +150,18 @@ class SingleTaskController extends AbstractController } + // error message: You should associate a person with task in order to check autorizations. + // consequently adapting TaskVoter to take into account accompanyinCourse throws new errors linked to authorizationHelper on line 151 + $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' . 'allowed to create this task'); + // error message: An error has occurred resolving the options of the form "Chill\TaskBundle\Form\SingleTaskType": + //The option "center" with value null is expected to be of type "Chill\MainBundle\Entity\Center", but is of type "null". + $form = $this->setCreateForm($task, new Role(TaskVoter::CREATE)); - $form->handleRequest($request); + $form->handleRequest($this->request); if ($form->isSubmitted()) { if ($form->isValid()) { @@ -184,13 +206,22 @@ class SingleTaskController extends AbstractController * name="chill_task_single_task_show" * ) */ - public function showAction(Request $request, $id) + public function showAction($id) { $em = $this->getDoctrine()->getManager(); $task = $em->getRepository(SingleTask::class)->find($id); + // In case no task is found + + if (!$task) { + throw $this->createNotFoundException('Unable to find Task entity.'); + } + + // In case task belongs to person + if ($task->getPerson() !== null) { + $personId = $task->getPerson()->getId(); if ($personId === null) { @@ -204,23 +235,42 @@ class SingleTaskController extends AbstractController if ($person === null) { throw $this->createNotFoundException("Invalid person id"); } - } - $this->denyAccessUnlessGranted(TaskVoter::SHOW, $task, 'You are not ' - . 'allowed to view this task'); - if (!$task) { - throw $this->createNotFoundException('Unable to find Task entity.'); - } - - $timeline = $this->timelineBuilder - ->getTimelineHTML('task', array('task' => $task)); - - $event = new PrivacyEvent($person, array( + $event = new PrivacyEvent($person, array( 'element_class' => SingleTask::class, 'element_id' => $task->getId(), 'action' => 'show' - )); - $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); + )); + $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); + + } + + // In case task belongs to accompanying course + + if ($task->getCourse() !== null) + { + $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 course id"); + } + } + + + $this->denyAccessUnlessGranted(TaskVoter::SHOW, $task, 'You are not ' + . 'allowed to view this task'); + + $timeline = $this->timelineBuilder + ->getTimelineHTML('task', array('task' => $task)); return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array( 'task' => $task, @@ -236,7 +286,6 @@ class SingleTaskController extends AbstractController * ) */ public function editAction( - Request $request, $id, TranslatorInterface $translator ) { @@ -273,7 +322,7 @@ class SingleTaskController extends AbstractController $form = $event->getForm(); - $form->handleRequest($request); + $form->handleRequest($this->request); if ($form->isSubmitted()) { if ($form->isValid()) { @@ -294,7 +343,7 @@ class SingleTaskController extends AbstractController return $this->redirectToRoute( 'chill_task_singletask_list', - $request->query->get('list_params', []) + $this->request->query->get('list_params', []) ); } else { @@ -440,6 +489,7 @@ class SingleTaskController extends AbstractController * Arguments: * - user_id * - scope_id + * - course_id * - person_id * - hide_form (hide the form to filter the tasks) * - status: date state, amongst SingleTaskRepository::DATE_STATUSES, or 'closed' @@ -450,10 +500,10 @@ class SingleTaskController extends AbstractController * ) */ public function listAction( - Request $request, PaginatorFactory $paginatorFactory, SingleTaskRepository $taskRepository, PersonRepository $personRepository, + AccompanyingPeriodRepository $courseRepository, CenterRepository $centerRepository, FormFactoryInterface $formFactory ) { @@ -466,12 +516,13 @@ class SingleTaskController extends AbstractController $params['user'] = null; $viewParams['center'] = null; $params['types'] = null; + $viewParams['accompanyingCourse'] = null; // Get parameters from url - if (!empty($request->query->get('person_id', NULL))) { + if (!empty($this->request->query->get('person_id', NULL))) { - $personId = $request->query->getInt('person_id', 0); + $personId = $this->request->query->getInt('person_id', 0); $person = $personRepository->find($personId); if ($person === null) { @@ -482,28 +533,42 @@ class SingleTaskController extends AbstractController $viewParams['person'] = $person; $params['person'] = $person; } + + if (!empty($this->request->query->get('course_id', NULL))) { + + $courseId = $this->request->query->getInt('course_id', 0); + $course = $courseRepository->find($courseId); + + if ($course === null) { + throw $this->createNotFoundException("This accompanying course ' $courseId ' does not exist."); + } + $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $course); + + $viewParams['accompanyingCourse'] = $course; + $params['accompanyingCourse'] = $course; + } - if (!empty($request->query->get('center_id', NULL))) { - $center = $centerRepository->find($request->query->getInt('center_id')); + if (!empty($this->request->query->get('center_id', NULL))) { + $center = $centerRepository->find($this->request->query->getInt('center_id')); if ($center === null) { throw $this->createNotFoundException('center not found'); } $params['center'] = $center; } - if(!empty($request->query->get('types', []))) { - $types = $request->query->get('types', []); + if(!empty($this->request->query->get('types', []))) { + $types = $this->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') { + if (!empty($this->request->query->get('user_id', null))) { + if ($this->request->query->get('user_id') === '_unassigned') { $params['unassigned'] = true; } else { - $userId = $request->query->getInt('user_id', 0); + $userId = $this->request->query->getInt('user_id', 0); $user = $this->getDoctrine()->getManager() ->getRepository('ChillMainBundle:User') ->find($userId); @@ -517,9 +582,9 @@ class SingleTaskController extends AbstractController } } - if (!empty($request->query->get('scope_id'))) { + if (!empty($this->request->query->get('scope_id'))) { - $scopeId = $request->query->getInt('scope_id', 0); + $scopeId = $this->request->query->getInt('scope_id', 0); $scope = $this->getDoctrine()->getManager() ->getRepository('ChillMainBundle:Scope') @@ -535,7 +600,7 @@ class SingleTaskController extends AbstractController // collect parameters for filter $possibleStatuses = \array_merge(SingleTaskRepository::DATE_STATUSES, [ 'closed' ]); - $statuses = $request->query->get('status', $possibleStatuses); + $statuses = $this->request->query->get('status', $possibleStatuses); // check for invalid statuses $diff = \array_diff($statuses, $possibleStatuses); @@ -551,7 +616,7 @@ class SingleTaskController extends AbstractController $tasks_count = 0; foreach($statuses as $status) { - if($request->query->has('status') + if($this->request->query->has('status') && FALSE === \in_array($status, $statuses)) { continue; } @@ -586,6 +651,8 @@ class SingleTaskController extends AbstractController if ($viewParams['person'] !== null){ $viewParams['layout'] = '@ChillPerson/Person/layout.html.twig'; + } else if ($viewParams['accompanyingCourse'] !== null){ + $viewParams['layout'] = '@ChillPerson/AccompanyingCourse/layout.html.twig'; } else { $viewParams['layout'] = '@ChillMain/layout.html.twig'; } @@ -598,7 +665,7 @@ class SingleTaskController extends AbstractController 'add_type' => true ]); - $form->handleRequest($request); + $form->handleRequest($this->request); if (isset($person)) { $event = new PrivacyEvent($person, array( @@ -609,14 +676,14 @@ class SingleTaskController extends AbstractController } return $this->render('ChillTaskBundle:SingleTask:index.html.twig', - \array_merge($viewParams, [ 'form' => $form->createView() ])); + array_merge($viewParams, [ 'form' => $form->createView() ])); } - protected function getPersonParam(Request $request, EntityManagerInterface $em) + protected function getPersonParam(EntityManagerInterface $em) { $person = $em->getRepository(Person::class) - ->find($request->query->getInt('person_id')) + ->find($this->request->query->getInt('person_id')) ; if (NULL === $person) { @@ -629,10 +696,10 @@ class SingleTaskController extends AbstractController return $person; } - protected function getUserParam(Request $request, EntityManagerInterface $em) + protected function getUserParam(EntityManagerInterface $em) { $user = $em->getRepository(User::class) - ->find($request->query->getInt('user_id')) + ->find($this->request->query->getInt('user_id')) ; if (NULL === $user) { diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig index a5a867dcc..e80e45e8c 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig @@ -1,244 +1,256 @@ {% macro date_status(title, tasks, count, paginator, status, isSingleStatus, person, user) %} - {% if tasks|length > 0 %} -

{{ title|trans }}

+ {% if tasks|length > 0 %} +

{{ title|trans }}

- - - {% for task in tasks %} - - + + {% endfor %} + +
-
- {{ task.title }} -
+ + + {% for task in tasks %} + + - + - - {% endfor %} - -
+
+ {{ task.title }} +
- {% if person is null %} -
- {{ 'For person'|trans }} : {{ task.person}} -
- {% endif %} + {% if person is null %} +
+ {{ 'For person'|trans }} : + + {{ task.person}} + +
+ {% endif %} -
- {{ task_workflow_metadata(task, 'definition.name')|trans }} -
+
+ {{ task_workflow_metadata(task, 'definition.name')|trans }} +
-
- {% for place in workflow_marked_places(task) %} - {{ place|trans }} - {% endfor %} - {% if task.assignee is not null %} -
{{ 'By'|trans }} : {{ task.assignee.username }}
- {% endif %} -
+
+ {% for place in workflow_marked_places(task) %} + {{ place|trans }} + {% endfor %} + {% if task.assignee is not null %} +
+ {{ 'By'|trans }} : + {{ task.assignee.username }}
+ {% endif %} +
- {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} -
-
    - {% if task.startDate is not null %} -
  • - {{ task.startDate|format_date('medium') }} -
  • - {% endif %} - {% if task.warningDate is not null %} -
  • - {{ task.warningDate|format_date('medium') }} -
  • - {% endif %} - {% if task.endDate is not null %} -
  • - {{ task.endDate|format_date('medium') }} -
  • - {% endif %} -
-
- {% endif %} + {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} +
+
    + {% if task.startDate is not null %} +
  • + + {{ task.startDate|format_date('medium') }} +
  • + {% endif %} + {% if task.warningDate is not null %} +
  • + + {{ task.warningDate|format_date('medium') }} +
  • + {% endif %} + {% if task.endDate is not null %} +
  • + + {{ task.endDate|format_date('medium') }} +
  • + {% endif %} +
+
+ {% endif %} -
- + -
+ {% if is_granted('CHILL_TASK_TASK_DELETE', task) %} +
  • + +
  • + {% endif %} + +
    - {% if isSingleStatus %} - {% if tasks|length < paginator.getTotalItems %} - {{ chill_pagination(paginator) }} - {% endif %} + {% if isSingleStatus %} + {% if tasks|length < paginator.getTotalItems %} + {{ chill_pagination(paginator) }} + {% endif %} - - - {% else %} - + {% endif %} - {% endif %} + {% endif %} {% endmacro %} {% import _self as helper %} -

    {{ app.request.query.get('title', null)|escape('html')|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 }}

    - {{ form_start(form) }} - {{ form_row(form.user_id) }} +{% if false == app.request.query.boolean('hide_form', false) %} +

    {{ 'Filter the tasks'|trans }}

    + {{ form_start(form) }} + {{ form_row(form.user_id) }} - {% if form.status is defined %} - {{ form_row(form.status) }} - {% endif %} + {% if form.status is defined %} + {{ form_row(form.status) }} + {% endif %} - {% if form.types is defined %} - {{ form_row(form.types) }} - {% endif %} + {% if form.types is defined %} + {{ form_row(form.types) }} + {% endif %} - {% if form.person_id is defined %} - {{ form_row(form.person_id) }} - {% endif %} + {% if form.person_id is defined %} + {{ form_row(form.person_id) }} + {% endif %} - {% if form.center_id is defined %} - {{ form_row(form.center_id) }} - {% endif %} + {% if form.center_id is defined %} + {{ form_row(form.center_id) }} + {% endif %} -
      -
    • - -
    • -
    +
      +
    • + +
    • +
    - {{ form_end(form)}} - {% endif %} + {{ form_end(form)}} +{% endif %} - {% if tasks_count == 0 %} -

    {{ "There is no tasks."|trans }}

    - {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} - - {% endif %} - {% else %} +{% if tasks_count == 0 %} +

    {{ "There is no tasks."|trans }}

    + {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} + + {% endif %} +{% else %} - {% if false == app.request.query.boolean('hide_form', false) %} -

    {{ 'Tasks'|trans }}

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

    {{ 'Tasks'|trans }}

    + {% endif %} - {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} - - {% endif %} + {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} + + {% endif %} - {% if single_task_ended_tasks is defined %} - {{ helper.date_status('Tasks with expired deadline', single_task_ended_tasks, single_task_ended_count, single_task_ended_paginator, 'ended', isSingleStatus, person) }} - {% endif %} + {% if single_task_ended_tasks is defined %} + {{ helper.date_status('Tasks with expired deadline', single_task_ended_tasks, single_task_ended_count, single_task_ended_paginator, 'ended', isSingleStatus, person) }} + {% endif %} - {% if single_task_warning_tasks is defined %} - {{ helper.date_status('Tasks with warning deadline reached', single_task_warning_tasks, single_task_warning_count, single_task_warning_paginator, 'warning', isSingleStatus, person) }} - {% endif %} + {% if single_task_warning_tasks is defined %} + {{ helper.date_status('Tasks with warning deadline reached', single_task_warning_tasks, single_task_warning_count, single_task_warning_paginator, 'warning', isSingleStatus, person) }} + {% endif %} - {% if single_task_current_tasks is defined %} - {{ helper.date_status('Current tasks', single_task_current_tasks, single_task_current_count, single_task_current_paginator, 'current', isSingleStatus, person) }} - {% endif %} + {% if single_task_current_tasks is defined %} + {{ helper.date_status('Current tasks', single_task_current_tasks, single_task_current_count, single_task_current_paginator, 'current', isSingleStatus, person) }} + {% endif %} - {% if single_task_not_started_tasks is defined %} - {{ helper.date_status('Tasks not started', single_task_not_started_tasks, single_task_not_started_count, single_task_not_started_paginator, 'not_started', isSingleStatus, person) }} - {% endif %} + {% if single_task_not_started_tasks is defined %} + {{ helper.date_status('Tasks not started', single_task_not_started_tasks, single_task_not_started_count, single_task_not_started_paginator, 'not_started', isSingleStatus, person) }} + {% endif %} - {% if single_task_closed_tasks is defined %} - {{ helper.date_status('Closed tasks', single_task_closed_tasks, single_task_closed_count, single_task_closed_paginator, 'closed', isSingleStatus, person) }} - {% endif %} + {% if single_task_closed_tasks is defined %} + {{ helper.date_status('Closed tasks', single_task_closed_tasks, single_task_closed_count, single_task_closed_paginator, 'closed', isSingleStatus, person) }} + {% endif %} - {% if isSingleStatus == false %} - - {% endif %} + {% if isSingleStatus == false %} + + {% endif %} {% endif %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig index 2a33fc10c..1932d7525 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig @@ -15,30 +15,30 @@ * along with this program. If not, see . #} -{% extends '@ChillPerson/Person/layout.html.twig' %} +{% extends layout %} {% set activeRouteKey = 'chill_task_single_task_new' %} -{% block title %}{{ 'Task list'|trans }}{% endblock %} +{% block title %} + {{ 'Task list'|trans }} +{% endblock %} -{% macro thead() %} -{% endmacro %} +{% macro thead() %}{% endmacro %} -{% macro row(task) %} -{% endmacro %} +{% macro row(task) %}{% endmacro %} {% block filtertasks %} -{% if person is not null %} - {% block personcontent %} -
    - {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} -
    - {% endblock %} -{% else %} - {% block content %} -
    - {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} -
    - {% endblock %} -{% endif %} + {% if person is not null %} + {% block personcontent %} +
    + {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} +
    + {% endblock %} + {% else %} + {% block content %} +
    + {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} +
    + {% endblock %} + {% endif %} {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/config/services/controller.yaml b/src/Bundle/ChillTaskBundle/config/services/controller.yaml index 533c57f47..cfe083e07 100644 --- a/src/Bundle/ChillTaskBundle/config/services/controller.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/controller.yaml @@ -1,11 +1,12 @@ services: - Chill\TaskBundle\Controller\: - resource: '../../Controller' - tags: ['controller.service_arguments'] + Chill\TaskBundle\Controller\: + resource: "../../Controller" + tags: ["controller.service_arguments"] - Chill\TaskBundle\Controller\SingleTaskController: - arguments: - $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface' - $timelineBuilder: '@chill_main.timeline_builder' - $logger: '@chill.main.logger' - tags: ['controller.service_arguments'] + Chill\TaskBundle\Controller\SingleTaskController: + arguments: + $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface' + $timelineBuilder: "@chill_main.timeline_builder" + $logger: "@chill.main.logger" + $requestStack: '@Symfony\Component\HttpFoundation\RequestStack' + tags: ["controller.service_arguments"] From b1dbd8b0111f06a5481d47b4dfb264d9bc5a9344 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 15 Sep 2021 11:13:09 +0200 Subject: [PATCH 05/79] Menu entry added in PersonMenuBuilder of taskbundle. Removed from menubuilder in Personbundle. Rename file, PersonMenuBuilder to MenuBuilder? --- .../Menu/AccompanyingCourseMenuBuilder.php | 7 --- .../Menu/PersonMenuBuilder.php | 49 ++++++++++++++----- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php index 1ac0f8008..744e5dbbe 100644 --- a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php @@ -67,13 +67,6 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface 'id' => $period->getId() ]]) ->setExtras(['order' => 40]); - - $menu->addChild($this->translator->trans('Tasks'), [ - 'route' => 'chill_task_singletask_list', - 'routeParameters' => [ - 'course_id' => $period->getId() - ]]) - ->setExtras(['order' => 50]); } diff --git a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php index ee0afa497..8c45b0beb 100644 --- a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php +++ b/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php @@ -53,28 +53,55 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface public function buildMenu($menuId, MenuItem $menu, array $parameters) { - /* @var $person \Chill\PersonBundle\Entity\Person */ + switch($menuId) { + case 'person': + $this->buildPersonMenu($menu, $parameters); + break; + case 'accompanyingCourse': + $this->buildAccompanyingCourseMenu($menu, $parameters); + break; + case 'section': + $menu->setExtras('icons', 'tasks'); + break; + default: + throw new \LogicException("this menuid $menuId is not implemented"); + } + } + + public function buildPersonMenu($menu, $parameters){ + + //var $person \Chill\PersonBundle\Entity\Person */ $person = $parameters['person'] ?? null; if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $person)) { $menu->addChild( $this->translator->trans('Tasks'), [ 'route' => 'chill_task_singletask_list', - 'routeParameters' => $menuId === 'person' ? + 'routeParameters' => [ 'person_id' => $person->getId() ] - : - null, - ]) - ->setExtra('order', 400) - ; - if ($menuId === 'section') { - $menu->setExtra('icons', 'tasks'); - } + ]) + ->setExtra('order', 400); } } + public function buildAccompanyingCourseMenu($menu, $parameters){ + + //var $person \Chill\PersonBundle\Entity\Person */ + $course = $parameters['accompanyingCourse']; + + // if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { + $menu->addChild( + $this->translator->trans('Tasks'), [ + 'route' => 'chill_task_singletask_list', + 'routeParameters' => + [ 'course_id' => $course->getId() ] + ]) + ->setExtra('order', 400); + // } + } + public static function getMenuIds(): array { - return ['person']; + return ['person', 'accompanyingCourse']; } } From a156bd08636f3ae35b07a2f4e58fa75984cbe376 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 16 Sep 2021 15:55:09 +0200 Subject: [PATCH 06/79] controller and templates adapted to display list of accompanying period tasks + detailpage of task --- .../Controller/SingleTaskController.php | 150 ++++++++++++++---- .../Menu/PersonMenuBuilder.php | 4 +- .../views/SingleTask/_listCourse.html.twig | 107 +++++++++++++ .../views/SingleTask/_show.html.twig | 110 +++++++++++++ .../views/SingleTask/index.html.twig | 2 +- .../Resources/views/SingleTask/show.html.twig | 119 +------------- .../views/SingleTask/showCourseTask.html.twig | 16 ++ .../translations/messages.fr.yml | 117 +++++++------- 8 files changed, 423 insertions(+), 202 deletions(-) create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index aed805bcf..e98688aa6 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -32,6 +32,8 @@ use Chill\MainBundle\Timeline\TimelineBuilder; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; +use Chill\TaskBundle\Form\SingleTaskCourseType; +use Chill\TaskBundle\Repository\AbstractTaskRepository; use Symfony\Component\HttpFoundation\RequestStack; /** @@ -81,7 +83,7 @@ class SingleTaskController extends AbstractController } - public function getEntity() + private function getEntityContext() { if($this->request->query->has('person_id')){ return 'person'; @@ -108,7 +110,7 @@ class SingleTaskController extends AbstractController ->setType('task_default') ; - $entityType = $this->getEntity(); + $entityType = $this->getEntityContext(); if ($entityType !== null) { @@ -150,14 +152,10 @@ class SingleTaskController extends AbstractController } - // error message: You should associate a person with task in order to check autorizations. - // consequently adapting TaskVoter to take into account accompanyinCourse throws new errors linked to authorizationHelper on line 151 + //TODO : resolve access rights - $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' - . 'allowed to create this task'); - - // error message: An error has occurred resolving the options of the form "Chill\TaskBundle\Form\SingleTaskType": - //The option "center" with value null is expected to be of type "Chill\MainBundle\Entity\Center", but is of type "null". + // $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' + // . 'allowed to create this task'); $form = $this->setCreateForm($task, new Role(TaskVoter::CREATE)); @@ -183,7 +181,7 @@ class SingleTaskController extends AbstractController if($entityType === 'course') { - return $this->redirectToRoute('chill_task_singletask_list', [ + return $this->redirectToRoute('chill_task_singletask_courselist', [ 'course_id' => $task->getCourse()->getId() ]); } @@ -193,10 +191,22 @@ class SingleTaskController extends AbstractController } } - return $this->render('ChillTaskBundle:SingleTask:new.html.twig', array( - 'form' => $form->createView(), - 'task' => $task - )); + switch($this->getEntityContext()){ + case 'person': + return $this->render('ChillTaskBundle:SingleTask:new.html.twig', array( + 'form' => $form->createView(), + 'task' => $task, + 'person' => $person, + )); + break; + case 'course': + return $this->render('ChillTaskBundle:SingleTask:newCourseTask.html.twig', array( + 'form' => $form->createView(), + 'task' => $task, + 'accompanyingCourse' => $course, + )); + } + } @@ -271,11 +281,19 @@ class SingleTaskController extends AbstractController $timeline = $this->timelineBuilder ->getTimelineHTML('task', array('task' => $task)); - - return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array( - 'task' => $task, - 'timeline' => $timeline - )); + + if($this->getEntityContext() === 'person'){ + return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array( + 'task' => $task, + 'timeline' => $timeline + )); + } else { + return $this->render('ChillTaskBundle:SingleTask:showCourseTask.html.twig', array( + 'task' => $task, + 'timeline' => $timeline + )); + } + } @@ -457,10 +475,18 @@ class SingleTaskController extends AbstractController */ protected function setCreateForm(SingleTask $task, Role $role) { - $form = $this->createForm(SingleTaskType::class, $task, [ - 'center' => $task->getCenter(), - 'role' => $role - ]); + if($this->getEntityContext() === 'person'){ + $form = $this->createForm(SingleTaskType::class, $task, [ + 'center' => $task->getCenter(), + 'role' => $role, + ]); + } + + if($this->getEntityContext() === 'course'){ + $form = $this->createForm(SingleTaskCourseType::class, $task, [ + 'center' => $task->getCenter(), + ]); + } $form->add('submit', SubmitType::class); @@ -517,6 +543,7 @@ class SingleTaskController extends AbstractController $viewParams['center'] = null; $params['types'] = null; $viewParams['accompanyingCourse'] = null; + $params['accompanyingCourse'] = null; // Get parameters from url @@ -658,12 +685,23 @@ class SingleTaskController extends AbstractController } // Form for filtering tasks - $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ - 'person' => $viewParams['person'], - 'method' => Request::METHOD_GET, - 'csrf_protection' => false, - 'add_type' => true - ]); + if($this->getEntityContext() === 'person'){ + $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ + 'person' => $viewParams['person'], + 'method' => Request::METHOD_GET, + 'csrf_protection' => false, + 'add_type' => true + ]); + } + + if($this->getEntityContext() === 'course'){ + $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ + 'accompanyingCourse' => $viewParams['accompanyingCourse'], + 'method' => Request::METHOD_GET, + 'csrf_protection' => false, + 'add_type' => true + ]); + } $form->handleRequest($this->request); @@ -728,4 +766,58 @@ class SingleTaskController extends AbstractController ; } + /** + * @Route( + * "/{_locale}/task/singletask/courselist", + * name="chill_task_singletask_courselist") + */ + + public function listCourseTasks( + AccompanyingPeriodRepository $courseRepository, + SingleTaskRepository $taskRepository, + FormFactoryInterface $formFactory, + TranslatorInterface $translator + ): Response + { + + if (!empty($this->request->query->get('course_id', NULL))) { + + $courseId = $this->request->query->getInt('course_id', 0); + $course = $courseRepository->find($courseId); + + if ($course === null) { + throw $this->createNotFoundException("This accompanying course ' $courseId ' does not exist."); + } + + } + + $em = $this->getDoctrine()->getManager(); + + if($course === NULL) { + throw $this->createNotFoundException('Accompanying course not found'); + } + + $tasks = $taskRepository + ->findBy( + array('course' => $course) + ); + + $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ + 'accompanyingCourse' => $course, + 'method' => Request::METHOD_GET, + 'csrf_protection' => false, + 'add_type' => true + ]); + + return $this->render( + 'ChillTaskBundle:SingleTask:index.html.twig', + [ + 'tasks' => $tasks, + 'accompanyingCourse' => $course, + 'layout' => '@ChillPerson/AccompanyingCourse/layout.html.twig', + 'form' => $form->createView(), + 'title' => $translator->trans('Tasks for this accompanying period') + ]); + } + } diff --git a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php index 8c45b0beb..26f06d1b2 100644 --- a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php +++ b/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php @@ -89,10 +89,12 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface //var $person \Chill\PersonBundle\Entity\Person */ $course = $parameters['accompanyingCourse']; + //TODO: implement voter again? + // if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { $menu->addChild( $this->translator->trans('Tasks'), [ - 'route' => 'chill_task_singletask_list', + 'route' => 'chill_task_singletask_courselist', 'routeParameters' => [ 'course_id' => $course->getId() ] ]) diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig new file mode 100644 index 000000000..16c907e4c --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig @@ -0,0 +1,107 @@ +{% if tasks|length > 0 %} +

    {{ title|trans }}

    + + + + {% for task in tasks %} + + + + + {% endfor %} + +
    +
    + {{ task.title }} +
    + +
    + {{ task_workflow_metadata(task, 'definition.name')|trans }} +
    + +
    + {% for place in workflow_marked_places(task) %} + {{ place|trans }} + {% endfor %} + {% if task.assignee is not null %} +
    + {{ 'By'|trans }} : + {{ task.assignee.username }}
    + {% endif %} +
    + + {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} +
    +
      + {% if task.startDate is not null %} +
    • + + {{ task.startDate|format_date('medium') }} +
    • + {% endif %} + {% if task.warningDate is not null %} +
    • + + {{ task.warningDate|format_date('medium') }} +
    • + {% endif %} + {% if task.endDate is not null %} +
    • + + {{ task.endDate|format_date('medium') }} +
    • + {% endif %} +
    +
    + {% endif %} + +
    + +
    +{% endif %} + + diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig new file mode 100644 index 000000000..a8905285a --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig @@ -0,0 +1,110 @@ +
    + +

    {{ 'Task'|trans }}

    + +

    {{ task.title }} + {% for place in workflow_marked_places(task) %} + {{ place|trans }} + {% endfor %} +

    + +
    + +
    {{ 'Description'|trans }}
    +
    + {% if task.description is empty %} + {{"No description"|trans}} + {% else %} +
    + {{ task.description|chill_markdown_to_html }} +
    + {% endif %} +
    + +
    {{ 'Assignee'|trans }}
    +
    + {% if task.assignee is null %} + {{"No one assignee"|trans}} + {% else %} + {{ task.assignee }} + {% endif %} +
    + +
    {{ 'Scope'|trans }}
    +
    + {{ task.scope.name|localize_translatable_string }} +
    + +

    {{"Dates"|trans}}

    + {% if task.startDate is null and task.endDate is null and task.warningDate is null %} +
    +
    + {{"No dates specified"|trans}} +
    + + {% else %} + {% if task.startDate is not null %} +
    {{ 'Start'|trans }}
    +
    {{ task.startDate|format_date('long') }}
    + {% endif %} + + {% if task.endDate is not null %} +
    {{ 'End'|trans }}
    +
    {{ task.endDate|format_date('long') }}
    + {% endif %} + + {% if task.warningDate is not null %} +
    {{ 'Warning'|trans }}
    +
    {{ task.warningDate|format_date('long') }}
    + {% endif %} + + {% endif %} +
    + +{% if timeline is not null %} +

    {{"Timeline"|trans}}

    + {{ timeline|raw }} +{% endif %} + +
    diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig index 1932d7525..4c0b9dc84 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig @@ -37,7 +37,7 @@ {% else %} {% block content %}
    - {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} + {% include 'ChillTaskBundle:SingleTask:_listCourse.html.twig' %}
    {% endblock %} {% endif %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig index 8cfa1ea33..19bf70777 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig @@ -16,124 +16,17 @@ #} {% extends "@ChillPerson/Person/layout.html.twig" %} + {% set activeRouteKey = 'chill_task_single_task_show' %} {% set person = task.person %} -{% block title %}{{ 'Task'|trans }}{% endblock %} +{% block title %} + {{ 'Task'|trans }} +{% endblock %} {% block personcontent %} -
    - -

    {{ 'Task'|trans }}

    - -

    {{ task.title }} {% for place in workflow_marked_places(task) %} - {{ place|trans }} - {% endfor %}

    - -
    - -
    {{ 'Description'|trans }}
    -
    - {% if task.description is empty %} - {{"No description"|trans}} - {% else %} -
    - {{ task.description|chill_markdown_to_html }} -
    - {% endif %} -
    - -
    {{ 'Assignee'|trans }}
    -
    - {% if task.assignee is null %} - {{"No one assignee"|trans}} - {% else %} - {{ task.assignee }} - {% endif %} -
    - -
    {{ 'Scope'|trans }}
    -
    {{ task.scope.name|localize_translatable_string }}
    - -

    {{"Dates"|trans}}

    - {% if task.startDate is null and task.endDate is null and task.warningDate is null %} -
    -
    {{"No dates specified"|trans}}
    - - {% else %} - {% if task.startDate is not null %} -
    {{ 'Start'|trans }}
    -
    {{ task.startDate|format_date('long') }}
    - {% endif %} - - {% if task.endDate is not null %} -
    {{ 'End'|trans }}
    -
    {{ task.endDate|format_date('long') }}
    - {% endif %} - - {% if task.warningDate is not null %} -
    {{ 'Warning'|trans }}
    -
    {{ task.warningDate|format_date('long') }}
    - {% endif %} - - {% endif %} -
    - - {% if timeline is not null %} -

    {{"Timeline"|trans}}

    - {{ timeline|raw }} - {% endif %} - - - -
    + + {% include 'ChillTaskBundle:SingleTask:_show.html.twig' %} {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig new file mode 100644 index 000000000..643617a55 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig @@ -0,0 +1,16 @@ +{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} + + +{% set activeRouteKey = 'chill_task_single_task_show' %} +{% set accompanyingCourse = task.course %} + +{% block title %} + {{ 'Task'|trans }} +{% endblock %} + + +{% block content %} + + {% include 'ChillTaskBundle:SingleTask:_show.html.twig' %} + +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml index 71bc22612..7af9d450d 100644 --- a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml @@ -1,53 +1,54 @@ -Tasks: 'Tâches' -'New task': 'Nouvelle tâche' -'Add a new task': 'Ajouter une nouvelle tâche' +Tasks: "Tâches" +"New task": "Nouvelle tâche" +"Add a new task": "Ajouter une nouvelle tâche" Title: Titre Description: Description -Assignee: 'Personne assignée' +Assignee: "Personne assignée" Scope: Cercle -'Start date': 'Date de début' -'End date': "Date d'échéance" -'Warning date': "Date d'avertissement" -'Warning interval': "Délai d'avertissement avant la date d'échéance" -'Unknown dates': 'Dates non spécifiées' -'N': '' -'Unit': '' +"Start date": "Date de début" +"End date": "Date d'échéance" +"Warning date": "Date d'avertissement" +"Warning interval": "Délai d'avertissement avant la date d'échéance" +"Unknown dates": "Dates non spécifiées" +"N": "" +"Unit": "" Task: Tâche Details: Détails Person: Personne Date: Date Dates: Dates User: Utilisateur -'Task list': 'Liste des tâches' -'Tasks with expired deadline': "Tâches avec une date d'échéance dépassée" -'Tasks with warning deadline reached': "Tâches avec une date d'avertissement atteinte" -'Current tasks': 'Tâches en cours' -'Closed tasks': Tâches terminées -'Tasks not started': 'Tâches non commencées' -'Task start date': 'Date de début' -'Task warning date': "Date d'avertissement" -'Task end date': "Date d'échéance" -'Start': 'Début' -'Warning': 'Avertissement' -'End': 'Échéance' -'Task type': 'Type' -'Task status': 'Statut' -'Edit the task': 'Modifier la tâche' -'Edit task': 'Modifier la tâche' -'Save task': 'Enregistrer la tâche' -'View the task': 'Voir la tâche' -'Update the task': 'Mettre à jour la tâche' -'Remove task': 'Supprimer la tâche' -'Delete': 'Supprimer' -'Change task status': 'Changer le statut' +"Task list": "Liste des tâches" +"Tasks with expired deadline": "Tâches avec une date d'échéance dépassée" +"Tasks with warning deadline reached": "Tâches avec une date d'avertissement atteinte" +"Current tasks": "Tâches en cours" +"Closed tasks": Tâches terminées +"Tasks not started": "Tâches non commencées" +"Task start date": "Date de début" +"Task warning date": "Date d'avertissement" +"Task end date": "Date d'échéance" +"Start": "Début" +"Warning": "Avertissement" +"End": "Échéance" +"Task type": "Type" +"Task status": "Statut" +"Edit the task": "Modifier la tâche" +"Edit task": "Modifier la tâche" +"Save task": "Enregistrer la tâche" +"View the task": "Voir la tâche" +"Update the task": "Mettre à jour la tâche" +"Remove task": "Supprimer la tâche" +"Delete": "Supprimer" +"Change task status": "Changer le statut" 'Are you sure you want to remove the task about "%name%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche de "%name%"?' -'See more': 'Voir plus' -'Associated tasks': 'Tâches associées' -'My tasks': 'Mes tâches' -'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 +"See more": "Voir plus" +"Associated tasks": "Tâches associées" +"My tasks": "Mes tâches" +"Tasks for this accompanying period": "Tâches pour ce parcours d'accompagnement" +"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 @@ -63,36 +64,36 @@ For person: Pour By: Par # transitions - default task definition -'new': 'nouvelle' -'in_progress': 'en cours' -'closed': 'fermée' -'canceled': 'supprimée' +"new": "nouvelle" +"in_progress": "en cours" +"closed": "fermée" +"canceled": "supprimée" 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' +"%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' -'There is no tasks.': Aucune tâche. -'The task has been successfully removed.': 'La tâche a bien été supprimée' -'This form contains errors': 'Ce formulaire contient des erreurs' -'The task has been updated': 'La tâche a été mise à jour' -'The transition is successfully applied': 'La transition a bien été effectuée' -'The transition could not be applied': "La transition n'a pas pu être appliquée" +"The task is created": "La tâche a été créée" +"There is no tasks.": Aucune tâche. +"The task has been successfully removed.": "La tâche a bien été supprimée" +"This form contains errors": "Ce formulaire contient des erreurs" +"The task has been updated": "La tâche a été mise à jour" +"The transition is successfully applied": "La transition a bien été effectuée" +"The transition could not be applied": "La transition n'a pas pu être appliquée" #widget -'%number% tasks over deadline': '{0} Aucune tâche dépassée|{1} Une tâche dépassée | ]1,Inf[ %count% tâches dépassées' -'%number% tasks near deadline': '{0} Aucune tâche en rappel|{1} Une tâche en rappel | ]1,Inf[ %count% tâches en rappel' +"%number% tasks over deadline": "{0} Aucune tâche dépassée|{1} Une tâche dépassée | ]1,Inf[ %count% tâches dépassées" +"%number% tasks near deadline": "{0} Aucune tâche en rappel|{1} Une tâche en rappel | ]1,Inf[ %count% tâches en rappel" #title My tasks near deadline: Mes tâches à échéance proche @@ -107,4 +108,4 @@ All centers: Tous les centres CHILL_TASK_TASK_CREATE: Ajouter une tâche CHILL_TASK_TASK_DELETE: Supprimer une tâche CHILL_TASK_TASK_SHOW: Voir une tâche -CHILL_TASK_TASK_UPDATE: Modifier une tâche \ No newline at end of file +CHILL_TASK_TASK_UPDATE: Modifier une tâche From 01ae50aca75122d3c53d8d983d880c5c1a726aed Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 17 Sep 2021 14:33:42 +0200 Subject: [PATCH 07/79] new/show/edit/delete/list functionality added for accompanyingperiod task --- .../Entity/AccompanyingPeriod.php | 10 ++ .../Controller/SingleTaskController.php | 143 ++++++++++++------ .../ChillTaskBundle/Entity/AbstractTask.php | 12 ++ .../ChillTaskBundle/Form/SingleTaskType.php | 7 +- ...{PersonMenuBuilder.php => MenuBuilder.php} | 0 .../views/SingleTask/_edit.html.twig | 25 +++ .../Resources/views/SingleTask/_new.html.twig | 30 ++++ .../views/SingleTask/_show.html.twig | 4 +- .../confirm_deleteCourseTask.html.twig | 19 +++ .../Resources/views/SingleTask/edit.html.twig | 39 +---- .../views/SingleTask/editCourseTask.html.twig | 14 ++ .../Resources/views/SingleTask/new.html.twig | 33 +--- .../views/SingleTask/newCourseTask.html.twig | 12 ++ .../Security/Authorization/TaskVoter.php | 13 +- .../translations/messages.fr.yml | 1 + 15 files changed, 243 insertions(+), 119 deletions(-) rename src/Bundle/ChillTaskBundle/Menu/{PersonMenuBuilder.php => MenuBuilder.php} (100%) create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 1c375d051..0458a1e74 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -26,6 +26,7 @@ use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\Address; +use Chill\MainBundle\Entity\Center; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment; @@ -967,6 +968,15 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface return $this->addressLocation; } + public function getCenter(): ?Center + { + if (count($this->getPersons()) === 0){ + return null; + } else { + return $this->getPersons()->first()->getCenter(); + } + } + /** * @Groups({"write"}) */ diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index e98688aa6..456699091 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -6,7 +6,6 @@ use Chill\PersonBundle\Privacy\PrivacyEvent; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; -use Doctrine\ORM\EntityManager; use Chill\PersonBundle\Entity\Person; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -22,7 +21,6 @@ use Chill\TaskBundle\Repository\SingleTaskRepository; use Chill\MainBundle\Entity\User; use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Repository\PersonRepository; -use Chill\MainBundle\Entity\UserRepository; use Chill\TaskBundle\Event\TaskEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Translation\TranslatorInterface; @@ -32,8 +30,6 @@ use Chill\MainBundle\Timeline\TimelineBuilder; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; -use Chill\TaskBundle\Form\SingleTaskCourseType; -use Chill\TaskBundle\Repository\AbstractTaskRepository; use Symfony\Component\HttpFoundation\RequestStack; /** @@ -205,6 +201,7 @@ class SingleTaskController extends AbstractController 'task' => $task, 'accompanyingCourse' => $course, )); + break; } } @@ -281,8 +278,9 @@ class SingleTaskController extends AbstractController $timeline = $this->timelineBuilder ->getTimelineHTML('task', array('task' => $task)); + - if($this->getEntityContext() === 'person'){ + if($task->getContext() instanceof Person){ return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array( 'task' => $task, 'timeline' => $timeline @@ -311,7 +309,7 @@ class SingleTaskController extends AbstractController $em = $this->getDoctrine()->getManager(); $task = $em->getRepository(SingleTask::class)->find($id); - if ($task->getPerson() !== null) { + if ($task->getContext() instanceof Person) { $personId = $task->getPerson()->getId(); if ($personId === null) { return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST); @@ -324,7 +322,21 @@ class SingleTaskController extends AbstractController 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"); + } } + $this->denyAccessUnlessGranted(TaskVoter::UPDATE, $task, 'You are not ' . 'allowed to edit this task'); @@ -351,19 +363,25 @@ class SingleTaskController extends AbstractController $this->addFlash('success', $translator ->trans("The task has been updated")); - - $event = new PrivacyEvent($person, array( + + if($task->getContext() instanceof Person){ + $event = new PrivacyEvent($person, array( 'element_class' => SingleTask::class, 'element_id' => $task->getId(), 'action' => 'update' - )); - $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - - return $this->redirectToRoute( - 'chill_task_singletask_list', - $this->request->query->get('list_params', []) - ); + )); + $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); + return $this->redirectToRoute( + 'chill_task_singletask_list', + $this->request->query->get('list_params', []) + ); + } else { + return $this->redirectToRoute( + 'chill_task_singletask_courselist', + $this->request->query->get('list_params', []) + ); + } } else { $this->addFlash('error', $translator->trans("This form contains errors")); } @@ -375,17 +393,26 @@ class SingleTaskController extends AbstractController return $event->getResponse(); } - $event = new PrivacyEvent($person, array( - 'element_class' => SingleTask::class, - 'element_id' => $task->getId(), - 'action' => 'edit' - )); - $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - - return $this->render('ChillTaskBundle:SingleTask:edit.html.twig', array( - 'task' => $task, - 'form' => $form->createView() - )); + if($task->getContext() instanceof Person){ + $event = new PrivacyEvent($person, array( + 'element_class' => SingleTask::class, + 'element_id' => $task->getId(), + 'action' => 'edit' + )); + $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); + + return $this->render('ChillTaskBundle:SingleTask:edit.html.twig', array( + 'task' => $task, + 'form' => $form->createView() + )); + } else { + return $this->render('ChillTaskBundle:SingleTask:editCourseTask.html.twig', array( + 'task' => $task, + 'form' => $form->createView(), + 'accompanyingCourse' => $course + )); + } + } @@ -422,8 +449,22 @@ class SingleTaskController extends AbstractController 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"); + } } + $this->denyAccessUnlessGranted(TaskVoter::DELETE, $task, 'You are not ' . 'allowed to delete this task'); @@ -452,19 +493,35 @@ class SingleTaskController extends AbstractController $this->addFlash('success', $translator ->trans("The task has been successfully removed.")); - return $this->redirect($this->generateUrl( - 'chill_task_singletask_list', - $request->query->get('list_params', [ - 'person_id' => $person->getId() - ]))); + if($task->getContext() instanceof Person){ + return $this->redirect($this->generateUrl( + 'chill_task_singletask_list', + $request->query->get('list_params', [ + 'person_id' => $person->getId() + ]))); + } else { + return $this->redirect($this->generateUrl( + 'chill_task_singletask_courselist', + $request->query->get('list_params', [ + 'course_id' => $course->getId() + ]))); + } } } + if($task->getContext() instanceof Person){ + return $this->render('ChillTaskBundle:SingleTask:confirm_delete.html.twig', array( + 'task' => $task, + 'delete_form' => $form->createView() + )); + } else { + return $this->render('ChillTaskBundle:SingleTask:confirm_deleteCourseTask.html.twig', array( + 'task' => $task, + 'delete_form' => $form->createView(), + 'accompanyingCourse' => $course + )); + } - return $this->render('ChillTaskBundle:SingleTask:confirm_delete.html.twig', array( - 'task' => $task, - 'delete_form' => $form->createView() - )); } /** @@ -475,18 +532,10 @@ class SingleTaskController extends AbstractController */ protected function setCreateForm(SingleTask $task, Role $role) { - if($this->getEntityContext() === 'person'){ - $form = $this->createForm(SingleTaskType::class, $task, [ - 'center' => $task->getCenter(), - 'role' => $role, - ]); - } - - if($this->getEntityContext() === 'course'){ - $form = $this->createForm(SingleTaskCourseType::class, $task, [ - 'center' => $task->getCenter(), - ]); - } + $form = $this->createForm(SingleTaskType::class, $task, [ + 'center' => $task->getCenter(), + 'role' => $role, + ]); $form->add('submit', SubmitType::class); diff --git a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php index 649de0422..57a194ef8 100644 --- a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php +++ b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php @@ -248,11 +248,23 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface { if ($this->getPerson() instanceof Person) { return $this->getPerson()->getCenter(); + } else { + return $this->getCourse()->getCenter(); } return null; } + + public function getContext() + { + // if ($this->getCourse() instanceof AccompanyingPeriod){ + // return $this->getCourse(); + // } else { + // return $this->getPerson(); + // } + return $this->getPerson() ?? $this->getCourse(); + } public function getScope(): ?\Chill\MainBundle\Entity\Scope { diff --git a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php index 16f000aa2..5802512b7 100644 --- a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php +++ b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php @@ -48,11 +48,11 @@ class SingleTaskType extends AbstractType 'center' => $options['center'], 'role' => $options['role'], 'placeholder' => 'Not assigned' - ]) + ]) ->add('circle', ScopePickerType::class, [ 'center' => $options['center'], 'role' => $options['role'] - ]) + ]) ->add('startDate', ChillDateType::class, [ 'required' => false ]) @@ -61,8 +61,7 @@ class SingleTaskType extends AbstractType ]) ->add('warningInterval', DateIntervalType::class, [ 'required' => false - ]) - ; + ]); } public function configureOptions(OptionsResolver $resolver) diff --git a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php similarity index 100% rename from src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php rename to src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig new file mode 100644 index 000000000..98b070114 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig @@ -0,0 +1,25 @@ +
    + +

    {{ 'Edit task'|trans }}

    + + {{ form_start(form) }} + + {{ form_row(form.title) }} + {{ form_row(form.description) }} + {{ form_row(form.assignee) }} + {{ form_row(form.circle) }} + {{ form_row(form.startDate) }} + {{ form_row(form.endDate) }} + {{ form_row(form.warningInterval) }} + +
      +
    • + + {{ 'Cancel'|trans }} +
    • +
    • + {{ form_widget(form.submit, { 'label': 'Save task', 'attr': {'class' : 'btn btn-update'}})}} +
    • +
    + {{ form_end(form) }} +
    diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig new file mode 100644 index 000000000..7cb19b19f --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig @@ -0,0 +1,30 @@ +
    + +

    {{ 'New task'|trans }}

    + + {{ form_start(form) }} + + {{ form_errors(form) }} + + {{ form_row(form.title) }} + {{ form_row(form.description) }} + {{ form_row(form.assignee) }} + {{ form_row(form.circle) }} + {{ form_row(form.startDate) }} + {{ form_row(form.endDate) }} + {{ form_row(form.warningInterval) }} + +
      +
    • + + {{'Cancel'|trans}} + +
    • +
    • + {{ form_widget(form.submit, { 'label': 'Add a new task'|trans, 'attr': {'class': 'btn btn-save'} }) }} +
    • +
    + + {{ form_end(form) }} + +
    diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig index a8905285a..f73f2740d 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig @@ -68,8 +68,8 @@
    • - - {{ app.request.query.get('returnLabel')|default('Back to the list'|trans) }} + + {{'Back to the list'|trans}}
    • diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig new file mode 100644 index 000000000..49857188c --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig @@ -0,0 +1,19 @@ +{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} + +{% set activeRouteKey = 'chill_task_task_list' %} +{% set course = task.course %} + +{% block title 'Remove task'|trans %} + +{% block content %} + + {{ include('@ChillMain/Util/confirmation_template.html.twig', + { + 'title' : 'Remove task'|trans, + 'confirm_question' : 'Are you sure you want to remove the task about accompanying period "%id%" ?'|trans({ '%id%' : course.id } ), + 'cancel_route' : 'chill_task_singletask_courselist', + 'cancel_parameters' : app.request.query.get('list_params', { } ), + 'form' : delete_form, + } ) }} + +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig index 67604f015..590740cf0 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig @@ -14,46 +14,17 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . #} -{% extends "@ChillPerson/Person/layout.html.twig" %} +{% extends person is defined ? "@ChillPerson/Person/layout.html.twig" %} {% set activeRouteKey = 'chill_task_single_task_edit' %} {% set person = task.person %} -{% block title %}{{ 'Edit task'|trans }}{% endblock %} +{% block title %} + {{ 'Edit task'|trans }} +{% endblock %} {% block personcontent %} -
      - -

      {{ 'Edit task'|trans }}

      - {{ form_start(form) }} + {% include 'ChillTaskBundle:SingleTask:_edit.html.twig' %} - {{ form_row(form.title) }} - {{ form_row(form.description) }} - {{ form_row(form.assignee) }} - {{ form_row(form.circle) }} - {{ form_row(form.startDate) }} - {{ form_row(form.endDate) }} - {{ form_row(form.warningInterval) }} - - - - {{ form_end(form) }} - -
      {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig new file mode 100644 index 000000000..1805761ae --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig @@ -0,0 +1,14 @@ +{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} + +{% set activeRouteKey = 'chill_task_single_task_edit' %} +{% set course = task.course %} + +{% block title %} + {{ 'Edit task'|trans }} +{% endblock %} + +{% block content %} + + {% include 'ChillTaskBundle:SingleTask:_edit.html.twig' %} + +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig index 8f6ba2a4b..2b59fd5bf 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig @@ -14,37 +14,16 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . #} -{% extends "@ChillPerson/Person/layout.html.twig" %} +{% extends person is defined ? "@ChillPerson/Person/layout.html.twig" : "@ChillPerson/AccompanyingCourse/layout.html.twig" %} {% set activeRouteKey = 'chill_task_single_task_new' %} {% set person = task.person %} -{% block title %}{{ 'New task'|trans }}{% endblock %} +{% block title %} + {{ 'New task'|trans }} +{% endblock %} + {% block personcontent %} -
      - -

      {{ 'New task'|trans }}

      - - {{ form_start(form) }} - - {{ form_errors(form) }} - - {{ form_row(form.title) }} - {{ form_row(form.description) }} - {{ form_row(form.assignee) }} - {{ form_row(form.circle) }} - {{ form_row(form.startDate) }} - {{ form_row(form.endDate) }} - {{ form_row(form.warningInterval) }} - -
        -
      • - {{ form_widget(form.submit, { 'label': 'Add a new task'|trans, 'attr': {'class': 'btn btn-save'} }) }} -
      • -
      - - {{ form_end(form) }} - -
      + {% include 'ChillTaskBundle:SingleTask:_new.html.twig' %} {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig new file mode 100644 index 000000000..083c0795c --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig @@ -0,0 +1,12 @@ +{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} + +{% set activeRouteKey = 'chill_task_single_task_new' %} +{# {% set person = task.person %} #} + +{% block title %} + {{ 'New task'|trans }} +{% endblock %} + +{% block content %} + {% include 'ChillTaskBundle:SingleTask:_new.html.twig' %} +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php index 8b8bce839..1da58c5ce 100644 --- a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php +++ b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php @@ -27,7 +27,9 @@ use Psr\Log\LoggerInterface; use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Chill\MainBundle\Entity\User; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Chill\TaskBundle\Security\Authorization\AuthorizationEvent; @@ -128,14 +130,13 @@ class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterf } if ($subject instanceof AbstractTask) { - if ($subject->getPerson() === null) { + $associated = $subject->getPerson() ?? $subject->getCourse(); + if ($associated === null) { throw new \LogicException("You should associate a person with task " . "in order to check autorizations"); } - - $person = $subject->getPerson(); } elseif ($subject instanceof Person) { - $person = $subject; + $associated = $subject; } else { // subject is null. We check that at least one center is reachable $centers = $this->authorizationHelper->getReachableCenters($token->getUser(), new Role($attribute)); @@ -143,8 +144,10 @@ class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterf return count($centers) > 0; } - if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) { + if ($associated instanceof Person && !$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $associated)) { + return false; + } elseif ($associated instanceof AccompanyingPeriod && !$this->accessDecisionManager->decide($token, [AccompanyingPeriodVoter::SEE], $associated)) { return false; } diff --git a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml index 7af9d450d..50af895e7 100644 --- a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml @@ -41,6 +41,7 @@ User: Utilisateur "Delete": "Supprimer" "Change task status": "Changer le statut" 'Are you sure you want to remove the task about "%name%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche de "%name%"?' +'Are you sure you want to remove the task about accompanying period "%id%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche du parcours "%id%"?' "See more": "Voir plus" "Associated tasks": "Tâches associées" "My tasks": "Mes tâches" From 17b6f287dc03f77f43ba3e30e4ede8ef0a7165a5 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 17 Sep 2021 14:49:43 +0200 Subject: [PATCH 08/79] Changed name of PersonMenuBuilder More general name since it also contains the AccompanyingPeriod task menu entry now. --- .../ChillTaskBundle/Menu/MenuBuilder.php | 8 ++-- .../ChillTaskBundle/config/services/menu.yaml | 44 +++++++++---------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php index 26f06d1b2..9b1890958 100644 --- a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php +++ b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php @@ -28,7 +28,7 @@ use Symfony\Component\Translation\TranslatorInterface; * * @author Julien Fastré */ -class PersonMenuBuilder implements LocalMenuBuilderInterface +class MenuBuilder implements LocalMenuBuilderInterface { /** * @@ -86,12 +86,10 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface public function buildAccompanyingCourseMenu($menu, $parameters){ - //var $person \Chill\PersonBundle\Entity\Person */ $course = $parameters['accompanyingCourse']; - //TODO: implement voter again? - // if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { + if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { $menu->addChild( $this->translator->trans('Tasks'), [ 'route' => 'chill_task_singletask_courselist', @@ -99,7 +97,7 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface [ 'course_id' => $course->getId() ] ]) ->setExtra('order', 400); - // } + } } public static function getMenuIds(): array diff --git a/src/Bundle/ChillTaskBundle/config/services/menu.yaml b/src/Bundle/ChillTaskBundle/config/services/menu.yaml index b4ff7a421..75726fcf4 100644 --- a/src/Bundle/ChillTaskBundle/config/services/menu.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/menu.yaml @@ -1,23 +1,23 @@ services: - Chill\TaskBundle\Menu\UserMenuBuilder: - arguments: - $tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface' - $counter: '@Chill\TaskBundle\Templating\UI\CountNotificationTask' - $translator: '@Symfony\Component\Translation\TranslatorInterface' - $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' - tags: - - { name: 'chill.menu_builder' } - - Chill\TaskBundle\Menu\PersonMenuBuilder: - arguments: - $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' - $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' } + Chill\TaskBundle\Menu\UserMenuBuilder: + arguments: + $tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface' + $counter: '@Chill\TaskBundle\Templating\UI\CountNotificationTask' + $translator: '@Symfony\Component\Translation\TranslatorInterface' + $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' + tags: + - { name: "chill.menu_builder" } + + Chill\TaskBundle\Menu\MenuBuilder: + arguments: + $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' + $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" } From 27a52ce1666637ebefd0d6438a8383ba7ec20995 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 17 Sep 2021 14:53:15 +0200 Subject: [PATCH 09/79] Fix SingleTaskListType accompanyingCourse also defined as a form option --- src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php b/src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php index 99e8ddb42..634035f96 100644 --- a/src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php +++ b/src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php @@ -284,6 +284,7 @@ class SingleTaskListType extends AbstractType ->setDefined('person') ->setDefault('person', null) ->setAllowedTypes('person', [Person::class, 'null']) + ->setDefined('accompanyingCourse') ->setDefined('add_status') ->setDefault('add_status', false) ->setAllowedTypes('add_status', ['bool']) From 7515415888f4f99060de05f6ed8e9731eb7d58ec Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 17 Sep 2021 15:17:13 +0200 Subject: [PATCH 10/79] autowire and configure MenuBuilder --- src/Bundle/ChillTaskBundle/config/services/menu.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/config/services/menu.yaml b/src/Bundle/ChillTaskBundle/config/services/menu.yaml index 75726fcf4..a9e1948d6 100644 --- a/src/Bundle/ChillTaskBundle/config/services/menu.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/menu.yaml @@ -9,9 +9,8 @@ services: - { name: "chill.menu_builder" } Chill\TaskBundle\Menu\MenuBuilder: - arguments: - $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' - $translator: '@Symfony\Component\Translation\TranslatorInterface' + autowire: true + autoconfigure: true tags: - { name: "chill.menu_builder" } From 8318458805d2e18935d2b11304564b7cd2b271c9 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 9 Sep 2021 17:43:35 +0200 Subject: [PATCH 11/79] relation between task and accompanyingcourse created --- .../ChillTaskBundle/Entity/AbstractTask.php | 21 +++++++++- .../migrations/Version20210909153533.php | 38 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php diff --git a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php index 73e98c0dd..649de0422 100644 --- a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php +++ b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php @@ -10,6 +10,7 @@ use Chill\MainBundle\Entity\HasScopeInterface; use Chill\MainBundle\Entity\HasCenterInterface; use Symfony\Component\Validator\Constraints as Assert; use Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency; +use Chill\PersonBundle\Entity\AccompanyingPeriod; /** * AbstractTask @@ -67,9 +68,16 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface * @ORM\ManyToOne( * targetEntity="\Chill\PersonBundle\Entity\Person" * ) - * @Assert\NotNull() */ private $person; + + + /** + * @var AccompanyingPeriod + * @ORM\ManyToOne(targetEntity="\Chill\PersonBundle\Entity\AccompanyingPeriod") + */ + + private $course; /** * @@ -202,6 +210,11 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface return $this->person; } + public function getCourse(): ?AccompanyingPeriod + { + return $this->course; + } + public function getCircle(): ?Scope { return $this->circle; @@ -219,6 +232,12 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface return $this; } + public function setCourse(AccompanyingPeriod $course) + { + $this->course = $course; + return $this; + } + public function setCircle(Scope $circle) { $this->circle = $circle; diff --git a/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php b/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php new file mode 100644 index 000000000..e2f59621b --- /dev/null +++ b/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php @@ -0,0 +1,38 @@ +addSql('ALTER TABLE chill_task.recurring_task ADD course_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_task.recurring_task ADD CONSTRAINT FK_9F663B90591CC992 FOREIGN KEY (course_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_9F663B90591CC992 ON chill_task.recurring_task (course_id)'); + $this->addSql('ALTER TABLE chill_task.single_task ADD course_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_task.single_task ADD CONSTRAINT FK_194CB3D8591CC992 FOREIGN KEY (course_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_194CB3D8591CC992 ON chill_task.single_task (course_id)'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_task.recurring_task DROP CONSTRAINT FK_9F663B90591CC992'); + $this->addSql('ALTER TABLE chill_task.recurring_task DROP course_id'); + $this->addSql('ALTER TABLE chill_task.single_task DROP CONSTRAINT FK_194CB3D8591CC992'); + $this->addSql('ALTER TABLE chill_task.single_task DROP course_id'); + } +} From 53fc5b83993e5a04d60cc3da568dae6c9367fec3 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 9 Sep 2021 18:21:41 +0200 Subject: [PATCH 12/79] adaptation of newTask() method in singleTaskController --- .../Controller/SingleTaskController.php | 64 +++++++++++++++---- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index bc76dacd4..8c742c627 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -86,24 +86,52 @@ class SingleTaskController extends AbstractController ->setType('task_default') ; - if ($request->query->has('person_id')) { + if($request->query->has('person_id')){ + $entityType = 'person'; + } else if ($request->query->has('course_id')) { + $entityType = 'course'; + } else { + $entityType = null; + } + + if ($entityType !== null) { - $personId = $request->query->getInt('person_id', 0); // sf4 check: + $entityId = $request->query->getInt("{$entityType}_id", 0); // sf4 check: // prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given` - if ($personId === null) { - return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST); + if ($entityId === null) { + return new Response("You must provide a {$entityType}_id", Response::HTTP_BAD_REQUEST); } - $person = $this->getDoctrine()->getManager() - ->getRepository(Person::class) - ->find($personId); + if($entityType === 'person') + { + $person = $this->getDoctrine()->getManager() + ->getRepository(Person::class) + ->find($entityId); - if ($person === null) { - $this->createNotFoundException("Invalid person id"); + if ($person === null) { + $this->createNotFoundException("Invalid person id"); + } + + $task->setPerson($person); } - $task->setPerson($person); + if($entityType === 'course') + { + + $course = $this->getDoctrine()->getManager() + ->getRepository(AccompanyingPeriod::class) + ->find($entityId); + + if($course === null) { + $this->createNotFoundException("Invalid accompanying course id"); + } + + $task->setCourse($course); + + } + + } $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' @@ -124,9 +152,19 @@ class SingleTaskController extends AbstractController $this->addFlash('success', $translator->trans("The task is created")); - return $this->redirectToRoute('chill_task_singletask_list', [ - 'person_id' => $task->getPerson()->getId() - ]); + if($entityType === 'person') + { + return $this->redirectToRoute('chill_task_singletask_list', [ + 'person_id' => $task->getPerson()->getId() + ]); + } + + if($entityType === 'course') + { + return $this->redirectToRoute('chill_task_singletask_list', [ + 'course_id' => $task->getCourse()->getId() + ]); + } } else { $this->addFlash('error', $translator->trans("This form contains errors")); From 1fb14834b72adc06e0a0eda56643fc1a3131a134 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 10 Sep 2021 14:57:41 +0200 Subject: [PATCH 13/79] tasks added to accompanyingCourse menu --- .../Menu/AccompanyingCourseMenuBuilder.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php index d82f8604b..4d1245f42 100644 --- a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php @@ -66,6 +66,13 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface 'routeParameters' => [ 'id' => $period->getId() ]]) + ->setExtras(['order' => 40]); + + $menu->addChild($this->translator->trans('Tasks'), [ + 'route' => 'chill_task_singletask_list', + 'routeParameters' => [ + 'course_id' => $period->getId() + ]]) ->setExtras(['order' => 50]); } From 5a936cd20b7ea900f762e5570d4062a54c825fe6 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 10 Sep 2021 15:19:41 +0200 Subject: [PATCH 14/79] templates + controller further adapted to work with accompanyingCourse. new and show methods don't work yet due to authorization/voter issues templates adapted for use with accompanyingCourse tasks also --- .../Controller/SingleTaskController.php | 165 +++++-- .../views/SingleTask/_list.html.twig | 428 +++++++++--------- .../views/SingleTask/index.html.twig | 38 +- .../config/services/controller.yaml | 19 +- 4 files changed, 365 insertions(+), 285 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index 8c742c627..aed805bcf 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -29,6 +29,10 @@ use Symfony\Component\Translation\TranslatorInterface; use Chill\TaskBundle\Event\UI\UIEvent; use Chill\MainBundle\Repository\CenterRepository; use Chill\MainBundle\Timeline\TimelineBuilder; +use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; +use Symfony\Component\HttpFoundation\RequestStack; /** * Class SingleTaskController @@ -53,6 +57,11 @@ class SingleTaskController extends AbstractController * @var LoggerInterface */ protected $logger; + + /** + * @var RequestStack + */ + protected $request; /** * SingleTaskController constructor. @@ -62,11 +71,25 @@ class SingleTaskController extends AbstractController public function __construct( EventDispatcherInterface $eventDispatcher, TimelineBuilder $timelineBuilder, - LoggerInterface $logger + LoggerInterface $logger, + RequestStack $requestStack ) { $this->eventDispatcher = $eventDispatcher; $this->timelineBuilder = $timelineBuilder; $this->logger = $logger; + $this->request = $requestStack->getCurrentRequest(); + } + + + public function getEntity() + { + if($this->request->query->has('person_id')){ + return 'person'; + } else if ($this->request->query->has('course_id')) { + return 'course'; + } else { + return null; + } } @@ -77,7 +100,6 @@ class SingleTaskController extends AbstractController * ) */ public function newAction( - Request $request, TranslatorInterface $translator ) { @@ -85,18 +107,12 @@ class SingleTaskController extends AbstractController ->setAssignee($this->getUser()) ->setType('task_default') ; - - if($request->query->has('person_id')){ - $entityType = 'person'; - } else if ($request->query->has('course_id')) { - $entityType = 'course'; - } else { - $entityType = null; - } + + $entityType = $this->getEntity(); if ($entityType !== null) { - $entityId = $request->query->getInt("{$entityType}_id", 0); // sf4 check: + $entityId = $this->request->query->getInt("{$entityType}_id", 0); // sf4 check: // prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given` if ($entityId === null) { @@ -134,12 +150,18 @@ class SingleTaskController extends AbstractController } + // error message: You should associate a person with task in order to check autorizations. + // consequently adapting TaskVoter to take into account accompanyinCourse throws new errors linked to authorizationHelper on line 151 + $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' . 'allowed to create this task'); + // error message: An error has occurred resolving the options of the form "Chill\TaskBundle\Form\SingleTaskType": + //The option "center" with value null is expected to be of type "Chill\MainBundle\Entity\Center", but is of type "null". + $form = $this->setCreateForm($task, new Role(TaskVoter::CREATE)); - $form->handleRequest($request); + $form->handleRequest($this->request); if ($form->isSubmitted()) { if ($form->isValid()) { @@ -184,13 +206,22 @@ class SingleTaskController extends AbstractController * name="chill_task_single_task_show" * ) */ - public function showAction(Request $request, $id) + public function showAction($id) { $em = $this->getDoctrine()->getManager(); $task = $em->getRepository(SingleTask::class)->find($id); + // In case no task is found + + if (!$task) { + throw $this->createNotFoundException('Unable to find Task entity.'); + } + + // In case task belongs to person + if ($task->getPerson() !== null) { + $personId = $task->getPerson()->getId(); if ($personId === null) { @@ -204,23 +235,42 @@ class SingleTaskController extends AbstractController if ($person === null) { throw $this->createNotFoundException("Invalid person id"); } - } - $this->denyAccessUnlessGranted(TaskVoter::SHOW, $task, 'You are not ' - . 'allowed to view this task'); - if (!$task) { - throw $this->createNotFoundException('Unable to find Task entity.'); - } - - $timeline = $this->timelineBuilder - ->getTimelineHTML('task', array('task' => $task)); - - $event = new PrivacyEvent($person, array( + $event = new PrivacyEvent($person, array( 'element_class' => SingleTask::class, 'element_id' => $task->getId(), 'action' => 'show' - )); - $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); + )); + $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); + + } + + // In case task belongs to accompanying course + + if ($task->getCourse() !== null) + { + $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 course id"); + } + } + + + $this->denyAccessUnlessGranted(TaskVoter::SHOW, $task, 'You are not ' + . 'allowed to view this task'); + + $timeline = $this->timelineBuilder + ->getTimelineHTML('task', array('task' => $task)); return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array( 'task' => $task, @@ -236,7 +286,6 @@ class SingleTaskController extends AbstractController * ) */ public function editAction( - Request $request, $id, TranslatorInterface $translator ) { @@ -273,7 +322,7 @@ class SingleTaskController extends AbstractController $form = $event->getForm(); - $form->handleRequest($request); + $form->handleRequest($this->request); if ($form->isSubmitted()) { if ($form->isValid()) { @@ -294,7 +343,7 @@ class SingleTaskController extends AbstractController return $this->redirectToRoute( 'chill_task_singletask_list', - $request->query->get('list_params', []) + $this->request->query->get('list_params', []) ); } else { @@ -440,6 +489,7 @@ class SingleTaskController extends AbstractController * Arguments: * - user_id * - scope_id + * - course_id * - person_id * - hide_form (hide the form to filter the tasks) * - status: date state, amongst SingleTaskRepository::DATE_STATUSES, or 'closed' @@ -450,10 +500,10 @@ class SingleTaskController extends AbstractController * ) */ public function listAction( - Request $request, PaginatorFactory $paginatorFactory, SingleTaskRepository $taskRepository, PersonRepository $personRepository, + AccompanyingPeriodRepository $courseRepository, CenterRepository $centerRepository, FormFactoryInterface $formFactory ) { @@ -466,12 +516,13 @@ class SingleTaskController extends AbstractController $params['user'] = null; $viewParams['center'] = null; $params['types'] = null; + $viewParams['accompanyingCourse'] = null; // Get parameters from url - if (!empty($request->query->get('person_id', NULL))) { + if (!empty($this->request->query->get('person_id', NULL))) { - $personId = $request->query->getInt('person_id', 0); + $personId = $this->request->query->getInt('person_id', 0); $person = $personRepository->find($personId); if ($person === null) { @@ -482,28 +533,42 @@ class SingleTaskController extends AbstractController $viewParams['person'] = $person; $params['person'] = $person; } + + if (!empty($this->request->query->get('course_id', NULL))) { + + $courseId = $this->request->query->getInt('course_id', 0); + $course = $courseRepository->find($courseId); + + if ($course === null) { + throw $this->createNotFoundException("This accompanying course ' $courseId ' does not exist."); + } + $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $course); + + $viewParams['accompanyingCourse'] = $course; + $params['accompanyingCourse'] = $course; + } - if (!empty($request->query->get('center_id', NULL))) { - $center = $centerRepository->find($request->query->getInt('center_id')); + if (!empty($this->request->query->get('center_id', NULL))) { + $center = $centerRepository->find($this->request->query->getInt('center_id')); if ($center === null) { throw $this->createNotFoundException('center not found'); } $params['center'] = $center; } - if(!empty($request->query->get('types', []))) { - $types = $request->query->get('types', []); + if(!empty($this->request->query->get('types', []))) { + $types = $this->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') { + if (!empty($this->request->query->get('user_id', null))) { + if ($this->request->query->get('user_id') === '_unassigned') { $params['unassigned'] = true; } else { - $userId = $request->query->getInt('user_id', 0); + $userId = $this->request->query->getInt('user_id', 0); $user = $this->getDoctrine()->getManager() ->getRepository('ChillMainBundle:User') ->find($userId); @@ -517,9 +582,9 @@ class SingleTaskController extends AbstractController } } - if (!empty($request->query->get('scope_id'))) { + if (!empty($this->request->query->get('scope_id'))) { - $scopeId = $request->query->getInt('scope_id', 0); + $scopeId = $this->request->query->getInt('scope_id', 0); $scope = $this->getDoctrine()->getManager() ->getRepository('ChillMainBundle:Scope') @@ -535,7 +600,7 @@ class SingleTaskController extends AbstractController // collect parameters for filter $possibleStatuses = \array_merge(SingleTaskRepository::DATE_STATUSES, [ 'closed' ]); - $statuses = $request->query->get('status', $possibleStatuses); + $statuses = $this->request->query->get('status', $possibleStatuses); // check for invalid statuses $diff = \array_diff($statuses, $possibleStatuses); @@ -551,7 +616,7 @@ class SingleTaskController extends AbstractController $tasks_count = 0; foreach($statuses as $status) { - if($request->query->has('status') + if($this->request->query->has('status') && FALSE === \in_array($status, $statuses)) { continue; } @@ -586,6 +651,8 @@ class SingleTaskController extends AbstractController if ($viewParams['person'] !== null){ $viewParams['layout'] = '@ChillPerson/Person/layout.html.twig'; + } else if ($viewParams['accompanyingCourse'] !== null){ + $viewParams['layout'] = '@ChillPerson/AccompanyingCourse/layout.html.twig'; } else { $viewParams['layout'] = '@ChillMain/layout.html.twig'; } @@ -598,7 +665,7 @@ class SingleTaskController extends AbstractController 'add_type' => true ]); - $form->handleRequest($request); + $form->handleRequest($this->request); if (isset($person)) { $event = new PrivacyEvent($person, array( @@ -609,14 +676,14 @@ class SingleTaskController extends AbstractController } return $this->render('ChillTaskBundle:SingleTask:index.html.twig', - \array_merge($viewParams, [ 'form' => $form->createView() ])); + array_merge($viewParams, [ 'form' => $form->createView() ])); } - protected function getPersonParam(Request $request, EntityManagerInterface $em) + protected function getPersonParam(EntityManagerInterface $em) { $person = $em->getRepository(Person::class) - ->find($request->query->getInt('person_id')) + ->find($this->request->query->getInt('person_id')) ; if (NULL === $person) { @@ -629,10 +696,10 @@ class SingleTaskController extends AbstractController return $person; } - protected function getUserParam(Request $request, EntityManagerInterface $em) + protected function getUserParam(EntityManagerInterface $em) { $user = $em->getRepository(User::class) - ->find($request->query->getInt('user_id')) + ->find($this->request->query->getInt('user_id')) ; if (NULL === $user) { diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig index a5a867dcc..e80e45e8c 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig @@ -1,244 +1,256 @@ {% macro date_status(title, tasks, count, paginator, status, isSingleStatus, person, user) %} - {% if tasks|length > 0 %} -

      {{ title|trans }}

      + {% if tasks|length > 0 %} +

      {{ title|trans }}

      - - - {% for task in tasks %} - - + + {% endfor %} + +
      -
      - {{ task.title }} -
      + + + {% for task in tasks %} + + - + - - {% endfor %} - -
      +
      + {{ task.title }} +
      - {% if person is null %} -
      - {{ 'For person'|trans }} : {{ task.person}} -
      - {% endif %} + {% if person is null %} +
      + {{ 'For person'|trans }} : + + {{ task.person}} + +
      + {% endif %} -
      - {{ task_workflow_metadata(task, 'definition.name')|trans }} -
      +
      + {{ task_workflow_metadata(task, 'definition.name')|trans }} +
      -
      - {% for place in workflow_marked_places(task) %} - {{ place|trans }} - {% endfor %} - {% if task.assignee is not null %} -
      {{ 'By'|trans }} : {{ task.assignee.username }}
      - {% endif %} -
      +
      + {% for place in workflow_marked_places(task) %} + {{ place|trans }} + {% endfor %} + {% if task.assignee is not null %} +
      + {{ 'By'|trans }} : + {{ task.assignee.username }}
      + {% endif %} +
      - {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} -
      -
        - {% if task.startDate is not null %} -
      • - {{ task.startDate|format_date('medium') }} -
      • - {% endif %} - {% if task.warningDate is not null %} -
      • - {{ task.warningDate|format_date('medium') }} -
      • - {% endif %} - {% if task.endDate is not null %} -
      • - {{ task.endDate|format_date('medium') }} -
      • - {% endif %} -
      -
      - {% endif %} + {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} +
      +
        + {% if task.startDate is not null %} +
      • + + {{ task.startDate|format_date('medium') }} +
      • + {% endif %} + {% if task.warningDate is not null %} +
      • + + {{ task.warningDate|format_date('medium') }} +
      • + {% endif %} + {% if task.endDate is not null %} +
      • + + {{ task.endDate|format_date('medium') }} +
      • + {% endif %} +
      +
      + {% endif %} -
      - + -
      + {% if is_granted('CHILL_TASK_TASK_DELETE', task) %} +
    • + +
    • + {% endif %} + +
      - {% if isSingleStatus %} - {% if tasks|length < paginator.getTotalItems %} - {{ chill_pagination(paginator) }} - {% endif %} + {% if isSingleStatus %} + {% if tasks|length < paginator.getTotalItems %} + {{ chill_pagination(paginator) }} + {% endif %} - - - {% else %} - + {% endif %} - {% endif %} + {% endif %} {% endmacro %} {% import _self as helper %} -

      {{ app.request.query.get('title', null)|escape('html')|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 }}

      - {{ form_start(form) }} - {{ form_row(form.user_id) }} +{% if false == app.request.query.boolean('hide_form', false) %} +

      {{ 'Filter the tasks'|trans }}

      + {{ form_start(form) }} + {{ form_row(form.user_id) }} - {% if form.status is defined %} - {{ form_row(form.status) }} - {% endif %} + {% if form.status is defined %} + {{ form_row(form.status) }} + {% endif %} - {% if form.types is defined %} - {{ form_row(form.types) }} - {% endif %} + {% if form.types is defined %} + {{ form_row(form.types) }} + {% endif %} - {% if form.person_id is defined %} - {{ form_row(form.person_id) }} - {% endif %} + {% if form.person_id is defined %} + {{ form_row(form.person_id) }} + {% endif %} - {% if form.center_id is defined %} - {{ form_row(form.center_id) }} - {% endif %} + {% if form.center_id is defined %} + {{ form_row(form.center_id) }} + {% endif %} -
        -
      • - -
      • -
      +
        +
      • + +
      • +
      - {{ form_end(form)}} - {% endif %} + {{ form_end(form)}} +{% endif %} - {% if tasks_count == 0 %} -

      {{ "There is no tasks."|trans }}

      - {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} - - {% endif %} - {% else %} +{% if tasks_count == 0 %} +

      {{ "There is no tasks."|trans }}

      + {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} + + {% endif %} +{% else %} - {% if false == app.request.query.boolean('hide_form', false) %} -

      {{ 'Tasks'|trans }}

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

      {{ 'Tasks'|trans }}

      + {% endif %} - {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} - - {% endif %} + {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} + + {% endif %} - {% if single_task_ended_tasks is defined %} - {{ helper.date_status('Tasks with expired deadline', single_task_ended_tasks, single_task_ended_count, single_task_ended_paginator, 'ended', isSingleStatus, person) }} - {% endif %} + {% if single_task_ended_tasks is defined %} + {{ helper.date_status('Tasks with expired deadline', single_task_ended_tasks, single_task_ended_count, single_task_ended_paginator, 'ended', isSingleStatus, person) }} + {% endif %} - {% if single_task_warning_tasks is defined %} - {{ helper.date_status('Tasks with warning deadline reached', single_task_warning_tasks, single_task_warning_count, single_task_warning_paginator, 'warning', isSingleStatus, person) }} - {% endif %} + {% if single_task_warning_tasks is defined %} + {{ helper.date_status('Tasks with warning deadline reached', single_task_warning_tasks, single_task_warning_count, single_task_warning_paginator, 'warning', isSingleStatus, person) }} + {% endif %} - {% if single_task_current_tasks is defined %} - {{ helper.date_status('Current tasks', single_task_current_tasks, single_task_current_count, single_task_current_paginator, 'current', isSingleStatus, person) }} - {% endif %} + {% if single_task_current_tasks is defined %} + {{ helper.date_status('Current tasks', single_task_current_tasks, single_task_current_count, single_task_current_paginator, 'current', isSingleStatus, person) }} + {% endif %} - {% if single_task_not_started_tasks is defined %} - {{ helper.date_status('Tasks not started', single_task_not_started_tasks, single_task_not_started_count, single_task_not_started_paginator, 'not_started', isSingleStatus, person) }} - {% endif %} + {% if single_task_not_started_tasks is defined %} + {{ helper.date_status('Tasks not started', single_task_not_started_tasks, single_task_not_started_count, single_task_not_started_paginator, 'not_started', isSingleStatus, person) }} + {% endif %} - {% if single_task_closed_tasks is defined %} - {{ helper.date_status('Closed tasks', single_task_closed_tasks, single_task_closed_count, single_task_closed_paginator, 'closed', isSingleStatus, person) }} - {% endif %} + {% if single_task_closed_tasks is defined %} + {{ helper.date_status('Closed tasks', single_task_closed_tasks, single_task_closed_count, single_task_closed_paginator, 'closed', isSingleStatus, person) }} + {% endif %} - {% if isSingleStatus == false %} - - {% endif %} + {% if isSingleStatus == false %} + + {% endif %} {% endif %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig index 2a33fc10c..1932d7525 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig @@ -15,30 +15,30 @@ * along with this program. If not, see . #} -{% extends '@ChillPerson/Person/layout.html.twig' %} +{% extends layout %} {% set activeRouteKey = 'chill_task_single_task_new' %} -{% block title %}{{ 'Task list'|trans }}{% endblock %} +{% block title %} + {{ 'Task list'|trans }} +{% endblock %} -{% macro thead() %} -{% endmacro %} +{% macro thead() %}{% endmacro %} -{% macro row(task) %} -{% endmacro %} +{% macro row(task) %}{% endmacro %} {% block filtertasks %} -{% if person is not null %} - {% block personcontent %} -
      - {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} -
      - {% endblock %} -{% else %} - {% block content %} -
      - {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} -
      - {% endblock %} -{% endif %} + {% if person is not null %} + {% block personcontent %} +
      + {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} +
      + {% endblock %} + {% else %} + {% block content %} +
      + {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} +
      + {% endblock %} + {% endif %} {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/config/services/controller.yaml b/src/Bundle/ChillTaskBundle/config/services/controller.yaml index 533c57f47..cfe083e07 100644 --- a/src/Bundle/ChillTaskBundle/config/services/controller.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/controller.yaml @@ -1,11 +1,12 @@ services: - Chill\TaskBundle\Controller\: - resource: '../../Controller' - tags: ['controller.service_arguments'] + Chill\TaskBundle\Controller\: + resource: "../../Controller" + tags: ["controller.service_arguments"] - Chill\TaskBundle\Controller\SingleTaskController: - arguments: - $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface' - $timelineBuilder: '@chill_main.timeline_builder' - $logger: '@chill.main.logger' - tags: ['controller.service_arguments'] + Chill\TaskBundle\Controller\SingleTaskController: + arguments: + $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface' + $timelineBuilder: "@chill_main.timeline_builder" + $logger: "@chill.main.logger" + $requestStack: '@Symfony\Component\HttpFoundation\RequestStack' + tags: ["controller.service_arguments"] From 4ad9714e8b7bfa745fdf185d991420ec7ae584fb Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 15 Sep 2021 11:13:09 +0200 Subject: [PATCH 15/79] Menu entry added in PersonMenuBuilder of taskbundle. Removed from menubuilder in Personbundle. Rename file, PersonMenuBuilder to MenuBuilder? --- .../Menu/AccompanyingCourseMenuBuilder.php | 7 --- .../Menu/PersonMenuBuilder.php | 49 ++++++++++++++----- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php index 4d1245f42..df70b9e64 100644 --- a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php @@ -67,13 +67,6 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface 'id' => $period->getId() ]]) ->setExtras(['order' => 40]); - - $menu->addChild($this->translator->trans('Tasks'), [ - 'route' => 'chill_task_singletask_list', - 'routeParameters' => [ - 'course_id' => $period->getId() - ]]) - ->setExtras(['order' => 50]); } diff --git a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php index ee0afa497..8c45b0beb 100644 --- a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php +++ b/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php @@ -53,28 +53,55 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface public function buildMenu($menuId, MenuItem $menu, array $parameters) { - /* @var $person \Chill\PersonBundle\Entity\Person */ + switch($menuId) { + case 'person': + $this->buildPersonMenu($menu, $parameters); + break; + case 'accompanyingCourse': + $this->buildAccompanyingCourseMenu($menu, $parameters); + break; + case 'section': + $menu->setExtras('icons', 'tasks'); + break; + default: + throw new \LogicException("this menuid $menuId is not implemented"); + } + } + + public function buildPersonMenu($menu, $parameters){ + + //var $person \Chill\PersonBundle\Entity\Person */ $person = $parameters['person'] ?? null; if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $person)) { $menu->addChild( $this->translator->trans('Tasks'), [ 'route' => 'chill_task_singletask_list', - 'routeParameters' => $menuId === 'person' ? + 'routeParameters' => [ 'person_id' => $person->getId() ] - : - null, - ]) - ->setExtra('order', 400) - ; - if ($menuId === 'section') { - $menu->setExtra('icons', 'tasks'); - } + ]) + ->setExtra('order', 400); } } + public function buildAccompanyingCourseMenu($menu, $parameters){ + + //var $person \Chill\PersonBundle\Entity\Person */ + $course = $parameters['accompanyingCourse']; + + // if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { + $menu->addChild( + $this->translator->trans('Tasks'), [ + 'route' => 'chill_task_singletask_list', + 'routeParameters' => + [ 'course_id' => $course->getId() ] + ]) + ->setExtra('order', 400); + // } + } + public static function getMenuIds(): array { - return ['person']; + return ['person', 'accompanyingCourse']; } } From 537518b66f073ef500ad58984b2b32a9af76495e Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 16 Sep 2021 15:55:09 +0200 Subject: [PATCH 16/79] controller and templates adapted to display list of accompanying period tasks + detailpage of task --- .../Controller/SingleTaskController.php | 150 ++++++++++++++---- .../Menu/PersonMenuBuilder.php | 4 +- .../views/SingleTask/_listCourse.html.twig | 107 +++++++++++++ .../views/SingleTask/_show.html.twig | 110 +++++++++++++ .../views/SingleTask/index.html.twig | 2 +- .../Resources/views/SingleTask/show.html.twig | 119 +------------- .../views/SingleTask/showCourseTask.html.twig | 16 ++ .../translations/messages.fr.yml | 117 +++++++------- 8 files changed, 423 insertions(+), 202 deletions(-) create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index aed805bcf..e98688aa6 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -32,6 +32,8 @@ use Chill\MainBundle\Timeline\TimelineBuilder; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; +use Chill\TaskBundle\Form\SingleTaskCourseType; +use Chill\TaskBundle\Repository\AbstractTaskRepository; use Symfony\Component\HttpFoundation\RequestStack; /** @@ -81,7 +83,7 @@ class SingleTaskController extends AbstractController } - public function getEntity() + private function getEntityContext() { if($this->request->query->has('person_id')){ return 'person'; @@ -108,7 +110,7 @@ class SingleTaskController extends AbstractController ->setType('task_default') ; - $entityType = $this->getEntity(); + $entityType = $this->getEntityContext(); if ($entityType !== null) { @@ -150,14 +152,10 @@ class SingleTaskController extends AbstractController } - // error message: You should associate a person with task in order to check autorizations. - // consequently adapting TaskVoter to take into account accompanyinCourse throws new errors linked to authorizationHelper on line 151 + //TODO : resolve access rights - $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' - . 'allowed to create this task'); - - // error message: An error has occurred resolving the options of the form "Chill\TaskBundle\Form\SingleTaskType": - //The option "center" with value null is expected to be of type "Chill\MainBundle\Entity\Center", but is of type "null". + // $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' + // . 'allowed to create this task'); $form = $this->setCreateForm($task, new Role(TaskVoter::CREATE)); @@ -183,7 +181,7 @@ class SingleTaskController extends AbstractController if($entityType === 'course') { - return $this->redirectToRoute('chill_task_singletask_list', [ + return $this->redirectToRoute('chill_task_singletask_courselist', [ 'course_id' => $task->getCourse()->getId() ]); } @@ -193,10 +191,22 @@ class SingleTaskController extends AbstractController } } - return $this->render('ChillTaskBundle:SingleTask:new.html.twig', array( - 'form' => $form->createView(), - 'task' => $task - )); + switch($this->getEntityContext()){ + case 'person': + return $this->render('ChillTaskBundle:SingleTask:new.html.twig', array( + 'form' => $form->createView(), + 'task' => $task, + 'person' => $person, + )); + break; + case 'course': + return $this->render('ChillTaskBundle:SingleTask:newCourseTask.html.twig', array( + 'form' => $form->createView(), + 'task' => $task, + 'accompanyingCourse' => $course, + )); + } + } @@ -271,11 +281,19 @@ class SingleTaskController extends AbstractController $timeline = $this->timelineBuilder ->getTimelineHTML('task', array('task' => $task)); - - return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array( - 'task' => $task, - 'timeline' => $timeline - )); + + if($this->getEntityContext() === 'person'){ + return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array( + 'task' => $task, + 'timeline' => $timeline + )); + } else { + return $this->render('ChillTaskBundle:SingleTask:showCourseTask.html.twig', array( + 'task' => $task, + 'timeline' => $timeline + )); + } + } @@ -457,10 +475,18 @@ class SingleTaskController extends AbstractController */ protected function setCreateForm(SingleTask $task, Role $role) { - $form = $this->createForm(SingleTaskType::class, $task, [ - 'center' => $task->getCenter(), - 'role' => $role - ]); + if($this->getEntityContext() === 'person'){ + $form = $this->createForm(SingleTaskType::class, $task, [ + 'center' => $task->getCenter(), + 'role' => $role, + ]); + } + + if($this->getEntityContext() === 'course'){ + $form = $this->createForm(SingleTaskCourseType::class, $task, [ + 'center' => $task->getCenter(), + ]); + } $form->add('submit', SubmitType::class); @@ -517,6 +543,7 @@ class SingleTaskController extends AbstractController $viewParams['center'] = null; $params['types'] = null; $viewParams['accompanyingCourse'] = null; + $params['accompanyingCourse'] = null; // Get parameters from url @@ -658,12 +685,23 @@ class SingleTaskController extends AbstractController } // Form for filtering tasks - $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ - 'person' => $viewParams['person'], - 'method' => Request::METHOD_GET, - 'csrf_protection' => false, - 'add_type' => true - ]); + if($this->getEntityContext() === 'person'){ + $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ + 'person' => $viewParams['person'], + 'method' => Request::METHOD_GET, + 'csrf_protection' => false, + 'add_type' => true + ]); + } + + if($this->getEntityContext() === 'course'){ + $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ + 'accompanyingCourse' => $viewParams['accompanyingCourse'], + 'method' => Request::METHOD_GET, + 'csrf_protection' => false, + 'add_type' => true + ]); + } $form->handleRequest($this->request); @@ -728,4 +766,58 @@ class SingleTaskController extends AbstractController ; } + /** + * @Route( + * "/{_locale}/task/singletask/courselist", + * name="chill_task_singletask_courselist") + */ + + public function listCourseTasks( + AccompanyingPeriodRepository $courseRepository, + SingleTaskRepository $taskRepository, + FormFactoryInterface $formFactory, + TranslatorInterface $translator + ): Response + { + + if (!empty($this->request->query->get('course_id', NULL))) { + + $courseId = $this->request->query->getInt('course_id', 0); + $course = $courseRepository->find($courseId); + + if ($course === null) { + throw $this->createNotFoundException("This accompanying course ' $courseId ' does not exist."); + } + + } + + $em = $this->getDoctrine()->getManager(); + + if($course === NULL) { + throw $this->createNotFoundException('Accompanying course not found'); + } + + $tasks = $taskRepository + ->findBy( + array('course' => $course) + ); + + $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ + 'accompanyingCourse' => $course, + 'method' => Request::METHOD_GET, + 'csrf_protection' => false, + 'add_type' => true + ]); + + return $this->render( + 'ChillTaskBundle:SingleTask:index.html.twig', + [ + 'tasks' => $tasks, + 'accompanyingCourse' => $course, + 'layout' => '@ChillPerson/AccompanyingCourse/layout.html.twig', + 'form' => $form->createView(), + 'title' => $translator->trans('Tasks for this accompanying period') + ]); + } + } diff --git a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php index 8c45b0beb..26f06d1b2 100644 --- a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php +++ b/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php @@ -89,10 +89,12 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface //var $person \Chill\PersonBundle\Entity\Person */ $course = $parameters['accompanyingCourse']; + //TODO: implement voter again? + // if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { $menu->addChild( $this->translator->trans('Tasks'), [ - 'route' => 'chill_task_singletask_list', + 'route' => 'chill_task_singletask_courselist', 'routeParameters' => [ 'course_id' => $course->getId() ] ]) diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig new file mode 100644 index 000000000..16c907e4c --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig @@ -0,0 +1,107 @@ +{% if tasks|length > 0 %} +

      {{ title|trans }}

      + + + + {% for task in tasks %} + + + + + {% endfor %} + +
      +
      + {{ task.title }} +
      + +
      + {{ task_workflow_metadata(task, 'definition.name')|trans }} +
      + +
      + {% for place in workflow_marked_places(task) %} + {{ place|trans }} + {% endfor %} + {% if task.assignee is not null %} +
      + {{ 'By'|trans }} : + {{ task.assignee.username }}
      + {% endif %} +
      + + {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} +
      +
        + {% if task.startDate is not null %} +
      • + + {{ task.startDate|format_date('medium') }} +
      • + {% endif %} + {% if task.warningDate is not null %} +
      • + + {{ task.warningDate|format_date('medium') }} +
      • + {% endif %} + {% if task.endDate is not null %} +
      • + + {{ task.endDate|format_date('medium') }} +
      • + {% endif %} +
      +
      + {% endif %} + +
      + +
      +{% endif %} + + diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig new file mode 100644 index 000000000..a8905285a --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig @@ -0,0 +1,110 @@ +
      + +

      {{ 'Task'|trans }}

      + +

      {{ task.title }} + {% for place in workflow_marked_places(task) %} + {{ place|trans }} + {% endfor %} +

      + +
      + +
      {{ 'Description'|trans }}
      +
      + {% if task.description is empty %} + {{"No description"|trans}} + {% else %} +
      + {{ task.description|chill_markdown_to_html }} +
      + {% endif %} +
      + +
      {{ 'Assignee'|trans }}
      +
      + {% if task.assignee is null %} + {{"No one assignee"|trans}} + {% else %} + {{ task.assignee }} + {% endif %} +
      + +
      {{ 'Scope'|trans }}
      +
      + {{ task.scope.name|localize_translatable_string }} +
      + +

      {{"Dates"|trans}}

      + {% if task.startDate is null and task.endDate is null and task.warningDate is null %} +
      +
      + {{"No dates specified"|trans}} +
      + + {% else %} + {% if task.startDate is not null %} +
      {{ 'Start'|trans }}
      +
      {{ task.startDate|format_date('long') }}
      + {% endif %} + + {% if task.endDate is not null %} +
      {{ 'End'|trans }}
      +
      {{ task.endDate|format_date('long') }}
      + {% endif %} + + {% if task.warningDate is not null %} +
      {{ 'Warning'|trans }}
      +
      {{ task.warningDate|format_date('long') }}
      + {% endif %} + + {% endif %} +
      + +{% if timeline is not null %} +

      {{"Timeline"|trans}}

      + {{ timeline|raw }} +{% endif %} + +
      diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig index 1932d7525..4c0b9dc84 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig @@ -37,7 +37,7 @@ {% else %} {% block content %}
      - {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} + {% include 'ChillTaskBundle:SingleTask:_listCourse.html.twig' %}
      {% endblock %} {% endif %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig index 8cfa1ea33..19bf70777 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig @@ -16,124 +16,17 @@ #} {% extends "@ChillPerson/Person/layout.html.twig" %} + {% set activeRouteKey = 'chill_task_single_task_show' %} {% set person = task.person %} -{% block title %}{{ 'Task'|trans }}{% endblock %} +{% block title %} + {{ 'Task'|trans }} +{% endblock %} {% block personcontent %} -
      - -

      {{ 'Task'|trans }}

      - -

      {{ task.title }} {% for place in workflow_marked_places(task) %} - {{ place|trans }} - {% endfor %}

      - -
      - -
      {{ 'Description'|trans }}
      -
      - {% if task.description is empty %} - {{"No description"|trans}} - {% else %} -
      - {{ task.description|chill_markdown_to_html }} -
      - {% endif %} -
      - -
      {{ 'Assignee'|trans }}
      -
      - {% if task.assignee is null %} - {{"No one assignee"|trans}} - {% else %} - {{ task.assignee }} - {% endif %} -
      - -
      {{ 'Scope'|trans }}
      -
      {{ task.scope.name|localize_translatable_string }}
      - -

      {{"Dates"|trans}}

      - {% if task.startDate is null and task.endDate is null and task.warningDate is null %} -
      -
      {{"No dates specified"|trans}}
      - - {% else %} - {% if task.startDate is not null %} -
      {{ 'Start'|trans }}
      -
      {{ task.startDate|format_date('long') }}
      - {% endif %} - - {% if task.endDate is not null %} -
      {{ 'End'|trans }}
      -
      {{ task.endDate|format_date('long') }}
      - {% endif %} - - {% if task.warningDate is not null %} -
      {{ 'Warning'|trans }}
      -
      {{ task.warningDate|format_date('long') }}
      - {% endif %} - - {% endif %} -
      - - {% if timeline is not null %} -

      {{"Timeline"|trans}}

      - {{ timeline|raw }} - {% endif %} - - - -
      + + {% include 'ChillTaskBundle:SingleTask:_show.html.twig' %} {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig new file mode 100644 index 000000000..643617a55 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig @@ -0,0 +1,16 @@ +{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} + + +{% set activeRouteKey = 'chill_task_single_task_show' %} +{% set accompanyingCourse = task.course %} + +{% block title %} + {{ 'Task'|trans }} +{% endblock %} + + +{% block content %} + + {% include 'ChillTaskBundle:SingleTask:_show.html.twig' %} + +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml index 71bc22612..7af9d450d 100644 --- a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml @@ -1,53 +1,54 @@ -Tasks: 'Tâches' -'New task': 'Nouvelle tâche' -'Add a new task': 'Ajouter une nouvelle tâche' +Tasks: "Tâches" +"New task": "Nouvelle tâche" +"Add a new task": "Ajouter une nouvelle tâche" Title: Titre Description: Description -Assignee: 'Personne assignée' +Assignee: "Personne assignée" Scope: Cercle -'Start date': 'Date de début' -'End date': "Date d'échéance" -'Warning date': "Date d'avertissement" -'Warning interval': "Délai d'avertissement avant la date d'échéance" -'Unknown dates': 'Dates non spécifiées' -'N': '' -'Unit': '' +"Start date": "Date de début" +"End date": "Date d'échéance" +"Warning date": "Date d'avertissement" +"Warning interval": "Délai d'avertissement avant la date d'échéance" +"Unknown dates": "Dates non spécifiées" +"N": "" +"Unit": "" Task: Tâche Details: Détails Person: Personne Date: Date Dates: Dates User: Utilisateur -'Task list': 'Liste des tâches' -'Tasks with expired deadline': "Tâches avec une date d'échéance dépassée" -'Tasks with warning deadline reached': "Tâches avec une date d'avertissement atteinte" -'Current tasks': 'Tâches en cours' -'Closed tasks': Tâches terminées -'Tasks not started': 'Tâches non commencées' -'Task start date': 'Date de début' -'Task warning date': "Date d'avertissement" -'Task end date': "Date d'échéance" -'Start': 'Début' -'Warning': 'Avertissement' -'End': 'Échéance' -'Task type': 'Type' -'Task status': 'Statut' -'Edit the task': 'Modifier la tâche' -'Edit task': 'Modifier la tâche' -'Save task': 'Enregistrer la tâche' -'View the task': 'Voir la tâche' -'Update the task': 'Mettre à jour la tâche' -'Remove task': 'Supprimer la tâche' -'Delete': 'Supprimer' -'Change task status': 'Changer le statut' +"Task list": "Liste des tâches" +"Tasks with expired deadline": "Tâches avec une date d'échéance dépassée" +"Tasks with warning deadline reached": "Tâches avec une date d'avertissement atteinte" +"Current tasks": "Tâches en cours" +"Closed tasks": Tâches terminées +"Tasks not started": "Tâches non commencées" +"Task start date": "Date de début" +"Task warning date": "Date d'avertissement" +"Task end date": "Date d'échéance" +"Start": "Début" +"Warning": "Avertissement" +"End": "Échéance" +"Task type": "Type" +"Task status": "Statut" +"Edit the task": "Modifier la tâche" +"Edit task": "Modifier la tâche" +"Save task": "Enregistrer la tâche" +"View the task": "Voir la tâche" +"Update the task": "Mettre à jour la tâche" +"Remove task": "Supprimer la tâche" +"Delete": "Supprimer" +"Change task status": "Changer le statut" 'Are you sure you want to remove the task about "%name%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche de "%name%"?' -'See more': 'Voir plus' -'Associated tasks': 'Tâches associées' -'My tasks': 'Mes tâches' -'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 +"See more": "Voir plus" +"Associated tasks": "Tâches associées" +"My tasks": "Mes tâches" +"Tasks for this accompanying period": "Tâches pour ce parcours d'accompagnement" +"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 @@ -63,36 +64,36 @@ For person: Pour By: Par # transitions - default task definition -'new': 'nouvelle' -'in_progress': 'en cours' -'closed': 'fermée' -'canceled': 'supprimée' +"new": "nouvelle" +"in_progress": "en cours" +"closed": "fermée" +"canceled": "supprimée" 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' +"%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' -'There is no tasks.': Aucune tâche. -'The task has been successfully removed.': 'La tâche a bien été supprimée' -'This form contains errors': 'Ce formulaire contient des erreurs' -'The task has been updated': 'La tâche a été mise à jour' -'The transition is successfully applied': 'La transition a bien été effectuée' -'The transition could not be applied': "La transition n'a pas pu être appliquée" +"The task is created": "La tâche a été créée" +"There is no tasks.": Aucune tâche. +"The task has been successfully removed.": "La tâche a bien été supprimée" +"This form contains errors": "Ce formulaire contient des erreurs" +"The task has been updated": "La tâche a été mise à jour" +"The transition is successfully applied": "La transition a bien été effectuée" +"The transition could not be applied": "La transition n'a pas pu être appliquée" #widget -'%number% tasks over deadline': '{0} Aucune tâche dépassée|{1} Une tâche dépassée | ]1,Inf[ %count% tâches dépassées' -'%number% tasks near deadline': '{0} Aucune tâche en rappel|{1} Une tâche en rappel | ]1,Inf[ %count% tâches en rappel' +"%number% tasks over deadline": "{0} Aucune tâche dépassée|{1} Une tâche dépassée | ]1,Inf[ %count% tâches dépassées" +"%number% tasks near deadline": "{0} Aucune tâche en rappel|{1} Une tâche en rappel | ]1,Inf[ %count% tâches en rappel" #title My tasks near deadline: Mes tâches à échéance proche @@ -107,4 +108,4 @@ All centers: Tous les centres CHILL_TASK_TASK_CREATE: Ajouter une tâche CHILL_TASK_TASK_DELETE: Supprimer une tâche CHILL_TASK_TASK_SHOW: Voir une tâche -CHILL_TASK_TASK_UPDATE: Modifier une tâche \ No newline at end of file +CHILL_TASK_TASK_UPDATE: Modifier une tâche From 6e3ce06fcf9faaf9c50c1953e9150cebcca55b06 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 17 Sep 2021 14:33:42 +0200 Subject: [PATCH 17/79] new/show/edit/delete/list functionality added for accompanyingperiod task --- .../Entity/AccompanyingPeriod.php | 10 ++ .../Controller/SingleTaskController.php | 143 ++++++++++++------ .../ChillTaskBundle/Entity/AbstractTask.php | 12 ++ .../ChillTaskBundle/Form/SingleTaskType.php | 7 +- ...{PersonMenuBuilder.php => MenuBuilder.php} | 0 .../views/SingleTask/_edit.html.twig | 25 +++ .../Resources/views/SingleTask/_new.html.twig | 30 ++++ .../views/SingleTask/_show.html.twig | 4 +- .../confirm_deleteCourseTask.html.twig | 19 +++ .../Resources/views/SingleTask/edit.html.twig | 39 +---- .../views/SingleTask/editCourseTask.html.twig | 14 ++ .../Resources/views/SingleTask/new.html.twig | 33 +--- .../views/SingleTask/newCourseTask.html.twig | 12 ++ .../Security/Authorization/TaskVoter.php | 9 +- .../translations/messages.fr.yml | 1 + 15 files changed, 242 insertions(+), 116 deletions(-) rename src/Bundle/ChillTaskBundle/Menu/{PersonMenuBuilder.php => MenuBuilder.php} (100%) create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 14fb26f1a..82b2ee10c 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -28,6 +28,7 @@ use Chill\MainBundle\Entity\HasCentersInterface; use Chill\MainBundle\Entity\HasScopesInterface; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\Address; +use Chill\MainBundle\Entity\Center; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment; @@ -977,6 +978,15 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface return $this->addressLocation; } + public function getCenter(): ?Center + { + if (count($this->getPersons()) === 0){ + return null; + } else { + return $this->getPersons()->first()->getCenter(); + } + } + /** * @Groups({"write"}) */ diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index e98688aa6..456699091 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -6,7 +6,6 @@ use Chill\PersonBundle\Privacy\PrivacyEvent; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; -use Doctrine\ORM\EntityManager; use Chill\PersonBundle\Entity\Person; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -22,7 +21,6 @@ use Chill\TaskBundle\Repository\SingleTaskRepository; use Chill\MainBundle\Entity\User; use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Repository\PersonRepository; -use Chill\MainBundle\Entity\UserRepository; use Chill\TaskBundle\Event\TaskEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Translation\TranslatorInterface; @@ -32,8 +30,6 @@ use Chill\MainBundle\Timeline\TimelineBuilder; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; -use Chill\TaskBundle\Form\SingleTaskCourseType; -use Chill\TaskBundle\Repository\AbstractTaskRepository; use Symfony\Component\HttpFoundation\RequestStack; /** @@ -205,6 +201,7 @@ class SingleTaskController extends AbstractController 'task' => $task, 'accompanyingCourse' => $course, )); + break; } } @@ -281,8 +278,9 @@ class SingleTaskController extends AbstractController $timeline = $this->timelineBuilder ->getTimelineHTML('task', array('task' => $task)); + - if($this->getEntityContext() === 'person'){ + if($task->getContext() instanceof Person){ return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array( 'task' => $task, 'timeline' => $timeline @@ -311,7 +309,7 @@ class SingleTaskController extends AbstractController $em = $this->getDoctrine()->getManager(); $task = $em->getRepository(SingleTask::class)->find($id); - if ($task->getPerson() !== null) { + if ($task->getContext() instanceof Person) { $personId = $task->getPerson()->getId(); if ($personId === null) { return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST); @@ -324,7 +322,21 @@ class SingleTaskController extends AbstractController 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"); + } } + $this->denyAccessUnlessGranted(TaskVoter::UPDATE, $task, 'You are not ' . 'allowed to edit this task'); @@ -351,19 +363,25 @@ class SingleTaskController extends AbstractController $this->addFlash('success', $translator ->trans("The task has been updated")); - - $event = new PrivacyEvent($person, array( + + if($task->getContext() instanceof Person){ + $event = new PrivacyEvent($person, array( 'element_class' => SingleTask::class, 'element_id' => $task->getId(), 'action' => 'update' - )); - $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - - return $this->redirectToRoute( - 'chill_task_singletask_list', - $this->request->query->get('list_params', []) - ); + )); + $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); + return $this->redirectToRoute( + 'chill_task_singletask_list', + $this->request->query->get('list_params', []) + ); + } else { + return $this->redirectToRoute( + 'chill_task_singletask_courselist', + $this->request->query->get('list_params', []) + ); + } } else { $this->addFlash('error', $translator->trans("This form contains errors")); } @@ -375,17 +393,26 @@ class SingleTaskController extends AbstractController return $event->getResponse(); } - $event = new PrivacyEvent($person, array( - 'element_class' => SingleTask::class, - 'element_id' => $task->getId(), - 'action' => 'edit' - )); - $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - - return $this->render('ChillTaskBundle:SingleTask:edit.html.twig', array( - 'task' => $task, - 'form' => $form->createView() - )); + if($task->getContext() instanceof Person){ + $event = new PrivacyEvent($person, array( + 'element_class' => SingleTask::class, + 'element_id' => $task->getId(), + 'action' => 'edit' + )); + $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); + + return $this->render('ChillTaskBundle:SingleTask:edit.html.twig', array( + 'task' => $task, + 'form' => $form->createView() + )); + } else { + return $this->render('ChillTaskBundle:SingleTask:editCourseTask.html.twig', array( + 'task' => $task, + 'form' => $form->createView(), + 'accompanyingCourse' => $course + )); + } + } @@ -422,8 +449,22 @@ class SingleTaskController extends AbstractController 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"); + } } + $this->denyAccessUnlessGranted(TaskVoter::DELETE, $task, 'You are not ' . 'allowed to delete this task'); @@ -452,19 +493,35 @@ class SingleTaskController extends AbstractController $this->addFlash('success', $translator ->trans("The task has been successfully removed.")); - return $this->redirect($this->generateUrl( - 'chill_task_singletask_list', - $request->query->get('list_params', [ - 'person_id' => $person->getId() - ]))); + if($task->getContext() instanceof Person){ + return $this->redirect($this->generateUrl( + 'chill_task_singletask_list', + $request->query->get('list_params', [ + 'person_id' => $person->getId() + ]))); + } else { + return $this->redirect($this->generateUrl( + 'chill_task_singletask_courselist', + $request->query->get('list_params', [ + 'course_id' => $course->getId() + ]))); + } } } + if($task->getContext() instanceof Person){ + return $this->render('ChillTaskBundle:SingleTask:confirm_delete.html.twig', array( + 'task' => $task, + 'delete_form' => $form->createView() + )); + } else { + return $this->render('ChillTaskBundle:SingleTask:confirm_deleteCourseTask.html.twig', array( + 'task' => $task, + 'delete_form' => $form->createView(), + 'accompanyingCourse' => $course + )); + } - return $this->render('ChillTaskBundle:SingleTask:confirm_delete.html.twig', array( - 'task' => $task, - 'delete_form' => $form->createView() - )); } /** @@ -475,18 +532,10 @@ class SingleTaskController extends AbstractController */ protected function setCreateForm(SingleTask $task, Role $role) { - if($this->getEntityContext() === 'person'){ - $form = $this->createForm(SingleTaskType::class, $task, [ - 'center' => $task->getCenter(), - 'role' => $role, - ]); - } - - if($this->getEntityContext() === 'course'){ - $form = $this->createForm(SingleTaskCourseType::class, $task, [ - 'center' => $task->getCenter(), - ]); - } + $form = $this->createForm(SingleTaskType::class, $task, [ + 'center' => $task->getCenter(), + 'role' => $role, + ]); $form->add('submit', SubmitType::class); diff --git a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php index 649de0422..57a194ef8 100644 --- a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php +++ b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php @@ -248,11 +248,23 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface { if ($this->getPerson() instanceof Person) { return $this->getPerson()->getCenter(); + } else { + return $this->getCourse()->getCenter(); } return null; } + + public function getContext() + { + // if ($this->getCourse() instanceof AccompanyingPeriod){ + // return $this->getCourse(); + // } else { + // return $this->getPerson(); + // } + return $this->getPerson() ?? $this->getCourse(); + } public function getScope(): ?\Chill\MainBundle\Entity\Scope { diff --git a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php index 16f000aa2..5802512b7 100644 --- a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php +++ b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php @@ -48,11 +48,11 @@ class SingleTaskType extends AbstractType 'center' => $options['center'], 'role' => $options['role'], 'placeholder' => 'Not assigned' - ]) + ]) ->add('circle', ScopePickerType::class, [ 'center' => $options['center'], 'role' => $options['role'] - ]) + ]) ->add('startDate', ChillDateType::class, [ 'required' => false ]) @@ -61,8 +61,7 @@ class SingleTaskType extends AbstractType ]) ->add('warningInterval', DateIntervalType::class, [ 'required' => false - ]) - ; + ]); } public function configureOptions(OptionsResolver $resolver) diff --git a/src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php similarity index 100% rename from src/Bundle/ChillTaskBundle/Menu/PersonMenuBuilder.php rename to src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig new file mode 100644 index 000000000..98b070114 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig @@ -0,0 +1,25 @@ +
      + +

      {{ 'Edit task'|trans }}

      + + {{ form_start(form) }} + + {{ form_row(form.title) }} + {{ form_row(form.description) }} + {{ form_row(form.assignee) }} + {{ form_row(form.circle) }} + {{ form_row(form.startDate) }} + {{ form_row(form.endDate) }} + {{ form_row(form.warningInterval) }} + +
        +
      • + + {{ 'Cancel'|trans }} +
      • +
      • + {{ form_widget(form.submit, { 'label': 'Save task', 'attr': {'class' : 'btn btn-update'}})}} +
      • +
      + {{ form_end(form) }} +
      diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig new file mode 100644 index 000000000..7cb19b19f --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig @@ -0,0 +1,30 @@ +
      + +

      {{ 'New task'|trans }}

      + + {{ form_start(form) }} + + {{ form_errors(form) }} + + {{ form_row(form.title) }} + {{ form_row(form.description) }} + {{ form_row(form.assignee) }} + {{ form_row(form.circle) }} + {{ form_row(form.startDate) }} + {{ form_row(form.endDate) }} + {{ form_row(form.warningInterval) }} + +
        +
      • + + {{'Cancel'|trans}} + +
      • +
      • + {{ form_widget(form.submit, { 'label': 'Add a new task'|trans, 'attr': {'class': 'btn btn-save'} }) }} +
      • +
      + + {{ form_end(form) }} + +
      diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig index a8905285a..f73f2740d 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig @@ -68,8 +68,8 @@
      • - - {{ app.request.query.get('returnLabel')|default('Back to the list'|trans) }} + + {{'Back to the list'|trans}}
      • diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig new file mode 100644 index 000000000..49857188c --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig @@ -0,0 +1,19 @@ +{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} + +{% set activeRouteKey = 'chill_task_task_list' %} +{% set course = task.course %} + +{% block title 'Remove task'|trans %} + +{% block content %} + + {{ include('@ChillMain/Util/confirmation_template.html.twig', + { + 'title' : 'Remove task'|trans, + 'confirm_question' : 'Are you sure you want to remove the task about accompanying period "%id%" ?'|trans({ '%id%' : course.id } ), + 'cancel_route' : 'chill_task_singletask_courselist', + 'cancel_parameters' : app.request.query.get('list_params', { } ), + 'form' : delete_form, + } ) }} + +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig index 67604f015..590740cf0 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig @@ -14,46 +14,17 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . #} -{% extends "@ChillPerson/Person/layout.html.twig" %} +{% extends person is defined ? "@ChillPerson/Person/layout.html.twig" %} {% set activeRouteKey = 'chill_task_single_task_edit' %} {% set person = task.person %} -{% block title %}{{ 'Edit task'|trans }}{% endblock %} +{% block title %} + {{ 'Edit task'|trans }} +{% endblock %} {% block personcontent %} -
        - -

        {{ 'Edit task'|trans }}

        - {{ form_start(form) }} + {% include 'ChillTaskBundle:SingleTask:_edit.html.twig' %} - {{ form_row(form.title) }} - {{ form_row(form.description) }} - {{ form_row(form.assignee) }} - {{ form_row(form.circle) }} - {{ form_row(form.startDate) }} - {{ form_row(form.endDate) }} - {{ form_row(form.warningInterval) }} - - - - {{ form_end(form) }} - -
        {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig new file mode 100644 index 000000000..1805761ae --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig @@ -0,0 +1,14 @@ +{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} + +{% set activeRouteKey = 'chill_task_single_task_edit' %} +{% set course = task.course %} + +{% block title %} + {{ 'Edit task'|trans }} +{% endblock %} + +{% block content %} + + {% include 'ChillTaskBundle:SingleTask:_edit.html.twig' %} + +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig index 8f6ba2a4b..2b59fd5bf 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig @@ -14,37 +14,16 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . #} -{% extends "@ChillPerson/Person/layout.html.twig" %} +{% extends person is defined ? "@ChillPerson/Person/layout.html.twig" : "@ChillPerson/AccompanyingCourse/layout.html.twig" %} {% set activeRouteKey = 'chill_task_single_task_new' %} {% set person = task.person %} -{% block title %}{{ 'New task'|trans }}{% endblock %} +{% block title %} + {{ 'New task'|trans }} +{% endblock %} + {% block personcontent %} -
        - -

        {{ 'New task'|trans }}

        - - {{ form_start(form) }} - - {{ form_errors(form) }} - - {{ form_row(form.title) }} - {{ form_row(form.description) }} - {{ form_row(form.assignee) }} - {{ form_row(form.circle) }} - {{ form_row(form.startDate) }} - {{ form_row(form.endDate) }} - {{ form_row(form.warningInterval) }} - -
          -
        • - {{ form_widget(form.submit, { 'label': 'Add a new task'|trans, 'attr': {'class': 'btn btn-save'} }) }} -
        • -
        - - {{ form_end(form) }} - -
        + {% include 'ChillTaskBundle:SingleTask:_new.html.twig' %} {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig new file mode 100644 index 000000000..083c0795c --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig @@ -0,0 +1,12 @@ +{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} + +{% set activeRouteKey = 'chill_task_single_task_new' %} +{# {% set person = task.person %} #} + +{% block title %} + {{ 'New task'|trans }} +{% endblock %} + +{% block content %} + {% include 'ChillTaskBundle:SingleTask:_new.html.twig' %} +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php index 2f760a84b..f5e1d167f 100644 --- a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php +++ b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php @@ -32,7 +32,9 @@ use Psr\Log\LoggerInterface; use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Chill\MainBundle\Entity\User; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Chill\TaskBundle\Security\Authorization\AuthorizationEvent; @@ -146,14 +148,15 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy /* if ($subject instanceof AbstractTask) { - if ($subject->getPerson() === null) { + $associated = $subject->getPerson() ?? $subject->getCourse(); + if ($associated === null) { throw new \LogicException("You should associate a person with task " . "in order to check autorizations"); } $person = $subject->getPerson(); } elseif ($subject instanceof Person) { - $person = $subject; + $associated = $subject; } else { // subject is null. We check that at least one center is reachable $centers = $this->authorizationHelper->getReachableCenters($token->getUser(), new Role($attribute)); @@ -168,6 +171,8 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy if (NULL === $center) { return false; + } elseif ($associated instanceof AccompanyingPeriod && !$this->accessDecisionManager->decide($token, [AccompanyingPeriodVoter::SEE], $associated)) { + return false; } return $this->authorizationHelper->userHasAccess( diff --git a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml index 7af9d450d..50af895e7 100644 --- a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml @@ -41,6 +41,7 @@ User: Utilisateur "Delete": "Supprimer" "Change task status": "Changer le statut" 'Are you sure you want to remove the task about "%name%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche de "%name%"?' +'Are you sure you want to remove the task about accompanying period "%id%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche du parcours "%id%"?' "See more": "Voir plus" "Associated tasks": "Tâches associées" "My tasks": "Mes tâches" From 34dd35f2e266a232556b74be4a41492a63cd7037 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 17 Sep 2021 14:49:43 +0200 Subject: [PATCH 18/79] Changed name of PersonMenuBuilder More general name since it also contains the AccompanyingPeriod task menu entry now. --- .../ChillTaskBundle/Menu/MenuBuilder.php | 8 ++-- .../ChillTaskBundle/config/services/menu.yaml | 44 +++++++++---------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php index 26f06d1b2..9b1890958 100644 --- a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php +++ b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php @@ -28,7 +28,7 @@ use Symfony\Component\Translation\TranslatorInterface; * * @author Julien Fastré */ -class PersonMenuBuilder implements LocalMenuBuilderInterface +class MenuBuilder implements LocalMenuBuilderInterface { /** * @@ -86,12 +86,10 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface public function buildAccompanyingCourseMenu($menu, $parameters){ - //var $person \Chill\PersonBundle\Entity\Person */ $course = $parameters['accompanyingCourse']; - //TODO: implement voter again? - // if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { + if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { $menu->addChild( $this->translator->trans('Tasks'), [ 'route' => 'chill_task_singletask_courselist', @@ -99,7 +97,7 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface [ 'course_id' => $course->getId() ] ]) ->setExtra('order', 400); - // } + } } public static function getMenuIds(): array diff --git a/src/Bundle/ChillTaskBundle/config/services/menu.yaml b/src/Bundle/ChillTaskBundle/config/services/menu.yaml index b4ff7a421..75726fcf4 100644 --- a/src/Bundle/ChillTaskBundle/config/services/menu.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/menu.yaml @@ -1,23 +1,23 @@ services: - Chill\TaskBundle\Menu\UserMenuBuilder: - arguments: - $tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface' - $counter: '@Chill\TaskBundle\Templating\UI\CountNotificationTask' - $translator: '@Symfony\Component\Translation\TranslatorInterface' - $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' - tags: - - { name: 'chill.menu_builder' } - - Chill\TaskBundle\Menu\PersonMenuBuilder: - arguments: - $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' - $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' } + Chill\TaskBundle\Menu\UserMenuBuilder: + arguments: + $tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface' + $counter: '@Chill\TaskBundle\Templating\UI\CountNotificationTask' + $translator: '@Symfony\Component\Translation\TranslatorInterface' + $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' + tags: + - { name: "chill.menu_builder" } + + Chill\TaskBundle\Menu\MenuBuilder: + arguments: + $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' + $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" } From 4c47a354572054f7f4f5bb0dbe8b419e05dbd752 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 17 Sep 2021 14:53:15 +0200 Subject: [PATCH 19/79] Fix SingleTaskListType accompanyingCourse also defined as a form option --- src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php b/src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php index 99e8ddb42..634035f96 100644 --- a/src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php +++ b/src/Bundle/ChillTaskBundle/Form/SingleTaskListType.php @@ -284,6 +284,7 @@ class SingleTaskListType extends AbstractType ->setDefined('person') ->setDefault('person', null) ->setAllowedTypes('person', [Person::class, 'null']) + ->setDefined('accompanyingCourse') ->setDefined('add_status') ->setDefault('add_status', false) ->setAllowedTypes('add_status', ['bool']) From cb5b45cbe8b4754ac9f8bb6f671bd4f7dd0f5183 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 17 Sep 2021 15:17:13 +0200 Subject: [PATCH 20/79] autowire and configure MenuBuilder --- src/Bundle/ChillTaskBundle/config/services/menu.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/config/services/menu.yaml b/src/Bundle/ChillTaskBundle/config/services/menu.yaml index 75726fcf4..a9e1948d6 100644 --- a/src/Bundle/ChillTaskBundle/config/services/menu.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/menu.yaml @@ -9,9 +9,8 @@ services: - { name: "chill.menu_builder" } Chill\TaskBundle\Menu\MenuBuilder: - arguments: - $authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface' - $translator: '@Symfony\Component\Translation\TranslatorInterface' + autowire: true + autoconfigure: true tags: - { name: "chill.menu_builder" } From 41fc41b1da87274c664ea912c5b3f9cdd0b0896b Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Tue, 28 Sep 2021 11:45:56 +0200 Subject: [PATCH 21/79] migration namespace adjusted --- src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php b/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php index e2f59621b..8a81b4ab9 100644 --- a/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php +++ b/src/Bundle/ChillTaskBundle/migrations/Version20210909153533.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Chill\TaskBundle\Migrations; +namespace Chill\Migrations\Task; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; From 8411c909fffb9a57a017be64791afe4e8bffd04e Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Tue, 28 Sep 2021 11:53:19 +0200 Subject: [PATCH 22/79] notation modifications --- .../Controller/SingleTaskController.php | 21 ++++++++++--------- .../Resources/views/SingleTask/edit.html.twig | 3 ++- .../views/SingleTask/editCourseTask.html.twig | 3 ++- .../views/SingleTask/index.html.twig | 6 ++++-- .../Resources/views/SingleTask/new.html.twig | 3 ++- .../views/SingleTask/newCourseTask.html.twig | 3 ++- .../Resources/views/SingleTask/show.html.twig | 3 ++- .../views/SingleTask/showCourseTask.html.twig | 3 ++- 8 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index 456699091..0af61e55c 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -2,6 +2,7 @@ namespace Chill\TaskBundle\Controller; +use Chill\MainBundle\Entity\Scope; use Chill\PersonBundle\Privacy\PrivacyEvent; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -281,12 +282,12 @@ class SingleTaskController extends AbstractController if($task->getContext() instanceof Person){ - return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array( + return $this->render('@ChillTask/SingleTask/show.html.twig', array( 'task' => $task, 'timeline' => $timeline )); } else { - return $this->render('ChillTaskBundle:SingleTask:showCourseTask.html.twig', array( + return $this->render('@ChillTask/SingleTask/showCourseTask.html.twig', array( 'task' => $task, 'timeline' => $timeline )); @@ -401,12 +402,12 @@ class SingleTaskController extends AbstractController )); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - return $this->render('ChillTaskBundle:SingleTask:edit.html.twig', array( + return $this->render('@ChillTask/SingleTask/edit.html.twig', array( 'task' => $task, 'form' => $form->createView() )); } else { - return $this->render('ChillTaskBundle:SingleTask:editCourseTask.html.twig', array( + return $this->render('@ChillTask/SingleTask/editCourseTask.html.twig', array( 'task' => $task, 'form' => $form->createView(), 'accompanyingCourse' => $course @@ -510,12 +511,12 @@ class SingleTaskController extends AbstractController } if($task->getContext() instanceof Person){ - return $this->render('ChillTaskBundle:SingleTask:confirm_delete.html.twig', array( + return $this->render('@ChillTask/SingleTask/confirm_delete.html.twig', array( 'task' => $task, 'delete_form' => $form->createView() )); } else { - return $this->render('ChillTaskBundle:SingleTask:confirm_deleteCourseTask.html.twig', array( + return $this->render('@ChillTask/SingleTask/confirm_deleteCourseTask.html.twig', array( 'task' => $task, 'delete_form' => $form->createView(), 'accompanyingCourse' => $course @@ -646,7 +647,7 @@ class SingleTaskController extends AbstractController $userId = $this->request->query->getInt('user_id', 0); $user = $this->getDoctrine()->getManager() - ->getRepository('ChillMainBundle:User') + ->getRepository(User::class) ->find($userId); if ($user === null) { @@ -663,7 +664,7 @@ class SingleTaskController extends AbstractController $scopeId = $this->request->query->getInt('scope_id', 0); $scope = $this->getDoctrine()->getManager() - ->getRepository('ChillMainBundle:Scope') + ->getRepository(Scope::class) ->find($scopeId); if ($scope === null) { @@ -762,7 +763,7 @@ class SingleTaskController extends AbstractController $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); } - return $this->render('ChillTaskBundle:SingleTask:index.html.twig', + return $this->render('@ChillTask/SingleTask/index.html.twig', array_merge($viewParams, [ 'form' => $form->createView() ])); } @@ -859,7 +860,7 @@ class SingleTaskController extends AbstractController ]); return $this->render( - 'ChillTaskBundle:SingleTask:index.html.twig', + '@ChillTask/SingleTask/index.html.twig', [ 'tasks' => $tasks, 'accompanyingCourse' => $course, diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig index 590740cf0..6188bd859 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig @@ -25,6 +25,7 @@ {% block personcontent %} - {% include 'ChillTaskBundle:SingleTask:_edit.html.twig' %} + {% include '@ChillTask/SingleTask/_edit.html.twig' %} + {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig index 1805761ae..ccf0bab04 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig @@ -9,6 +9,7 @@ {% block content %} - {% include 'ChillTaskBundle:SingleTask:_edit.html.twig' %} + {% include '@ChillTask/SingleTask/_edit.html.twig' %} + {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig index 4c0b9dc84..2f5819388 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig @@ -31,13 +31,15 @@ {% if person is not null %} {% block personcontent %}
        - {% include 'ChillTaskBundle:SingleTask:_list.html.twig' %} + {% include '@ChillTask/SingleTask/_list.html.twig' %} +
        {% endblock %} {% else %} {% block content %}
        - {% include 'ChillTaskBundle:SingleTask:_listCourse.html.twig' %} + {% include '@ChillTask/SingleTask/_listCourse.html.twig' %} +
        {% endblock %} {% endif %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig index 2b59fd5bf..226956aec 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig @@ -25,5 +25,6 @@ {% block personcontent %} - {% include 'ChillTaskBundle:SingleTask:_new.html.twig' %} + {% include '@ChillTask/SingleTask/_new.html.twig' %} + {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig index 083c0795c..b8f810540 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig @@ -8,5 +8,6 @@ {% endblock %} {% block content %} - {% include 'ChillTaskBundle:SingleTask:_new.html.twig' %} + {% include '@ChillTask/SingleTask/_new.html.twig' %} + {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig index 19bf70777..d9792ebde 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig @@ -27,6 +27,7 @@ {% block personcontent %} - {% include 'ChillTaskBundle:SingleTask:_show.html.twig' %} + {% include '@ChillTask/SingleTask/_show.html.twig' %} + {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig index 643617a55..06ff62a60 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig @@ -11,6 +11,7 @@ {% block content %} - {% include 'ChillTaskBundle:SingleTask:_show.html.twig' %} + {% include '@ChillTask/SingleTask/_show.html.twig' %} + {% endblock %} From cb4059e5c3e95c63c2ead7fbf4865c093631c019 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Tue, 28 Sep 2021 14:37:27 +0200 Subject: [PATCH 23/79] comment out use of voter for menu entry, new way of handling rights? --- src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php index 9b1890958..ac0990433 100644 --- a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php +++ b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php @@ -89,7 +89,7 @@ class MenuBuilder implements LocalMenuBuilderInterface $course = $parameters['accompanyingCourse']; - if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { + // if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { $menu->addChild( $this->translator->trans('Tasks'), [ 'route' => 'chill_task_singletask_courselist', @@ -97,7 +97,7 @@ class MenuBuilder implements LocalMenuBuilderInterface [ 'course_id' => $course->getId() ] ]) ->setExtra('order', 400); - } + // } } public static function getMenuIds(): array From 5d69e48787f8abb1930c74e6895c4f31bd3d9e2f Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 30 Sep 2021 14:22:20 +0200 Subject: [PATCH 24/79] more conformity between URL's single-task --- .../ChillTaskBundle/Controller/SingleTaskController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index 0af61e55c..b16b15bd1 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -571,7 +571,7 @@ class SingleTaskController extends AbstractController * - status: date state, amongst SingleTaskRepository::DATE_STATUSES, or 'closed' * * @Route( - * "/{_locale}/task/singletask/list", + * "/{_locale}/task/single-task/list", * name="chill_task_singletask_list" * ) */ @@ -818,7 +818,7 @@ class SingleTaskController extends AbstractController /** * @Route( - * "/{_locale}/task/singletask/courselist", + * "/{_locale}/task/single-task/courselist", * name="chill_task_singletask_courselist") */ From cc258ba1648371897acb7a2eb081617afc18d07e Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 30 Sep 2021 14:22:55 +0200 Subject: [PATCH 25/79] templates added for transition of course task and taskcontroller adapted to show correct template --- .../Controller/TaskController.php | 2 +- .../views/SingleTask/_transition.html.twig | 23 ++++++++++++++++++ .../views/SingleTask/transition.html.twig | 24 +------------------ .../SingleTask/transitionCourseTask.html.twig | 12 ++++++++++ 4 files changed, 37 insertions(+), 24 deletions(-) create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_transition.html.twig create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transitionCourseTask.html.twig diff --git a/src/Bundle/ChillTaskBundle/Controller/TaskController.php b/src/Bundle/ChillTaskBundle/Controller/TaskController.php index 0c2b1f8d2..23f4ba51f 100644 --- a/src/Bundle/ChillTaskBundle/Controller/TaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/TaskController.php @@ -64,7 +64,7 @@ class TaskController extends AbstractController 'id' => $task->getId(), 'list_params' => $request->query->get('list_params', []) ]); - $defaultTemplate = '@ChillTask/SingleTask/transition.html.twig'; + $task->getCourse() === null ? $defaultTemplate = '@ChillTask/SingleTask/transition.html.twig' : $defaultTemplate = '@ChillTask/SingleTask/transitionCourseTask.html.twig'; break; default: return new Response("The type '$kind' is not implemented", diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_transition.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_transition.html.twig new file mode 100644 index 000000000..8f6345352 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_transition.html.twig @@ -0,0 +1,23 @@ +

        {{ '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) }} + +
          +
        • + + {{ 'Back to the list'|trans }} + +
        • +
        • + {{ form_widget(form.submit, { 'attr' : { 'class' : "btn btn-task-exchange green" }, 'label': task_workflow_metadata(task, 'transition.apply_transition_submit_label', transition)|default('apply')|trans } ) }} +
        • +
        + +{{ form_end(form) }} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transition.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transition.html.twig index 617816e4c..05009a2f0 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transition.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transition.html.twig @@ -7,28 +7,6 @@ {% 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) }} - -
          -
        • - - {{ 'Back to the list'|trans }} - -
        • -
        • - {{ form_widget(form.submit, { 'attr' : { 'class' : "btn btn-task-exchange green" }, 'label': task_workflow_metadata(task, 'transition.apply_transition_submit_label', transition)|default('apply')|trans } ) }} -
        • -
        - -{{ form_end(form) }} +{% include '@ChillTask/SingleTask/_transition.html.twig' %} {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transitionCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transitionCourseTask.html.twig new file mode 100644 index 000000000..08bedd31c --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transitionCourseTask.html.twig @@ -0,0 +1,12 @@ +{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} + +{% set activeRouteKey = 'chill_task_task_list' %} +{% set accompanyingCourse = task.course %} + +{% block title 'Remove task'|trans %} + +{% block content %} + + {% include '@ChillTask/SingleTask/_transition.html.twig' %} + +{% endblock %} From 88b8ff86d163ac48c83b6aa6061e7d3d1405fe92 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 1 Oct 2021 10:45:04 +0200 Subject: [PATCH 26/79] reorganize templates for a better structure --- .../confirm_delete.html.twig} | 0 .../edit.html.twig} | 0 .../{_listCourse.html.twig => AccompanyingCourse/list.html.twig} | 0 .../{newCourseTask.html.twig => AccompanyingCourse/new.html.twig} | 0 .../show.html.twig} | 0 .../transition.html.twig} | 0 .../views/SingleTask/{ => Person}/confirm_delete.html.twig | 0 .../Resources/views/SingleTask/{ => Person}/edit.html.twig | 0 .../views/SingleTask/{_list.html.twig => Person/list.html.twig} | 0 .../Resources/views/SingleTask/{ => Person}/new.html.twig | 0 .../Resources/views/SingleTask/{ => Person}/show.html.twig | 0 .../Resources/views/SingleTask/{ => Person}/transition.html.twig | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{confirm_deleteCourseTask.html.twig => AccompanyingCourse/confirm_delete.html.twig} (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{editCourseTask.html.twig => AccompanyingCourse/edit.html.twig} (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{_listCourse.html.twig => AccompanyingCourse/list.html.twig} (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{newCourseTask.html.twig => AccompanyingCourse/new.html.twig} (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{showCourseTask.html.twig => AccompanyingCourse/show.html.twig} (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{transitionCourseTask.html.twig => AccompanyingCourse/transition.html.twig} (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{ => Person}/confirm_delete.html.twig (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{ => Person}/edit.html.twig (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{_list.html.twig => Person/list.html.twig} (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{ => Person}/new.html.twig (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{ => Person}/show.html.twig (100%) rename src/Bundle/ChillTaskBundle/Resources/views/SingleTask/{ => Person}/transition.html.twig (100%) diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/edit.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/edit.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/new.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/new.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/show.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/show.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transitionCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/transition.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transitionCourseTask.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/transition.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_delete.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/confirm_delete.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_delete.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/confirm_delete.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/edit.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/edit.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/new.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/new.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/new.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/show.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/show.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transition.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/transition.html.twig similarity index 100% rename from src/Bundle/ChillTaskBundle/Resources/views/SingleTask/transition.html.twig rename to src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/transition.html.twig From 6a34046e937180c0d38c156c11217af41fd429d5 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 1 Oct 2021 10:47:32 +0200 Subject: [PATCH 27/79] comments deleted, notations adjusted, ... --- .../Controller/SingleTaskController.php | 77 +++++++------------ 1 file changed, 26 insertions(+), 51 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index b16b15bd1..5bcc8f53c 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -31,7 +31,9 @@ use Chill\MainBundle\Timeline\TimelineBuilder; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Contracts\Translation\TranslatorInterface as TranslationTranslatorInterface; /** * Class SingleTaskController @@ -78,7 +80,6 @@ class SingleTaskController extends AbstractController $this->logger = $logger; $this->request = $requestStack->getCurrentRequest(); } - private function getEntityContext() { @@ -99,7 +100,7 @@ class SingleTaskController extends AbstractController * ) */ public function newAction( - TranslatorInterface $translator + TranslationTranslatorInterface $translator ) { $task = (new SingleTask()) @@ -190,14 +191,14 @@ class SingleTaskController extends AbstractController switch($this->getEntityContext()){ case 'person': - return $this->render('ChillTaskBundle:SingleTask:new.html.twig', array( + return $this->render('@ChillTask/SingleTask/Person/new.html.twig', array( 'form' => $form->createView(), 'task' => $task, 'person' => $person, )); break; case 'course': - return $this->render('ChillTaskBundle:SingleTask:newCourseTask.html.twig', array( + return $this->render('@ChillTask/SingleTask/AccompanyingCourse/show.html.twig', array( 'form' => $form->createView(), 'task' => $task, 'accompanyingCourse' => $course, @@ -207,7 +208,6 @@ class SingleTaskController extends AbstractController } - /** * @Route( * "/{_locale}/task/single-task/{id}/show", @@ -220,14 +220,10 @@ class SingleTaskController extends AbstractController $em = $this->getDoctrine()->getManager(); $task = $em->getRepository(SingleTask::class)->find($id); - // In case no task is found - if (!$task) { throw $this->createNotFoundException('Unable to find Task entity.'); } - // In case task belongs to person - if ($task->getPerson() !== null) { $personId = $task->getPerson()->getId(); @@ -253,8 +249,6 @@ class SingleTaskController extends AbstractController } - // In case task belongs to accompanying course - if ($task->getCourse() !== null) { $courseId = $task->getCourse()->getId(); @@ -282,12 +276,12 @@ class SingleTaskController extends AbstractController if($task->getContext() instanceof Person){ - return $this->render('@ChillTask/SingleTask/show.html.twig', array( + return $this->render('@ChillTask/SingleTask/Person/show.html.twig', array( 'task' => $task, 'timeline' => $timeline )); } else { - return $this->render('@ChillTask/SingleTask/showCourseTask.html.twig', array( + return $this->render('@ChillTask/SingleTask/AccompanyingCourse/show.html.twig', array( 'task' => $task, 'timeline' => $timeline )); @@ -304,7 +298,7 @@ class SingleTaskController extends AbstractController */ public function editAction( $id, - TranslatorInterface $translator + TranslationTranslatorInterface $translator ) { $em = $this->getDoctrine()->getManager(); @@ -402,12 +396,12 @@ class SingleTaskController extends AbstractController )); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - return $this->render('@ChillTask/SingleTask/edit.html.twig', array( + return $this->render('@ChillTask/SingleTask/Person/edit.html.twig', array( 'task' => $task, 'form' => $form->createView() )); } else { - return $this->render('@ChillTask/SingleTask/editCourseTask.html.twig', array( + return $this->render('@ChillTask/SingleTask/AccompanyingCourse/edit.html.twig', array( 'task' => $task, 'form' => $form->createView(), 'accompanyingCourse' => $course @@ -426,7 +420,7 @@ class SingleTaskController extends AbstractController public function deleteAction( Request $request, $id, - TranslatorInterface $translator + TranslationTranslatorInterface $translator ) { $em = $this->getDoctrine()->getManager(); @@ -465,9 +459,10 @@ class SingleTaskController extends AbstractController } } + // TODO: reactivate right to delete - $this->denyAccessUnlessGranted(TaskVoter::DELETE, $task, 'You are not ' - . 'allowed to delete this task'); + // $this->denyAccessUnlessGranted(TaskVoter::DELETE, $task, 'You are not ' + // . 'allowed to delete this task'); $form = $this->createDeleteForm($id); @@ -481,10 +476,8 @@ class SingleTaskController extends AbstractController 'task_id' => $task->getId(), 'description' => $task->getDescription(), 'assignee' => $task->getAssignee(), - 'scope_id' => $task->getScope()->getId(), - //'start_date' => $task->getStartDate()->format('Y-m-d'), - //'end_date' => $task->getEndDate()->format('Y-m-d'), - //'warning_interval' => $task->getWarningInterval()->format('Y-m-d') + // TODO reimplement scope + // 'scope_id' => $task->getScope()->getId(), )); $em = $this->getDoctrine()->getManager(); @@ -511,12 +504,12 @@ class SingleTaskController extends AbstractController } if($task->getContext() instanceof Person){ - return $this->render('@ChillTask/SingleTask/confirm_delete.html.twig', array( + return $this->render('@ChillTask/SingleTask/Person/confirm_delete.html.twig', array( 'task' => $task, 'delete_form' => $form->createView() )); } else { - return $this->render('@ChillTask/SingleTask/confirm_deleteCourseTask.html.twig', array( + return $this->render('@ChillTask/SingleTask/AccompanyingCourse/confirm_delete.html.twig', array( 'task' => $task, 'delete_form' => $form->createView(), 'accompanyingCourse' => $course @@ -551,7 +544,7 @@ class SingleTaskController extends AbstractController * name="chill_task_single_my_tasks" * ) */ - public function myTasksAction(TranslatorInterface $translator) + public function myTasksAction(TranslationTranslatorInterface $translator) { return $this->redirectToRoute('chill_task_singletask_list', [ 'user_id' => $this->getUser()->getId(), @@ -595,8 +588,6 @@ class SingleTaskController extends AbstractController $viewParams['accompanyingCourse'] = null; $params['accompanyingCourse'] = null; - - // Get parameters from url if (!empty($this->request->query->get('person_id', NULL))) { $personId = $this->request->query->getInt('person_id', 0); @@ -675,11 +666,9 @@ class SingleTaskController extends AbstractController $params['scope'] = $scope; } - // collect parameters for filter $possibleStatuses = \array_merge(SingleTaskRepository::DATE_STATUSES, [ 'closed' ]); $statuses = $this->request->query->get('status', $possibleStatuses); - // check for invalid statuses $diff = \array_diff($statuses, $possibleStatuses); if (count($diff) > 0) { return new Response( @@ -698,7 +687,6 @@ class SingleTaskController extends AbstractController continue; } - // different query if regarding to date or 'closed' if (in_array($status, SingleTaskRepository::DATE_STATUSES)) { $params['date_status'] = $status; $params['is_closed'] = false; @@ -723,7 +711,6 @@ class SingleTaskController extends AbstractController $tasks_count = $tasks_count + $count; } - // total number of tasks $viewParams['tasks_count'] = $tasks_count; if ($viewParams['person'] !== null){ @@ -734,24 +721,12 @@ class SingleTaskController extends AbstractController $viewParams['layout'] = '@ChillMain/layout.html.twig'; } - // Form for filtering tasks - if($this->getEntityContext() === 'person'){ - $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ - 'person' => $viewParams['person'], - 'method' => Request::METHOD_GET, - 'csrf_protection' => false, - 'add_type' => true - ]); - } - - if($this->getEntityContext() === 'course'){ - $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ - 'accompanyingCourse' => $viewParams['accompanyingCourse'], - 'method' => Request::METHOD_GET, - 'csrf_protection' => false, - 'add_type' => true - ]); - } + $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ + 'person' => $viewParams['person'], + 'method' => Request::METHOD_GET, + 'csrf_protection' => false, + 'add_type' => true + ]); $form->handleRequest($this->request); @@ -826,7 +801,7 @@ class SingleTaskController extends AbstractController AccompanyingPeriodRepository $courseRepository, SingleTaskRepository $taskRepository, FormFactoryInterface $formFactory, - TranslatorInterface $translator + TranslationTranslatorInterface $translator ): Response { From 548247188f78c9f88b990f0e12bc5fa06e78f501 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 1 Oct 2021 10:49:07 +0200 Subject: [PATCH 28/79] temporarily lifted scope validation, currently no scopes in select list to be selected --- src/Bundle/ChillTaskBundle/Entity/AbstractTask.php | 1 - src/Bundle/ChillTaskBundle/Form/SingleTaskType.php | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php index 57a194ef8..ce2de5a6f 100644 --- a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php +++ b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php @@ -85,7 +85,6 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface * @ORM\ManyToOne( * targetEntity="\Chill\MainBundle\Entity\Scope" * ) - * @Assert\NotNull() */ private $circle; diff --git a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php index 5802512b7..e0bae1608 100644 --- a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php +++ b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php @@ -51,7 +51,8 @@ class SingleTaskType extends AbstractType ]) ->add('circle', ScopePickerType::class, [ 'center' => $options['center'], - 'role' => $options['role'] + 'role' => $options['role'], + 'required' => false ]) ->add('startDate', ChillDateType::class, [ 'required' => false From 1403ee2ba5578d043c01044bea18aa57b01dabe4 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 1 Oct 2021 10:49:55 +0200 Subject: [PATCH 29/79] redirect after submit bug fixed for edit and delete when coming from show.html.twig --- .../Resources/views/SingleTask/_show.html.twig | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig index f73f2740d..1186fb125 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig @@ -30,10 +30,12 @@ {% endif %} -
        {{ 'Scope'|trans }}
        -
        - {{ task.scope.name|localize_translatable_string }} -
        + {% if task.scope is not null %} +
        {{ 'Scope'|trans }}
        +
        + {{ task.scope.name|localize_translatable_string }} +
        + {% endif %}

        {{"Dates"|trans}}

        {% if task.startDate is null and task.endDate is null and task.warningDate is null %} @@ -94,7 +96,7 @@ {% if is_granted('CHILL_TASK_TASK_UPDATE', task) %}
      • - + {{ 'Edit the task'|trans }}
      • @@ -102,7 +104,7 @@ {% if is_granted('CHILL_TASK_TASK_CREATE', task) %}
      • - + {{ 'Delete'|trans }}
      • From 262fefa92f998f80a737aa201af02e5995ff2a31 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 1 Oct 2021 10:51:02 +0200 Subject: [PATCH 30/79] access granted condition lifted temporarily to show the 'add new task' button on index page for Person --- .../views/SingleTask/Person/list.html.twig | 23 ++++++++++--------- .../views/SingleTask/index.html.twig | 4 ++-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig index e80e45e8c..1163ee4ef 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig @@ -203,17 +203,18 @@

        {{ 'Tasks'|trans }}

        {% endif %} - {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} - - {% endif %} + {# TODO reimplement right to create task. #} + {# {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} #} + + {# {% endif %} #} {% if single_task_ended_tasks is defined %} {{ helper.date_status('Tasks with expired deadline', single_task_ended_tasks, single_task_ended_count, single_task_ended_paginator, 'ended', isSingleStatus, person) }} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig index 2f5819388..3cd2a1971 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/index.html.twig @@ -31,14 +31,14 @@ {% if person is not null %} {% block personcontent %}
        - {% include '@ChillTask/SingleTask/_list.html.twig' %} + {% include '@ChillTask/SingleTask/Person/list.html.twig' %}
        {% endblock %} {% else %} {% block content %}
        - {% include '@ChillTask/SingleTask/_listCourse.html.twig' %} + {% include '@ChillTask/SingleTask/AccompanyingCourse/list.html.twig' %}
        {% endblock %} From 43daab1f7b2cd81775c82c45cad1a190e82b4446 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 1 Oct 2021 11:32:05 +0200 Subject: [PATCH 31/79] transition button added to listitems of course tasks --- .../Controller/TaskController.php | 2 +- .../AccompanyingCourse/list.html.twig | 39 +++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/TaskController.php b/src/Bundle/ChillTaskBundle/Controller/TaskController.php index 23f4ba51f..c137c166d 100644 --- a/src/Bundle/ChillTaskBundle/Controller/TaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/TaskController.php @@ -64,7 +64,7 @@ class TaskController extends AbstractController 'id' => $task->getId(), 'list_params' => $request->query->get('list_params', []) ]); - $task->getCourse() === null ? $defaultTemplate = '@ChillTask/SingleTask/transition.html.twig' : $defaultTemplate = '@ChillTask/SingleTask/transitionCourseTask.html.twig'; + $task->getCourse() === null ? $defaultTemplate = '@ChillTask/SingleTask/Person/transition.html.twig' : $defaultTemplate = '@ChillTask/SingleTask/AccompanyingCourse/transition.html.twig'; break; default: return new Response("The type '$kind' is not implemented", diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig index 16c907e4c..493fab920 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig @@ -52,26 +52,25 @@ -
          - {# {% if workflow_transitions(task)|length > 0 %} -
        • -
          - - -
          -
        • - {% endif %} #} +
            + {% if workflow_transitions(task)|length > 0 %} +
          • +
            + + +
            +
          • + {% endif %}
          • From fc237db98aa404934e7cd1e4a8f80c046d652471 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 1 Oct 2021 11:32:32 +0200 Subject: [PATCH 32/79] fix: wrong template rendered for new course task --- src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index 76f12acda..be39f4900 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -199,7 +199,7 @@ class SingleTaskController extends AbstractController )); break; case 'course': - return $this->render('@ChillTask/SingleTask/AccompanyingCourse/show.html.twig', array( + return $this->render('@ChillTask/SingleTask/AccompanyingCourse/new.html.twig', array( 'form' => $form->createView(), 'task' => $task, 'accompanyingCourse' => $course, From 461b96ea37c78d67edeaae13378675fe22c1994a Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 1 Oct 2021 11:32:46 +0200 Subject: [PATCH 33/79] duplicate templates deleted --- .../views/SingleTask/_list.html.twig | 256 ------------------ .../views/SingleTask/_listCourse.html.twig | 107 -------- .../confirm_deleteCourseTask.html.twig | 19 -- .../Resources/views/SingleTask/edit.html.twig | 30 -- .../views/SingleTask/editCourseTask.html.twig | 14 - .../views/SingleTask/newCourseTask.html.twig | 12 - .../Resources/views/SingleTask/show.html.twig | 32 --- .../views/SingleTask/showCourseTask.html.twig | 16 -- 8 files changed, 486 deletions(-) delete mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig delete mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig delete mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig delete mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig delete mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig delete mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig delete mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig delete mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig deleted file mode 100644 index e80e45e8c..000000000 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_list.html.twig +++ /dev/null @@ -1,256 +0,0 @@ -{% macro date_status(title, tasks, count, paginator, status, isSingleStatus, person, user) %} - {% if tasks|length > 0 %} -

            {{ title|trans }}

            - - - - {% for task in tasks %} - - - - - {% endfor %} - -
            -
            - {{ task.title }} -
            - - {% if person is null %} -
            - {{ 'For person'|trans }} : - - {{ task.person}} - -
            - {% endif %} - -
            - {{ task_workflow_metadata(task, 'definition.name')|trans }} -
            - -
            - {% for place in workflow_marked_places(task) %} - {{ place|trans }} - {% endfor %} - {% if task.assignee is not null %} -
            - {{ 'By'|trans }} : - {{ task.assignee.username }}
            - {% endif %} -
            - - {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} -
            -
              - {% if task.startDate is not null %} -
            • - - {{ task.startDate|format_date('medium') }} -
            • - {% endif %} - {% if task.warningDate is not null %} -
            • - - {{ task.warningDate|format_date('medium') }} -
            • - {% endif %} - {% if task.endDate is not null %} -
            • - - {{ task.endDate|format_date('medium') }} -
            • - {% endif %} -
            -
            - {% endif %} - -
            - -
            - - {% if isSingleStatus %} - {% if tasks|length < paginator.getTotalItems %} - {{ chill_pagination(paginator) }} - {% endif %} - - - - {% else %} - - {% endif %} - - {% endif %} -{% endmacro %} - -{% import _self as helper %} - -

            {{ 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 }}

            - {{ form_start(form) }} - {{ form_row(form.user_id) }} - - {% if form.status is defined %} - {{ 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 %} - - {% if form.center_id is defined %} - {{ form_row(form.center_id) }} - {% endif %} - -
              -
            • - -
            • -
            - - {{ form_end(form)}} -{% endif %} - -{% if tasks_count == 0 %} -

            {{ "There is no tasks."|trans }}

            - {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} - - {% endif %} -{% else %} - - {% if false == app.request.query.boolean('hide_form', false) %} -

            {{ 'Tasks'|trans }}

            - {% endif %} - - {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} - - {% endif %} - - {% if single_task_ended_tasks is defined %} - {{ helper.date_status('Tasks with expired deadline', single_task_ended_tasks, single_task_ended_count, single_task_ended_paginator, 'ended', isSingleStatus, person) }} - {% endif %} - - {% if single_task_warning_tasks is defined %} - {{ helper.date_status('Tasks with warning deadline reached', single_task_warning_tasks, single_task_warning_count, single_task_warning_paginator, 'warning', isSingleStatus, person) }} - {% endif %} - - {% if single_task_current_tasks is defined %} - {{ helper.date_status('Current tasks', single_task_current_tasks, single_task_current_count, single_task_current_paginator, 'current', isSingleStatus, person) }} - {% endif %} - - {% if single_task_not_started_tasks is defined %} - {{ helper.date_status('Tasks not started', single_task_not_started_tasks, single_task_not_started_count, single_task_not_started_paginator, 'not_started', isSingleStatus, person) }} - {% endif %} - - {% if single_task_closed_tasks is defined %} - {{ helper.date_status('Closed tasks', single_task_closed_tasks, single_task_closed_count, single_task_closed_paginator, 'closed', isSingleStatus, person) }} - {% endif %} - - {% if isSingleStatus == false %} - - {% endif %} - -{% endif %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig deleted file mode 100644 index 16c907e4c..000000000 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_listCourse.html.twig +++ /dev/null @@ -1,107 +0,0 @@ -{% if tasks|length > 0 %} -

            {{ title|trans }}

            - - - - {% for task in tasks %} - - - - - {% endfor %} - -
            -
            - {{ task.title }} -
            - -
            - {{ task_workflow_metadata(task, 'definition.name')|trans }} -
            - -
            - {% for place in workflow_marked_places(task) %} - {{ place|trans }} - {% endfor %} - {% if task.assignee is not null %} -
            - {{ 'By'|trans }} : - {{ task.assignee.username }}
            - {% endif %} -
            - - {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} -
            -
              - {% if task.startDate is not null %} -
            • - - {{ task.startDate|format_date('medium') }} -
            • - {% endif %} - {% if task.warningDate is not null %} -
            • - - {{ task.warningDate|format_date('medium') }} -
            • - {% endif %} - {% if task.endDate is not null %} -
            • - - {{ task.endDate|format_date('medium') }} -
            • - {% endif %} -
            -
            - {% endif %} - -
            - -
            -{% endif %} - - diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig deleted file mode 100644 index 49857188c..000000000 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/confirm_deleteCourseTask.html.twig +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} - -{% set activeRouteKey = 'chill_task_task_list' %} -{% set course = task.course %} - -{% block title 'Remove task'|trans %} - -{% block content %} - - {{ include('@ChillMain/Util/confirmation_template.html.twig', - { - 'title' : 'Remove task'|trans, - 'confirm_question' : 'Are you sure you want to remove the task about accompanying period "%id%" ?'|trans({ '%id%' : course.id } ), - 'cancel_route' : 'chill_task_singletask_courselist', - 'cancel_parameters' : app.request.query.get('list_params', { } ), - 'form' : delete_form, - } ) }} - -{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig deleted file mode 100644 index 590740cf0..000000000 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/edit.html.twig +++ /dev/null @@ -1,30 +0,0 @@ -{# - * Copyright (C) 2014, Champs Libres Cooperative SCRLFS, - * - * 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 . -#} -{% extends person is defined ? "@ChillPerson/Person/layout.html.twig" %} - -{% set activeRouteKey = 'chill_task_single_task_edit' %} -{% set person = task.person %} - -{% block title %} - {{ 'Edit task'|trans }} -{% endblock %} - -{% block personcontent %} - - {% include 'ChillTaskBundle:SingleTask:_edit.html.twig' %} - -{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig deleted file mode 100644 index 1805761ae..000000000 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/editCourseTask.html.twig +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} - -{% set activeRouteKey = 'chill_task_single_task_edit' %} -{% set course = task.course %} - -{% block title %} - {{ 'Edit task'|trans }} -{% endblock %} - -{% block content %} - - {% include 'ChillTaskBundle:SingleTask:_edit.html.twig' %} - -{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig deleted file mode 100644 index 083c0795c..000000000 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/newCourseTask.html.twig +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} - -{% set activeRouteKey = 'chill_task_single_task_new' %} -{# {% set person = task.person %} #} - -{% block title %} - {{ 'New task'|trans }} -{% endblock %} - -{% block content %} - {% include 'ChillTaskBundle:SingleTask:_new.html.twig' %} -{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig deleted file mode 100644 index 19bf70777..000000000 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/show.html.twig +++ /dev/null @@ -1,32 +0,0 @@ -{# - * Copyright (C) 2014, Champs Libres Cooperative SCRLFS, - * - * 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 . -#} -{% extends "@ChillPerson/Person/layout.html.twig" %} - - -{% set activeRouteKey = 'chill_task_single_task_show' %} -{% set person = task.person %} - -{% block title %} - {{ 'Task'|trans }} -{% endblock %} - - -{% block personcontent %} - - {% include 'ChillTaskBundle:SingleTask:_show.html.twig' %} - -{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig deleted file mode 100644 index 643617a55..000000000 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/showCourseTask.html.twig +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %} - - -{% set activeRouteKey = 'chill_task_single_task_show' %} -{% set accompanyingCourse = task.course %} - -{% block title %} - {{ 'Task'|trans }} -{% endblock %} - - -{% block content %} - - {% include 'ChillTaskBundle:SingleTask:_show.html.twig' %} - -{% endblock %} From 965ea528e39788b534038ca055356a329a35afad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 26 Oct 2021 18:05:06 +0200 Subject: [PATCH 34/79] adaptations for acl with tasks --- .../ChillMainExtension.php | 4 + .../DependencyInjection/Configuration.php | 20 +- .../Form/Type/ScopePickerType.php | 34 ++- .../Form/Type/UserPickerType.php | 22 +- .../Repository/UserACLAwareRepository.php | 54 ++++ .../UserACLAwareRepositoryInterface.php | 22 ++ .../Util/confirmation_template.html.twig | 6 +- .../Authorization/AuthorizationHelper.php | 34 +-- .../ChillMainBundle/config/services.yaml | 2 + .../Controller/SingleTaskController.php | 277 ++++++++---------- .../ChillTaskBundle/Entity/AbstractTask.php | 38 ++- .../ChillTaskBundle/Entity/SingleTask.php | 36 +-- .../ChillTaskBundle/Form/SingleTaskType.php | 49 +++- .../confirm_delete.html.twig | 2 +- .../AccompanyingCourse/list.html.twig | 4 +- .../views/SingleTask/Person/list.html.twig | 10 +- .../views/SingleTask/_edit.html.twig | 6 +- .../Resources/views/SingleTask/_new.html.twig | 4 +- .../Security/Authorization/TaskVoter.php | 28 +- .../config/services/controller.yaml | 10 +- .../ChillTaskBundle/config/services/form.yaml | 5 + .../translations/messages.fr.yml | 2 +- 22 files changed, 371 insertions(+), 298 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Repository/UserACLAwareRepository.php create mode 100644 src/Bundle/ChillMainBundle/Repository/UserACLAwareRepositoryInterface.php diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 8050fd9b6..7ec8d3585 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -90,6 +90,10 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); + // replace all config with a main key: + $container->setParameter('chill_main', $config); + + // legacy config $container->setParameter('chill_main.installation_name', $config['installation_name']); diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php b/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php index c711f911f..b7cff5821 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php @@ -16,23 +16,23 @@ use Symfony\Component\HttpFoundation\Request; */ class Configuration implements ConfigurationInterface { - + use AddWidgetConfigurationTrait; - + /** * * @var ContainerBuilder */ private $containerBuilder; - - public function __construct(array $widgetFactories = array(), + + public function __construct(array $widgetFactories = array(), ContainerBuilder $containerBuilder) { $this->setWidgetFactories($widgetFactories); $this->containerBuilder = $containerBuilder; } - + /** * {@inheritDoc} */ @@ -97,6 +97,14 @@ class Configuration implements ConfigurationInterface ->end() ->end() ->end() + ->arrayNode('acl') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('form_show_scopes') + ->defaultTrue() + ->end() + ->end() + ->end() ->arrayNode('redis') ->children() ->scalarNode('host') @@ -247,7 +255,7 @@ class Configuration implements ConfigurationInterface ->end() // end of root ; - + return $treeBuilder; } } diff --git a/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php b/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php index ebf474657..2785365cf 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/ScopePickerType.php @@ -23,7 +23,9 @@ use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Form\DataMapper\ScopePickerDataMapper; use Chill\MainBundle\Repository\ScopeRepository; +use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -36,6 +38,7 @@ use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Role\Role; +use Symfony\Component\Security\Core\Security; /** * Allow to pick amongst available scope for the current @@ -46,14 +49,10 @@ use Symfony\Component\Security\Core\Role\Role; * - `center`: the center of the entity * - `role` : the role of the user * - * @author Julien Fastré */ class ScopePickerType extends AbstractType { - /** - * @var AuthorizationHelper - */ - protected $authorizationHelper; + protected AuthorizationHelperInterface $authorizationHelper; /** * @var TokenStorageInterface @@ -70,22 +69,26 @@ class ScopePickerType extends AbstractType */ protected $translatableStringHelper; + protected Security $security; + public function __construct( - AuthorizationHelper $authorizationHelper, + AuthorizationHelperInterface $authorizationHelper, TokenStorageInterface $tokenStorage, ScopeRepository $scopeRepository, + Security $security, TranslatableStringHelper $translatableStringHelper ) { $this->authorizationHelper = $authorizationHelper; $this->tokenStorage = $tokenStorage; $this->scopeRepository = $scopeRepository; + $this->security = $security; $this->translatableStringHelper = $translatableStringHelper; } public function buildForm(FormBuilderInterface $builder, array $options) { - $query = $this->buildAccessibleScopeQuery($options['center'], $options['role']); - $items = $query->getQuery()->execute(); + $items = $this->authorizationHelper->getReachableScopes($this->security->getUser(), + $options['role'], $options['center']); if (1 !== count($items)) { $builder->add('scope', EntityType::class, [ @@ -94,9 +97,7 @@ class ScopePickerType extends AbstractType 'choice_label' => function (Scope $c) { return $this->translatableStringHelper->localize($c->getName()); }, - 'query_builder' => function () use ($options) { - return $this->buildAccessibleScopeQuery($options['center'], $options['role']); - }, + 'choices' => $items, ]); $builder->setDataMapper(new ScopePickerDataMapper()); } else { @@ -121,19 +122,22 @@ class ScopePickerType extends AbstractType $resolver // create `center` option ->setRequired('center') - ->setAllowedTypes('center', [Center::class]) + ->setAllowedTypes('center', [Center::class, 'array', 'null']) // create ``role` option ->setRequired('role') ->setAllowedTypes('role', ['string', Role::class]); } /** + * @param Center|array|Center[] $center + * @param string $role * @return \Doctrine\ORM\QueryBuilder */ - protected function buildAccessibleScopeQuery(Center $center, Role $role) + protected function buildAccessibleScopeQuery($center, $role) { $roles = $this->authorizationHelper->getParentRoles($role); $roles[] = $role; + $centers = $center instanceof Center ? [$center]: $center; $qb = $this->scopeRepository->createQueryBuilder('s'); $qb @@ -142,8 +146,8 @@ class ScopePickerType extends AbstractType ->join('rs.permissionsGroups', 'pg') ->join('pg.groupCenters', 'gc') // add center constraint - ->where($qb->expr()->eq('IDENTITY(gc.center)', ':center')) - ->setParameter('center', $center->getId()) + ->where($qb->expr()->in('IDENTITY(gc.center)', ':centers')) + ->setParameter('centers', \array_map(fn(Center $c) => $c->getId(), $centers)) // role constraints ->andWhere($qb->expr()->in('rs.role', ':roles')) ->setParameter('roles', $roles) diff --git a/src/Bundle/ChillMainBundle/Form/Type/UserPickerType.php b/src/Bundle/ChillMainBundle/Form/Type/UserPickerType.php index fece2d6d4..2558f4a4f 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/UserPickerType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/UserPickerType.php @@ -17,6 +17,8 @@ */ namespace Chill\MainBundle\Form\Type; +use Chill\MainBundle\Entity\Scope; +use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface; use Symfony\Component\Form\AbstractType; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Doctrine\ORM\EntityRepository; @@ -56,14 +58,18 @@ class UserPickerType extends AbstractType protected UserRepository $userRepository; + protected UserACLAwareRepositoryInterface $userACLAwareRepository; + public function __construct( AuthorizationHelper $authorizationHelper, TokenStorageInterface $tokenStorage, - UserRepository $userRepository + UserRepository $userRepository, + UserACLAwareRepositoryInterface $userACLAwareRepository ) { $this->authorizationHelper = $authorizationHelper; $this->tokenStorage = $tokenStorage; $this->userRepository = $userRepository; + $this->userACLAwareRepository = $userACLAwareRepository; } @@ -72,7 +78,7 @@ class UserPickerType extends AbstractType $resolver // create `center` option ->setRequired('center') - ->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class ]) + ->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class, 'null', 'array' ]) // create ``role` option ->setRequired('role') ->setAllowedTypes('role', ['string', \Symfony\Component\Security\Core\Role\Role::class ]) @@ -86,17 +92,19 @@ class UserPickerType extends AbstractType ->setDefault('choice_label', function(User $u) { return $u->getUsername(); }) + ->setDefault('scope', null) + ->setAllowedTypes('scope', [Scope::class, 'array', 'null']) ->setNormalizer('choices', function(Options $options) { - - $users = $this->authorizationHelper - ->findUsersReaching($options['role'], $options['center']); - + + $users = $this->userACLAwareRepository + ->findUsersByReachedACL($options['role'], $options['center'], $options['scope'], true); + if (NULL !== $options['having_permissions_group_flag']) { return $this->userRepository ->findUsersHavingFlags($options['having_permissions_group_flag'], $users) ; } - + return $users; }) ; diff --git a/src/Bundle/ChillMainBundle/Repository/UserACLAwareRepository.php b/src/Bundle/ChillMainBundle/Repository/UserACLAwareRepository.php new file mode 100644 index 000000000..44399000b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/UserACLAwareRepository.php @@ -0,0 +1,54 @@ +authorizationHelper->getParentRoles($role); + $parents[] = $role; + + $qb = $this->em->createQueryBuilder(); + + $qb + ->select('u') + ->from(User::class, 'u') + ->join('u.groupCenters', 'gc') + ->join('gc.permissionsGroup', 'pg') + ->join('pg.roleScopes', 'rs') + ->where($qb->expr()->in('rs.role', $parents)) + ; + + if ($onlyEnabled) { + $qb->andWhere($qb->expr()->eq('u.enabled', "'TRUE'")); + } + + if (NULL !== $center) { + $centers = $center instanceof Center ? [$center] : $center; + $qb + ->andWhere($qb->expr()->in('gc.center', ':centers')) + ->setParameter('centers', $centers) + ; + } + + if (NULL !== $scope) { + $scopes = $scope instanceof Scope ? [$scope] : $scope; + $qb + ->andWhere($qb->expr()->in('rs.scope', ':scopes')) + ->setParameter('scopes', $scopes) + ; + } + + return $qb->getQuery()->getResult(); + } + +} diff --git a/src/Bundle/ChillMainBundle/Repository/UserACLAwareRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/UserACLAwareRepositoryInterface.php new file mode 100644 index 000000000..eb249911b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/UserACLAwareRepositoryInterface.php @@ -0,0 +1,22 @@ + + - -{{ form_end(form) }} \ No newline at end of file + +{{ form_end(form) }} diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php index afff931ed..8713d6ce3 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php @@ -23,6 +23,8 @@ use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\HasCenterInterface; use Chill\MainBundle\Entity\HasScopeInterface; +use Chill\MainBundle\Repository\UserACLAwareRepository; +use Chill\MainBundle\Repository\UserACLAwareRepositoryInterface; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher; use Chill\MainBundle\Security\Resolver\ScopeResolverInterface; @@ -62,6 +64,8 @@ class AuthorizationHelper implements AuthorizationHelperInterface protected LoggerInterface $logger; + private UserACLAwareRepositoryInterface $userACLAwareRepository; + public function __construct( RoleHierarchyInterface $roleHierarchy, ParameterBagInterface $parameterBag, @@ -320,33 +324,15 @@ class AuthorizationHelper implements AuthorizationHelperInterface /** * + * @deprecated use UserACLAwareRepositoryInterface::findUsersByReachedACL instead + * @param Center|Center[]|array $center + * @param Scope|Scope[]|array|null $scope + * @param bool $onlyActive true if get only active users * @return User[] */ - public function findUsersReaching(string $role, Center $center, Scope $circle = null): array + public function findUsersReaching(string $role, $center, $scope = null, bool $onlyEnabled = true): array { - $parents = $this->getParentRoles($role); - $parents[] = $role; - - $qb = $this->em->createQueryBuilder(); - $qb - ->select('u') - ->from(User::class, 'u') - ->join('u.groupCenters', 'gc') - ->join('gc.permissionsGroup', 'pg') - ->join('pg.roleScopes', 'rs') - ->where('gc.center = :center') - ->andWhere($qb->expr()->in('rs.role', $parents)) - ; - - $qb->setParameter('center', $center); - - if ($circle !== null) { - $qb->andWhere('rs.scope = :circle') - ->setParameter('circle', $circle) - ; - } - - return $qb->getQuery()->getResult(); + return $this->userACLAwareRepository->findUsersByReachedACL($role, $center, $scope, $onlyEnabled); } /** diff --git a/src/Bundle/ChillMainBundle/config/services.yaml b/src/Bundle/ChillMainBundle/config/services.yaml index 6540069fa..f5f10cf57 100644 --- a/src/Bundle/ChillMainBundle/config/services.yaml +++ b/src/Bundle/ChillMainBundle/config/services.yaml @@ -11,6 +11,8 @@ services: autowire: true autoconfigure: true + Chill\MainBundle\Repository\UserACLAwareRepositoryInterface: '@Chill\MainBundle\Repository\UserACLAwareRepository' + Chill\MainBundle\Serializer\Normalizer\: resource: '../Serializer/Normalizer' autoconfigure: true diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index be39f4900..5bb5ebe6c 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -3,9 +3,12 @@ namespace Chill\TaskBundle\Controller; use Chill\MainBundle\Entity\Scope; +use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; +use Chill\MainBundle\Security\Resolver\CenterResolverInterface; use Chill\PersonBundle\Privacy\PrivacyEvent; 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; @@ -24,7 +27,6 @@ use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Repository\PersonRepository; use Chill\TaskBundle\Event\TaskEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\Translation\TranslatorInterface; use Chill\TaskBundle\Event\UI\UIEvent; use Chill\MainBundle\Repository\CenterRepository; use Chill\MainBundle\Timeline\TimelineBuilder; @@ -33,6 +35,7 @@ use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface as TranslationTranslatorInterface; /** @@ -40,126 +43,100 @@ use Symfony\Contracts\Translation\TranslatorInterface as TranslationTranslatorIn * * @package Chill\TaskBundle\Controller */ -class SingleTaskController extends AbstractController +final class SingleTaskController extends AbstractController { - - /** - * @var EventDispatcherInterface - */ - protected $eventDispatcher; - - /** - * - * @var TimelineBuilder - */ - protected $timelineBuilder; - - /** - * @var LoggerInterface - */ - protected $logger; - /** - * @var RequestStack - */ - protected $request; - - /** - * SingleTaskController constructor. - * - * @param EventDispatcherInterface $eventDispatcher - */ + private EventDispatcherInterface $eventDispatcher; + private TimelineBuilder $timelineBuilder; + private LoggerInterface $logger; + private CenterResolverDispatcher $centerResolverDispatcher; + private TranslatorInterface $translator; + public function __construct( + CenterResolverDispatcher $centerResolverDispatcher, + TranslatorInterface $translator, EventDispatcherInterface $eventDispatcher, TimelineBuilder $timelineBuilder, - LoggerInterface $logger, - RequestStack $requestStack + LoggerInterface $logger ) { $this->eventDispatcher = $eventDispatcher; $this->timelineBuilder = $timelineBuilder; $this->logger = $logger; - $this->request = $requestStack->getCurrentRequest(); + $this->translator = $translator; + $this->centerResolverDispatcher = $centerResolverDispatcher; } - - private function getEntityContext() + private function getEntityContext(Request $request) { - if($this->request->query->has('person_id')){ + if ($request->query->has('person_id')) { return 'person'; - } else if ($this->request->query->has('course_id')) { + } 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( - TranslationTranslatorInterface $translator - ) { + public function newAction(Request $request) { $task = (new SingleTask()) ->setAssignee($this->getUser()) ->setType('task_default') ; - - $entityType = $this->getEntityContext(); - if ($entityType !== null) { - - $entityId = $this->request->query->getInt("{$entityType}_id", 0); // sf4 check: - // prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given` + $entityType = $this->getEntityContext($request); - if ($entityId === null) { - return new Response("You must provide a {$entityType}_id", Response::HTTP_BAD_REQUEST); - } + if (NULL === $entityType) { + throw new BadRequestHttpException("You must provide a entity_type"); + } - if($entityType === 'person') - { + $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); + ->getRepository(Person::class) + ->find($entityId); if ($person === null) { $this->createNotFoundException("Invalid person id"); } - $task->setPerson($person); - } - - if($entityType === 'course') - { - + $task->setPerson($person); + break; + case 'course': $course = $this->getDoctrine()->getManager() - ->getRepository(AccompanyingPeriod::class) - ->find($entityId); + ->getRepository(AccompanyingPeriod::class) + ->find($entityId); - if($course === null) { + if ($course === null) { $this->createNotFoundException("Invalid accompanying course id"); } $task->setCourse($course); - - } - - + break; + default: + return new BadRequestHttpException("context with {$entityType} is not supported"); } - //TODO : resolve access rights - - // $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' - // . 'allowed to create this task'); + $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' + . 'allowed to create this task'); $form = $this->setCreateForm($task, new Role(TaskVoter::CREATE)); - $form->handleRequest($this->request); - + $form->handleRequest($request); + if ($form->isSubmitted()) { if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); @@ -169,44 +146,38 @@ class SingleTaskController extends AbstractController $em->flush(); - $this->addFlash('success', $translator->trans("The task is created")); + $this->addFlash('success', $this->translator->trans("The task is created")); - if($entityType === 'person') - { + if ($entityType === 'person') { return $this->redirectToRoute('chill_task_singletask_list', [ 'person_id' => $task->getPerson()->getId() ]); - } - - if($entityType === 'course') - { + } elseif ($entityType === 'course') { return $this->redirectToRoute('chill_task_singletask_courselist', [ 'course_id' => $task->getCourse()->getId() ]); } - } else { - $this->addFlash('error', $translator->trans("This form contains errors")); + $this->addFlash('error', $this->translator->trans("This form contains errors")); } } - switch($this->getEntityContext()){ - case 'person': + switch ($entityType) { + case 'person': return $this->render('@ChillTask/SingleTask/Person/new.html.twig', array( 'form' => $form->createView(), 'task' => $task, 'person' => $person, )); - break; case 'course': return $this->render('@ChillTask/SingleTask/AccompanyingCourse/new.html.twig', array( 'form' => $form->createView(), 'task' => $task, 'accompanyingCourse' => $course, )); - break; - } - + default: + throw new \LogicException("entity context not supported"); + } } /** @@ -215,9 +186,9 @@ class SingleTaskController extends AbstractController * name="chill_task_single_task_show" * ) */ - public function showAction($id) + public function showAction($id, Request $request) { - + $em = $this->getDoctrine()->getManager(); $task = $em->getRepository(SingleTask::class)->find($id); @@ -226,7 +197,7 @@ class SingleTaskController extends AbstractController } if ($task->getPerson() !== null) { - + $personId = $task->getPerson()->getId(); if ($personId === null) { @@ -250,7 +221,7 @@ class SingleTaskController extends AbstractController } - if ($task->getCourse() !== null) + if ($task->getCourse() !== null) { $courseId = $task->getCourse()->getId(); @@ -274,7 +245,7 @@ class SingleTaskController extends AbstractController $timeline = $this->timelineBuilder ->getTimelineHTML('task', array('task' => $task)); - + if($task->getContext() instanceof Person){ return $this->render('@ChillTask/SingleTask/Person/show.html.twig', array( @@ -299,9 +270,9 @@ class SingleTaskController extends AbstractController */ public function editAction( $id, - TranslationTranslatorInterface $translator + Request $request ) { - + $em = $this->getDoctrine()->getManager(); $task = $em->getRepository(SingleTask::class)->find($id); @@ -339,16 +310,16 @@ class SingleTaskController extends AbstractController if (!$task) { throw $this->createNotFoundException('Unable to find Task entity.'); } - + $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($this->request); + $form->handleRequest($request); if ($form->isSubmitted()) { if ($form->isValid()) { @@ -357,7 +328,7 @@ class SingleTaskController extends AbstractController $em->flush(); - $this->addFlash('success', $translator + $this->addFlash('success', $this->translator ->trans("The task has been updated")); if($task->getContext() instanceof Person){ @@ -369,26 +340,26 @@ class SingleTaskController extends AbstractController $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); return $this->redirectToRoute( - 'chill_task_singletask_list', - $this->request->query->get('list_params', []) + 'chill_task_singletask_list', + $request->query->get('list_params', []) ); } else { return $this->redirectToRoute( - 'chill_task_singletask_courselist', - $this->request->query->get('list_params', []) + 'chill_task_singletask_courselist', + $request->query->get('list_params', []) ); } } else { - $this->addFlash('error', $translator->trans("This form contains errors")); + $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($person, array( 'element_class' => SingleTask::class, @@ -420,10 +391,9 @@ class SingleTaskController extends AbstractController */ public function deleteAction( Request $request, - $id, - TranslationTranslatorInterface $translator + $id ) { - + $em = $this->getDoctrine()->getManager(); $task = $em->getRepository(SingleTask::class)->find($id); @@ -471,7 +441,7 @@ class SingleTaskController extends AbstractController $form->handleRequest($request); if ($form->isValid()) { - + $this->logger->notice("A task has been removed", array( 'by_user' => $this->getUser()->getUsername(), 'task_id' => $task->getId(), @@ -485,18 +455,18 @@ class SingleTaskController extends AbstractController $em->remove($task); $em->flush(); - $this->addFlash('success', $translator + $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_list', + 'chill_task_singletask_list', $request->query->get('list_params', [ 'person_id' => $person->getId() ]))); } else { return $this->redirect($this->generateUrl( - 'chill_task_singletask_courselist', + 'chill_task_singletask_courselist', $request->query->get('list_params', [ 'course_id' => $course->getId() ]))); @@ -528,7 +498,7 @@ class SingleTaskController extends AbstractController protected function setCreateForm(SingleTask $task, Role $role) { $form = $this->createForm(SingleTaskType::class, $task, [ - 'center' => $task->getCenter(), + 'center' => $this->centerResolverDispatcher->resolveCenter($task), 'role' => $role, ]); @@ -545,12 +515,12 @@ class SingleTaskController extends AbstractController * name="chill_task_single_my_tasks" * ) */ - public function myTasksAction(TranslationTranslatorInterface $translator) + public function myTasksAction() { return $this->redirectToRoute('chill_task_singletask_list', [ 'user_id' => $this->getUser()->getId(), 'hide_form' => true, - 'title' => $translator->trans('My tasks') + 'title' => $this->translator->trans('My tasks') ]); } @@ -575,7 +545,8 @@ class SingleTaskController extends AbstractController PersonRepository $personRepository, AccompanyingPeriodRepository $courseRepository, CenterRepository $centerRepository, - FormFactoryInterface $formFactory + FormFactoryInterface $formFactory, + Request $request ) { /* @var $viewParams array The parameters for the view */ /* @var $params array The parameters for the query */ @@ -589,9 +560,9 @@ class SingleTaskController extends AbstractController $viewParams['accompanyingCourse'] = null; $params['accompanyingCourse'] = null; - if (!empty($this->request->query->get('person_id', NULL))) { - - $personId = $this->request->query->getInt('person_id', 0); + if (!empty($request->query->get('person_id', NULL))) { + + $personId = $request->query->getInt('person_id', 0); $person = $personRepository->find($personId); if ($person === null) { @@ -603,9 +574,9 @@ class SingleTaskController extends AbstractController $params['person'] = $person; } - if (!empty($this->request->query->get('course_id', NULL))) { - - $courseId = $this->request->query->getInt('course_id', 0); + if (!empty($request->query->get('course_id', NULL))) { + + $courseId = $request->query->getInt('course_id', 0); $course = $courseRepository->find($courseId); if ($course === null) { @@ -616,28 +587,28 @@ class SingleTaskController extends AbstractController $viewParams['accompanyingCourse'] = $course; $params['accompanyingCourse'] = $course; } - - if (!empty($this->request->query->get('center_id', NULL))) { - $center = $centerRepository->find($this->request->query->getInt('center_id')); + + if (!empty($request->query->get('center_id', NULL))) { + $center = $centerRepository->find($request->query->getInt('center_id')); if ($center === null) { throw $this->createNotFoundException('center not found'); } $params['center'] = $center; } - - if(!empty($this->request->query->get('types', []))) { - $types = $this->request->query->get('types', []); + + if(!empty($request->query->get('types', []))) { + $types = $request->query->get('types', []); if (count($types) > 0) { $params['types'] = $types; } } - - if (!empty($this->request->query->get('user_id', null))) { - if ($this->request->query->get('user_id') === '_unassigned') { + + if (!empty($request->query->get('user_id', null))) { + if ($request->query->get('user_id') === '_unassigned') { $params['unassigned'] = true; } else { - - $userId = $this->request->query->getInt('user_id', 0); + + $userId = $request->query->getInt('user_id', 0); $user = $this->getDoctrine()->getManager() ->getRepository(User::class) ->find($userId); @@ -651,10 +622,10 @@ class SingleTaskController extends AbstractController } } - if (!empty($this->request->query->get('scope_id'))) { + if (!empty($request->query->get('scope_id'))) { + + $scopeId = $request->query->getInt('scope_id', 0); - $scopeId = $this->request->query->getInt('scope_id', 0); - $scope = $this->getDoctrine()->getManager() ->getRepository(Scope::class) ->find($scopeId); @@ -668,7 +639,7 @@ class SingleTaskController extends AbstractController } $possibleStatuses = \array_merge(SingleTaskRepository::DATE_STATUSES, [ 'closed' ]); - $statuses = $this->request->query->get('status', $possibleStatuses); + $statuses = $request->query->get('status', $possibleStatuses); $diff = \array_diff($statuses, $possibleStatuses); if (count($diff) > 0) { @@ -683,7 +654,7 @@ class SingleTaskController extends AbstractController $tasks_count = 0; foreach($statuses as $status) { - if($this->request->query->has('status') + if($request->query->has('status') && FALSE === \in_array($status, $statuses)) { continue; } @@ -729,8 +700,8 @@ class SingleTaskController extends AbstractController 'add_type' => true ]); - $form->handleRequest($this->request); - + $form->handleRequest($request); + if (isset($person)) { $event = new PrivacyEvent($person, array( 'element_class' => SingleTask::class, @@ -738,7 +709,7 @@ class SingleTaskController extends AbstractController )); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); } - + return $this->render('@ChillTask/SingleTask/index.html.twig', array_merge($viewParams, [ 'form' => $form->createView() ])); } @@ -747,7 +718,7 @@ class SingleTaskController extends AbstractController protected function getPersonParam(EntityManagerInterface $em) { $person = $em->getRepository(Person::class) - ->find($this->request->query->getInt('person_id')) + ->find($request->query->getInt('person_id')) ; if (NULL === $person) { @@ -763,7 +734,7 @@ class SingleTaskController extends AbstractController protected function getUserParam(EntityManagerInterface $em) { $user = $em->getRepository(User::class) - ->find($this->request->query->getInt('user_id')) + ->find($request->query->getInt('user_id')) ; if (NULL === $user) { @@ -799,16 +770,16 @@ class SingleTaskController extends AbstractController */ public function listCourseTasks( - AccompanyingPeriodRepository $courseRepository, + AccompanyingPeriodRepository $courseRepository, SingleTaskRepository $taskRepository, FormFactoryInterface $formFactory, - TranslationTranslatorInterface $translator + Request $request ): Response { - if (!empty($this->request->query->get('course_id', NULL))) { - - $courseId = $this->request->query->getInt('course_id', 0); + if (!empty($request->query->get('course_id', NULL))) { + + $courseId = $request->query->getInt('course_id', 0); $course = $courseRepository->find($courseId); if ($course === null) { @@ -834,15 +805,15 @@ class SingleTaskController extends AbstractController 'csrf_protection' => false, 'add_type' => true ]); - + return $this->render( - '@ChillTask/SingleTask/index.html.twig', + '@ChillTask/SingleTask/index.html.twig', [ 'tasks' => $tasks, 'accompanyingCourse' => $course, 'layout' => '@ChillPerson/AccompanyingCourse/layout.html.twig', 'form' => $form->createView(), - 'title' => $translator->trans('Tasks for this accompanying period') + 'title' => $this->translator->trans('Tasks for this accompanying period') ]); } diff --git a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php index ce2de5a6f..b3f0c6a9f 100644 --- a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php +++ b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php @@ -16,10 +16,6 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod; * AbstractTask * * @ORM\MappedSuperclass() - * @UserCircleConsistency( - * "CHILL_TASK_TASK_SHOW", - * getUserFunction="getAssignee" - * ) */ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface { @@ -52,7 +48,7 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface * @ORM\Column(name="description", type="text") */ private $description = ''; - + /** * * @var User @@ -61,7 +57,7 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface * ) */ private $assignee; - + /** * * @var Person @@ -78,25 +74,25 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface */ private $course; - + /** * * @var Scope * @ORM\ManyToOne( * targetEntity="\Chill\MainBundle\Entity\Scope" - * ) + * ) */ private $circle; - + /** * @var boolean * @ORM\Column(name="closed", type="boolean", options={ "default"=false }) */ private $closed = false; - + public function __construct() { - + } /** @@ -125,7 +121,7 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface /** * Set currentStates - * + * * The current states are sorted in a single array, non associative. * * @param $currentStates @@ -141,8 +137,8 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface /** * Get currentStates - * - * The states are returned as required by marking store format. + * + * The states are returned as required by marking store format. * * @return array */ @@ -198,7 +194,7 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface { return $this->description; } - + public function getAssignee(): ?User { return $this->assignee; @@ -218,7 +214,7 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface { return $this->circle; } - + public function setAssignee(User $assignee = null) { $this->assignee = $assignee; @@ -250,9 +246,9 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface } else { return $this->getCourse()->getCenter(); } - + return null; - + } public function getContext() @@ -264,7 +260,7 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface // } return $this->getPerson() ?? $this->getCourse(); } - + public function getScope(): ?\Chill\MainBundle\Entity\Scope { return $this->getCircle(); @@ -277,9 +273,9 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface { return $this->closed; } - + /** - * + * * @param bool $closed */ public function setClosed(bool $closed) diff --git a/src/Bundle/ChillTaskBundle/Entity/SingleTask.php b/src/Bundle/ChillTaskBundle/Entity/SingleTask.php index 23bdd61df..c22f47361 100644 --- a/src/Bundle/ChillTaskBundle/Entity/SingleTask.php +++ b/src/Bundle/ChillTaskBundle/Entity/SingleTask.php @@ -40,12 +40,12 @@ class SingleTask extends AbstractTask * * @ORM\Column(name="start_date", type="date", nullable=true) * @Assert\Date() - * + * * @Assert\Expression( * "value === null or this.getEndDate() === null or value < this.getEndDate()", * message="The start date must be before the end date" * ) - * + * * @Assert\Expression( * "value === null or this.getWarningDate() === null or this.getWarningDate() > this.getStartDate()", * message="The start date must be before warning date" @@ -66,16 +66,16 @@ class SingleTask extends AbstractTask * and this.getEndDate() === null * * @ORM\Column(name="warning_interval", type="dateinterval", nullable=true) - * + * * @Assert\Expression( * "!(value != null and this.getEndDate() == null)", * message="An end date is required if a warning interval is set" * ) - * - * + * + * */ private $warningInterval; - + /** * * @var RecurringTask @@ -85,7 +85,7 @@ class SingleTask extends AbstractTask * ) */ private $recurringTask; - + /** * * @var \Doctrine\Common\Collections\Collection @@ -96,15 +96,15 @@ class SingleTask extends AbstractTask * ) */ private $taskPlaceEvents; - + public function __construct() { $this->taskPlaceEvents = $events = new \Doctrine\Common\Collections\ArrayCollection; - + parent::__construct(); } - + /** * Get id * @@ -186,13 +186,13 @@ class SingleTask extends AbstractTask { return $this->warningInterval; } - + /** - * Get the Warning date, computed from the difference between the + * Get the Warning date, computed from the difference between the * end date and the warning interval - * + * * Return null if warningDate or endDate is null - * + * * @return \DateTimeImmutable */ public function getWarningDate() @@ -200,15 +200,15 @@ class SingleTask extends AbstractTask if ($this->getWarningInterval() === null) { return null; } - + if ($this->getEndDate() === null) { return null; } - + return \DateTimeImmutable::createFromMutable($this->getEndDate()) ->sub($this->getWarningInterval()); } - + function getRecurringTask(): RecurringTask { return $this->recurringTask; @@ -227,7 +227,7 @@ class SingleTask extends AbstractTask public function setTaskPlaceEvents(Collection $taskPlaceEvents) { $this->taskPlaceEvents = $taskPlaceEvents; - + return $this; } } diff --git a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php index e0bae1608..7fbc84800 100644 --- a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php +++ b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php @@ -17,6 +17,9 @@ */ namespace Chill\TaskBundle\Form; +use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; +use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Chill\MainBundle\Form\Type\ChillDateType; @@ -29,15 +32,26 @@ use Symfony\Component\Security\Core\Role\Role; use Chill\MainBundle\Form\Type\DateIntervalType; use Chill\MainBundle\Form\Type\ChillTextareaType; -/** - * - * - * @author Julien Fastré - */ class SingleTaskType extends AbstractType { + private ParameterBagInterface $parameterBag; + private CenterResolverDispatcher $centerResolverDispatcher; + private ScopeResolverDispatcher $scopeResolverDispatcher; + + public function __construct(ParameterBagInterface $parameterBag, CenterResolverDispatcher $centerResolverDispatcher, ScopeResolverDispatcher $scopeResolverDispatcher) + { + $this->parameterBag = $parameterBag; + $this->centerResolverDispatcher = $centerResolverDispatcher; + $this->scopeResolverDispatcher = $scopeResolverDispatcher; + } + public function buildForm(FormBuilderInterface $builder, array $options) { + if (NULL !== $task = $options['data']) { + $center = $this->centerResolverDispatcher->resolveCenter($task); + $isScopeConcerned = $this->scopeResolverDispatcher->isConcerned($task); + } + $builder ->add('title', TextType::class) ->add('description', ChillTextareaType::class, [ @@ -45,15 +59,10 @@ class SingleTaskType extends AbstractType ]) ->add('assignee', UserPickerType::class, [ 'required' => false, - 'center' => $options['center'], + 'center' => $center, 'role' => $options['role'], 'placeholder' => 'Not assigned' ]) - ->add('circle', ScopePickerType::class, [ - 'center' => $options['center'], - 'role' => $options['role'], - 'required' => false - ]) ->add('startDate', ChillDateType::class, [ 'required' => false ]) @@ -62,16 +71,26 @@ class SingleTaskType extends AbstractType ]) ->add('warningInterval', DateIntervalType::class, [ 'required' => false - ]); + ]); + + if ($this->parameterBag->get('chill_main')['acl']['form_show_scopes'] + && $isScopeConcerned) { + $builder + ->add('circle', ScopePickerType::class, [ + 'center' => $center, + 'role' => $options['role'], + 'required' => false + ]); + } } - + public function configureOptions(OptionsResolver $resolver) { $resolver ->setRequired('center') - ->setAllowedTypes('center', [ Center::class ]) + ->setAllowedTypes('center', [ Center::class, 'array', 'null' ]) ->setRequired('role') - ->setAllowedTypes('role', [ Role::class ]) + ->setAllowedTypes('role', [ Role::class, 'string' ]) ; } } diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig index 49857188c..01b7aa499 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig @@ -10,7 +10,7 @@ {{ include('@ChillMain/Util/confirmation_template.html.twig', { 'title' : 'Remove task'|trans, - 'confirm_question' : 'Are you sure you want to remove the task about accompanying period "%id%" ?'|trans({ '%id%' : course.id } ), + 'confirm_question' : 'Are you sure you want to remove the task "%title%" ?'|trans({ '%title%' : task.title } ), 'cancel_route' : 'chill_task_singletask_courselist', 'cancel_parameters' : app.request.query.get('list_params', { } ), 'form' : delete_form, diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig index 493fab920..be491e2f9 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig @@ -95,11 +95,11 @@ {% endif %} -
              +
              • {% if accompanyingCourse is not null %} - {{ 'Add a new task' | trans }} + {{ 'Create' | trans }} {% endif %}
              • diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig index 1163ee4ef..ac08cf1b0 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig @@ -237,17 +237,11 @@ {% endif %} {% if isSingleStatus == false %} -
                  +
                  • {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} - {{ 'Add a new task' | trans }} - - {% endif %} - - {% if accompanyingCourse is not null %} - - {{ 'Add a new task' | trans }} + {{ 'Create' | trans }} {% endif %}
                  • diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig index 98b070114..d0a906c9c 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_edit.html.twig @@ -7,14 +7,16 @@ {{ form_row(form.title) }} {{ form_row(form.description) }} {{ form_row(form.assignee) }} - {{ form_row(form.circle) }} + {% if form.circle is defined %} + {{ form_row(form.circle) }} + {% endif %} {{ form_row(form.startDate) }} {{ form_row(form.endDate) }} {{ form_row(form.warningInterval) }}
                    • - + {{ 'Cancel'|trans }}
                    • diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig index 7cb19b19f..37f8b431c 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig @@ -9,7 +9,9 @@ {{ form_row(form.title) }} {{ form_row(form.description) }} {{ form_row(form.assignee) }} - {{ form_row(form.circle) }} + {% if form.circle is defined %} + {{ form_row(form.circle) }} + {% endif %} {{ form_row(form.startDate) }} {{ form_row(form.endDate) }} {{ form_row(form.warningInterval) }} diff --git a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php index e316a1311..ca77171cb 100644 --- a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php +++ b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php @@ -65,7 +65,9 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy protected VoterHelperInterface $voter; + public function __construct( + VoterHelperFactoryInterface $voterHelperFactory, AccessDecisionManagerInterface $accessDecisionManager, AuthorizationHelper $authorizationHelper, EventDispatcherInterface $eventDispatcher, @@ -82,7 +84,8 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy $this->voter = $voterFactory ->generate(AbstractTask::class) ->addCheckFor(AbstractTask::class, self::ROLES) - ->addCheckFor(Person::class, [self::SHOW]) + ->addCheckFor(Person::class, [self::SHOW, self::CREATE]) + ->addCheckFor(AccompanyingPeriod::class, [self::SHOW, self::CREATE]) ->addCheckFor(null, [self::SHOW]) ->build() ; @@ -91,14 +94,6 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy public function supports($attribute, $subject) { return $this->voter->supports($attribute, $subject); - /* - return ($subject instanceof AbstractTask && in_array($attribute, self::ROLES)) - || - ($subject instanceof Person && \in_array($attribute, [ self::CREATE, self::SHOW ])) - || - (NULL === $subject && $attribute === self::SHOW ) - ; - */ } /** @@ -134,19 +129,26 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy // do pre-flight check, relying on other decision manager // those pre-flight check concern associated entities if ($subject instanceof AbstractTask) { + // a user can always see his own tasks + if ($subject->getAssignee() === $token->getUser()) { + return true; + } + if (NULL !== $person = $subject->getPerson()) { if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) { return false; } - } elseif (false) { - // here will come the test if the task is associated to an accompanying course + } elseif (NULL !== $period = $subject->getCourse()) { + if (!$this->accessDecisionManager->decide($token, [AccompanyingPeriodVoter::SEE], $period)) { + return false; + } } } // do regular check. return $this->voter->voteOnAttribute($attribute, $subject, $token); - + if ($subject instanceof AbstractTask) { $associated = $subject->getPerson() ?? $subject->getCourse(); if ($associated === null) { @@ -179,7 +181,7 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy $subject, $attribute ); - + } public function getRoles() diff --git a/src/Bundle/ChillTaskBundle/config/services/controller.yaml b/src/Bundle/ChillTaskBundle/config/services/controller.yaml index cfe083e07..76799c72d 100644 --- a/src/Bundle/ChillTaskBundle/config/services/controller.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/controller.yaml @@ -1,12 +1,6 @@ services: Chill\TaskBundle\Controller\: resource: "../../Controller" - tags: ["controller.service_arguments"] - - Chill\TaskBundle\Controller\SingleTaskController: - arguments: - $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface' - $timelineBuilder: "@chill_main.timeline_builder" - $logger: "@chill.main.logger" - $requestStack: '@Symfony\Component\HttpFoundation\RequestStack' + autowire: true + autoconfigure: ture tags: ["controller.service_arguments"] diff --git a/src/Bundle/ChillTaskBundle/config/services/form.yaml b/src/Bundle/ChillTaskBundle/config/services/form.yaml index f82e4a562..728b8d7da 100644 --- a/src/Bundle/ChillTaskBundle/config/services/form.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/form.yaml @@ -1,4 +1,9 @@ services: + Chill\TaskBundle\Form\: + resource: '../../Form/' + autowire: true + autoconfigure: true + Chill\TaskBundle\Form\SingleTaskListType: arguments: $em: '@Doctrine\ORM\EntityManagerInterface' diff --git a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml index 50af895e7..fc725195f 100644 --- a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml @@ -41,7 +41,7 @@ User: Utilisateur "Delete": "Supprimer" "Change task status": "Changer le statut" 'Are you sure you want to remove the task about "%name%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche de "%name%"?' -'Are you sure you want to remove the task about accompanying period "%id%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche du parcours "%id%"?' +'Are you sure you want to remove the task "%title%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche "%title%" ?' "See more": "Voir plus" "Associated tasks": "Tâches associées" "My tasks": "Mes tâches" From 333a4d94b2408942246d81bca63508a203fbe566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 27 Oct 2021 11:57:51 +0200 Subject: [PATCH 35/79] layout task list --- .../Entity/AccompanyingPeriod.php | 7 +- .../Controller/SingleTaskController.php | 23 ++- .../SingleTaskAclAwareRepository.php | 67 ++++++++ .../SingleTaskAclAwareRepositoryInterface.php | 10 ++ .../public/chill/scss/_task-list.scss | 26 +-- .../public/chill/scss/_task-statuses.scss | 42 ++--- .../Resources/public/page/tile_list/index.js | 1 + .../public/page/tile_list/task_list.scss | 24 +++ .../views/SingleTask/List/index.html.twig | 153 ++++++++++++++++++ .../ChillTaskBundle/chill.webpack.config.js | 2 + .../config/services/repositories.yaml | 6 + .../translations/messages.fr.yml | 1 + 12 files changed, 321 insertions(+), 41 deletions(-) create mode 100644 src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php create mode 100644 src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php create mode 100644 src/Bundle/ChillTaskBundle/Resources/public/page/tile_list/index.js create mode 100644 src/Bundle/ChillTaskBundle/Resources/public/page/tile_list/task_list.scss create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 296acde19..9ad0c75dd 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -503,7 +503,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface return $collection->count() > 0 ? $collection->first() : NULL; } - public function getOPenParticipations(): Collection + public function getOpenParticipations(): Collection { return $this ->getParticipations() @@ -514,6 +514,11 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface ); } + public function getCurrentParticipations(): Collection + { + return $this->getOpenParticipations(); + } + /** * Return an array with open participations sorted by household * [ diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index 5bb5ebe6c..eb8e6de69 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -6,6 +6,7 @@ use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; use Chill\MainBundle\Security\Resolver\CenterResolverInterface; 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; @@ -51,9 +52,13 @@ final class SingleTaskController extends AbstractController private LoggerInterface $logger; private CenterResolverDispatcher $centerResolverDispatcher; private TranslatorInterface $translator; + private PaginatorFactory $paginatorFactory; + private SingleTaskAclAwareRepositoryInterface $singleTaskAclAwareRepository; public function __construct( CenterResolverDispatcher $centerResolverDispatcher, + PaginatorFactory $paginatorFactory, + SingleTaskAclAwareRepositoryInterface $singleTaskAclAwareRepository, TranslatorInterface $translator, EventDispatcherInterface $eventDispatcher, TimelineBuilder $timelineBuilder, @@ -64,6 +69,8 @@ final class SingleTaskController extends AbstractController $this->logger = $logger; $this->translator = $translator; $this->centerResolverDispatcher = $centerResolverDispatcher; + $this->paginatorFactory = $paginatorFactory; + $this->singleTaskAclAwareRepository = $singleTaskAclAwareRepository; } private function getEntityContext(Request $request) @@ -517,10 +524,18 @@ final class SingleTaskController extends AbstractController */ public function myTasksAction() { - return $this->redirectToRoute('chill_task_singletask_list', [ - 'user_id' => $this->getUser()->getId(), - 'hide_form' => true, - 'title' => $this->translator->trans('My tasks') + $this->denyAccessUnlessGranted('ROLE_USER'); + + $nb = $this->singleTaskAclAwareRepository->countByCurrentUsersTasks(); + $paginator = $this->paginatorFactory->create($nb); + $tasks = $this->singleTaskAclAwareRepository->findByCurrentUsersTasks( + null, [], $paginator->getCurrentPageFirstItemNumber(), + $paginator->getItemsPerPage() + ); + + return $this->render('@ChillTask/SingleTask/List/index.html.twig', [ + 'tasks' => $tasks, + 'paginator' => $paginator, ]); } diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php new file mode 100644 index 000000000..e85a6abd7 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -0,0 +1,67 @@ +em = $em; + $this->security = $security; + } + + public function findByCurrentUsersTasks( + ?string $pattern = null, + ?array $flags = [], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [] + ): array { + $qb = $this->buildQueryMyTasks($pattern, $flags); + $qb->select('t'); + + $qb + ->setFirstResult($start) + ->setMaxResults($limit) + ; + + foreach ($orderBy as $field => $direction) { + $qb->addOrderBy('t.'.$field, $direction); + } + + return $qb->getQuery()->getResult(); + } + + public function countByCurrentUsersTasks( + ?string $pattern = null, + ?array $flags = [] + ): int { + $qb = $this->buildQueryMyTasks($pattern, $flags); + $qb->select('COUNT(t)'); + + return $qb->getQuery()->getSingleScalarResult(); + } + + public function buildQueryMyTasks( + ?string $pattern = null, + ?array $flags = [] + ): QueryBuilder { + $qb = $this->em->createQueryBuilder(); + $qb + ->from(SingleTask::class, 't') + ->where($qb->expr()->eq('t.assignee', ':user')) + ->setParameter('user', $this->security->getUser()) + ; + + return $qb; + } + +} diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php new file mode 100644 index 000000000..b5d83a5f0 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php @@ -0,0 +1,10 @@ + div { margin-bottom: 0.50rem; } - + .chill-task-list__row__title { font-weight: bold; font-size: 1.40rem; } - + .chill-task-list__row__type { font-variant: small-caps; display: inline; @@ -21,31 +29,31 @@ table.chill-task-list { border: 1px solid var(--chill-dark-gray); color: var(--chill-dark-gray); } - + .chill-task-list__row__person-for { display: inline; font-weight: bold; } - + .chill-task-list__row__assignee { display: inline; - + } - + .chill_task-list__row__assignee_by { display: inline; font-weight: bold; } - + .chill-task-list__row__dates { & > ul { display: inline; list-style: none; - + & > li { display: inline; margin-right: 0.25rem; - + } } } diff --git a/src/Bundle/ChillTaskBundle/Resources/public/chill/scss/_task-statuses.scss b/src/Bundle/ChillTaskBundle/Resources/public/chill/scss/_task-statuses.scss index 5e371359e..4f598fc7b 100644 --- a/src/Bundle/ChillTaskBundle/Resources/public/chill/scss/_task-statuses.scss +++ b/src/Bundle/ChillTaskBundle/Resources/public/chill/scss/_task-statuses.scss @@ -1,34 +1,22 @@ +// Access to Bootstrap variables and mixins +@import '~ChillMainAssets/module/bootstrap/shared'; + .task-status { - &.box { - font-variant: small-caps; - display: inline; - padding: .2em .6em .3em; - font-size: 0.88rem; - font-weight: bold; - line-height: 1; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; - color: white; + + // 'new', 'in_progress', 'closed', 'canceled' + &.place-new { + background-color: $chill-yellow; } - - &.type-task_default { - // 'new', 'in_progress', 'closed', 'canceled' - &.place-new { - background-color: var(--chill-yellow); - } - &.place-in_progress { - background-color: var(--chill-green); - } + &.place-in_progress { + background-color: $chill-green; + } - &.place-closed { - background-color: var(--chill-blue); - } + &.place-closed { + background-color: $chill-blue; + } - &.place-canceled { - background-color: var(--chill-beige); - } + &.place-canceled { + background-color: $chill-beige; } } diff --git a/src/Bundle/ChillTaskBundle/Resources/public/page/tile_list/index.js b/src/Bundle/ChillTaskBundle/Resources/public/page/tile_list/index.js new file mode 100644 index 000000000..a8217c7a7 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/public/page/tile_list/index.js @@ -0,0 +1 @@ +require("./task_list.scss"); diff --git a/src/Bundle/ChillTaskBundle/Resources/public/page/tile_list/task_list.scss b/src/Bundle/ChillTaskBundle/Resources/public/page/tile_list/task_list.scss new file mode 100644 index 000000000..4c71462d1 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/public/page/tile_list/task_list.scss @@ -0,0 +1,24 @@ +.chill-task-list { + .task-type { + font-variant: small-caps; + display: inline; + padding: 0.05rem .15rem; + font-size: 0.88rem; + font-weight: light; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border: 1px solid var(--chill-dark-gray); + color: var(--chill-dark-gray); + } + + .assignee { + text-align: right; + } + + .dates { + text-align: right; + } + +} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig new file mode 100644 index 000000000..87de25ccd --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig @@ -0,0 +1,153 @@ +{% extends 'ChillMainBundle::layout.html.twig' %} + +{% block title 'My tasks'|trans %} + +{% block content %} +
                      + +

                      {{ block('title') }}

                      + + {% if tasks|length == 0 %} +

                      {{ 'Any tasks'|trans }}

                      + {% else %} +
                      + {% for task in tasks %} +
                      +
                      +
                      +
                      + {{ task.title }} + {% for place in workflow_marked_places(task) %} + + {{ place|trans }} + + {% endfor %} +
                      + {% if task.type != 'task_default'%} + + {{ task_workflow_metadata(task, 'definition.name')|trans }} + + {% endif %} +
                      + {% if task.person is not null %} + + {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { + targetEntity: { name: 'person', id: task.person.id }, + action: 'show', + displayBadge: true, + buttonText: task.person|chill_entity_render_string + } %} + + {% elseif task.course is not null %} + + + + + {% for part in task.course.currentParticipations %} + {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { + targetEntity: { name: 'person', id: part.person.id }, + action: 'show', + displayBadge: true, + buttonText: part.person|chill_entity_render_string + } %} + {% endfor %} + + {% endif %} +
                      +
                      + + +
                      +
                      + {% if task.assignee is not null %} +
                      + {{ 'By'|trans }} : + {{ task.assignee.username }} +
                      + {% endif %} + + {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} +
                      +
                        + {% if task.startDate is not null %} +
                      • + + {{ task.startDate|format_date('medium') }} +
                      • + {% endif %} + {% if task.warningDate is not null %} +
                      • + + {{ task.warningDate|format_date('medium') }} +
                      • + {% endif %} + {% if task.endDate is not null %} +
                      • + + {{ task.endDate|format_date('medium') }} +
                      • + {% endif %} +
                      +
                      + {% endif %} +
                      + +
                      + +
                      +
                      + + +
                      +
                      + {% endfor %} +
                      + {% endif %} + + {{ chill_pagination(paginator) }} + +
                      +{% endblock %} + +{% block css %} + {{ encore_entry_link_tags('page_task_list') }} +{% endblock %} +{% block js %} + {{ encore_entry_script_tags('page_task_list') }} +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/chill.webpack.config.js b/src/Bundle/ChillTaskBundle/chill.webpack.config.js index 83c08a6ce..e96855d80 100644 --- a/src/Bundle/ChillTaskBundle/chill.webpack.config.js +++ b/src/Bundle/ChillTaskBundle/chill.webpack.config.js @@ -1,4 +1,6 @@ module.exports = function(encore, entries) { entries.push(__dirname + '/Resources/public/chill/index.js'); + + encore.addEntry('page_task_list', __dirname + '/Resources/public/page/tile_list/index.js'); }; diff --git a/src/Bundle/ChillTaskBundle/config/services/repositories.yaml b/src/Bundle/ChillTaskBundle/config/services/repositories.yaml index 3cc867d96..7bee5abd0 100644 --- a/src/Bundle/ChillTaskBundle/config/services/repositories.yaml +++ b/src/Bundle/ChillTaskBundle/config/services/repositories.yaml @@ -9,3 +9,9 @@ services: arguments: - "@chill.main.security.authorization.helper" Chill\TaskBundle\Repository\SingleTaskRepository: '@chill_task.single_task_repository' + + Chill\TaskBundle\Repository\SingleTaskAclAwareRepository: + autowire: true + autoconfigure: true + + Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface: '@Chill\TaskBundle\Repository\SingleTaskAclAwareRepository' diff --git a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml index fc725195f..76c2c0674 100644 --- a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml @@ -63,6 +63,7 @@ Default task: Tâche par défaut Not assigned: Aucun utilisateur assigné For person: Pour By: Par +Any tasks: Aucune tâche # transitions - default task definition "new": "nouvelle" From 691c5ffd21c5f674e7df695e8c82ed9cb3bda989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 27 Oct 2021 13:32:33 +0200 Subject: [PATCH 36/79] improve filter order by adding checkboxes --- .../Form/Type/Listing/FilterOrderType.php | 32 ++++++++++++++ .../views/FilterOrder/base.html.twig | 24 +++++++--- .../Templating/Listing/FilterOrderHelper.php | 44 +++++++++++++++++-- .../Listing/FilterOrderHelperBuilder.php | 15 +++++++ 4 files changed, 107 insertions(+), 8 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php b/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php index 3976625a1..3bbd6b9ae 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php @@ -3,9 +3,12 @@ namespace Chill\MainBundle\Form\Type\Listing; use Chill\MainBundle\Templating\Listing\FilterOrderHelper; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\SearchType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\HttpFoundation\RequestStack; final class FilterOrderType extends \Symfony\Component\Form\AbstractType @@ -29,9 +32,27 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType ]); } + foreach ($helper->getCheckboxes() as $name => $c) { + + $choices = \array_combine( + \array_map(function($c, $t) { + if ($t !== NULL) { return $t; } + else { return $c; } + }, $c['choices'], $c['trans']), + $c['choices'] + ); + + $builder->add('c_'.$name, ChoiceType::class, [ + 'choices' => $choices, + 'expanded' => true, + 'multiple' => true, + ]); + } + foreach ($this->requestStack->getCurrentRequest()->query->getIterator() as $key => $value) { switch($key) { case 'q': + case 'c_'.$key: break; case 'page': $builder->add($key, HiddenType::class, [ @@ -47,6 +68,17 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType } } + public function buildView(FormView $view, FormInterface $form, array $options) + { + /** @var FilterOrderHelper $helper */ + $helper = $options['helper']; + $view->vars['has_search_box'] = $helper->hasSearchBox(); + $view->vars['checkboxes'] = []; + foreach ($helper->getCheckboxes() as $name => $c) { + $view->vars['checkboxes']['c_'.$name] = []; + } + } + public function configureOptions(\Symfony\Component\OptionsResolver\OptionsResolver $resolver) { $resolver->setRequired('helper') diff --git a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig index 074f3bb94..8358d0e9b 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig @@ -1,12 +1,26 @@ {{ form_start(form) }}
                      -
                      -
                      - {{ form_widget(form.q)}} - + {% if form.vars.has_search_box %} +
                      +
                      + {{ form_widget(form.q)}} + +
                      -
                      + {% endif %}
                      + {% for checkbox_name, options in form.vars.checkboxes %} +
                      +
                      + {{ form_widget(form[checkbox_name]) }} +
                      + {% if loop.last %} +
                      + +
                      + {% endif %} +
                      + {% endfor %}
                      {{ form_end(form) }} diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index 0b9eb60bb..b91dddb9e 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -12,6 +12,7 @@ class FilterOrderHelper private FormFactoryInterface $formFactory; private RequestStack $requestStack; private ?array $searchBoxFields = null; + private array $checkboxes = []; public function __construct( FormFactoryInterface $formFactory, @@ -28,6 +29,33 @@ class FilterOrderHelper return $this; } + public function addCheckbox(string $name, array $choices, ?array $default = [], ?array $trans = []): self + { + $missing = count($choices) - count($trans) - 1; + $this->checkboxes[$name] = [ + 'choices' => $choices, 'default' => $default, + 'trans' => + \array_merge( + $trans, + 0 < $missing ? + array_fill(0, $missing, null) : [] + ) + ]; + + return $this; + } + + public function getCheckbox(string $name): array + { + return $this->requestStack->getCurrentRequest() + ->query->get('c_'.$name, $this->checkboxes[$name]['default']); + } + + public function getCheckboxes(): array + { + return $this->checkboxes; + } + public function hasSearchBox(): bool { return $this->searchBoxFields !== null; @@ -35,9 +63,17 @@ class FilterOrderHelper private function getFormData(): array { - return [ - 'q' => $this->getQueryString() + $r = [ + 'q' => $this->getQueryString(), ]; + + foreach ($this->checkboxes as $name => $c) { + $r['c_'.$name] = $this->getCheckbox($name); + } + + dump($r); + + return $r; } public function getQueryString(): ?string @@ -50,11 +86,13 @@ class FilterOrderHelper public function buildForm($name = null, string $type = FilterOrderType::class, array $options = []): FormInterface { - return $this->formFactory + $form = $this->formFactory ->createNamed($name, $type, $this->getFormData(), \array_merge([ 'helper' => $this, 'method' => 'GET', 'csrf_protection' => false, ], $options)); + + return $form; } } diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php index ec0f6b60b..a1791f89f 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php @@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\RequestStack; class FilterOrderHelperBuilder { private ?array $searchBoxFields = null; + private array $checkboxes = []; private FormFactoryInterface $formFactory; private RequestStack $requestStack; @@ -26,6 +27,13 @@ class FilterOrderHelperBuilder return $this; } + public function addCheckbox(string $name, array $choices, ?array $default = [], ?array $trans = []): self + { + $this->checkboxes[$name] = [ 'choices' => $choices, 'default' => $default, 'trans' => $trans]; + + return $this; + } + public function build(): FilterOrderHelper { $helper = new FilterOrderHelper( @@ -34,6 +42,13 @@ class FilterOrderHelperBuilder ); $helper->setSearchBox($this->searchBoxFields); + foreach ($this->checkboxes as $name => [ + 'choices' => $choices, + 'default' => $default, + 'trans' => $trans + ]) { + $helper->addCheckbox($name, $choices, $default, $trans); + } return $helper; } From f4fb375fd17ed23a98a90c91b5f0c892e6073030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 27 Oct 2021 13:32:52 +0200 Subject: [PATCH 37/79] add filtering in task list --- .../Controller/SingleTaskController.php | 34 ++++++++++- .../SingleTaskAclAwareRepository.php | 60 +++++++++++++++++++ .../views/SingleTask/List/index.html.twig | 2 + .../translations/messages.fr.yml | 4 ++ 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index eb8e6de69..90dbe4744 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -5,6 +5,8 @@ namespace Chill\TaskBundle\Controller; use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; use Chill\MainBundle\Security\Resolver\CenterResolverInterface; +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; @@ -54,6 +56,7 @@ final class SingleTaskController extends AbstractController private TranslatorInterface $translator; private PaginatorFactory $paginatorFactory; private SingleTaskAclAwareRepositoryInterface $singleTaskAclAwareRepository; + private FilterOrderHelperFactoryInterface $filterOrderHelperFactory; public function __construct( CenterResolverDispatcher $centerResolverDispatcher, @@ -62,7 +65,8 @@ final class SingleTaskController extends AbstractController TranslatorInterface $translator, EventDispatcherInterface $eventDispatcher, TimelineBuilder $timelineBuilder, - LoggerInterface $logger + LoggerInterface $logger, + FilterOrderHelperFactoryInterface $filterOrderHelperFactory ) { $this->eventDispatcher = $eventDispatcher; $this->timelineBuilder = $timelineBuilder; @@ -71,6 +75,7 @@ final class SingleTaskController extends AbstractController $this->centerResolverDispatcher = $centerResolverDispatcher; $this->paginatorFactory = $paginatorFactory; $this->singleTaskAclAwareRepository = $singleTaskAclAwareRepository; + $this->filterOrderHelperFactory = $filterOrderHelperFactory; } private function getEntityContext(Request $request) @@ -526,19 +531,42 @@ final class SingleTaskController extends AbstractController { $this->denyAccessUnlessGranted('ROLE_USER'); - $nb = $this->singleTaskAclAwareRepository->countByCurrentUsersTasks(); + $filterOrder = $this->buildFilterOrder(); + $nb = $this->singleTaskAclAwareRepository->countByCurrentUsersTasks( + $filterOrder->getQueryString(), + $filterOrder->getCheckbox('status') + ); $paginator = $this->paginatorFactory->create($nb); $tasks = $this->singleTaskAclAwareRepository->findByCurrentUsersTasks( - null, [], $paginator->getCurrentPageFirstItemNumber(), + $filterOrder->getQueryString(), + $filterOrder->getCheckbox('status'), + $paginator->getCurrentPageFirstItemNumber(), $paginator->getItemsPerPage() ); return $this->render('@ChillTask/SingleTask/List/index.html.twig', [ 'tasks' => $tasks, 'paginator' => $paginator, + 'filter_order' => $filterOrder, ]); } + private function buildFilterOrder(): FilterOrderHelper + { + $statuses = ['no-alert', 'warning', 'alert']; + $statusTrans = [ + 'Tasks near deadline', + 'Tasks over deadline', + 'Tasks without alert', + ]; + return $this->filterOrderHelperFactory + ->create(self::class) + ->addSearchBox() + ->addCheckbox('status', $statuses, $statuses, $statusTrans) + ->build() + ; + } + /** * * Arguments: diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index e85a6abd7..b034bf973 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -61,6 +61,66 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository ->setParameter('user', $this->security->getUser()) ; + if (!empty($pattern)) { + $qb->andWhere($qb->expr()->like('LOWER(UNACCENT(t.title))', 'LOWER(UNACCENT(:pattern))')) + ->setParameter('pattern', $pattern) + ; + } + + if (count($flags) > 0) { + $orX = $qb->expr()->orX(); + $now = new \DateTime(); + + if (\in_array('no-alert', $flags)) { + $orX + ->add( + $qb->expr()->orX( + $qb->expr()->isNull('t.endDate'), + $qb->expr()->gte('t.endDate - COALESCE(t.warningInterval, :intervalBlank)', ':now') + ) + ); + $qb + ->setParameter('intervalBlank', new \DateInterval('P0D')) + ->setParameter('now', $now) + ; + } + + if (\in_array('warning', $flags)) { + $orX + ->add( + $qb->expr()->andX( + $qb->expr()->eq('t.closed', "'FALSE'"), + $qb->expr()->not($qb->expr()->isNull('t.endDate')), + $qb->expr()->not($qb->expr()->isNull('t.warningInterval')), + $qb->expr()->lte('t.endDate - t.warningInterval', ':now') + ) + ) + ; + $qb + ->setParameter('now', $now) + ; + } + + if (\in_array('alert', $flags)) { + $orX + ->add( + $qb->expr()->andX( + $qb->expr()->eq('t.closed', "'FALSE'"), + $qb->expr()->not($qb->expr()->isNull('t.endDate')), + $qb->expr()->lte('t.endDate', ':now') + ) + ) + ; + $qb + ->setParameter('now', $now) + ; + } + + $qb->andWhere($orX); + } + + + return $qb; } diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig index 87de25ccd..5b06fabed 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig @@ -7,6 +7,8 @@

                      {{ block('title') }}

                      + {{ filter_order|chill_render_filter_order_helper }} + {% if tasks|length == 0 %}

                      {{ 'Any tasks'|trans }}

                      {% else %} diff --git a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml index 76c2c0674..0b9ef6251 100644 --- a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml @@ -97,6 +97,10 @@ Are you sure you want to start this task ?: Êtes-vous sûrs de vouloir démarre "%number% tasks over deadline": "{0} Aucune tâche dépassée|{1} Une tâche dépassée | ]1,Inf[ %count% tâches dépassées" "%number% tasks near deadline": "{0} Aucune tâche en rappel|{1} Une tâche en rappel | ]1,Inf[ %count% tâches en rappel" +Tasks near deadline: Tâches à échéance proche +Tasks over deadline: Tâches à échéance dépassée +Tasks without alert: Tâches sans alerte + #title My tasks near deadline: Mes tâches à échéance proche My tasks over deadline: Mes tâches à échéance dépassée From b4b661882a6fbbb4362b56a290341dd2c80b73f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 27 Oct 2021 13:40:55 +0200 Subject: [PATCH 38/79] remove dump --- .../ChillMainBundle/Templating/Listing/FilterOrderHelper.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index b91dddb9e..ffd601b91 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -68,11 +68,9 @@ class FilterOrderHelper ]; foreach ($this->checkboxes as $name => $c) { - $r['c_'.$name] = $this->getCheckbox($name); + $r[$name] = $this->getCheckbox($name); } - dump($r); - return $r; } From 97dbc4bc168a5b8da6dd8f6672da3019b9e542ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 28 Oct 2021 00:11:05 +0200 Subject: [PATCH 39/79] add more filtering possibilities with order helper --- .../Form/Type/Listing/FilterOrderType.php | 11 +- .../views/FilterOrder/base.html.twig | 31 ++++-- .../Templating/Listing/FilterOrderHelper.php | 45 +++++--- .../Controller/SingleTaskController.php | 15 ++- .../SingleTaskAclAwareRepository.php | 102 ++++++++++-------- .../translations/messages.fr.yml | 2 +- 6 files changed, 127 insertions(+), 79 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php b/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php index 3bbd6b9ae..564b08596 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php @@ -32,6 +32,7 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType ]); } + $checkboxesBuilder = $builder->create('checkboxes', null, [ 'compound' => true ]); foreach ($helper->getCheckboxes() as $name => $c) { $choices = \array_combine( @@ -42,17 +43,21 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType $c['choices'] ); - $builder->add('c_'.$name, ChoiceType::class, [ + $checkboxesBuilder->add($name, ChoiceType::class, [ 'choices' => $choices, 'expanded' => true, 'multiple' => true, ]); } + if (0 < count($helper->getCheckboxes())) { + $builder->add($checkboxesBuilder); + } + foreach ($this->requestStack->getCurrentRequest()->query->getIterator() as $key => $value) { switch($key) { case 'q': - case 'c_'.$key: + case 'checkboxes'.$key: break; case 'page': $builder->add($key, HiddenType::class, [ @@ -75,7 +80,7 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType $view->vars['has_search_box'] = $helper->hasSearchBox(); $view->vars['checkboxes'] = []; foreach ($helper->getCheckboxes() as $name => $c) { - $view->vars['checkboxes']['c_'.$name] = []; + $view->vars['checkboxes'][$name] = []; } } diff --git a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig index 8358d0e9b..3457c883a 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/FilterOrder/base.html.twig @@ -10,17 +10,30 @@
                      {% endif %} - {% for checkbox_name, options in form.vars.checkboxes %} -
                      -
                      - {{ form_widget(form[checkbox_name]) }} + {% if form.checkboxes|length > 0 %} + {% for checkbox_name, options in form.checkboxes %} +
                      +
                      + {% for c in form['checkboxes'][checkbox_name].children %} +
                      + {{ form_widget(c) }} + {{ form_label(c) }} +
                      + {% endfor %} +
                      {% if loop.last %} -
                      - -
                      +
                      +
                      +
                        +
                      • + +
                      • +
                      +
                      +
                      {% endif %} -
                      - {% endfor %} + {% endfor %} + {% endif %}
                      {{ form_end(form) }} diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index ffd601b91..0a28d39ca 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -13,6 +13,11 @@ class FilterOrderHelper private RequestStack $requestStack; private ?array $searchBoxFields = null; private array $checkboxes = []; + private ?array $submitted = null; + private ?string $formName = 'filter'; + private string $formType = FilterOrderType::class; + private array $formOptions = []; + public function __construct( FormFactoryInterface $formFactory, @@ -45,10 +50,9 @@ class FilterOrderHelper return $this; } - public function getCheckbox(string $name): array + public function getCheckboxData(string $name): array { - return $this->requestStack->getCurrentRequest() - ->query->get('c_'.$name, $this->checkboxes[$name]['default']); + return $this->getFormData()['checkboxes'][$name]; } public function getCheckboxes(): array @@ -63,12 +67,23 @@ class FilterOrderHelper private function getFormData(): array { - $r = [ - 'q' => $this->getQueryString(), - ]; + if (NULL === $this->submitted) { + $this->submitted = $this->buildForm() + ->getData(); + } + return $this->submitted; + } + + private function getDefaultData(): array + { + $r = []; + + if ($this->hasSearchBox()) { + $r['q'] = ''; + } foreach ($this->checkboxes as $name => $c) { - $r[$name] = $this->getCheckbox($name); + $r['checkboxes'][$name] = $c['default']; } return $r; @@ -76,21 +91,17 @@ class FilterOrderHelper public function getQueryString(): ?string { - $q = $this->requestStack->getCurrentRequest() - ->query->get('q', null); - - return empty($q) ? NULL : $q; + return $this->getFormData()['q']; } - public function buildForm($name = null, string $type = FilterOrderType::class, array $options = []): FormInterface + public function buildForm(): FormInterface { - $form = $this->formFactory - ->createNamed($name, $type, $this->getFormData(), \array_merge([ + return $this->formFactory + ->createNamed($this->formName, $this->formType, $this->getDefaultData(), \array_merge([ 'helper' => $this, 'method' => 'GET', 'csrf_protection' => false, - ], $options)); - - return $form; + ], $this->formOptions)) + ->handleRequest($this->requestStack->getCurrentRequest()); } } diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index 90dbe4744..359dc8909 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -532,14 +532,18 @@ final class SingleTaskController extends AbstractController $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(), - $filterOrder->getCheckbox('status') + $flags ); $paginator = $this->paginatorFactory->create($nb); $tasks = $this->singleTaskAclAwareRepository->findByCurrentUsersTasks( $filterOrder->getQueryString(), - $filterOrder->getCheckbox('status'), + $flags, $paginator->getCurrentPageFirstItemNumber(), $paginator->getItemsPerPage() ); @@ -555,14 +559,19 @@ final class SingleTaskController extends AbstractController { $statuses = ['no-alert', 'warning', 'alert']; $statusTrans = [ + 'Tasks without alert', 'Tasks near deadline', 'Tasks over deadline', - 'Tasks without alert', + ]; + $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() ; } diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index b034bf973..2e5b8d803 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -63,64 +63,74 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository if (!empty($pattern)) { $qb->andWhere($qb->expr()->like('LOWER(UNACCENT(t.title))', 'LOWER(UNACCENT(:pattern))')) - ->setParameter('pattern', $pattern) + ->setParameter('pattern', '%'.$pattern.'%') ; } if (count($flags) > 0) { - $orX = $qb->expr()->orX(); + $orXDate = $qb->expr()->orX(); + $orXState = $qb->expr()->orX(); $now = new \DateTime(); - if (\in_array('no-alert', $flags)) { - $orX - ->add( - $qb->expr()->orX( - $qb->expr()->isNull('t.endDate'), - $qb->expr()->gte('t.endDate - COALESCE(t.warningInterval, :intervalBlank)', ':now') - ) - ); - $qb - ->setParameter('intervalBlank', new \DateInterval('P0D')) - ->setParameter('now', $now) - ; + foreach ($flags as $key => $flag) { + switch ($flag) { + case 'no-alert': + $orXDate + ->add( + $qb->expr()->orX( + $qb->expr()->isNull('t.endDate'), + $qb->expr()->gte('t.endDate - COALESCE(t.warningInterval, :intervalBlank)', ':now') + ) + ); + $qb + ->setParameter('intervalBlank', new \DateInterval('P0D')) + ->setParameter('now', $now); + break; + case 'warning': + $orXDate + ->add( + $qb->expr()->andX( + $qb->expr()->not($qb->expr()->isNull('t.endDate')), + $qb->expr()->not($qb->expr()->isNull('t.warningInterval')), + $qb->expr()->gte('t.endDate - t.warningInterval', ':now'), + $qb->expr()->lt('t.endDate', ':now') + ) + ); + $qb + ->setParameter('now', $now); + break; + case 'alert': + $orXDate + ->add( + $qb->expr()->andX( + $qb->expr()->not($qb->expr()->isNull('t.endDate')), + $qb->expr()->lte('t.endDate', ':now') + ) + ); + $qb + ->setParameter('now', $now); + break; + case \substr($flag, 0, 6) === 'state_': + $state = \substr($flag, 6); + $orXState + ->add( + "JSONB_EXISTS_IN_ARRAY(t.currentStates, :state_$key) = 'TRUE'" + ); + $qb->setParameter("state_$key", $state); + break; + default: + throw new \LogicException("this flag is not supported: $flag"); + } } - if (\in_array('warning', $flags)) { - $orX - ->add( - $qb->expr()->andX( - $qb->expr()->eq('t.closed', "'FALSE'"), - $qb->expr()->not($qb->expr()->isNull('t.endDate')), - $qb->expr()->not($qb->expr()->isNull('t.warningInterval')), - $qb->expr()->lte('t.endDate - t.warningInterval', ':now') - ) - ) - ; - $qb - ->setParameter('now', $now) - ; + if ($orXDate->count() > 0) { + $qb->andWhere($orXDate); } - - if (\in_array('alert', $flags)) { - $orX - ->add( - $qb->expr()->andX( - $qb->expr()->eq('t.closed', "'FALSE'"), - $qb->expr()->not($qb->expr()->isNull('t.endDate')), - $qb->expr()->lte('t.endDate', ':now') - ) - ) - ; - $qb - ->setParameter('now', $now) - ; + if ($orXState->count() > 0) { + $qb->andWhere($orXState); } - - $qb->andWhere($orX); } - - return $qb; } diff --git a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml index 0b9ef6251..5633c0219 100644 --- a/src/Bundle/ChillTaskBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillTaskBundle/translations/messages.fr.yml @@ -99,7 +99,7 @@ Are you sure you want to start this task ?: Êtes-vous sûrs de vouloir démarre Tasks near deadline: Tâches à échéance proche Tasks over deadline: Tâches à échéance dépassée -Tasks without alert: Tâches sans alerte +Tasks without alert: Tâches à échéance future ou sans échéance #title My tasks near deadline: Mes tâches à échéance proche From e63d645f8ab977f8a3a17551cbe0fe0564d6aa77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 28 Oct 2021 00:50:18 +0200 Subject: [PATCH 40/79] fix return path in tasks --- .../Controller/SingleTaskController.php | 162 ++++-------------- .../ChillTaskBundle/Entity/AbstractTask.php | 5 - .../ChillTaskBundle/Form/SingleTaskType.php | 5 +- .../ChillTaskBundle/Menu/MenuBuilder.php | 28 +-- .../confirm_delete.html.twig | 6 +- .../views/SingleTask/List/index.html.twig | 14 +- .../Resources/views/SingleTask/_new.html.twig | 2 +- .../views/SingleTask/_show.html.twig | 21 +-- 8 files changed, 75 insertions(+), 168 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index 359dc8909..b11601589 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -160,13 +160,17 @@ final class SingleTaskController extends AbstractController $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_list', [ 'person_id' => $task->getPerson()->getId() ]); } elseif ($entityType === 'course') { - return $this->redirectToRoute('chill_task_singletask_courselist', [ - 'course_id' => $task->getCourse()->getId() + return $this->redirectToRoute('chill_task_singletask_by-course_list', [ + 'id' => $task->getCourse()->getId() ]); } } else { @@ -179,13 +183,13 @@ final class SingleTaskController extends AbstractController return $this->render('@ChillTask/SingleTask/Person/new.html.twig', array( 'form' => $form->createView(), 'task' => $task, - 'person' => $person, + 'person' => $task->getPerson(), )); case 'course': return $this->render('@ChillTask/SingleTask/AccompanyingCourse/new.html.twig', array( 'form' => $form->createView(), 'task' => $task, - 'accompanyingCourse' => $course, + 'accompanyingCourse' => $task->getCourse(), )); default: throw new \LogicException("entity context not supported"); @@ -198,68 +202,23 @@ final class SingleTaskController extends AbstractController * name="chill_task_single_task_show" * ) */ - public function showAction($id, Request $request) + public function showAction(SingleTask $task, Request $request) { + $this->denyAccessUnlessGranted(TaskVoter::SHOW, $task); - $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"); - } - + if ($person = $task->getContext() instanceof Person) { $event = new PrivacyEvent($person, array( - 'element_class' => SingleTask::class, - 'element_id' => $task->getId(), - 'action' => 'show' + 'element_class' => SingleTask::class, + 'element_id' => $task->getId(), + 'action' => 'show' )); $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - } - if ($task->getCourse() !== null) - { - $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 course id"); - } - } - - - $this->denyAccessUnlessGranted(TaskVoter::SHOW, $task, 'You are not ' - . 'allowed to view this task'); - $timeline = $this->timelineBuilder ->getTimelineHTML('task', array('task' => $task)); - - if($task->getContext() instanceof Person){ + if ($task->getContext() instanceof Person) { return $this->render('@ChillTask/SingleTask/Person/show.html.twig', array( 'task' => $task, 'timeline' => $timeline @@ -281,52 +240,16 @@ final class SingleTaskController extends AbstractController * ) */ public function editAction( - $id, + SingleTask $task, Request $request ) { - $em = $this->getDoctrine()->getManager(); - $task = $em->getRepository(SingleTask::class)->find($id); - - if ($task->getContext() instanceof Person) { - $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"); - } - } - $this->denyAccessUnlessGranted(TaskVoter::UPDATE, $task, 'You are not ' . 'allowed to edit this task'); - if (!$task) { - throw $this->createNotFoundException('Unable to find Task entity.'); - } - $event = (new UIEvent('single-task', $task)) ->setForm($this->setCreateForm($task, new Role(TaskVoter::UPDATE))) ; - $this->eventDispatcher->dispatch(UIEvent::EDIT_FORM, $event); $form = $event->getForm(); @@ -343,7 +266,7 @@ final class SingleTaskController extends AbstractController $this->addFlash('success', $this->translator ->trans("The task has been updated")); - if($task->getContext() instanceof Person){ + if ($person = $task->getContext() instanceof Person) { $event = new PrivacyEvent($person, array( 'element_class' => SingleTask::class, 'element_id' => $task->getId(), @@ -351,14 +274,20 @@ final class SingleTaskController extends AbstractController )); $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', - $request->query->get('list_params', []) ); } else { + if ($request->query->has('returnPath')) { + return $this->redirect($request->query->get('returnPath')); + } + return $this->redirectToRoute( - 'chill_task_singletask_courselist', - $request->query->get('list_params', []) + 'chill_task_singletask_by-course_list', ['id' => $task->getCourse()->getId()] ); } } else { @@ -372,7 +301,7 @@ final class SingleTaskController extends AbstractController return $event->getResponse(); } - if($task->getContext() instanceof Person){ + if ($person = $task->getContext() instanceof Person) { $event = new PrivacyEvent($person, array( 'element_class' => SingleTask::class, 'element_id' => $task->getId(), @@ -388,7 +317,7 @@ final class SingleTaskController extends AbstractController return $this->render('@ChillTask/SingleTask/AccompanyingCourse/edit.html.twig', array( 'task' => $task, 'form' => $form->createView(), - 'accompanyingCourse' => $course + 'accompanyingCourse' => $task->getCourse() )); } @@ -478,10 +407,9 @@ final class SingleTaskController extends AbstractController ]))); } else { return $this->redirect($this->generateUrl( - 'chill_task_singletask_courselist', - $request->query->get('list_params', [ - 'course_id' => $course->getId() - ]))); + 'chill_task_singletask_by-course_list', + ['id' => $course->getId()] + )); } } } @@ -510,7 +438,6 @@ final class SingleTaskController extends AbstractController protected function setCreateForm(SingleTask $task, Role $role) { $form = $this->createForm(SingleTaskType::class, $task, [ - 'center' => $this->centerResolverDispatcher->resolveCenter($task), 'role' => $role, ]); @@ -817,39 +744,24 @@ final class SingleTaskController extends AbstractController /** * @Route( - * "/{_locale}/task/single-task/courselist", - * name="chill_task_singletask_courselist") + * "/{_locale}/task/single-task/by-course/{id}", + * name="chill_task_singletask_by-course_list") */ public function listCourseTasks( - AccompanyingPeriodRepository $courseRepository, + AccompanyingPeriod $course, SingleTaskRepository $taskRepository, FormFactoryInterface $formFactory, Request $request ): Response { - if (!empty($request->query->get('course_id', NULL))) { - - $courseId = $request->query->getInt('course_id', 0); - $course = $courseRepository->find($courseId); - - if ($course === null) { - throw $this->createNotFoundException("This accompanying course ' $courseId ' does not exist."); - } - - } - $em = $this->getDoctrine()->getManager(); - if($course === NULL) { - throw $this->createNotFoundException('Accompanying course not found'); - } - $tasks = $taskRepository - ->findBy( - array('course' => $course) - ); + ->findBy( + array('course' => $course) + ); $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ 'accompanyingCourse' => $course, diff --git a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php index b3f0c6a9f..d34a0b05f 100644 --- a/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php +++ b/src/Bundle/ChillTaskBundle/Entity/AbstractTask.php @@ -253,11 +253,6 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface public function getContext() { - // if ($this->getCourse() instanceof AccompanyingPeriod){ - // return $this->getCourse(); - // } else { - // return $this->getPerson(); - // } return $this->getPerson() ?? $this->getCourse(); } diff --git a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php index 7fbc84800..a3dd1777f 100644 --- a/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php +++ b/src/Bundle/ChillTaskBundle/Form/SingleTaskType.php @@ -19,6 +19,7 @@ namespace Chill\TaskBundle\Form; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher; +use Chill\TaskBundle\Security\Authorization\TaskVoter; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -60,7 +61,7 @@ class SingleTaskType extends AbstractType ->add('assignee', UserPickerType::class, [ 'required' => false, 'center' => $center, - 'role' => $options['role'], + 'role' => TaskVoter::SHOW, 'placeholder' => 'Not assigned' ]) ->add('startDate', ChillDateType::class, [ @@ -87,8 +88,6 @@ class SingleTaskType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver - ->setRequired('center') - ->setAllowedTypes('center', [ Center::class, 'array', 'null' ]) ->setRequired('role') ->setAllowedTypes('role', [ Role::class, 'string' ]) ; diff --git a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php index 89f511d2a..53751de91 100644 --- a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php +++ b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php @@ -24,7 +24,7 @@ use Chill\TaskBundle\Security\Authorization\TaskVoter; use Symfony\Component\Translation\TranslatorInterface; /** - * + * * * @author Julien Fastré */ @@ -35,13 +35,13 @@ class MenuBuilder implements LocalMenuBuilderInterface * @var TranslatorInterface */ protected $translator; - + /** * * @var AuthorizationCheckerInterface */ protected $authorizationChecker; - + public function __construct( AuthorizationCheckerInterface $authorizationChecker, TranslatorInterface $translator) @@ -50,7 +50,7 @@ class MenuBuilder implements LocalMenuBuilderInterface $this->authorizationChecker = $authorizationChecker; } - + public function buildMenu($menuId, MenuItem $menu, array $parameters) { switch($menuId) { @@ -69,15 +69,15 @@ class MenuBuilder implements LocalMenuBuilderInterface } public function buildPersonMenu($menu, $parameters){ - + //var $person \Chill\PersonBundle\Entity\Person */ $person = $parameters['person'] ?? null; - + if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $person)) { $menu->addChild( $this->translator->trans('Tasks'), [ 'route' => 'chill_task_singletask_list', - 'routeParameters' => + 'routeParameters' => [ 'person_id' => $person->getId() ] ]) ->setExtra('order', 400); @@ -85,18 +85,18 @@ class MenuBuilder implements LocalMenuBuilderInterface } public function buildAccompanyingCourseMenu($menu, $parameters){ - + $course = $parameters['accompanyingCourse']; - - // if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { + + if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) { $menu->addChild( $this->translator->trans('Tasks'), [ - 'route' => 'chill_task_singletask_courselist', - 'routeParameters' => - [ 'course_id' => $course->getId() ] + 'route' => 'chill_task_singletask_by-course_list', + 'routeParameters' => + [ 'id' => $course->getId() ] ]) ->setExtra('order', 400); - // } + } } diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig index 01b7aa499..02a0aa31d 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/confirm_delete.html.twig @@ -3,7 +3,7 @@ {% set activeRouteKey = 'chill_task_task_list' %} {% set course = task.course %} -{% block title 'Remove task'|trans %} +{% block title 'Remove task'|trans %} {% block content %} @@ -11,8 +11,8 @@ { 'title' : 'Remove task'|trans, 'confirm_question' : 'Are you sure you want to remove the task "%title%" ?'|trans({ '%title%' : task.title } ), - 'cancel_route' : 'chill_task_singletask_courselist', - 'cancel_parameters' : app.request.query.get('list_params', { } ), + 'cancel_route' : 'chill_task_singletask_by-course_list', + 'cancel_parameters' : {'id' : task.course.id }, 'form' : delete_form, } ) }} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig index 5b06fabed..c7c622692 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig @@ -120,20 +120,20 @@ {% endif %}
                    • - +
                    • - {# {% if is_granted('CHILL_TASK_TASK_UPDATE', task) %} #} + {% if is_granted('CHILL_TASK_TASK_UPDATE', task) %}
                    • - +
                    • - {# {% endif %} #} + {% endif %} - {# {% if is_granted('CHILL_TASK_TASK_DELETE', task) %} #} + {% if is_granted('CHILL_TASK_TASK_DELETE', task) %}
                    • - +
                    • - {# {% endif %} #} + {% endif %}
                    diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig index 37f8b431c..9d324ea85 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_new.html.twig @@ -18,7 +18,7 @@
                    • - + {{'Cancel'|trans}}
                    • diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig index 1186fb125..26d269191 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/_show.html.twig @@ -70,8 +70,8 @@ From 4da32dc5cac7becd4be69cef239b7e0ba59a392d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 28 Oct 2021 01:05:36 +0200 Subject: [PATCH 41/79] fix filtering of new tasks --- .../ChillMainExtension.php | 2 ++ .../Doctrine/DQL/JsonbArrayLength.php | 35 +++++++++++++++++++ .../SingleTaskAclAwareRepository.php | 6 ++++ 3 files changed, 43 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbArrayLength.php diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index c90638893..7b2f0a3a1 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -23,6 +23,7 @@ use Chill\MainBundle\Controller\AddressApiController; use Chill\MainBundle\Controller\LocationController; use Chill\MainBundle\Controller\LocationTypeController; use Chill\MainBundle\Controller\UserController; +use Chill\MainBundle\Doctrine\DQL\JsonbArrayLength; use Chill\MainBundle\Doctrine\DQL\STContains; use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS; use Chill\MainBundle\Entity\User; @@ -203,6 +204,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, 'OVERLAPSI' => OverlapsI::class, 'STRICT_WORD_SIMILARITY_OPS' => StrictWordSimilarityOPS::class, 'ST_CONTAINS' => STContains::class, + 'JSONB_ARRAY_LENGTH' => JsonbArrayLength::class, ], ], 'hydrators' => [ diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbArrayLength.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbArrayLength.php new file mode 100644 index 000000000..39049748c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbArrayLength.php @@ -0,0 +1,35 @@ +expr1->dispatch($sqlWalker), + ); + } + + public function parse(Parser $parser): void + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $this->expr1 = $parser->StringPrimary(); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index 2e5b8d803..bc5124d95 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -110,6 +110,12 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository $qb ->setParameter('now', $now); break; + case 'state_new': + $orXState + ->add( + "JSONB_ARRAY_LENGTH(t.currentStates) = 0" + ); + break; case \substr($flag, 0, 6) === 'state_': $state = \substr($flag, 6); $orXState From 4017f8db4809443cafd1d6a0fedb399f34e84499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 28 Oct 2021 14:41:19 +0200 Subject: [PATCH 42/79] refactor list by course: use acl aware repository --- .../Controller/SingleTaskController.php | 52 ++++--- .../SingleTaskAclAwareRepository.php | 108 +++++++++++++-- .../SingleTaskAclAwareRepositoryInterface.php | 18 +++ .../AccompanyingCourse/list.html.twig | 130 +++++------------- .../views/SingleTask/List/index.html.twig | 125 +---------------- .../SingleTask/List/index_item.html.twig | 125 +++++++++++++++++ 6 files changed, 304 insertions(+), 254 deletions(-) create mode 100644 src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index_item.html.twig diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index b11601589..90ec16ec3 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -472,7 +472,11 @@ final class SingleTaskController extends AbstractController $filterOrder->getQueryString(), $flags, $paginator->getCurrentPageFirstItemNumber(), - $paginator->getItemsPerPage() + $paginator->getItemsPerPage(), + [ + 'startDate' => 'DESC', + 'endDate' => 'DESC', + ] ); return $this->render('@ChillTask/SingleTask/List/index.html.twig', [ @@ -694,6 +698,7 @@ final class SingleTaskController extends AbstractController } + /* protected function getPersonParam(EntityManagerInterface $em) { $person = $em->getRepository(Person::class) @@ -722,6 +727,7 @@ final class SingleTaskController extends AbstractController return $user; } + */ /** * Creates a form to delete a Task entity by id. @@ -750,34 +756,40 @@ final class SingleTaskController extends AbstractController public function listCourseTasks( AccompanyingPeriod $course, - SingleTaskRepository $taskRepository, FormFactoryInterface $formFactory, Request $request ): Response { - - $em = $this->getDoctrine()->getManager(); - - $tasks = $taskRepository - ->findBy( - array('course' => $course) - ); - - $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ - 'accompanyingCourse' => $course, - 'method' => Request::METHOD_GET, - 'csrf_protection' => false, - 'add_type' => true - ]); + $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); + $tasks = $this->singleTaskAclAwareRepository->findByCourse( + $course, + $filterOrder->getQueryString(), + $flags, + $paginator->getCurrentPageFirstItemNumber(), + $paginator->getItemsPerPage(), + [ + 'startDate' => 'DESC', + 'endDate' => 'DESC', + ] + ); return $this->render( - '@ChillTask/SingleTask/index.html.twig', + '@ChillTask/SingleTask/AccompanyingCourse/list.html.twig', [ 'tasks' => $tasks, 'accompanyingCourse' => $course, - 'layout' => '@ChillPerson/AccompanyingCourse/layout.html.twig', - 'form' => $form->createView(), - 'title' => $this->translator->trans('Tasks for this accompanying period') + 'paginator' => $paginator, + 'filter_order' => $filterOrder ]); } diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index bc5124d95..cd3f819f4 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -2,20 +2,32 @@ namespace Chill\TaskBundle\Repository; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; +use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\TaskBundle\Entity\SingleTask; +use Chill\TaskBundle\Security\Authorization\TaskVoter; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Security\Core\Security; final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepositoryInterface { + private AuthorizationHelperInterface $authorizationHelper; private EntityManagerInterface $em; private Security $security; + private CenterResolverDispatcher $centerResolverDispatcher; - public function __construct(EntityManagerInterface $em, Security $security) - { + public function __construct( + CenterResolverDispatcher $centerResolverDispatcher, + EntityManagerInterface $em, + Security $security, + AuthorizationHelperInterface $authorizationHelper + ) { + $this->centerResolverDispatcher = $centerResolverDispatcher; $this->em = $em; $this->security = $security; + $this->authorizationHelper = $authorizationHelper; } public function findByCurrentUsersTasks( @@ -26,12 +38,83 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository ?array $orderBy = [] ): array { $qb = $this->buildQueryMyTasks($pattern, $flags); + + return $this->getResult($qb, $start, $limit, $orderBy); + } + + public function countByCurrentUsersTasks( + ?string $pattern = null, + ?array $flags = [] + ): int { + return $this->buildQueryMyTasks($pattern, $flags) + ->select('COUNT(t)') + ->getQuery()->getSingleScalarResult(); + } + + public function findByCourse( + AccompanyingPeriod $course, + ?string $pattern = null, + ?array $flags = [], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [] + ): array { + $qb = $this->buildQueryByCourse($course, $pattern, $flags); + $qb = $this->addACL($qb, $course); + + return $this->getResult($qb, $start, $limit, $orderBy); + } + + public function countByCourse( + AccompanyingPeriod $course, + ?string $pattern = null, + ?array $flags = [] + ): int { + $qb = $this->buildQueryByCourse($course, $pattern, $flags); + + return $this + ->addACL($qb, $course) + ->select('COUNT(t)') + ->getQuery()->getSingleScalarResult(); + } + + public function buildQueryByCourse( + AccompanyingPeriod $course, + ?string $pattern = null, + ?array $flags = [] + ) : QueryBuilder { + $qb = $this->buildBaseQuery($pattern, $flags); + + return $qb + ->andWhere($qb->expr()->eq('t.course', ':course')) + ->setParameter('course', $course) + ; + } + + public function buildQueryMyTasks( + ?string $pattern = null, + ?array $flags = [] + ): QueryBuilder { + $qb = $this->buildBaseQuery($pattern, $flags); + + return $qb + ->andWhere($qb->expr()->eq('t.assignee', ':user')) + ->setParameter('user', $this->security->getUser()) + ; + } + + public function getResult( + QueryBuilder $qb, + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [] + ): array { $qb->select('t'); $qb ->setFirstResult($start) ->setMaxResults($limit) - ; + ; foreach ($orderBy as $field => $direction) { $qb->addOrderBy('t.'.$field, $direction); @@ -40,25 +123,24 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository return $qb->getQuery()->getResult(); } - public function countByCurrentUsersTasks( - ?string $pattern = null, - ?array $flags = [] - ): int { - $qb = $this->buildQueryMyTasks($pattern, $flags); - $qb->select('COUNT(t)'); + public function addACL( + QueryBuilder $qb, + $entity + ): QueryBuilder { + $scopes = $this->authorizationHelper->getReachableScopes($this->security->getUser(), + TaskVoter::SHOW, $this->centerResolverDispatcher->resolveCenter($entity)); - return $qb->getQuery()->getSingleScalarResult(); + return $qb->andWhere($qb->expr()->in('t.circle', ':scopes')) + ->setParameter('scopes', $scopes); } - public function buildQueryMyTasks( + public function buildBaseQuery ( ?string $pattern = null, ?array $flags = [] ): QueryBuilder { $qb = $this->em->createQueryBuilder(); $qb ->from(SingleTask::class, 't') - ->where($qb->expr()->eq('t.assignee', ':user')) - ->setParameter('user', $this->security->getUser()) ; if (!empty($pattern)) { diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php index b5d83a5f0..55c5f07ff 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php @@ -2,9 +2,27 @@ namespace Chill\TaskBundle\Repository; +use Chill\PersonBundle\Entity\AccompanyingPeriod; + interface SingleTaskAclAwareRepositoryInterface { public function findByCurrentUsersTasks(?string $pattern = null, ?array $flags = [], ?int $start = 0, ?int $limit = 50, ?array $orderBy = []): array; public function countByCurrentUsersTasks(?string $pattern = null, ?array $flags = []): int; + + public function findByCourse( + AccompanyingPeriod $course, + ?string $pattern = null, + ?array $flags = [], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [] + ): array; + + public function countByCourse( + AccompanyingPeriod $course, + ?string $pattern = null, + ?array $flags = [] + ): int; + } diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig index be491e2f9..480ea705f 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig @@ -1,106 +1,42 @@ -{% if tasks|length > 0 %} -

                      {{ title|trans }}

                      +{% extends '@ChillPerson/AccompanyingCourse/layout.html.twig' %} - - - {% for task in tasks %} - - - - - {% endfor %} - -
                      -
                      - {{ task.title }} -
                      +{% block title 'Tasks for this accompanying period'|trans %} -
                      - {{ task_workflow_metadata(task, 'definition.name')|trans }} -
                      +{% block content %} +
                      -
                      - {% for place in workflow_marked_places(task) %} - {{ place|trans }} - {% endfor %} - {% if task.assignee is not null %} -
                      - {{ 'By'|trans }} : - {{ task.assignee.username }}
                      - {% endif %} -
                      +

                      {{ block('title') }}

                      - {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} -
                      -
                        - {% if task.startDate is not null %} -
                      • - - {{ task.startDate|format_date('medium') }} -
                      • - {% endif %} - {% if task.warningDate is not null %} -
                      • - - {{ task.warningDate|format_date('medium') }} -
                      • - {% endif %} - {% if task.endDate is not null %} -
                      • - - {{ task.endDate|format_date('medium') }} -
                      • - {% endif %} -
                      -
                      - {% endif %} + {{ filter_order|chill_render_filter_order_helper }} -
                      -
                        - {% if workflow_transitions(task)|length > 0 %} -
                      • -
                        - - -
                        -
                      • - {% endif %} + {% if tasks|length == 0 %} +

                        {{ 'Any tasks'|trans }}

                        + {% else %} +
                        + {% for task in tasks %} + {% include 'ChillTaskBundle:SingleTask/List:index_item.html.twig' with { 'showContext' : false } %} + {% endfor %} +
                        + {% endif %} -
                      • - -
                      • + {{ chill_pagination(paginator) }} - {# {% if is_granted('CHILL_TASK_TASK_UPDATE', task) %} #} -
                      • - -
                      • - {# {% endif %} #} + - {# {% if is_granted('CHILL_TASK_TASK_DELETE', task) %} #} -
                      • - -
                      • - {# {% endif %} #} -
                      -
                      -{% endif %} + +{% endblock %} - +{% block css %} + {{ encore_entry_link_tags('page_task_list') }} +{% endblock %} +{% block js %} + {{ encore_entry_script_tags('page_task_list') }} +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig index c7c622692..14bac167e 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index.html.twig @@ -14,130 +14,7 @@ {% else %}
                      {% for task in tasks %} -
                      -
                      -
                      -
                      - {{ task.title }} - {% for place in workflow_marked_places(task) %} - - {{ place|trans }} - - {% endfor %} -
                      - {% if task.type != 'task_default'%} - - {{ task_workflow_metadata(task, 'definition.name')|trans }} - - {% endif %} -
                      - {% if task.person is not null %} - - {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { - targetEntity: { name: 'person', id: task.person.id }, - action: 'show', - displayBadge: true, - buttonText: task.person|chill_entity_render_string - } %} - - {% elseif task.course is not null %} - - - - - {% for part in task.course.currentParticipations %} - {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { - targetEntity: { name: 'person', id: part.person.id }, - action: 'show', - displayBadge: true, - buttonText: part.person|chill_entity_render_string - } %} - {% endfor %} - - {% endif %} -
                      -
                      - - -
                      -
                      - {% if task.assignee is not null %} -
                      - {{ 'By'|trans }} : - {{ task.assignee.username }} -
                      - {% endif %} - - {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} -
                      -
                        - {% if task.startDate is not null %} -
                      • - - {{ task.startDate|format_date('medium') }} -
                      • - {% endif %} - {% if task.warningDate is not null %} -
                      • - - {{ task.warningDate|format_date('medium') }} -
                      • - {% endif %} - {% if task.endDate is not null %} -
                      • - - {{ task.endDate|format_date('medium') }} -
                      • - {% endif %} -
                      -
                      - {% endif %} -
                      - -
                      - -
                      -
                      - - -
                      -
                      + {% include 'ChillTaskBundle:SingleTask/List:index_item.html.twig' with { 'showContext' : true} %} {% endfor %}
                      {% endif %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index_item.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index_item.html.twig new file mode 100644 index 000000000..284769448 --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/List/index_item.html.twig @@ -0,0 +1,125 @@ +
                      +
                      +
                      +
                      + {{ task.title }} + {% for place in workflow_marked_places(task) %} + + {{ place|trans }} + + {% endfor %} +
                      + {% if task.type != 'task_default'%} + + {{ task_workflow_metadata(task, 'definition.name')|trans }} + + {% endif %} + {% if showContext %} +
                      + {% if task.person is not null %} + + {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { + targetEntity: { name: 'person', id: task.person.id }, + action: 'show', + displayBadge: true, + buttonText: task.person|chill_entity_render_string + } %} + + {% elseif task.course is not null %} + + + + + {% for part in task.course.currentParticipations %} + {% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with { + targetEntity: { name: 'person', id: part.person.id }, + action: 'show', + displayBadge: true, + buttonText: part.person|chill_entity_render_string + } %} + {% endfor %} + + {% endif %} +
                      + {% endif %} +
                      + + +
                      +
                      + {% if task.assignee is not null %} +
                      + {{ task.assignee|chill_entity_render_box }} +
                      + {% endif %} + + {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} +
                      +
                        + {% if task.startDate is not null %} +
                      • + + {{ task.startDate|format_date('medium') }} +
                      • + {% endif %} + {% if task.warningDate is not null %} +
                      • + + {{ task.warningDate|format_date('medium') }} +
                      • + {% endif %} + {% if task.endDate is not null %} +
                      • + + {{ task.endDate|format_date('medium') }} +
                      • + {% endif %} +
                      +
                      + {% endif %} +
                      + +
                      + +
                      +
                      + + +
                      +
                      From db15a3d53c3143bc159132f88b5fccba882b256b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 29 Oct 2021 16:26:19 +0200 Subject: [PATCH 43/79] adapta list of tasks for a person --- .../Menu/UserMenuBuilder.php | 27 +- .../Controller/SingleTaskController.php | 95 ++++-- .../DataFixtures/ORM/LoadTaskACL.php | 27 +- .../ChillTaskExtension.php | 9 +- .../ChillTaskBundle/Menu/MenuBuilder.php | 4 +- .../SingleTaskAclAwareRepository.php | 43 ++- .../SingleTaskAclAwareRepositoryInterface.php | 15 + .../AccompanyingCourse/list.html.twig | 20 +- .../views/SingleTask/Person/list.html.twig | 275 +++--------------- .../Security/Authorization/TaskVoter.php | 53 +--- 10 files changed, 224 insertions(+), 344 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Menu/UserMenuBuilder.php b/src/Bundle/ChillCalendarBundle/Menu/UserMenuBuilder.php index 14e775234..7eb161a50 100644 --- a/src/Bundle/ChillCalendarBundle/Menu/UserMenuBuilder.php +++ b/src/Bundle/ChillCalendarBundle/Menu/UserMenuBuilder.php @@ -17,6 +17,7 @@ */ namespace Chill\CalendarBundle\Menu; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Knp\Menu\MenuItem; use Chill\TaskBundle\Templating\UI\CountNotificationTask; @@ -72,17 +73,19 @@ class UserMenuBuilder implements LocalMenuBuilderInterface { $user = $this->tokenStorage->getToken()->getUser(); - if ($this->authorizationChecker->isGranted('ROLE_USER')){ - $menu->addChild("My calendar list", [ - 'route' => 'chill_calendar_calendar_list', - 'routeParameters' => [ - 'user_id' => $user->getId(), - ] - ]) - ->setExtras([ - 'order' => 9, - 'icon' => 'tasks' - ]); + if ($this->authorizationChecker->isGranted('ROLE_USER') + && $user instanceof User + ) { + $menu->addChild("My calendar list", [ + 'route' => 'chill_calendar_calendar_list', + 'routeParameters' => [ + 'user_id' => $user->getId(), + ] + ]) + ->setExtras([ + 'order' => 9, + 'icon' => 'tasks' + ]); } } @@ -90,5 +93,5 @@ class UserMenuBuilder implements LocalMenuBuilderInterface { return [ 'user' ]; } - + } diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index 90ec16ec3..fe262318e 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -165,8 +165,8 @@ final class SingleTaskController extends AbstractController } if ($entityType === 'person') { - return $this->redirectToRoute('chill_task_singletask_list', [ - 'person_id' => $task->getPerson()->getId() + 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', [ @@ -399,17 +399,16 @@ final class SingleTaskController extends AbstractController $this->addFlash('success', $this->translator ->trans("The task has been successfully removed.")); - if($task->getContext() instanceof Person){ + if ($task->getContext() instanceof Person) { return $this->redirect($this->generateUrl( - 'chill_task_singletask_list', - $request->query->get('list_params', [ - 'person_id' => $person->getId() - ]))); + 'chill_task_singletask_by-person_list', + [ 'id' => $task->getPerson()->getId() ] + )); } else { return $this->redirect($this->generateUrl( 'chill_task_singletask_by-course_list', - ['id' => $course->getId()] - )); + ['id' => $task->getCourse()->getId()] + )); } } } @@ -753,13 +752,14 @@ final class SingleTaskController extends AbstractController * "/{_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'), @@ -771,17 +771,22 @@ final class SingleTaskController extends AbstractController $flags ); $paginator = $this->paginatorFactory->create($nb); - $tasks = $this->singleTaskAclAwareRepository->findByCourse( - $course, - $filterOrder->getQueryString(), - $flags, - $paginator->getCurrentPageFirstItemNumber(), - $paginator->getItemsPerPage(), - [ - 'startDate' => 'DESC', - 'endDate' => 'DESC', - ] - ); + + 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', @@ -793,4 +798,52 @@ final class SingleTaskController extends AbstractController ]); } + /** + * @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 + ]); + } } diff --git a/src/Bundle/ChillTaskBundle/DataFixtures/ORM/LoadTaskACL.php b/src/Bundle/ChillTaskBundle/DataFixtures/ORM/LoadTaskACL.php index 5af11f6a2..513d18ea8 100644 --- a/src/Bundle/ChillTaskBundle/DataFixtures/ORM/LoadTaskACL.php +++ b/src/Bundle/ChillTaskBundle/DataFixtures/ORM/LoadTaskACL.php @@ -40,7 +40,7 @@ class LoadTaskACL extends AbstractFixture implements OrderedFixtureInterface return 16000; } - + public function load(ObjectManager $manager) { foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) { @@ -58,33 +58,38 @@ class LoadTaskACL extends AbstractFixture implements OrderedFixtureInterface case 'direction': if (in_array($scope->getName()['en'], array('administrative', 'social'))) { break 2; // we do not want any power on social or administrative - } + } break; } - + printf("Adding CHILL_TASK_TASK_UPDATE & CHILL_TASK_TASK_CREATE & Chill_TASK_TASK_DELETE permissions to %s " - . "permission group, scope '%s' \n", + . "permission group, scope '%s' \n", $permissionsGroup->getName(), $scope->getName()['en']); $roleScopeUpdate = (new RoleScope()) ->setRole(TaskVoter::UPDATE) ->setScope($scope); $permissionsGroup->addRoleScope($roleScopeUpdate); - $roleScopeCreate = (new RoleScope()) - ->setRole(TaskVoter::CREATE) + $roleScopeCreateP = (new RoleScope()) + ->setRole(TaskVoter::CREATE_PERSON) ->setScope($scope); - $permissionsGroup->addRoleScope($roleScopeCreate); + $permissionsGroup->addRoleScope($roleScopeCreateP); + $roleScopeCreateC = (new RoleScope()) + ->setRole(TaskVoter::CREATE_COURSE) + ->setScope($scope); + $permissionsGroup->addRoleScope($roleScopeCreateC); $roleScopeDelete = (new RoleScope()) ->setRole(TaskVoter::DELETE) ->setScope($scope); $permissionsGroup->addRoleScope($roleScopeDelete); - + $manager->persist($roleScopeUpdate); - $manager->persist($roleScopeCreate); + $manager->persist($roleScopeCreateP); + $manager->persist($roleScopeCreateC); $manager->persist($roleScopeDelete); } - + } - + $manager->flush(); } diff --git a/src/Bundle/ChillTaskBundle/DependencyInjection/ChillTaskExtension.php b/src/Bundle/ChillTaskBundle/DependencyInjection/ChillTaskExtension.php index 5ffbd38e7..ed2a36595 100644 --- a/src/Bundle/ChillTaskBundle/DependencyInjection/ChillTaskExtension.php +++ b/src/Bundle/ChillTaskBundle/DependencyInjection/ChillTaskExtension.php @@ -44,7 +44,7 @@ class ChillTaskExtension extends Extension implements PrependExtensionInterface $this->prependRoute($container); $this->prependWorkflows($container); } - + protected function prependRoute(ContainerBuilder $container) { //declare routes for task bundle @@ -56,17 +56,18 @@ class ChillTaskExtension extends Extension implements PrependExtensionInterface ) )); } - + protected function prependAuthorization(ContainerBuilder $container) { $container->prependExtensionConfig('security', array( 'role_hierarchy' => array( TaskVoter::UPDATE => [TaskVoter::SHOW], - TaskVoter::CREATE => [TaskVoter::SHOW] + TaskVoter::CREATE_COURSE => [TaskVoter::SHOW], + TaskVoter::CREATE_PERSON => [TaskVoter::SHOW], ) )); } - + protected function prependWorkflows(ContainerBuilder $container) { $container->prependExtensionConfig('framework', [ diff --git a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php index 53751de91..dfc95535b 100644 --- a/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php +++ b/src/Bundle/ChillTaskBundle/Menu/MenuBuilder.php @@ -76,9 +76,9 @@ class MenuBuilder implements LocalMenuBuilderInterface if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $person)) { $menu->addChild( $this->translator->trans('Tasks'), [ - 'route' => 'chill_task_singletask_list', + 'route' => 'chill_task_singletask_by-person_list', 'routeParameters' => - [ 'person_id' => $person->getId() ] + [ 'id' => $person->getId() ] ]) ->setExtra('order', 400); } diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index cd3f819f4..c97ea3549 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -5,6 +5,7 @@ namespace Chill\TaskBundle\Repository; use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\Person; use Chill\TaskBundle\Entity\SingleTask; use Chill\TaskBundle\Security\Authorization\TaskVoter; use Doctrine\ORM\EntityManagerInterface; @@ -78,6 +79,33 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository ->getQuery()->getSingleScalarResult(); } + public function findByPerson( + Person $person, + ?string $pattern = null, + ?array $flags = [], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [] + ): array { + $qb = $this->buildQueryByPerson($person, $pattern, $flags); + $qb = $this->addACL($qb, $person); + + return $this->getResult($qb, $start, $limit, $orderBy); + } + + public function countByPerson( + Person $person, + ?string $pattern = null, + ?array $flags = [] + ): int { + $qb = $this->buildQueryByPerson($person, $pattern, $flags); + + return $this + ->addACL($qb, $person) + ->select('COUNT(t)') + ->getQuery()->getSingleScalarResult(); + } + public function buildQueryByCourse( AccompanyingPeriod $course, ?string $pattern = null, @@ -91,6 +119,19 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository ; } + public function buildQueryByPerson( + Person $person, + ?string $pattern = null, + ?array $flags = [] + ): QueryBuilder + { + $qb = $this->buildBaseQuery($pattern, $flags); + + return $qb + ->andWhere($qb->expr()->eq('t.person', ':person')) + ->setParameter('person', $person); + } + public function buildQueryMyTasks( ?string $pattern = null, ?array $flags = [] @@ -123,7 +164,7 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository return $qb->getQuery()->getResult(); } - public function addACL( + private function addACL( QueryBuilder $qb, $entity ): QueryBuilder { diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php index 55c5f07ff..0c6f4fc57 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php @@ -3,6 +3,7 @@ namespace Chill\TaskBundle\Repository; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\Person; interface SingleTaskAclAwareRepositoryInterface { @@ -25,4 +26,18 @@ interface SingleTaskAclAwareRepositoryInterface ?array $flags = [] ): int; + public function findByPerson( + Person $person, + ?string $pattern = null, + ?array $flags = [], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [] + ): array; + + public function countByPerson( + Person $person, + ?string $pattern = null, + ?array $flags = [] + ): int; } diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig index 480ea705f..6622bca4f 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/AccompanyingCourse/list.html.twig @@ -21,15 +21,17 @@ {{ chill_pagination(paginator) }} - + {% if is_granted('CHILL_TASK_TASK_CREATE_FOR_COURSE', person) %} + + {% endif %} {% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig index ac08cf1b0..ee236b644 100644 --- a/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig +++ b/src/Bundle/ChillTaskBundle/Resources/views/SingleTask/Person/list.html.twig @@ -1,251 +1,44 @@ -{% macro date_status(title, tasks, count, paginator, status, isSingleStatus, person, user) %} - {% if tasks|length > 0 %} -

                      {{ title|trans }}

                      +{% extends '@ChillPerson/Person/layout.html.twig' %} - - - {% for task in tasks %} - - - - - {% endfor %} - -
                      -
                      - {{ task.title }} -
                      +{% set activeRouteKey = '' %} - {% if person is null %} -
                      - {{ 'For person'|trans }} : - - {{ task.person}} - -
                      - {% endif %} +{% block title 'Tasks for {{ name }}'|trans({ '{{ name }}' : person|chill_entity_render_string }) %} -
                      - {{ task_workflow_metadata(task, 'definition.name')|trans }} -
                      +{% block personcontent %} +
                      -
                      - {% for place in workflow_marked_places(task) %} - {{ place|trans }} - {% endfor %} - {% if task.assignee is not null %} -
                      - {{ 'By'|trans }} : - {{ task.assignee.username }}
                      - {% endif %} -
                      +

                      {{ block('title') }}

                      - {% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %} -
                      -
                        - {% if task.startDate is not null %} -
                      • - - {{ task.startDate|format_date('medium') }} -
                      • - {% endif %} - {% if task.warningDate is not null %} -
                      • - - {{ task.warningDate|format_date('medium') }} -
                      • - {% endif %} - {% if task.endDate is not null %} -
                      • - - {{ task.endDate|format_date('medium') }} -
                      • - {% endif %} -
                      -
                      - {% endif %} + {{ filter_order|chill_render_filter_order_helper }} -
                      -
                        - {% if workflow_transitions(task)|length > 0 %} -
                      • -
                        - - -
                        -
                      • - {% endif %} + {% if tasks|length == 0 %} +

                        {{ 'Any tasks'|trans }}

                        + {% else %} +
                        + {% for task in tasks %} + {% include 'ChillTaskBundle:SingleTask/List:index_item.html.twig' with { 'showContext' : false } %} + {% endfor %} +
                        + {% endif %} -
                      • - -
                      • + {{ chill_pagination(paginator) }} - {% if is_granted('CHILL_TASK_TASK_UPDATE', task) %} -
                      • - -
                      • - {% endif %} + {% if is_granted('CHILL_TASK_TASK_CREATE_FOR_PERSON', person) %} + + {% endif %} - {% if is_granted('CHILL_TASK_TASK_DELETE', task) %} -
                      • - -
                      • - {% endif %} -
                      -
                      + +{% endblock %} - {% if isSingleStatus %} - {% if tasks|length < paginator.getTotalItems %} - {{ chill_pagination(paginator) }} - {% endif %} - - - - {% else %} - - {% endif %} - - {% endif %} -{% endmacro %} - -{% import _self as helper %} - -

                      {{ 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 }}

                      - {{ form_start(form) }} - {{ form_row(form.user_id) }} - - {% if form.status is defined %} - {{ 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 %} - - {% if form.center_id is defined %} - {{ form_row(form.center_id) }} - {% endif %} - -
                        -
                      • - -
                      • -
                      - - {{ form_end(form)}} -{% endif %} - -{% if tasks_count == 0 %} -

                      {{ "There is no tasks."|trans }}

                      - {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} - - {% endif %} -{% else %} - - {% if false == app.request.query.boolean('hide_form', false) %} -

                      {{ 'Tasks'|trans }}

                      - {% endif %} - - {# TODO reimplement right to create task. #} - {# {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} #} - - {# {% endif %} #} - - {% if single_task_ended_tasks is defined %} - {{ helper.date_status('Tasks with expired deadline', single_task_ended_tasks, single_task_ended_count, single_task_ended_paginator, 'ended', isSingleStatus, person) }} - {% endif %} - - {% if single_task_warning_tasks is defined %} - {{ helper.date_status('Tasks with warning deadline reached', single_task_warning_tasks, single_task_warning_count, single_task_warning_paginator, 'warning', isSingleStatus, person) }} - {% endif %} - - {% if single_task_current_tasks is defined %} - {{ helper.date_status('Current tasks', single_task_current_tasks, single_task_current_count, single_task_current_paginator, 'current', isSingleStatus, person) }} - {% endif %} - - {% if single_task_not_started_tasks is defined %} - {{ helper.date_status('Tasks not started', single_task_not_started_tasks, single_task_not_started_count, single_task_not_started_paginator, 'not_started', isSingleStatus, person) }} - {% endif %} - - {% if single_task_closed_tasks is defined %} - {{ helper.date_status('Closed tasks', single_task_closed_tasks, single_task_closed_count, single_task_closed_paginator, 'closed', isSingleStatus, person) }} - {% endif %} - - {% if isSingleStatus == false %} - - {% endif %} - -{% endif %} +{% block css %} + {{ encore_entry_link_tags('page_task_list') }} +{% endblock %} +{% block js %} + {{ encore_entry_script_tags('page_task_list') }} +{% endblock %} diff --git a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php index ca77171cb..39d9eef32 100644 --- a/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php +++ b/src/Bundle/ChillTaskBundle/Security/Authorization/TaskVoter.php @@ -41,16 +41,18 @@ use Chill\TaskBundle\Security\Authorization\AuthorizationEvent; final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface { - const CREATE = 'CHILL_TASK_TASK_CREATE'; - const UPDATE = 'CHILL_TASK_TASK_UPDATE'; - const SHOW = 'CHILL_TASK_TASK_SHOW'; + const CREATE_COURSE = 'CHILL_TASK_TASK_CREATE_FOR_COURSE'; + const CREATE_PERSON = 'CHILL_TASK_TASK_CREATE_FOR_PERSON'; const DELETE = 'CHILL_TASK_TASK_DELETE'; + const SHOW = 'CHILL_TASK_TASK_SHOW'; + const UPDATE = 'CHILL_TASK_TASK_UPDATE'; const ROLES = [ - self::CREATE, - self::UPDATE, + self::CREATE_COURSE, + self::CREATE_PERSON, + self::DELETE, self::SHOW, - self::DELETE + self::UPDATE, ]; protected AuthorizationHelper $authorizationHelper; @@ -84,8 +86,8 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy $this->voter = $voterFactory ->generate(AbstractTask::class) ->addCheckFor(AbstractTask::class, self::ROLES) - ->addCheckFor(Person::class, [self::SHOW, self::CREATE]) - ->addCheckFor(AccompanyingPeriod::class, [self::SHOW, self::CREATE]) + ->addCheckFor(Person::class, [self::SHOW, self::CREATE_PERSON]) + ->addCheckFor(AccompanyingPeriod::class, [self::SHOW, self::CREATE_COURSE]) ->addCheckFor(null, [self::SHOW]) ->build() ; @@ -147,41 +149,6 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy // do regular check. return $this->voter->voteOnAttribute($attribute, $subject, $token); - - - if ($subject instanceof AbstractTask) { - $associated = $subject->getPerson() ?? $subject->getCourse(); - if ($associated === null) { - throw new \LogicException("You should associate a person with task " - . "in order to check autorizations"); - } - $person = $subject->getPerson(); - } elseif ($subject instanceof Person) { - // subject is null. We check that at least one center is reachable - $centers = $this->authorizationHelper->getReachableCenters($token->getUser(), new Role($attribute)); - - return count($centers) > 0; - } - - if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) { - return false; - } - $center = $this->centerResolverDispatcher->resolveCenter($subject); - - if (NULL === $center) { - return false; - } elseif ($associated instanceof AccompanyingPeriod && !$this->accessDecisionManager->decide($token, [AccompanyingPeriodVoter::SEE], $associated)) { - return false; - } elseif ($associated instanceof AccompanyingPeriod && !$this->accessDecisionManager->decide($token, [AccompanyingPeriodVoter::SEE], $associated)) { - return false; - } - - return $this->authorizationHelper->userHasAccess( - $token->getUser(), - $subject, - $attribute - ); - } public function getRoles() From 35a27de216d0a2765ce1357cf49983ddab78b1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 30 Oct 2021 00:25:25 +0200 Subject: [PATCH 44/79] update jsonb exists method by using question mark escaping --- .../Doctrine/DQL/JsonbExistsInArray.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbExistsInArray.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbExistsInArray.php index 6c4eaa5c3..94e85048c 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbExistsInArray.php +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/JsonbExistsInArray.php @@ -1,7 +1,7 @@ + @author Julien Fastré */ class JsonbExistsInArray extends FunctionNode { private $expr1; private $expr2; - + public function getSql(SqlWalker $sqlWalker): string { return sprintf( - 'jsonb_exists(%s, %s)', + '%s ?? %s', $this->expr1->dispatch($sqlWalker), $sqlWalker->walkInputParameter($this->expr2) ); From c63b1015e1bbda6dd6bec17a6462034433ffafde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 30 Oct 2021 00:38:59 +0200 Subject: [PATCH 45/79] add indexes for searching task by end date --- .../migrations/Version20211029213909.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/Bundle/ChillTaskBundle/migrations/Version20211029213909.php diff --git a/src/Bundle/ChillTaskBundle/migrations/Version20211029213909.php b/src/Bundle/ChillTaskBundle/migrations/Version20211029213909.php new file mode 100644 index 000000000..1ccf52bcb --- /dev/null +++ b/src/Bundle/ChillTaskBundle/migrations/Version20211029213909.php @@ -0,0 +1,26 @@ +addSql('CREATE INDEX by_end_date ON chill_task.single_task (end_date DESC NULLS FIRST)'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP INDEX chill_task.by_end_date'); + } +} From e0cb5a88bdd43762d50b193626340f1fff6ad6b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 30 Oct 2021 00:41:28 +0200 Subject: [PATCH 46/79] rewrite task list action --- .../Controller/SingleTaskController.php | 240 +++--------------- .../ChillTaskBundle/Entity/SingleTask.php | 4 + .../SingleTaskAclAwareRepository.php | 69 +++++ .../SingleTaskAclAwareRepositoryInterface.php | 13 + 4 files changed, 123 insertions(+), 203 deletions(-) diff --git a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php index fe262318e..141ca049d 100644 --- a/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php +++ b/src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php @@ -126,6 +126,7 @@ final class SingleTaskController extends AbstractController } $task->setPerson($person); + $role = TaskVoter::CREATE_PERSON; break; case 'course': $course = $this->getDoctrine()->getManager() @@ -137,15 +138,16 @@ final class SingleTaskController extends AbstractController } $task->setCourse($course); + $role = TaskVoter::CREATE_COURSE; break; default: return new BadRequestHttpException("context with {$entityType} is not supported"); } - $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not ' + $this->denyAccessUnlessGranted($role, $task, 'You are not ' . 'allowed to create this task'); - $form = $this->setCreateForm($task, new Role(TaskVoter::CREATE)); + $form = $this->setCreateForm($task, new Role($role)); $form->handleRequest($request); @@ -266,8 +268,8 @@ final class SingleTaskController extends AbstractController $this->addFlash('success', $this->translator ->trans("The task has been updated")); - if ($person = $task->getContext() instanceof Person) { - $event = new PrivacyEvent($person, array( + if ($task->getContext() instanceof Person) { + $event = new PrivacyEvent($task->getPerson(), array( 'element_class' => SingleTask::class, 'element_id' => $task->getId(), 'action' => 'update' @@ -301,8 +303,8 @@ final class SingleTaskController extends AbstractController return $event->getResponse(); } - if ($person = $task->getContext() instanceof Person) { - $event = new PrivacyEvent($person, array( + if ($task->getContext() instanceof Person) { + $event = new PrivacyEvent($task->getPerson(), array( 'element_class' => SingleTask::class, 'element_id' => $task->getId(), 'action' => 'edit' @@ -511,7 +513,7 @@ final class SingleTaskController extends AbstractController * Arguments: * - user_id * - scope_id - * - course_id + * - s * - person_id * - hide_form (hide the form to filter the tasks) * - status: date state, amongst SingleTaskRepository::DATE_STATUSES, or 'closed' @@ -522,212 +524,44 @@ final class SingleTaskController extends AbstractController * ) */ public function listAction( - PaginatorFactory $paginatorFactory, - SingleTaskRepository $taskRepository, - PersonRepository $personRepository, - AccompanyingPeriodRepository $courseRepository, - CenterRepository $centerRepository, - FormFactoryInterface $formFactory, Request $request ) { - /* @var $viewParams array The parameters for the view */ - /* @var $params array The parameters for the query */ + $this->denyAccessUnlessGranted(TaskVoter::SHOW, null); - $viewParams['person'] = null; - $params['person'] = null; - $viewParams['user'] = null; - $params['user'] = null; - $viewParams['center'] = null; - $params['types'] = null; - $viewParams['accompanyingCourse'] = null; - $params['accompanyingCourse'] = 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 (!empty($request->query->get('person_id', NULL))) { - - $personId = $request->query->getInt('person_id', 0); - $person = $personRepository->find($personId); - - if ($person === null) { - throw $this->createNotFoundException("This person ' $personId ' does not exist."); - } - $this->denyAccessUnlessGranted(PersonVoter::SEE, $person); - - $viewParams['person'] = $person; - $params['person'] = $person; - } - - if (!empty($request->query->get('course_id', NULL))) { - - $courseId = $request->query->getInt('course_id', 0); - $course = $courseRepository->find($courseId); - - if ($course === null) { - throw $this->createNotFoundException("This accompanying course ' $courseId ' does not exist."); - } - $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $course); - - $viewParams['accompanyingCourse'] = $course; - $params['accompanyingCourse'] = $course; - } - - if (!empty($request->query->get('center_id', NULL))) { - $center = $centerRepository->find($request->query->getInt('center_id')); - if ($center === null) { - throw $this->createNotFoundException('center not found'); - } - $params['center'] = $center; - } - - 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') { - $params['unassigned'] = true; - } else { - - $userId = $request->query->getInt('user_id', 0); - $user = $this->getDoctrine()->getManager() - ->getRepository(User::class) - ->find($userId); - - if ($user === null) { - throw $this->createNotFoundException("This user ' $userId ' does not exist."); - } - - $viewParams['user'] = $user; - $params['user'] = $user; - } - } - - if (!empty($request->query->get('scope_id'))) { - - $scopeId = $request->query->getInt('scope_id', 0); - - $scope = $this->getDoctrine()->getManager() - ->getRepository(Scope::class) - ->find($scopeId); - - if ($scope === null) { - throw $this->createNotFoundException("This scope' $scopeId 'does not exist."); - } - - $viewParams['scope'] = $scope; - $params['scope'] = $scope; - } - - $possibleStatuses = \array_merge(SingleTaskRepository::DATE_STATUSES, [ 'closed' ]); - $statuses = $request->query->get('status', $possibleStatuses); - - $diff = \array_diff($statuses, $possibleStatuses); - if (count($diff) > 0) { - return new Response( - 'date_status not allowed: '. \implode(', ', $diff), - Response::HTTP_BAD_REQUEST - ); - } - - $viewParams['isSingleStatus'] = $singleStatus = count($statuses) === 1; - - $tasks_count = 0; - - foreach($statuses as $status) { - if($request->query->has('status') - && FALSE === \in_array($status, $statuses)) { - continue; - } - - if (in_array($status, SingleTaskRepository::DATE_STATUSES)) { - $params['date_status'] = $status; - $params['is_closed'] = false; - } else { - $params['date_status'] = null; - $params['is_closed'] = true; - } - - $count = $taskRepository - ->countByParameters($params, $this->getUser()) - ; - $paginator = $paginatorFactory->create($count); - - $viewParams['single_task_'.$status.'_count'] = $count; - $viewParams['single_task_'.$status.'_paginator'] = $paginator; - $viewParams['single_task_'.$status.'_tasks'] = $taskRepository - ->findByParameters($params, $this->getUser(), - $singleStatus ? $paginator->getCurrentPage()->getFirstItemNumber() : 0, - $singleStatus ? $paginator->getItemsPerPage() : 10) - ; - - $tasks_count = $tasks_count + $count; - } - - $viewParams['tasks_count'] = $tasks_count; - - if ($viewParams['person'] !== null){ - $viewParams['layout'] = '@ChillPerson/Person/layout.html.twig'; - } else if ($viewParams['accompanyingCourse'] !== null){ - $viewParams['layout'] = '@ChillPerson/AccompanyingCourse/layout.html.twig'; + if (0 < $nb) { + $tasks = $this->singleTaskAclAwareRepository->findByAllViewable( + $filterOrder->getQueryString(), + $flags, + $paginator->getCurrentPageFirstItemNumber(), + $paginator->getItemsPerPage(), + [ + 'startDate' => 'DESC', + 'endDate' => 'DESC', + ] + ); } else { - $viewParams['layout'] = '@ChillMain/layout.html.twig'; + $tasks = []; } - $form = $formFactory->createNamed(null, SingleTaskListType::class, null, [ - 'person' => $viewParams['person'], - 'method' => Request::METHOD_GET, - 'csrf_protection' => false, - 'add_type' => true - ]); + return $this->render('@ChillTask/SingleTask/List/index.html.twig', [ + 'tasks' => $tasks, + 'paginator' => $paginator, + 'filter_order' => $filterOrder + ]); - $form->handleRequest($request); - - if (isset($person)) { - $event = new PrivacyEvent($person, array( - 'element_class' => SingleTask::class, - 'action' => 'list' - )); - $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); - } - - return $this->render('@ChillTask/SingleTask/index.html.twig', - array_merge($viewParams, [ 'form' => $form->createView() ])); } - - /* - protected function getPersonParam(EntityManagerInterface $em) - { - $person = $em->getRepository(Person::class) - ->find($request->query->getInt('person_id')) - ; - - if (NULL === $person) { - throw $this->createNotFoundException('person not found'); - } - - $this->denyAccessUnlessGranted(PersonVoter::SEE, $person, "You are " - . "not allowed to see this person"); - - return $person; - } - - protected function getUserParam(EntityManagerInterface $em) - { - $user = $em->getRepository(User::class) - ->find($request->query->getInt('user_id')) - ; - - if (NULL === $user) { - throw $this->createNotFoundException('user not found'); - } - - return $user; - } - */ - /** * Creates a form to delete a Task entity by id. * diff --git a/src/Bundle/ChillTaskBundle/Entity/SingleTask.php b/src/Bundle/ChillTaskBundle/Entity/SingleTask.php index c22f47361..cef59a10a 100644 --- a/src/Bundle/ChillTaskBundle/Entity/SingleTask.php +++ b/src/Bundle/ChillTaskBundle/Entity/SingleTask.php @@ -19,6 +19,10 @@ use Doctrine\Common\Collections\Collection; * @ORM\Index( * name="by_current_state", * columns={ "current_states" } + * ), + * @ORM\Index( + * name="by_end_date", + * columns={ "end_date" } * ) * } * ) diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index c97ea3549..c728096cb 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -106,6 +106,31 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository ->getQuery()->getSingleScalarResult(); } + public function countByAllViewable( + ?string $pattern = null, + ?array $flags = [] + ): int { + $qb = $this->buildBaseQuery($pattern, $flags); + + return $this + ->addACLGlobal($qb) + ->select('COUNT(t)') + ->getQuery()->getSingleScalarResult(); + } + + public function findByAllViewable( + ?string $pattern = null, + ?array $flags = [], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [] + ): array { + $qb = $this->buildBaseQuery($pattern, $flags); + $qb = $this->addACLGlobal($qb); + + return $this->getResult($qb, $start, $limit, $orderBy); + } + public function buildQueryByCourse( AccompanyingPeriod $course, ?string $pattern = null, @@ -175,6 +200,50 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository ->setParameter('scopes', $scopes); } + private function addACLGlobal( + QueryBuilder $qb + ): QueryBuilder { + $allowedCenters = $this->authorizationHelper + ->getReachableCenters($this->security->getUser(), TaskVoter::SHOW); + + if ([] === $allowedCenters) { + $qb + ->andWhere($qb->expr()->lt('t.id', ':falseid')) + ->setParameter('falseid', -1); + } + + $qb->leftJoin('t.person', 'person') + ->leftJoin('t.course', 'course') + ->leftJoin('course.participations', 'participation') + ->leftJoin('participation.person', 'person_p') + ; + $qb->distinct(true); + + $k = 0; + $orX = $qb->expr()->orX(); + foreach ($allowedCenters as $center) { + $allowedScopes = $this->authorizationHelper->getReachableScopes($this->security->getUser(), + TaskVoter::SHOW, $center); + + $and = $qb->expr()->andX( + $qb->expr()->orX( + $qb->expr()->eq('person.center', ':center_'.$k), + $qb->expr()->eq('person_p.center', ':center_'.$k) + ), + $qb->expr()->in('t.circle', ':scopes_'.$k) + ); + $qb + ->setParameter('center_'.$k, $center) + ->setParameter('scopes_'.$k, $allowedScopes); + $orX->add($and); + + $k++; + } + $qb->andWhere($orX); + + return $qb; + } + public function buildBaseQuery ( ?string $pattern = null, ?array $flags = [] diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php index 0c6f4fc57..58532daa0 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepositoryInterface.php @@ -40,4 +40,17 @@ interface SingleTaskAclAwareRepositoryInterface ?string $pattern = null, ?array $flags = [] ): int; + + public function countByAllViewable( + ?string $pattern = null, + ?array $flags = [] + ): int; + + public function findByAllViewable( + ?string $pattern = null, + ?array $flags = [], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [] + ): array; } From 1646740382e76dfb7e7372e38ef69f3a6fe9ebb4 Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 3 Nov 2021 09:59:40 +0100 Subject: [PATCH 47/79] location: filter location endpoint by NULL names --- .../ChillMainBundle/Controller/LocationApiController.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/LocationApiController.php b/src/Bundle/ChillMainBundle/Controller/LocationApiController.php index 5c0301959..c21eaaf26 100644 --- a/src/Bundle/ChillMainBundle/Controller/LocationApiController.php +++ b/src/Bundle/ChillMainBundle/Controller/LocationApiController.php @@ -22,12 +22,15 @@ class LocationApiController extends ApiController ), $query->expr()->andX( $query->expr()->eq('e.availableForUsers', "'TRUE'"), - $query->expr()->eq('e.active', "'TRUE'") + $query->expr()->eq('e.active', "'TRUE'"), + $query->expr()->isNotNull('e.name'), + $query->expr()->neq('e.name', ':emptyString'), ) )) ->setParameters([ 'user' => $this->getUser(), - 'dateBefore' => (new \DateTime())->sub(new \DateInterval('P6M')) + 'dateBefore' => (new \DateTime())->sub(new \DateInterval('P6M')), + 'emptyString' => '', ]); } -} +} \ No newline at end of file From 999658e79290b6c9f54daeb96d225632a4c83cf9 Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 3 Nov 2021 10:27:58 +0100 Subject: [PATCH 48/79] location: filter location-type by active and availableForUsers --- .../Controller/LocationTypeApiController.php | 25 +++++++++++++++++++ .../ChillMainExtension.php | 1 + 2 files changed, 26 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/Controller/LocationTypeApiController.php diff --git a/src/Bundle/ChillMainBundle/Controller/LocationTypeApiController.php b/src/Bundle/ChillMainBundle/Controller/LocationTypeApiController.php new file mode 100644 index 000000000..2a3a6cc59 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/LocationTypeApiController.php @@ -0,0 +1,25 @@ +andWhere( + $query->expr()->andX( + $query->expr()->eq('e.availableForUsers', "'TRUE'"), + $query->expr()->eq('e.active', "'TRUE'"), + ) + ); + } +} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 972c046d9..71b642b43 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -520,6 +520,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, ], [ 'class' => \Chill\MainBundle\Entity\LocationType::class, + 'controller' => \Chill\MainBundle\Controller\LocationTypeApiController::class, 'name' => 'location_type', 'base_path' => '/api/1.0/main/location-type', 'base_role' => 'ROLE_USER', From 6d248ab47bf24c2b8eb7ebe6254505b6a9271329 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 3 Nov 2021 10:55:51 +0100 Subject: [PATCH 49/79] Activity link added to admin homepage --- .../Menu/AdminMenuBuilder.php | 48 +++++++++++++++++++ .../translations/messages.fr.yml | 5 +- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillActivityBundle/Menu/AdminMenuBuilder.php diff --git a/src/Bundle/ChillActivityBundle/Menu/AdminMenuBuilder.php b/src/Bundle/ChillActivityBundle/Menu/AdminMenuBuilder.php new file mode 100644 index 000000000..ce7618b6d --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Menu/AdminMenuBuilder.php @@ -0,0 +1,48 @@ +security = $security; + } + + public static function getMenuIds(): array + { + return ['admin_index', 'admin_section', 'admin_activity']; + } + + public function buildMenu($menuId, MenuItem $menu, array $parameters) + { + if (!$this->security->isGranted('ROLE_ADMIN')) { + return; + } + + if (in_array($menuId, ['admin_index', 'admin_section'])) { + $menu->addChild('Activities', [ + 'route' => 'chill_admin_activity_index' + ]) + ->setExtras([ + 'order' => 2000, + 'explain' => "Activity configuration" + ]); + } else { + $menu + ->addChild('Activities', [ + 'route' => 'chill_admin_activity_index' + ]) + ->setExtras([ + 'order' => '60' + ]); + } + } +} \ No newline at end of file diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 55e55d370..d0c3ddc6d 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -99,10 +99,13 @@ CHILL_ACTIVITY_LIST: Liste des activités Activities: Activités Activity configuration: Configuration des activités Activity configuration menu: Configuration des activités -Activity Types: Types d'activité +Activity types: Types d'activité +Activity type configuration: Configuration des categories d'activités Activity Reasons: Sujets d'une activité Activity Reasons Category: Catégories de sujet d'activités Activity Types Categories: Catégories des types d'activité +Activity Presences: Presences des activités + # Crud crud: From b0471c485124d2b1e289ef96d407b59eb594fd4a Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 3 Nov 2021 10:56:28 +0100 Subject: [PATCH 50/79] translation fixes for aside activities --- .../src/Menu/AdminMenuBuilder.php | 2 +- .../Resources/views/asideActivityCategory/index.html.twig | 2 +- .../src/translations/messages.fr.yml | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillAsideActivityBundle/src/Menu/AdminMenuBuilder.php b/src/Bundle/ChillAsideActivityBundle/src/Menu/AdminMenuBuilder.php index ecc1a1f90..24fc89162 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Menu/AdminMenuBuilder.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Menu/AdminMenuBuilder.php @@ -32,7 +32,7 @@ final class AdminMenuBuilder implements \Chill\MainBundle\Routing\LocalMenuBuild ]) ->setExtras([ 'order' => 900, - 'explain' => "Configure aside activities categories" + 'explain' => "Aside activity type configuration" ]); } else { $menu diff --git a/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivityCategory/index.html.twig b/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivityCategory/index.html.twig index 2c8373daf..00dc1d3ea 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivityCategory/index.html.twig +++ b/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivityCategory/index.html.twig @@ -1,7 +1,7 @@ {% extends "@ChillAsideActivity/Admin/layout_asideactivity.html.twig" %} {% block admin_content %} -

                      {{ 'Aside Activity Type list'|trans }}

                      +

                      {{ 'Aside Activity Type List'|trans }}

                      diff --git a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml index dac2430bd..6d219626d 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml +++ b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml @@ -3,6 +3,7 @@ Show the aside activity: Voir l'activité annexe Edit the aside activity: Modifier l'activité annexe Remove aside activity: Supprimer l'activité annexe Aside activity: Activité annexe +Aside Activity Type List: Liste des catégories d'activités annexes Duration time: Durée durationTime: durée user_username: nom de l'utilisateur @@ -156,6 +157,11 @@ The activity has been successfully removed.: L'activité a été supprimée. #Menu Create an aside activity: "Créer une activité annexe" +Aside activity categories: Catégories des activités annexes Aside activity configuration menu: "Menu de configuration des activités annexes" -Aside activity configuration: "Configuration des activités annexes" Phonecall: "Appel téléphonique" + +# admin +Aside activities: Activités annexes +Aside activity types: Types d'activités annexes +Aside activity type configuration: Configuration des categories d'activités annexes From c8bc6ac4952febe89fe3be6f7484ffac0d9bb266 Mon Sep 17 00:00:00 2001 From: nobohan Date: Wed, 3 Nov 2021 11:34:52 +0100 Subject: [PATCH 51/79] location: code refactoring: move saveNewLocation + order of fields --- .../vuejs/Activity/components/Location.vue | 27 +-- .../components/Location/NewLocation.vue | 167 +++++++++++------- 2 files changed, 110 insertions(+), 84 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index 0dbf2652a..30a1c5bd9 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -20,7 +20,7 @@ v-model="location"> - + @@ -30,7 +30,7 @@ import { mapState } from "vuex"; import VueMultiselect from 'vue-multiselect'; import NewLocation from './Location/NewLocation.vue'; -import { getLocations, postLocation } from '../api.js'; +import { getLocations } from '../api.js'; export default { name: "Location", @@ -67,29 +67,6 @@ export default { }, customLabel(value) { return `${value.locationType.title.fr} ${value.name}`; - }, - saveNewLocation(selected) { - console.log('saveNewLocation', selected); - let body = { - type: 'location', - name: selected.name, - address: { - id: selected.addressId - }, - locationType: { - id: selected.type, - type: 'location-type' - }, - phonenumber1: selected.phonenumber1, - phonenumber2: selected.phonenumber2, - email: selected.email, - } - postLocation(body).then(location => new Promise(resolve => { - console.log('postLocation', location); - this.locations.push(location); - this.$store.dispatch('updateLocation', location); - resolve(); - })); } } } diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue index 3ffb2a33f..2515e37fc 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue @@ -1,75 +1,86 @@