From 59a64e9a6243c5e14bf6b22b892487942aa8b378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 26 May 2022 00:10:25 +0200 Subject: [PATCH] wip: continue sync to remote --- .../Entity/CalendarRange.php | 2 +- .../Handler/CalendarToRemoteHandler.php | 19 ++- .../MSGraph/RemoteEventConverter.php | 84 ++++++++----- .../MSGraphRemoteCalendarConnector.php | 119 ++++++++++++++---- .../MSGraph/calendar_event_body.html.twig | 16 +++ .../translations/messages.fr.yml | 1 + 6 files changed, 184 insertions(+), 57 deletions(-) create mode 100644 src/Bundle/ChillCalendarBundle/Resources/views/MSGraph/calendar_event_body.html.twig diff --git a/src/Bundle/ChillCalendarBundle/Entity/CalendarRange.php b/src/Bundle/ChillCalendarBundle/Entity/CalendarRange.php index ea1035340..088842876 100644 --- a/src/Bundle/ChillCalendarBundle/Entity/CalendarRange.php +++ b/src/Bundle/ChillCalendarBundle/Entity/CalendarRange.php @@ -35,7 +35,7 @@ class CalendarRange implements TrackCreationInterface, TrackUpdateInterface /** * @ORM\OneToOne(targetEntity=Calendar::class, mappedBy="calendarRange") */ - private Calendar $calendar; + private ?Calendar $calendar = null; /** * @ORM\Column(type="datetimetz_immutable") diff --git a/src/Bundle/ChillCalendarBundle/Messenger/Handler/CalendarToRemoteHandler.php b/src/Bundle/ChillCalendarBundle/Messenger/Handler/CalendarToRemoteHandler.php index ff4083e4a..8780c3d3a 100644 --- a/src/Bundle/ChillCalendarBundle/Messenger/Handler/CalendarToRemoteHandler.php +++ b/src/Bundle/ChillCalendarBundle/Messenger/Handler/CalendarToRemoteHandler.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\CalendarBundle\Messenger\Handler; +use Chill\CalendarBundle\Entity\CalendarRange; use Chill\CalendarBundle\Entity\Invite; use Chill\CalendarBundle\Messenger\Message\CalendarMessage; use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface; @@ -62,12 +63,17 @@ class CalendarToRemoteHandler implements MessageHandlerInterface if (null !== $calendarMessage->getPreviousCalendarRangeId()) { $previousCalendarRange = $this->calendarRangeRepository ->find($calendarMessage->getPreviousCalendarRangeId()); + } else { + $previousCalendarRange = null; } if (null !== $calendarMessage->getPreviousMainUserId()) { $previousMainUser = $this->userRepository ->find($calendarMessage->getPreviousMainUserId()); + } else { + $previousMainUser = null; } + $newInvites = array_filter( array_map( function ($id) { return $this->inviteRepository->find($id); }, @@ -79,13 +85,22 @@ class CalendarToRemoteHandler implements MessageHandlerInterface $this->calendarConnector->syncCalendar( $calendar, $calendarMessage->getAction(), - $previousCalendarRange ?? null, - $previousMainUser ?? null, + $previousCalendarRange, + $previousMainUser, $calendarMessage->getOldInvites(), $newInvites ); $calendar->preventEnqueueChanges = true; + + if ($calendar->hasCalendarRange()) { + $calendar->getCalendarRange()->preventEnqueueChanges = true; + } + + if ($previousCalendarRange instanceof CalendarRange) { + $previousCalendarRange->preventEnqueueChanges = true; + } + $this->entityManager->flush(); } } diff --git a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/RemoteEventConverter.php b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/RemoteEventConverter.php index f5dce2074..f73fcd31b 100644 --- a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/RemoteEventConverter.php +++ b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/RemoteEventConverter.php @@ -19,7 +19,9 @@ use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface; use DateTimeImmutable; use DateTimeZone; +use Symfony\Component\Templating\EngineInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use Twig\Environment; /** * Convert Chill Calendar event to Remote MS Graph event, and MS Graph @@ -39,8 +41,11 @@ class RemoteEventConverter private TranslatorInterface $translator; - public function __construct(PersonRenderInterface $personRender, TranslatorInterface $translator) + private EngineInterface $engine; + + public function __construct(EngineInterface $engine, PersonRenderInterface $personRender, TranslatorInterface $translator) { + $this->engine = $engine; $this->translator = $translator; $this->personRender = $personRender; $this->defaultDateTimeZone = (new DateTimeImmutable())->getTimezone(); @@ -79,38 +84,59 @@ class RemoteEventConverter } public function calendarToEvent(Calendar $calendar): array + { + return array_merge( + [ + 'subject' => '[Chill] ' . + implode( + ', ', + $calendar->getPersons()->map(function (Person $p) { + return $this->personRender->renderString($p, []); + })->toArray() + ), + 'start' => [ + 'dateTime' => $calendar->getStartDate()->setTimezone($this->remoteDateTimeZone) + ->format(self::REMOTE_DATE_FORMAT), + 'timeZone' => 'UTC', + ], + 'end' => [ + 'dateTime' => $calendar->getEndDate()->setTimezone($this->remoteDateTimeZone) + ->format(self::REMOTE_DATE_FORMAT), + 'timeZone' => 'UTC', + ], + 'allowNewTimeProposals' => false, + 'transactionId' => 'calendar_' . $calendar->getId(), + 'body' => [ + 'contentType' => 'text', + 'content' => $this->engine->render( + '@ChillCalendar/MSGraph/calendar_event_body.html.twig', + ['calendar' => $calendar] + ), + ] + ], + $this->calendarToEventAttendeesOnly($calendar) + ); + } + + public function calendarToEventAttendeesOnly(Calendar $calendar): array { return [ - 'subject' => '[Chill] ' . - implode( - ', ', - $calendar->getPersons()->map(function (Person $p) { - return $this->personRender->renderString($p, []); - })->toArray() - ), - 'start' => [ - 'dateTime' => $calendar->getStartDate()->setTimezone($this->remoteDateTimeZone) - ->format(self::REMOTE_DATE_FORMAT), - 'timeZone' => 'UTC', - ], - 'end' => [ - 'dateTime' => $calendar->getEndDate()->setTimezone($this->remoteDateTimeZone) - ->format(self::REMOTE_DATE_FORMAT), - 'timeZone' => 'UTC', - ], - 'allowNewTimeProposals' => false, 'attendees' => $calendar->getInvites()->map( - static function (Invite $i) { - return [ - 'emailAddress' => [ - 'address' => $i->getUser()->getEmail(), - 'name' => $i->getUser()->getLabel(), - ], - 'type' => 'Required', - ]; - } + function (Invite $i) { + return $this->buildInviteToAttendee($i); + } )->toArray(), - 'transactionId' => 'calendar_' . $calendar->getId(), + ]; + } + + private function buildInviteToAttendee(Invite $invite): array + { + return [ + 'emailAddress' => [ + 'address' => $invite->getUser()->getEmail(), + 'name' => $invite->getUser()->getLabel(), + ], + 'type' => 'Required', ]; } diff --git a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php index b38dc6491..80a5dd792 100644 --- a/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php +++ b/src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraphRemoteCalendarConnector.php @@ -27,6 +27,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface { @@ -40,6 +41,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface private RemoteEventConverter $remoteEventConverter; + private TranslatorInterface $translator; + private OnBehalfOfUserTokenStorage $tokenStorage; private UrlGeneratorInterface $urlGenerator; @@ -54,6 +57,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface OnBehalfOfUserTokenStorage $tokenStorage, OnBehalfOfUserHttpClient $userHttpClient, RemoteEventConverter $remoteEventConverter, + TranslatorInterface $translator, UrlGeneratorInterface $urlGenerator ) { $this->calendarRangeRepository = $calendarRangeRepository; @@ -62,6 +66,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface $this->logger = $logger; $this->remoteEventConverter = $remoteEventConverter; $this->tokenStorage = $tokenStorage; + $this->translator = $translator; $this->urlGenerator = $urlGenerator; $this->userHttpClient = $userHttpClient; } @@ -162,9 +167,16 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface } else { if (null !== $previousMainUser) { // cancel event in previousMainUserCalendar - // CREATE event in currentMainUser + $this->cancelOnRemote( + $calendar->getRemoteId(), + $this->translator->trans('remote_ms_graph.cancel_event_because_main_user_is_%label%', ['%label%' => $calendar->getMainUser()]), + $previousMainUser, + 'calendar_'.$calendar->getRemoteId() + ); + $this->createCalendarOnRemote($calendar); + } else { + $this->patchCalendarOnRemote($calendar, $newInvites); } - // PATCH event in currentMainUer } if ($calendar->hasCalendarRange() && $calendar->getCalendarRange()->hasRemoteId()) { @@ -172,10 +184,19 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface $calendar->getCalendarRange()->getRemoteId(), $calendar->getCalendarRange()->getUser() ); + + $calendar->getCalendarRange() + ->addRemoteAttributes([ + 'lastModifiedDateTime' => null, + 'changeKey' => null, + 'previousId' => $calendar->getCalendarRange()->getRemoteId() + ]) + ->setRemoteId('') + ; } if (null !== $previousCalendarRange) { - // create previousCalendarRange + $this->createRemoteCalendarRange($previousCalendarRange); } } @@ -188,6 +209,34 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface } } + private function patchCalendarOnRemote(Calendar $calendar, array $newInvites): void + { + $eventDatas = []; + $eventDatas[] = $this->remoteEventConverter->calendarToEvent($calendar); + + if (0 < count($newInvites)) { + $eventDatas[] = $this->remoteEventConverter->calendarToEventAttendeesOnly($calendar); + } + + foreach ($eventDatas as $eventData) { + [ + 'id' => $id, + 'lastModifiedDateTime' => $lastModified, + 'changeKey' => $changeKey + ] = $this->patchOnRemote( + $calendar->getRemoteId(), + $eventData, + $calendar->getMainUser(), + 'calendar_'.$calendar->getId() + ); + + $calendar->addRemoteAttributes([ + 'lastModifiedDateTime' => $lastModified, + 'changeKey' => $changeKey, + ]); + } + } + private function createCalendarOnRemote(Calendar $calendar): void { $eventData = $this->remoteEventConverter->calendarToEvent($calendar); @@ -227,7 +276,6 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface return []; } - dump($eventData); try { $event = $this->machineHttpClient->request( @@ -257,9 +305,8 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface private function createRemoteCalendarRange(CalendarRange $calendarRange): void { $userId = $this->mapCalendarToUser->getUserId($calendarRange->getUser()); - $calendarId = $this->mapCalendarToUser->getCalendarId($calendarRange->getUser()); - if (null === $userId || null === $calendarId) { + if (null === $userId) { $this->logger->warning('user does not have userId nor calendarId', [ 'user_id' => $calendarRange->getUser()->getId(), 'calendar_range_id' => $calendarRange->getId(), @@ -270,27 +317,20 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface $eventData = $this->remoteEventConverter->calendarRangeToEvent($calendarRange); - try { - $event = $this->machineHttpClient->request( - 'POST', - 'users/' . $userId . '/calendar/events', - [ - 'json' => $eventData, - ] - )->toArray(); - } catch (ClientExceptionInterface $e) { - $this->logger->warning('could not save calendar range to remote', [ - 'exception' => $e->getTraceAsString(), - 'calendarRangeId' => $calendarRange->getId(), - ]); + [ + 'id' => $id, + 'lastModifiedDateTime' => $lastModified, + 'changeKey' => $changeKey + ] = $this->createOnRemote( + $eventData, + $calendarRange->getUser(), + 'calendar_range_' . $calendarRange->getId() + ); - throw $e; - } - - $calendarRange->setRemoteId($event['id']) + $calendarRange->setRemoteId($id) ->addRemoteAttributes([ - 'lastModifiedDateTime' => $this->remoteEventConverter->getLastModifiedDate($event)->getTimestamp(), - 'changeKey' => $event['changeKey'], + 'lastModifiedDateTime' => $lastModified, + 'changeKey' => $changeKey ]); } @@ -364,6 +404,35 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface ); } + private function cancelOnRemote(string $remoteId, string $comment, User $user, string $identifier): void + { + $userId = $this->mapCalendarToUser->getUserId($user); + + if (null === $userId) { + return; + } + + try { + $this->machineHttpClient->request( + 'POST', + "users/{$userId}/calendar/events/{$remoteId}/cancel", + [ + 'json' => ['Comment' => $comment] + ] + ); + } catch (ClientExceptionInterface $e) { + $this->logger->warning('could not update calendar range to remote', [ + 'exception' => $e->getTraceAsString(), + 'content' => $e->getResponse()->getContent(), + 'calendarRangeId' => $identifier, + ]); + + throw $e; + } + + + } + /** * @param string $identifier an identifier for logging in case of something does not work * diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/MSGraph/calendar_event_body.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/MSGraph/calendar_event_body.html.twig new file mode 100644 index 000000000..63a206ae0 --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Resources/views/MSGraph/calendar_event_body.html.twig @@ -0,0 +1,16 @@ +Ce rendez-vous a été généré automatiquement par Chill. + +Pour modifier ou supprimer ce rendez-vous, utilisez l’adresse suivante: + +{{ absolute_url(path('chill_calendar_calendar_edit', {'id': calendar.id, '_locale': 'fr'})) }} + +{{ calendar.comment.comment|nl2br }} + +Usagers et tiers concernés: + +{% for p in calendar.persons %} +- {{ p|chill_entity_render_string }} +{%- endfor -%} +{% for t in calendar.professionals %} +- {{ t|chill_entity_render_string }} +{% endfor %} diff --git a/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml b/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml index 0c6e2ab5b..567b25491 100644 --- a/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillCalendarBundle/translations/messages.fr.yml @@ -40,6 +40,7 @@ remote_ms_graph: oof: En dehors du bureau workingElsewhere: Travaille à l'extérieur unknown: Inconnu + cancel_event_because_main_user_is_%label%: L'événement est transféré à l'utilisateur %label% remote_calendar: calendar_range_title: Plage de disponibilité Chill