57 $this->context = $context;
58 $this->helper = $context->getHelper();
59 parent::__construct($context->getConnection());
78 $internalDto = $this->getEventConverter()->eventToDto($event);
82 $dto = $this->getService()->createEvent($internalDto, $context->
getSectionConnection()->getVendorSectionId());
85 if ($event->getExcludedDateCollection() && $event->getExcludedDateCollection()->count())
87 $context->
add(
'sync',
'masterEventId', $dto->id);
89 foreach ($event->getExcludedDateCollection() as $item)
91 $context->
add(
'sync',
'excludeDate', $item);
98 $result->setData($this->prepareResultData($dto));
102 $result->addError(
new Main\Error(
'Error of create a series master event'));
107 $result->addError(
new Main\
Error(
'Error of create event'));
110 catch (ApiException $exception)
112 if ((
int)$exception->getCode() !== 400)
117 $result->addError(
new Main\
Error($exception->getMessage(), $exception->getCode()));
119 catch (AuthException $exception)
121 $result->addError(
new Main\
Error($exception->getMessage(), $exception->getCode()));
142 $internalDto = $this->getEventConverter()->eventToDto($event);
147 $dto = $this->getService()->updateEvent($context->
getEventConnection()->getVendorEventId(), $internalDto);
150 $result->setData($this->prepareResultData($dto));
155 if ((
int)$exception->getCode() !== 400)
160 $result->addError(
new Main\
Error($exception->getMessage(), $exception->getCode()));
164 $result->addError(
new Main\
Error($exception->getMessage(), $exception->getCode()));
197 private function prepareResultData(
EventDto $dto): array
203 'version' => $dto->changeKey,
204 'etag' => $dto->etag,
205 'recurrence' => $dto->seriesMasterId ??
null,
206 'data' => $this->prepareCustomData($dto),
224 $eventLink = $context->sync[
'instanceLink'];
229 ->setEventConnection($eventLink)
231 return $this->
update($event, $eventContext);
234 return (
new Result())->addError(
new Main\
Error(
'Not found link for instance'));
252 $event->getOriginalDateFrom()
253 && $event->getOriginalDateFrom()->format(
'Ymd') !== $event->getStart()->format(
'Ymd')
256 return $this->moveInstance($event, $context);
264 if ($masterLink && $instance = $this->getInstanceForDay($masterLink->getVendorEventId(), $event->getStart()->getDate()))
266 $dto = $this->getService()->updateEvent(
268 $this->getEventConverter()->eventToDto($event),
270 if ($dto && !empty($dto->id))
272 $result->setData($this->prepareResultData($dto));
276 $result->addError(
new Main\
Error(
"Error of create instance.", 404));
281 $result->addError(
new Main\
Error(
"Instances for event not found", 404));
286 if (!in_array((
int)$exception->getCode(), [400, 404],
true))
291 $result->addError(
new Main\
Error($exception->getMessage(), $exception->getCode()));
295 $result->addError(
new Main\
Error($exception->getMessage(), $exception->getCode()));
323 : ($context->sync[
'masterEventId'] ??
null)
328 $context->sync[
'excludeDate']->getDate()->format(
'Ymd 000000'),
332 : new \DateTimeZone(
'UTC')
336 if ($instance = $this->getInstanceForDay($masterEventId, $excludeDate))
338 $this->getService()->deleteEvent(
344 $result->addError(
new Main\
Error(
"Instances for event not found", 404));
349 if ((
int)$e->getCode() !== 400 && (
int)$e->getCode() !== 404)
354 $result->addError(
new Main\
Error($e->getMessage(), $e->getCode()));
358 $result->addError(
new Main\
Error($exception->getMessage(), $exception->getCode()));
389 $instance = $this->getInstanceForDay(
390 $masterLink->getVendorEventId(),
399 $dto = $this->getService()->updateEvent(
401 $this->getEventConverter()->eventToDto($event),
403 if ($dto && !empty($dto->id))
405 $result->setData($this->prepareResultData($dto));
409 $result->addError(
new Main\
Error(
'Error of move instance', 400));
412 catch (NotFoundException $e)
414 $result->addError(
new Main\Error(
'Instance not found'));
419 $result->addError(
new Main\
Error(
'Instance not found'));
442 public function fetchSectionEvents(SectionConnection $sectionLink): Generator
444 foreach ($this->getService()->getCalendarDelta($sectionLink) as $deltaData)
454 'version' => $dto->changeKey,
455 'etag' => $dto->etag,
467 'event' => $this->context->getConverter()
468 ->convertEvent($dto, $sectionLink->getSection()),
470 'version' => $dto->changeKey,
471 'etag' => $dto->etag,
472 'data' => $this->prepareCustomData($dto),
477 $data = $this->prepareSeries($deltaData, $sectionLink);
508 public function saveRecurrence(
509 SyncEvent $recurrenceEvent,
510 SectionConnection $sectionConnection,
514 $result =
new Result();
516 if ($recurrenceEvent->getEventConnection())
520 $masterResult = $this->updateRecurrenceInstance($recurrenceEvent, $context);
522 catch(Calendar\Sync\Exceptions\NotFoundException $e)
524 $this->getEventConnectionMapper()->delete($recurrenceEvent->getEventConnection());
525 $recurrenceEvent->setEventConnection(
null);
526 return $this->saveRecurrence($recurrenceEvent, $sectionConnection, $context);
531 $masterResult = $this->createRecurrenceInstance($recurrenceEvent, $sectionConnection, $context);
534 if (!$masterResult->isSuccess())
536 $result->addErrors($masterResult->getErrors());
540 if ($recurrenceEvent->getEventConnection())
542 $recurrenceEvent->getEventConnection()
548 $excludes = $this->getExcludedDatesCollection($recurrenceEvent);
549 if ($recurrenceEvent->getInstanceMap())
552 foreach ($recurrenceEvent->getInstanceMap()->getCollection() as $instance)
554 if ($instance->getEvent()->getOriginalDateFrom() ===
null)
557 new Main\
Error(
'Instance is invalid - there is not original date from. ['.$instance->getEvent()->getId().
']', 400));
561 if ($instance->getEventConnection())
565 $instanceResult = $this->updateRecurrenceInstance($instance, $context, $recurrenceEvent->getEventConnection());
567 catch(Calendar\Sync\Exceptions\NotFoundException $e)
569 $this->getEventConnectionMapper()->delete($instance->getEventConnection());
570 $instance->setEventConnection(
null);
571 $instanceResult = $this->createRecurrenceInstance(
575 $recurrenceEvent->getEventConnection()
581 $instanceResult = $this->createRecurrenceInstance(
585 $recurrenceEvent->getEventConnection()
588 $excludes->removeDateFromCollection($instance->getEvent()->getOriginalDateFrom());
590 $instance->getEvent()->getStart()->format(
'Ymd')
591 !== $instance->getEvent()->getOriginalDateFrom()->format(
'Ymd')
594 $excludes->removeDateFromCollection($instance->getEvent()->getStart());
596 if (!$instanceResult->isSuccess())
598 $result->addErrors($instanceResult->getErrors());
600 if ($instance->getEventConnection())
607 if ($excludes->count() > 0)
609 $context = (
new EventContext())->setEventConnection($recurrenceEvent->getEventConnection());
611 foreach ($excludes as $excludedDate)
613 $context->add(
'sync',
'excludeDate', $excludedDate);
624 private function getEventConnectionMapper(): Core\Mappers\EventConnection
626 if (empty($this->eventConnectionMapper))
628 $this->eventConnectionMapper =
new Core\Mappers\EventConnection();
631 return $this->eventConnectionMapper;
660 return $this->saveRecurrence($recurrenceEvent, $sectionConnection, $context);
681 return $this->saveRecurrence($recurrenceEvent, $sectionConnection, $context);
692 private function prepareSeries($deltaData,
SectionConnection $sectionLink): array
700 $masterEvent = $this->context->getConverter()->convertEvent($masterDto, $sectionLink->
getSection());
702 if (!empty($exceptionList)) {
703 $exceptionsDates = array_map(
function (
EventDto $exception) {
705 $exception->start->dateTime,
706 $this->helper::TIME_FORMAT_LONG,
707 new DateTimeZone($exception->start->timeZone),
713 $masterEvent->setExcludedDateCollection($excludeCollection);
718 'id' => $masterDto->id,
719 'event' => $masterEvent,
720 'version' => $masterDto->changeKey,
721 'etag' => $masterDto->etag,
722 'data' => $this->prepareCustomData($masterDto),
725 foreach ($exceptionList as $exception) {
726 $event = $this->context->getConverter()->convertEvent($exception, $sectionLink->
getSection());
728 'type' =>
'exception',
730 'id' => $exception->id,
731 'version' => $exception->changeKey,
732 'etag' => $exception->etag,
733 'recurrence' => $exception->seriesMasterId,
734 'data' => $this->prepareCustomData($exception),
757 private function getInstanceForDay(
string $eventId, Main\
Type\Date $dayStart): ? EventDto
759 $dateFrom = clone $dayStart;
760 if ($dateFrom instanceof Main\
Type\DateTime)
762 $dateFrom->setTime(0,0);
764 $dateTo = clone $dateFrom;
765 $dateTo->add(
'1 day');
767 $instances = $this->getService()->getEventInstances([
769 'event_id' => $eventId,
770 'from' => $dateFrom->format(
'c'),
771 'to' => $dateTo->format(
'c'),
775 return $instances ? $instances[0] :
null;
781 private function getEventConverter(): EventConverter
783 if (empty($this->eventConverter))
785 $this->eventConverter =
new EventConverter();
788 return $this->eventConverter;
799 private function getService(): VendorSyncService
801 return $this->context->getVendorSyncService();
810 private function prepareCustomData(EventDto $dto): array
813 if (!empty($dto->location))
815 $result[
'location'] = $dto->location->toArray(
true);
817 if (!empty($dto->locations))
819 foreach ($dto->locations as $location)
821 $result[
'locations'][] = $location->toArray(
true);
825 if (!empty($dto->attendees))
827 $result[
'attendees'] = [];
828 foreach ($dto->attendees as $attendee)
830 $result[
'attendees'][] = $attendee->toArray(
true);
843 private function enrichVendorData(EventDto $dto, EventConnection $link)
861 private function createRecurrenceInstance(
862 SyncEvent $syncEvent,
863 SectionConnection $sectionConnection,
865 EventConnection $masterLink =
null
870 $eventContext =
new EventContext();
871 $eventContext->merge($context);
874 $eventContext->setEventConnection($masterLink);
875 $event = (
new Core\Builders\EventCloner($syncEvent->getEvent()))->build();
880 $eventContext->setSectionConnection($sectionConnection);
881 $event = $this->prepareMasterEvent($syncEvent);
882 $result = $this->
create($event, $eventContext);
884 if ($result->isSuccess())
886 if (!$syncEvent->getEvent()->isDeleted())
888 if (!empty($result->getData()[
'event'][
'id']))
890 $link = (
new EventConnection())
892 ->setConnection($sectionConnection->getConnection())
894 ->setVendorEventId($result->getData()[
'event'][
'id'])
895 ->setEntityTag($result->getData()[
'event'][
'etag'])
896 ->setVendorVersionId($result->getData()[
'event'][
'version'])
897 ->setRecurrenceId($result->getData()[
'event'][
'recurrence'] ??
null)
901 ->setEventConnection($link)
906 $errMessage =
'Unknown error of creating recurrence '
907 . ($masterLink ?
'master' :
'instance');
908 $result->addError(
new Main\
Error($errMessage, 400, [
'data' => $result->getData()]));
920 catch (Core\Base\BaseException $e)
922 return (
new Result())->
addError(
new Main\
Error($e->getMessage()));
939 private function updateRecurrenceInstance(
940 SyncEvent $syncEvent,
942 EventConnection $masterLink =
null
945 $eventContext =
new EventContext();
946 $eventContext->merge($context);
950 ->setEventConnection($masterLink)
951 ->add(
'sync',
'instanceLink', $syncEvent->getEventConnection())
953 $result = $this->
updateInstance($syncEvent->getEvent(), $eventContext);
957 $eventContext->setEventConnection($syncEvent->getEventConnection());
958 $result = $this->
update($syncEvent->getEvent(), $eventContext);
960 if ($result->isSuccess())
962 $syncEvent->getEventConnection()
963 ->setEntityTag($result->getData()[
'event'][
'etag'])
964 ->setVendorVersionId($result->getData()[
'event'][
'version'])
976 private function getExcludedDatesCollection(SyncEvent $recurrenceEvent): Core\Event\Properties\ExcludedDatesCollection
978 $excludes =
new Core\Event\Properties\ExcludedDatesCollection();
981 foreach ($recurrenceEvent->getEvent()->getExcludedDateCollection() as $item)
983 $excludes->add($item);
993 private function prepareMasterEvent(SyncEvent $syncEvent): Event
995 $event = (
new Core\Builders\EventCloner($syncEvent->getEvent()))->build();
996 (
new Calendar\Sync\Util\ExcludeDatesHandler())->prepareEventExcludeDates($event, $syncEvent->getInstanceMap());