1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
eventmanager.php
См. документацию.
1<?php
2
3namespace Bitrix\Calendar\Sync\Office365;
4
5use Bitrix\Bizproc\Error;
6use Bitrix\Calendar;
7use Bitrix\Calendar\Sync\Exceptions\GoneException;
8use Bitrix\Main;
9use Bitrix\Calendar\Core;
10use Bitrix\Calendar\Core\Base\BaseException;
11use Bitrix\Calendar\Core\Event\Event;
12use Bitrix\Calendar\Sync\Entities\SyncEvent;
13use Bitrix\Calendar\Sync\Connection\EventConnection;
14use Bitrix\Calendar\Sync\Connection\SectionConnection;
15use Bitrix\Calendar\Sync\Exceptions\ApiException;
16use Bitrix\Calendar\Sync\Exceptions\AuthException;
17use Bitrix\Calendar\Sync\Exceptions\ConflictException;
18use Bitrix\Calendar\Sync\Exceptions\NotFoundException;
19use Bitrix\Calendar\Sync\Exceptions\RemoteAccountException;
20use Bitrix\Calendar\Sync\Internals\ContextInterface;
21use Bitrix\Calendar\Sync\Internals\HasContextTrait;
22use Bitrix\Calendar\Sync\Managers\EventManagerInterface;
23use Bitrix\Calendar\Sync\Util\EventContext;
24use Bitrix\Calendar\Sync\Office365\Converter\EventConverter;
25use Bitrix\Calendar\Sync\Office365\Dto\EventDto;
26use Bitrix\Calendar\Sync\Util\Context;
27use Bitrix\Calendar\Sync\Office365;
28use Bitrix\Calendar\Sync\Util\Result;
29use Bitrix\Main\ArgumentException;
30use Bitrix\Main\ArgumentNullException;
31use Bitrix\Main\LoaderException;
32use Bitrix\Main\ObjectException;
33use Bitrix\Main\ObjectPropertyException;
34use Bitrix\Main\SystemException;
35use Bitrix\Main\Type\Date;
36use DateTimeZone;
37use Generator;
38
40{
41 use HasContextTrait;
42
43 private Helper $helper;
44
48 private ?EventConverter $eventConverter;
49
50 private Core\Mappers\EventConnection $eventConnectionMapper;
51
56 {
57 $this->context = $context;
58 $this->helper = $context->getHelper();
59 parent::__construct($context->getConnection());
60 }
61
75 public function create(Core\Event\Event $event, EventContext $context): Result
76 {
77 $result = new Result();
78 $internalDto = $this->getEventConverter()->eventToDto($event);
79
80 try
81 {
82 $dto = $this->getService()->createEvent($internalDto, $context->getSectionConnection()->getVendorSectionId());
83 if ($dto)
84 {
85 if ($event->getExcludedDateCollection() && $event->getExcludedDateCollection()->count())
86 {
87 $context->add('sync', 'masterEventId', $dto->id);
89 foreach ($event->getExcludedDateCollection() as $item)
90 {
91 $context->add('sync', 'excludeDate', $item);
92 $this->deleteInstance($event, $context);
93 }
94 }
95
96 if (!empty($dto->id))
97 {
98 $result->setData($this->prepareResultData($dto));
99 }
100 else
101 {
102 $result->addError(new Main\Error('Error of create a series master event'));
103 }
104 }
105 else
106 {
107 $result->addError(new Main\Error('Error of create event'));
108 }
109 }
110 catch (ApiException $exception)
111 {
112 if ((int)$exception->getCode() !== 400)
113 {
114 throw $exception;
115 }
116
117 $result->addError(new Main\Error($exception->getMessage(), $exception->getCode()));
118 }
119 catch (AuthException $exception)
120 {
121 $result->addError(new Main\Error($exception->getMessage(), $exception->getCode()));
122 }
123
124 return $result;
125 }
126
140 {
141 $result = new Result();
142 $internalDto = $this->getEventConverter()->eventToDto($event);
143 $this->enrichVendorData($internalDto, $context->getEventConnection());
144
145 try
146 {
147 $dto = $this->getService()->updateEvent($context->getEventConnection()->getVendorEventId(), $internalDto);
148 if ($dto)
149 {
150 $result->setData($this->prepareResultData($dto));
151 }
152 }
153 catch (ApiException $exception)
154 {
155 if ((int)$exception->getCode() !== 400)
156 {
157 throw $exception;
158 }
159
160 $result->addError(new Main\Error($exception->getMessage(), $exception->getCode()));
161 }
162 catch (AuthException $exception)
163 {
164 $result->addError(new Main\Error($exception->getMessage(), $exception->getCode()));
165 }
166
167 return $result;
168 }
169
186 {
187 $this->getService()->deleteEvent($context->getEventConnection()->getVendorEventId());
188
189 return new Result();
190 }
191
197 private function prepareResultData(EventDto $dto): array
198 {
199 return [
200 'dto' => $dto,
201 'event' => [
202 'id' => $dto->id,
203 'version' => $dto->changeKey,
204 'etag' => $dto->etag,
205 'recurrence' => $dto->seriesMasterId ?? null,
206 'data' => $this->prepareCustomData($dto),
207 ],
208 ];
209 }
210
223 {
224 $eventLink = $context->sync['instanceLink'];
225 if ($eventLink)
226 {
227 $eventContext = (new EventContext())
228 ->setSectionConnection($context->getSectionConnection())
229 ->setEventConnection($eventLink)
230 ;
231 return $this->update($event, $eventContext);
232 }
233
234 return (new Result())->addError(new Main\Error('Not found link for instance'));
235 }
236
250 {
251 if (
252 $event->getOriginalDateFrom()
253 && $event->getOriginalDateFrom()->format('Ymd') !== $event->getStart()->format('Ymd')
254 )
255 {
256 return $this->moveInstance($event, $context);
257 }
258
259 $result = new Result();
260 $masterLink = $context->getEventConnection();
261
262 try
263 {
264 if ($masterLink && $instance = $this->getInstanceForDay($masterLink->getVendorEventId(), $event->getStart()->getDate()))
265 {
266 $dto = $this->getService()->updateEvent(
267 $instance->id,
268 $this->getEventConverter()->eventToDto($event),
269 );
270 if ($dto && !empty($dto->id))
271 {
272 $result->setData($this->prepareResultData($dto));
273 }
274 else
275 {
276 $result->addError(new Main\Error("Error of create instance.", 404));
277 }
278 }
279 else
280 {
281 $result->addError(new Main\Error("Instances for event not found", 404));
282 }
283 }
284 catch (ApiException $exception)
285 {
286 if (!in_array((int)$exception->getCode(), [400, 404], true))
287 {
288 throw $exception;
289 }
290
291 $result->addError(new Main\Error($exception->getMessage(), $exception->getCode()));
292 }
293 catch (AuthException $exception)
294 {
295 $result->addError(new Main\Error($exception->getMessage(), $exception->getCode()));
296 }
297
298 return $result;
299 }
300
319 {
320 $result = new Result();
321 $masterEventId = $context->getEventConnection()
322 ? $context->getEventConnection()->getVendorEventId()
323 : ($context->sync['masterEventId'] ?? null)
324 ;
325 if ($masterEventId)
326 {
327 $excludeDate = new Main\Type\DateTime(
328 $context->sync['excludeDate']->getDate()->format('Ymd 000000'),
329 'Ymd His',
330 $event->getStartTimeZone()
331 ? $event->getStartTimeZone()->getTimeZone()
332 : new \DateTimeZone('UTC')
333 );
334 try
335 {
336 if ($instance = $this->getInstanceForDay($masterEventId, $excludeDate))
337 {
338 $this->getService()->deleteEvent(
339 $instance->id,
340 );
341 }
342 else
343 {
344 $result->addError(new Main\Error("Instances for event not found", 404));
345 }
346 }
347 catch (ApiException $e)
348 {
349 if ((int)$e->getCode() !== 400 && (int)$e->getCode() !== 404)
350 {
351 throw $e;
352 }
353
354 $result->addError(new Main\Error($e->getMessage(), $e->getCode()));
355 }
356 catch (AuthException $exception)
357 {
358 $result->addError(new Main\Error($exception->getMessage(), $exception->getCode()));
359 }
360 }
361
362 return $result;
363 }
364
381 private function moveInstance(Event $event, EventContext $context): Result
382 {
383 $result = new Result();
384 $instance = null;
385 $masterLink = $context->getEventConnection();
386
387 if ($masterLink && $event->getOriginalDateFrom())
388 {
389 try
390 {
391 $instance = $this->getInstanceForDay(
392 $masterLink->getVendorEventId(),
393 $event->getOriginalDateFrom()->getDate()
394 );
395 }
396 catch (ApiException $exception)
397 {
398 if (!in_array((int)$exception->getCode(), [400, 404], true))
399 {
400 throw $exception;
401 }
402
403 $result->addError(new Main\Error($exception->getMessage(), $exception->getCode()));
404
405 return $result;
406 }
407 }
408
409 if ($instance)
410 {
411 try
412 {
413 $dto = $this->getService()->updateEvent(
414 $instance->id,
415 $this->getEventConverter()->eventToDto($event),
416 );
417 if ($dto && !empty($dto->id))
418 {
419 $result->setData($this->prepareResultData($dto));
420 }
421 else
422 {
423 $result->addError(new Main\Error('Error of move instance', 400));
424 }
425 }
426 catch (NotFoundException $e)
427 {
428 $result->addError(new Main\Error('Instance not found'));
429 }
430 }
431 else
432 {
433 $result->addError(new Main\Error('Instance not found'));
434 }
435
436 return $result;
437 }
438
456 public function fetchSectionEvents(SectionConnection $sectionLink): Generator
457 {
458 foreach ($this->getService()->getCalendarDelta($sectionLink) as $deltaData)
459 {
460 $data = [];
461 if (!empty($deltaData[Helper::EVENT_TYPES['deleted']]))
462 {
464 $dto = $deltaData[Helper::EVENT_TYPES['deleted']];
465 $data[] = [
466 'type' => 'deleted',
467 'id' => $dto->id,
468 'version' => $dto->changeKey,
469 'etag' => $dto->etag,
470 ];
471
472
473// $this->processEventInstance($deltaData[Helper::EVENT_TYPES['deleted']], $sectionLink);
474 }
475 elseif (!empty($deltaData[Helper::EVENT_TYPES['single']]))
476 {
478 $dto = $deltaData[Helper::EVENT_TYPES['single']];
479 $data[] = [
480 'type' => 'single',
481 'event' => $this->context->getConverter()
482 ->convertEvent($dto, $sectionLink->getSection()),
483 'id' => $dto->id,
484 'version' => $dto->changeKey,
485 'etag' => $dto->etag,
486 'data' => $this->prepareCustomData($dto),
487 ];
488 }
489 elseif (!empty($deltaData[Helper::EVENT_TYPES['series']]))
490 {
491 $data = $this->prepareSeries($deltaData, $sectionLink);
492
493 }
494
495 if ($data)
496 {
497 yield $data;
498 }
499 }
500 }
501
522 public function saveRecurrence(
523 SyncEvent $recurrenceEvent,
524 SectionConnection $sectionConnection,
525 Context $context
526 ): Result
527 {
528 $result = new Result();
529
530 if ($recurrenceEvent->getEventConnection())
531 {
532 try
533 {
534 $masterResult = $this->updateRecurrenceInstance($recurrenceEvent, $context);
535 }
536 catch(Calendar\Sync\Exceptions\NotFoundException $e)
537 {
538 $this->getEventConnectionMapper()->delete($recurrenceEvent->getEventConnection());
539 $recurrenceEvent->setEventConnection(null);
540 return $this->saveRecurrence($recurrenceEvent, $sectionConnection, $context);
541 }
542 }
543 else
544 {
545 $masterResult = $this->createRecurrenceInstance($recurrenceEvent, $sectionConnection, $context);
546 }
547
548 if (!$masterResult->isSuccess())
549 {
550 $result->addErrors($masterResult->getErrors());
551 return $result;
552 }
553
554 if ($recurrenceEvent->getEventConnection())
555 {
556 $recurrenceEvent->getEventConnection()
557 ->setLastSyncStatus(Calendar\Sync\Dictionary::SYNC_STATUS['success']);
558 }
559 $recurrenceEvent
560 ->setAction(Calendar\Sync\Dictionary::SYNC_STATUS['success']);
561
562 $excludes = $this->getExcludedDatesCollection($recurrenceEvent);
563 if ($recurrenceEvent->getInstanceMap())
564 {
566 foreach ($recurrenceEvent->getInstanceMap()->getCollection() as $instance)
567 {
568 if ($instance->getEvent()->getOriginalDateFrom() === null)
569 {
570 $result->addError(
571 new Main\Error('Instance is invalid - there is not original date from. ['.$instance->getEvent()->getId().']', 400));
572 continue;
573 }
574
575 if ($instance->getEventConnection())
576 {
577 try
578 {
579 $instanceResult = $this->updateRecurrenceInstance($instance, $context, $recurrenceEvent->getEventConnection());
580 }
581 catch(Calendar\Sync\Exceptions\NotFoundException $e)
582 {
583 $this->getEventConnectionMapper()->delete($instance->getEventConnection());
584 $instance->setEventConnection(null);
585 $instanceResult = $this->createRecurrenceInstance(
586 $instance,
587 $sectionConnection,
588 $context,
589 $recurrenceEvent->getEventConnection()
590 );
591 }
592 }
593 else
594 {
595 $instanceResult = $this->createRecurrenceInstance(
596 $instance,
597 $sectionConnection,
598 $context,
599 $recurrenceEvent->getEventConnection()
600 );
601 }
602 $excludes->removeDateFromCollection($instance->getEvent()->getOriginalDateFrom());
603 if (
604 $instance->getEvent()->getStart()->format('Ymd')
605 !== $instance->getEvent()->getOriginalDateFrom()->format('Ymd')
606 )
607 {
608 $excludes->removeDateFromCollection($instance->getEvent()->getStart());
609 }
610 if (!$instanceResult->isSuccess())
611 {
612 $result->addErrors($instanceResult->getErrors());
613 }
614 if ($instance->getEventConnection())
615 {
616 $instance->getEventConnection()->setLastSyncStatus(Calendar\Sync\Dictionary::SYNC_STATUS['success']);
617 }
618 }
619 }
620
621 if ($excludes->count() > 0)
622 {
623 $context = (new EventContext())->setEventConnection($recurrenceEvent->getEventConnection());
625 foreach ($excludes as $excludedDate)
626 {
627 $context->add('sync', 'excludeDate', $excludedDate);
628 $this->deleteInstance($recurrenceEvent->getEvent(), $context);
629 }
630 }
631
632 return $result;
633 }
634
638 private function getEventConnectionMapper(): Core\Mappers\EventConnection
639 {
640 if (empty($this->eventConnectionMapper))
641 {
642 $this->eventConnectionMapper = new Core\Mappers\EventConnection();
643 }
644
645 return $this->eventConnectionMapper;
646 }
647
668 public function createRecurrence(
669 SyncEvent $recurrenceEvent,
670 SectionConnection $sectionConnection,
672 ): Result
673 {
674 return $this->saveRecurrence($recurrenceEvent, $sectionConnection, $context);
675 }
676
689 public function updateRecurrence(
690 SyncEvent $recurrenceEvent,
691 SectionConnection $sectionConnection,
693 ): Result
694 {
695 return $this->saveRecurrence($recurrenceEvent, $sectionConnection, $context);
696 }
697
706 private function prepareSeries($deltaData, SectionConnection $sectionLink): array
707 {
708 $result = [];
710 $masterDto = $deltaData[Helper::EVENT_TYPES['series']];
712 $exceptionList = $deltaData[Helper::EVENT_TYPES['exception']] ?? [];
713
714 $masterEvent = $this->context->getConverter()->convertEvent($masterDto, $sectionLink->getSection());
715
716 if (!empty($exceptionList)) {
717 $exceptionsDates = array_map(function (EventDto $exception) {
718 return (new Main\Type\DateTime(
719 $exception->start->dateTime,
720 $this->helper::TIME_FORMAT_LONG,
721 new DateTimeZone($exception->start->timeZone),
722 ))->format('d.m.Y');
723 }, $exceptionList
724 );
725
726 $excludeCollection = new Core\Event\Properties\ExcludedDatesCollection($exceptionsDates);
727 $masterEvent->setExcludedDateCollection($excludeCollection);
728 }
729
730 $result[] = [
731 'type' => 'master',
732 'id' => $masterDto->id,
733 'event' => $masterEvent,
734 'version' => $masterDto->changeKey,
735 'etag' => $masterDto->etag,
736 'data' => $this->prepareCustomData($masterDto),
737 ];
738
739 foreach ($exceptionList as $exception) {
740 $event = $this->context->getConverter()->convertEvent($exception, $sectionLink->getSection());
741 $result[] = [
742 'type' => 'exception',
743 'event' => $event,
744 'id' => $exception->id,
745 'version' => $exception->changeKey,
746 'etag' => $exception->etag,
747 'recurrence' => $exception->seriesMasterId,
748 'data' => $this->prepareCustomData($exception),
749 ];
750 }
751
752 return $result;
753 }
754
771 private function getInstanceForDay(string $eventId, Main\Type\Date $dayStart): ? EventDto
772 {
773 $dateFrom = clone $dayStart;
774 if ($dateFrom instanceof Main\Type\DateTime)
775 {
776 $dateFrom->setTime(0,0);
777 }
778 $dateTo = clone $dateFrom;
779 $dateTo->add('1 day');
780
781 $instances = $this->getService()->getEventInstances([
782 'filter' => [
783 'event_id' => $eventId,
784 'from' => $dateFrom->format('c'), //($this->helper::TIME_FORMAT_LONG),
785 'to' => $dateTo->format('c'), //($this->helper::TIME_FORMAT_LONG),
786 ],
787 ]);
788
789 return $instances ? $instances[0] : null;
790 }
791
795 private function getEventConverter(): EventConverter
796 {
797 if (empty($this->eventConverter))
798 {
799 $this->eventConverter = new EventConverter();
800 }
801
802 return $this->eventConverter;
803 }
804
813 private function getService(): VendorSyncService
814 {
815 return $this->context->getVendorSyncService();
816 }
817
824 private function prepareCustomData(EventDto $dto): array
825 {
826 $result = [];
827 if (!empty($dto->location))
828 {
829 $result['location'] = $dto->location->toArray(true);
830 }
831 if (!empty($dto->locations))
832 {
833 foreach ($dto->locations as $location)
834 {
835 $result['locations'][] = $location->toArray(true);
836 }
837 }
838
839 if (!empty($dto->attendees))
840 {
841 $result['attendees'] = [];
842 foreach ($dto->attendees as $attendee)
843 {
844 $result['attendees'][] = $attendee->toArray(true);
845 }
846 }
847
848 return $result;
849 }
850
857 private function enrichVendorData(EventDto $dto, EventConnection $link)
858 {
859 // TODO: add info about attendees, locations and any others
860 }
861
875 private function createRecurrenceInstance(
876 SyncEvent $syncEvent,
877 SectionConnection $sectionConnection,
878 Context $context,
879 EventConnection $masterLink = null
880 ): Result
881 {
882 try
883 {
884 $eventContext = new EventContext();
885 $eventContext->merge($context);
886 if ($masterLink)
887 {
888 $eventContext->setEventConnection($masterLink);
889 $event = (new Core\Builders\EventCloner($syncEvent->getEvent()))->build();
890 $result = $this->createInstance($event, $eventContext);
891 }
892 else
893 {
894 $eventContext->setSectionConnection($sectionConnection);
895 $event = $this->prepareMasterEvent($syncEvent);
896 $result = $this->create($event, $eventContext);
897 }
898 if ($result->isSuccess())
899 {
900 if (!$syncEvent->getEvent()->isDeleted())
901 {
902 if (!empty($result->getData()['event']['id']))
903 {
904 $link = (new EventConnection())
905 ->setEvent($event)
906 ->setConnection($sectionConnection->getConnection())
907 ->setVersion($event->getVersion())
908 ->setVendorEventId($result->getData()['event']['id'])
909 ->setEntityTag($result->getData()['event']['etag'])
910 ->setVendorVersionId($result->getData()['event']['version'])
911 ->setRecurrenceId($result->getData()['event']['recurrence'] ?? null)
912 ->setLastSyncStatus(Calendar\Sync\Dictionary::SYNC_STATUS['success'])
913 ;
914 $syncEvent
915 ->setEventConnection($link)
916 ->setAction(Calendar\Sync\Dictionary::SYNC_STATUS['success']);
917 }
918 else
919 {
920 $errMessage = 'Unknown error of creating recurrence '
921 . ($masterLink ? 'master' : 'instance');
922 $result->addError(new Main\Error($errMessage, 400, ['data' => $result->getData()]));
923 }
924
925 }
926 else
927 {
928 $syncEvent->setAction(Calendar\Sync\Dictionary::SYNC_EVENT_ACTION['delete']);
929 }
930 }
931
932 return $result;
933 }
934 catch (Core\Base\BaseException $e)
935 {
936 return (new Result())->addError(new Main\Error($e->getMessage()));
937 }
938 }
939
953 private function updateRecurrenceInstance(
954 SyncEvent $syncEvent,
955 Context $context,
956 EventConnection $masterLink = null
957 ): Result
958 {
959 $eventContext = new EventContext();
960 $eventContext->merge($context);
961 if ($masterLink)
962 {
963 $eventContext
964 ->setEventConnection($masterLink)
965 ->add('sync', 'instanceLink', $syncEvent->getEventConnection())
966 ;
967 $result = $this->updateInstance($syncEvent->getEvent(), $eventContext);
968 }
969 else
970 {
971 $eventContext->setEventConnection($syncEvent->getEventConnection());
972 $result = $this->update($syncEvent->getEvent(), $eventContext);
973 }
974 if ($result->isSuccess())
975 {
976 $syncEvent->getEventConnection()
977 ->setEntityTag($result->getData()['event']['etag'])
978 ->setVendorVersionId($result->getData()['event']['version'])
979 ;
980 }
981
982 return $result;
983 }
984
990 private function getExcludedDatesCollection(SyncEvent $recurrenceEvent): Core\Event\Properties\ExcludedDatesCollection
991 {
992 $excludes = new Core\Event\Properties\ExcludedDatesCollection();
993
995 foreach ($recurrenceEvent->getEvent()->getExcludedDateCollection() as $item)
996 {
997 $excludes->add($item);
998 }
999 return $excludes;
1000 }
1001
1007 private function prepareMasterEvent(SyncEvent $syncEvent): Event
1008 {
1009 $event = (new Core\Builders\EventCloner($syncEvent->getEvent()))->build();
1010 (new Calendar\Sync\Util\ExcludeDatesHandler())->prepareEventExcludeDates($event, $syncEvent->getInstanceMap());
1011
1012 return $event;
1013 }
1014}
updateRecurrence(SyncEvent $recurrenceEvent, SectionConnection $sectionConnection, Context $context)
Определения eventmanager.php:689
createRecurrence(SyncEvent $recurrenceEvent, SectionConnection $sectionConnection, Context $context)
Определения eventmanager.php:668
__construct(ContextInterface $context)
Определения eventmanager.php:55
createInstance(Core\Event\Event $event, EventContext $context)
Определения eventmanager.php:249
update(Core\Event\Event $event, EventContext $context)
Определения eventmanager.php:139
updateInstance(Event $event, EventContext $context)
Определения eventmanager.php:222
deleteInstance(Event $event, EventContext $context)
Определения eventmanager.php:318
Определения error.php:15
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
create(Event $event, EventContext $context)
$context
Определения csv_new_setup.php:223
Определения culture.php:9
Определения collection.php:2
trait Error
Определения error.php:11
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$instance
Определения ps_b24_final.php:14
$location
Определения options.php:2729