From bc1a7c1d7bf24b6a368f5313a0e80fb5a372aba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 21 Oct 2022 13:24:02 +0200 Subject: [PATCH] Feature: [calendar] allow to create and generate calendar by person --- .../views/Activity/concernedGroups.html.twig | 8 +- .../Controller/CalendarController.php | 165 +++++++++------- .../ChillCalendarBundle/Entity/Calendar.php | 20 +- .../Menu/PersonMenuBuilder.php | 52 +++++ .../Repository/CalendarACLAwareRepository.php | 80 +++++++- .../CalendarACLAwareRepositoryInterface.php | 18 ++ .../Resources/views/Calendar/_list.html.twig | 177 ++++++++++++++++++ .../Calendar/confirm_deleteByPerson.html.twig | 16 ++ .../views/Calendar/editByPerson.html.twig | 34 ++++ .../listByAccompanyingCourse.html.twig | 165 +--------------- .../views/Calendar/listByPerson.html.twig | 52 +++++ .../Resources/views/Calendar/new.html.twig | 2 +- .../views/Calendar/newByPerson.html.twig | 34 ++++ .../Security/Voter/CalendarVoter.php | 21 +++ .../CalendarACLAwareRepositoryTest.php | 78 ++++++++ 15 files changed, 677 insertions(+), 245 deletions(-) create mode 100644 src/Bundle/ChillCalendarBundle/Menu/PersonMenuBuilder.php create mode 100644 src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig create mode 100644 src/Bundle/ChillCalendarBundle/Resources/views/Calendar/confirm_deleteByPerson.html.twig create mode 100644 src/Bundle/ChillCalendarBundle/Resources/views/Calendar/editByPerson.html.twig create mode 100644 src/Bundle/ChillCalendarBundle/Resources/views/Calendar/listByPerson.html.twig create mode 100644 src/Bundle/ChillCalendarBundle/Resources/views/Calendar/newByPerson.html.twig create mode 100644 src/Bundle/ChillCalendarBundle/Tests/Repository/CalendarACLAwareRepositoryTest.php diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig index 9b64403b1..96cfef39b 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig @@ -29,7 +29,7 @@ {% endmacro %} {% set blocks = [] %} -{% if context == 'calendar_accompanyingCourse' or entity.activityType.personsVisible %} +{% if context == 'calendar_accompanyingCourse' or context == 'calendar_person' or entity.activityType.personsVisible %} {% if context == 'person' %} {% set blocks = blocks|merge([{ 'title': 'Others persons'|trans, @@ -54,7 +54,7 @@ }]) %} {% endif %} {% endif %} -{% if context == 'calendar_accompanyingCourse' or entity.activityType.thirdPartiesVisible %} +{% if context == 'calendar_accompanyingCourse' or context == 'calendar_person' or entity.activityType.thirdPartiesVisible %} {% set blocks = blocks|merge([{ 'title': 'Third parties'|trans, 'items': entity.thirdParties, @@ -63,7 +63,7 @@ 'key' : 'id', }]) %} {% endif %} -{% if context == 'calendar_accompanyingCourse' or entity.activityType.usersVisible %} +{% if context == 'calendar_accompanyingCourse' or context == 'calendar_person' or entity.activityType.usersVisible %} {% set blocks = blocks|merge([{ 'title': 'Users concerned'|trans, 'items': entity.users, @@ -143,7 +143,7 @@ {% if bloc.type == 'user' %} {{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }} - {%- if context == 'calendar_accompanyingCourse' %} + {%- if context == 'calendar_accompanyingCourse' or context == 'calendar_person' %} {% set invite = entity.inviteForUser(item) %} {% if invite is not null %} {{ invite.invite_span(invite) }} diff --git a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php index 487d50e3a..16aea6cca 100644 --- a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php +++ b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php @@ -18,15 +18,19 @@ use Chill\CalendarBundle\Repository\CalendarACLAwareRepositoryInterface; use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Pagination\PaginatorFactory; -use Chill\MainBundle\Repository\UserRepository; use Chill\MainBundle\Templating\Listing\FilterOrderHelper; use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Repository\AccompanyingPeriodRepository; +use Chill\PersonBundle\Repository\PersonRepository; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; +use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\ThirdPartyBundle\Entity\ThirdParty; use DateTimeImmutable; use Exception; use Psr\Log\LoggerInterface; +use RuntimeException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Form; @@ -40,6 +44,8 @@ use Symfony\Component\Serializer\SerializerInterface; class CalendarController extends AbstractController { + private AccompanyingPeriodRepository $accompanyingPeriodRepository; + private CalendarACLAwareRepositoryInterface $calendarACLAwareRepository; private DocGeneratorTemplateRepository $docGeneratorTemplateRepository; @@ -50,12 +56,12 @@ class CalendarController extends AbstractController private PaginatorFactory $paginator; + private PersonRepository $personRepository; + private RemoteCalendarConnectorInterface $remoteCalendarConnector; private SerializerInterface $serializer; - private UserRepository $userRepository; - public function __construct( CalendarACLAwareRepositoryInterface $calendarACLAwareRepository, DocGeneratorTemplateRepository $docGeneratorTemplateRepository, @@ -64,7 +70,8 @@ class CalendarController extends AbstractController PaginatorFactory $paginator, RemoteCalendarConnectorInterface $remoteCalendarConnector, SerializerInterface $serializer, - UserRepository $userRepository + PersonRepository $personRepository, + AccompanyingPeriodRepository $accompanyingPeriodRepository ) { $this->calendarACLAwareRepository = $calendarACLAwareRepository; $this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository; @@ -73,7 +80,8 @@ class CalendarController extends AbstractController $this->paginator = $paginator; $this->remoteCalendarConnector = $remoteCalendarConnector; $this->serializer = $serializer; - $this->userRepository = $userRepository; + $this->personRepository = $personRepository; + $this->accompanyingPeriodRepository = $accompanyingPeriodRepository; } /** @@ -83,19 +91,21 @@ class CalendarController extends AbstractController */ public function deleteAction(Request $request, Calendar $entity) { - $view = null; $em = $this->getDoctrine()->getManager(); - $accompanyingPeriod = $entity->getAccompanyingPeriod(); - $user = null; // TODO legacy code ? remove it ? + [$person, $accompanyingPeriod] = [$entity->getPerson(), $entity->getAccompanyingPeriod()]; if ($accompanyingPeriod instanceof AccompanyingPeriod) { $view = '@ChillCalendar/Calendar/confirm_deleteByAccompanyingCourse.html.twig'; - } elseif ($user instanceof User) { - $view = '@ChillCalendar/Calendar/confirm_deleteByUser.html.twig'; + $redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]); + } elseif ($person instanceof Person) { + $view = '@ChillCalendar/Calendar/confirm_deleteByPerson.html.twig'; + $redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]); + } else { + throw new RuntimeException('nor person or accompanying period'); } - $form = $this->createDeleteForm($entity->getId(), $user, $accompanyingPeriod); + $form = $this->createDeleteForm($entity); if ($request->getMethod() === Request::METHOD_DELETE) { $form->handleRequest($request); @@ -112,20 +122,15 @@ class CalendarController extends AbstractController $this->addFlash('success', $this->get('translator') ->trans('The calendar item has been successfully removed.')); - $params = $this->buildParamsToUrl($user, $accompanyingPeriod); - - return $this->redirectToRoute('chill_calendar_calendar_list_by_period', $params); + return new RedirectResponse($redirectRoute); } } - if (null === $view) { - throw $this->createNotFoundException('Template not found'); - } - return $this->render($view, [ 'calendar' => $entity, 'delete_form' => $form->createView(), 'accompanyingCourse' => $accompanyingPeriod, + 'person' => $person, ]); } @@ -143,13 +148,16 @@ class CalendarController extends AbstractController $view = null; $em = $this->getDoctrine()->getManager(); - [$user, $accompanyingPeriod] = $this->getEntity($request); + [$person, $accompanyingPeriod] = [$entity->getPerson(), $entity->getAccompanyingPeriod()]; if ($accompanyingPeriod instanceof AccompanyingPeriod) { $view = '@ChillCalendar/Calendar/editByAccompanyingCourse.html.twig'; - } elseif ($user instanceof User) { - throw new Exception('to analyze'); - $view = '@ChillCalendar/Calendar/editByUser.html.twig'; + $redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]); + } elseif ($person instanceof Person) { + $view = '@ChillCalendar/Calendar/editByPerson.html.twig'; + $redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]); + } else { + throw new RuntimeException('no person nor accompanying period'); } $form = $this->createForm(CalendarType::class, $entity) @@ -163,33 +171,24 @@ class CalendarController extends AbstractController $this->addFlash('success', $this->get('translator')->trans('Success : calendar item updated!')); - $params = $this->buildParamsToUrl($user, $accompanyingPeriod); - if ($form->get('save_and_create_doc')->isClicked()) { return $this->redirectToRoute('chill_calendar_calendardoc_pick_template', ['id' => $entity->getId()]); } - return $this->redirectToRoute('chill_calendar_calendar_list_by_period', $params); + return new RedirectResponse($redirectRoute); } if ($form->isSubmitted() && !$form->isValid()) { $this->addFlash('error', $this->get('translator')->trans('This form contains errors')); } - $deleteForm = $this->createDeleteForm($entity->getId(), $user, $accompanyingPeriod); - - if (null === $view) { - throw $this->createNotFoundException('Template not found'); - } - $entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']); return $this->render($view, [ 'entity' => $entity, 'form' => $form->createView(), - 'delete_form' => $deleteForm->createView(), - 'accompanyingCourse' => $accompanyingPeriod, - // 'user' => $user, + 'accompanyingCourse' => $entity->getAccompanyingPeriod(), + 'person' => $entity->getPerson(), 'entity_json' => $entity_array, ]); } @@ -225,6 +224,37 @@ class CalendarController extends AbstractController ]); } + /** + * Lists all Calendar entities on a person. + * + * @Route("/{_locale}/calendar/calendar/by-person/{id}", name="chill_calendar_calendar_list_by_person") + */ + public function listActionByPerson(Person $person): Response + { + $filterOrder = $this->buildListFilterOrder(); + ['from' => $from, 'to' => $to] = $filterOrder->getDateRangeData('startDate'); + + $total = $this->calendarACLAwareRepository + ->countByPerson($person, $from, $to); + $paginator = $this->paginator->create($total); + $calendarItems = $this->calendarACLAwareRepository->findByPerson( + $person, + $from, + $to, + ['startDate' => 'DESC'], + $paginator->getCurrentPageFirstItemNumber(), + $paginator->getItemsPerPage() + ); + + return $this->render('@ChillCalendar/Calendar/listByPerson.html.twig', [ + 'calendarItems' => $calendarItems, + 'person' => $person, + 'paginator' => $paginator, + 'filterOrder' => $filterOrder, + 'hasDocs' => 0 < $this->docGeneratorTemplateRepository->countByEntity(Calendar::class), + ]); + } + /** * @Route("/{_locale}/calendar/calendar/my", name="chill_calendar_calendar_list_my") */ @@ -261,27 +291,22 @@ class CalendarController extends AbstractController $view = null; $em = $this->getDoctrine()->getManager(); - [$user, $accompanyingPeriod] = $this->getEntity($request); - - if ($accompanyingPeriod instanceof AccompanyingPeriod) { - $view = '@ChillCalendar/Calendar/newByAccompanyingCourse.html.twig'; - } - // elseif ($user instanceof User) { - // $view = '@ChillCalendar/Calendar/newUser.html.twig'; - // } + [$person, $accompanyingPeriod] = $this->getEntity($request); $entity = new Calendar(); - if ($request->query->has('mainUser')) { - $entity->setMainUser($this->userRepository->find($request->query->getInt('mainUser'))); + if ($accompanyingPeriod instanceof AccompanyingPeriod) { + $view = '@ChillCalendar/Calendar/newByAccompanyingCourse.html.twig'; + $entity->setAccompanyingPeriod($accompanyingPeriod); + $redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]); + } elseif ($person) { + $view = '@ChillCalendar/Calendar/newByPerson.html.twig'; + $entity->setPerson($person)->addPerson($person); + $redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]); } - // if ($user instanceof User) { - // $entity->setPerson($user); - // } - - if ($accompanyingPeriod instanceof AccompanyingPeriod) { - $entity->setAccompanyingPeriod($accompanyingPeriod); + if ($request->query->has('mainUser')) { + $entity->setMainUser($this->userRepository->find($request->query->getInt('mainUser'))); } $form = $this->createForm(CalendarType::class, $entity) @@ -296,13 +321,11 @@ class CalendarController extends AbstractController $this->addFlash('success', $this->get('translator')->trans('Success : calendar item created!')); - $params = $this->buildParamsToUrl($user, $accompanyingPeriod); - if ($form->get('save_and_create_doc')->isClicked()) { return $this->redirectToRoute('chill_calendar_calendardoc_pick_template', ['id' => $entity->getId()]); } - return $this->redirectToRoute('chill_calendar_calendar_list_by_period', $params); + return new RedirectResponse($redirectRoute); } if ($form->isSubmitted() && !$form->isValid()) { @@ -316,7 +339,8 @@ class CalendarController extends AbstractController $entity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']); return $this->render($view, [ - 'user' => $user, + 'context' => $entity->getContext(), + 'person' => $person, 'accompanyingCourse' => $accompanyingPeriod, 'entity' => $entity, 'form' => $form->createView(), @@ -331,6 +355,7 @@ class CalendarController extends AbstractController */ public function showAction(Request $request, int $id): Response { + throw new Exception('not implemented'); $view = null; $em = $this->getDoctrine()->getManager(); @@ -465,49 +490,45 @@ class CalendarController extends AbstractController /** * Creates a form to delete a Calendar entity by id. */ - private function createDeleteForm(int $id, ?User $user, ?AccompanyingPeriod $accompanyingPeriod): FormInterface + private function createDeleteForm(Calendar $calendar): FormInterface { - $params = $this->buildParamsToUrl($user, $accompanyingPeriod); - $params['id'] = $id; - return $this->createFormBuilder() - ->setAction($this->generateUrl('chill_calendar_calendar_delete', $params)) + ->setAction($this->generateUrl('chill_calendar_calendar_delete', ['id' => $calendar->getId()])) ->setMethod('DELETE') ->add('submit', SubmitType::class, ['label' => 'Delete']) ->getForm(); } + /** + * @return array{0: ?Person, 1: ?AccompanyingPeriod} + */ private function getEntity(Request $request): array { $em = $this->getDoctrine()->getManager(); - $user = $accompanyingPeriod = null; + $person = $accompanyingPeriod = null; - if ($request->query->has('user_id')) { - $user_id = $request->get('user_id'); - $user = $em->getRepository(User::class)->find($user_id); + if ($request->query->has('person_id')) { + $person = $this->personRepository->find($request->query->getInt('person_id')); - if (null === $user) { - throw $this->createNotFoundException('User not found'); + if (null === $person) { + throw $this->createNotFoundException('Person not found'); } - // TODO Add permission - // $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $user); + $this->denyAccessUnlessGranted(PersonVoter::SEE, $person); } elseif ($request->query->has('accompanying_period_id')) { - $accompanying_period_id = $request->get('accompanying_period_id'); - $accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($accompanying_period_id); + $accompanyingPeriod = $this->accompanyingPeriodRepository->find($request->query->getInt('accompanying_period_id')); if (null === $accompanyingPeriod) { throw $this->createNotFoundException('Accompanying Period not found'); } - // TODO Add permission - // $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person); + $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod); } else { throw $this->createNotFoundException('Person or Accompanying Period not found'); } return [ - $user, $accompanyingPeriod, + $person, $accompanyingPeriod, ]; } } diff --git a/src/Bundle/ChillCalendarBundle/Entity/Calendar.php b/src/Bundle/ChillCalendarBundle/Entity/Calendar.php index 760b36efb..351954492 100644 --- a/src/Bundle/ChillCalendarBundle/Entity/Calendar.php +++ b/src/Bundle/ChillCalendarBundle/Entity/Calendar.php @@ -92,7 +92,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface /** * @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod", inversedBy="calendars") */ - private AccompanyingPeriod $accompanyingPeriod; + private ?AccompanyingPeriod $accompanyingPeriod = null; /** * @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\Activity") @@ -173,7 +173,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface * @ORM\ManyToOne(targetEntity=Person::class) * @ORM\JoinColumn(nullable=true) */ - private ?Person $person; + private ?Person $person = null; /** * @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\Person", inversedBy="calendars") @@ -317,6 +317,22 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface return $this->comment; } + /** + * @return 'person'|'accompanying_period'|null + */ + public function getContext(): ?string + { + if ($this->getAccompanyingPeriod() !== null) { + return 'accompanying_period'; + } + + if ($this->getPerson() !== null) { + return 'person'; + } + + return null; + } + /** * Each time the date and time is update, this version is incremented. */ diff --git a/src/Bundle/ChillCalendarBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillCalendarBundle/Menu/PersonMenuBuilder.php new file mode 100644 index 000000000..eccbe1ffb --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Menu/PersonMenuBuilder.php @@ -0,0 +1,52 @@ +security = $security; + $this->translator = $translator; + } + + public function buildMenu($menuId, MenuItem $menu, array $parameters) + { + $person = $parameters['person']; + + if ($this->security->isGranted(CalendarVoter::SEE, $person)) { + $menu->addChild($this->translator->trans('Calendar'), [ + 'route' => 'chill_calendar_calendar_list_by_person', + 'routeParameters' => [ + 'id' => $person->getId(), + ], ]) + ->setExtras(['order' => 198]); + } + } + + public static function getMenuIds(): array + { + return ['person']; + } +} diff --git a/src/Bundle/ChillCalendarBundle/Repository/CalendarACLAwareRepository.php b/src/Bundle/ChillCalendarBundle/Repository/CalendarACLAwareRepository.php index c331a3d3c..15d0d83c0 100644 --- a/src/Bundle/ChillCalendarBundle/Repository/CalendarACLAwareRepository.php +++ b/src/Bundle/ChillCalendarBundle/Repository/CalendarACLAwareRepository.php @@ -20,16 +20,24 @@ namespace Chill\CalendarBundle\Repository; use Chill\CalendarBundle\Entity\Calendar; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Repository\AccompanyingPeriodACLAwareRepositoryInterface; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface { + private AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository; + private EntityManagerInterface $em; - public function __construct(EntityManagerInterface $em) - { + public function __construct( + AccompanyingPeriodACLAwareRepositoryInterface $accompanyingPeriodACLAwareRepository, + EntityManagerInterface $em + ) { + $this->accompanyingPeriodACLAwareRepository = $accompanyingPeriodACLAwareRepository; $this->em = $em; } @@ -56,6 +64,46 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface return $qb; } + /** + * Base implementation. The list of allowed accompanying period is retrieved "manually" from @see{AccompanyingPeriodACLAwareRepository}. + */ + public function buildQueryByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): QueryBuilder + { + // find the reachable accompanying periods for person + $periods = $this->accompanyingPeriodACLAwareRepository->findByPerson($person, AccompanyingPeriodVoter::SEE); + + $qb = $this->em->createQueryBuilder() + ->from(Calendar::class, 'c'); + + $qb + ->where( + $qb->expr()->orX( + // the calendar where the person is the main person: + $qb->expr()->eq('c.person', ':person'), + // when the calendar is in a reachable period, and contains person + $qb->expr()->andX( + $qb->expr()->in('c.accompanyingPeriod', ':periods'), + $qb->expr()->isMemberOf(':person', 'c.persons') + ) + ) + ) + ->setParameter('person', $person) + ->setParameter('periods', $periods); + + // filter by date + if (null !== $startDate) { + $qb->andWhere($qb->expr()->gte('c.startDate', ':startDate')) + ->setParameter('startDate', $startDate); + } + + if (null !== $endDate) { + $qb->andWhere($qb->expr()->lte('c.endDate', ':endDate')) + ->setParameter('endDate', $endDate); + } + + return $qb; + } + public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int { $qb = $this->buildQueryByAccompanyingPeriod($period, $startDate, $endDate)->select('count(c)'); @@ -63,6 +111,14 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface return $qb->getQuery()->getSingleScalarResult(); } + public function countByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int + { + return $this->buildQueryByPerson($person, $startDate, $endDate) + ->select('COUNT(c)') + ->getQuery() + ->getSingleScalarResult(); + } + /** * @return array|Calendar[] */ @@ -84,4 +140,24 @@ class CalendarACLAwareRepository implements CalendarACLAwareRepositoryInterface return $qb->getQuery()->getResult(); } + + public function findByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array + { + $qb = $this->buildQueryByPerson($person, $startDate, $endDate) + ->select('c'); + + foreach ($orderBy as $sort => $order) { + $qb->addOrderBy('c.' . $sort, $order); + } + + if (null !== $offset) { + $qb->setFirstResult($offset); + } + + if (null !== $limit) { + $qb->setMaxResults($limit); + } + + return $qb->getQuery()->getResult(); + } } diff --git a/src/Bundle/ChillCalendarBundle/Repository/CalendarACLAwareRepositoryInterface.php b/src/Bundle/ChillCalendarBundle/Repository/CalendarACLAwareRepositoryInterface.php index a90f6a28c..d860c3470 100644 --- a/src/Bundle/ChillCalendarBundle/Repository/CalendarACLAwareRepositoryInterface.php +++ b/src/Bundle/ChillCalendarBundle/Repository/CalendarACLAwareRepositoryInterface.php @@ -20,14 +20,32 @@ namespace Chill\CalendarBundle\Repository; use Chill\CalendarBundle\Entity\Calendar; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\Person; use DateTimeImmutable; interface CalendarACLAwareRepositoryInterface { public function countByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int; + /** + * Return the number or calendars associated with a person. See condition on @see{self::findByPerson}. + */ + public function countByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate): int; + /** * @return array|Calendar[] */ public function findByAccompanyingPeriod(AccompanyingPeriod $period, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array; + + /** + * Return all the calendars which are associated with a person, either on @see{Calendar::person} or within. + * + * @see{Calendar::persons}. The calendar may be associated with a person, or an accompanyingPeriod. + * + * The method may assume that the user is allowed to see the person, but must check that the user is allowed + * to see the calendar's accompanyingPeriod, if any. + * + * @return array|Calendar[] + */ + public function findByPerson(Person $person, ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?array $orderBy = [], ?int $offset = null, ?int $limit = null): array; } diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig new file mode 100644 index 000000000..3eb1d6634 --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig @@ -0,0 +1,177 @@ +{# list used in context of person or accompanyingPeriod #} + +{{ filterOrder|chill_render_filter_order_helper }} + +{% if calendarItems|length > 0 %} +
+ + {% for calendar in calendarItems %} + +
+
+
+
+
+
+

+ {% if context == 'person' and calendar.context == 'accompanying_period' %} + + + {{ calendar.accompanyingPeriod.id }} + + + {% endif %} + {% if calendar.endDate.diff(calendar.startDate).days >= 1 %} + {{ calendar.startDate|format_datetime('short', 'short') }} + - {{ calendar.endDate|format_datetime('short', 'short') }} + {% else %} + {{ calendar.startDate|format_datetime('short', 'short') }} + - {{ calendar.endDate|format_datetime('none', 'short') }} + {% endif %} +

+ +
+ + {{ calendar.duration|date('%H:%I') }} + {% if false == calendar.sendSMS or null == calendar.sendSMS %} + + {% else %} + {% if calendar.smsStatus == 'sms_sent' %} + + + + + {% else %} + + + + + {% endif %} + {% endif %} +
+ +
+
+
+ +
+
    + {% if calendar.mainUser is not empty %} + {{ calendar.mainUser|chill_entity_render_box }} + {% endif %} +
+
+ +
+
+ + {% if calendar.comment.comment is not empty + or calendar.users|length > 0 + or calendar.thirdParties|length > 0 + or calendar.users|length > 0 %} +
+
+ {% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with { + 'context': calendar.context == 'person' ? 'calendar_person' : 'calendar_accompanyingCourse', + 'render': 'wrap-list', + 'entity': calendar + } %} +
+ +
+ {% endif %} + + {% if calendar.comment.comment is not empty %} +
+
+ {{ calendar.comment|chill_entity_render_box( { 'limit_lines': 3, 'metadata': false } ) }} +
+
+ {% endif %} + + {% if calendar.location is not empty %} +
+
+ {% if calendar.location.address is not same as(null) and calendar.location.name is not empty %} + {% endif %} + {% if calendar.location.name is not empty %}{{ calendar.location.name }}{% endif %} + {% if calendar.location.address is not same as(null) %}{{ calendar.location.address|chill_entity_render_box({'multiline': false, 'with_picto': (calendar.location.name is empty)}) }}{% else %} + {% endif %} + {% if calendar.location.phonenumber1 is not empty %} {{ calendar.location.phonenumber1|chill_format_phonenumber }}{% endif %} + {% if calendar.location.phonenumber2 is not empty %} {{ calendar.location.phonenumber2|chill_format_phonenumber }}{% endif %} +
+
+ {% endif %} + +
+
+ + {{ include('@ChillCalendar/Calendar/_documents.twig.html') }} +
+
+ +
+
    + {% if is_granted('CHILL_CALENDAR_CALENDAR_SEE', calendar) and hasDocs %} +
  • + + {{ 'chill_calendar.Add a document'|trans }} + +
  • + {% endif %} + {% if accompanyingCourse is defined and is_granted('CHILL_ACTIVITY_CREATE', accompanyingCourse) and calendar.activity is null %} +
  • + + {{ 'Transform to activity'|trans }} + +
  • + {% endif %} + + {% if (calendar.isInvited(app.user)) %} + {% set invite = calendar.inviteForUser(app.user) %} +
  • +
    +
  • + {% endif %} + {% if false %} +
  • + +
  • + {% endif %} + {# TOOD + {% if is_granted('CHILL_ACTIVITY_UPDATE', calendar) %} + #} +
  • + +
  • + {# TOOD + {% endif %} + {% if is_granted('CHILL_ACTIVITY_DELETE', calendar) %} + #} +
  • + +
  • + {# + {% endif %} + #} +
+ +
+ +
+ {% endfor %} + + {% if calendarItems|length < paginator.getTotalItems %} + {{ chill_pagination(paginator) }} + {% endif %} + +
+{% endif %} diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/confirm_deleteByPerson.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/confirm_deleteByPerson.html.twig new file mode 100644 index 000000000..f72d0d21b --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/confirm_deleteByPerson.html.twig @@ -0,0 +1,16 @@ +{% extends "@ChillPerson/Person/layout.html.twig" %} + +{% set activeRouteKey = 'chill_calendar_calendar_list' %} + +{% block title 'Remove calendar item'|trans %} + +{% block content %} + {{ include('@ChillMain/Util/confirmation_template.html.twig', + { + 'title' : 'Remove calendar item'|trans, + 'confirm_question' : 'Are you sure you want to remove the calendar item?'|trans, + 'cancel_route' : 'chill_calendar_calendar_list_by_period', + 'cancel_parameters' : { 'id' : person.id }, + 'form' : delete_form + } ) }} +{% endblock %} diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/editByPerson.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/editByPerson.html.twig new file mode 100644 index 000000000..fc5319849 --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/editByPerson.html.twig @@ -0,0 +1,34 @@ +{% extends "@ChillPerson/Person/layout.html.twig" %} + +{% set activeRouteKey = 'chill_calendar_calendar_list' %} + +{% block title 'Update calendar'|trans %} + +{% block content %} +
+ + {% include 'ChillCalendarBundle:Calendar:edit.html.twig' with {'context': 'person'} %} + +
+{% endblock %} + +{% block js %} + {{ parent() }} + + {{ encore_entry_script_tags('vue_calendar') }} +{% endblock %} + +{% block css %} + {{ parent() }} + {{ encore_entry_link_tags('vue_calendar') }} + {{ encore_entry_link_tags('page_calendar') }} +{% endblock %} + +{% block block_post_menu %} +
+{% endblock %} diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/listByAccompanyingCourse.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/listByAccompanyingCourse.html.twig index 56d9a667d..f1b0c6110 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/listByAccompanyingCourse.html.twig +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/listByAccompanyingCourse.html.twig @@ -22,7 +22,6 @@ {% block content %}

{{ 'Calendar list' |trans }}

- {{ filterOrder|chill_render_filter_order_helper }} {% if calendarItems|length == 0 %}

@@ -31,171 +30,9 @@ class="btn btn-create button-small">

{% else %} - -
- - {% for calendar in calendarItems %} - -
-
-
-
-
-
- {% if calendar.endDate.diff(calendar.startDate).days >= 1 %} -

{{ calendar.startDate|format_datetime('short', 'short') }} - - {{ calendar.endDate|format_datetime('short', 'short') }}

- {% else %} -

{{ calendar.startDate|format_datetime('short', 'short') }} - - {{ calendar.endDate|format_datetime('none', 'short') }}

- {% endif %} - -
- - {{ calendar.duration|date('%H:%I') }} - {% if false == calendar.sendSMS or null == calendar.sendSMS %} - - {% else %} - {% if calendar.smsStatus == 'sms_sent' %} - - - - - {% else %} - - - - - {% endif %} - {% endif %} -
- -
-
-
- -
-
    - {% if calendar.mainUser is not empty %} - {{ calendar.mainUser|chill_entity_render_box }} - {% endif %} -
-
- -
-
- - {% if calendar.comment.comment is not empty - or calendar.users|length > 0 - or calendar.thirdParties|length > 0 - or calendar.users|length > 0 %} -
-
- {% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with { - 'context': 'calendar_accompanyingCourse', - 'render': 'wrap-list', - 'entity': calendar - } %} -
- -
- {% endif %} - - {% if calendar.comment.comment is not empty %} -
-
- {{ calendar.comment|chill_entity_render_box( { 'limit_lines': 3, 'metadata': false } ) }} -
-
- {% endif %} - - {% if calendar.location is not empty %} -
-
- {% if calendar.location.address is not same as(null) and calendar.location.name is not empty %} - {% endif %} - {% if calendar.location.name is not empty %}{{ calendar.location.name }}{% endif %} - {% if calendar.location.address is not same as(null) %}{{ calendar.location.address|chill_entity_render_box({'multiline': false, 'with_picto': (calendar.location.name is empty)}) }}{% else %} - {% endif %} - {% if calendar.location.phonenumber1 is not empty %} {{ calendar.location.phonenumber1|chill_format_phonenumber }}{% endif %} - {% if calendar.location.phonenumber2 is not empty %} {{ calendar.location.phonenumber2|chill_format_phonenumber }}{% endif %} -
-
- {% endif %} - -
-
- - {{ include('@ChillCalendar/Calendar/_documents.twig.html') }} -
-
- -
-
    - {% if is_granted('CHILL_CALENDAR_CALENDAR_SEE', calendar) and hasDocs %} -
  • - - {{ 'chill_calendar.Add a document'|trans }} - -
  • - {% endif %} - {% if is_granted('CHILL_ACTIVITY_CREATE', accompanyingCourse) and calendar.activity is null %} -
  • - - {{ 'Transform to activity'|trans }} - -
  • - {% endif %} - {% if (calendar.isInvited(app.user)) %} - {% set invite = calendar.inviteForUser(app.user) %} -
  • -
    -
  • - {% endif %} - {% if false %} -
  • - -
  • - {% endif %} - {# TOOD - {% if is_granted('CHILL_ACTIVITY_UPDATE', calendar) %} - #} -
  • - -
  • - {# TOOD - {% endif %} - {% if is_granted('CHILL_ACTIVITY_DELETE', calendar) %} - #} -
  • - -
  • - {# - {% endif %} - #} -
- -
- -
- {% endfor %} - - {% if calendarItems|length < paginator.getTotalItems %} - {{ chill_pagination(paginator) }} - {% endif %} - -
+ {{ include('@ChillCalendar/Calendar/_list.html.twig', {context: 'accompanying_course'}) }} {% endif %} -