Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
synchronization.php
1<?php
2
4
5use Bitrix\Calendar\Core;
34use CCalendar;
35use CCalendarSect;
36use Exception;
37
39{
41 private array $subManagers = [];
45 private FactoriesCollection $factories;
49 private Core\Mappers\Factory $mapperFactory;
50
56 public function __construct(FactoriesCollection $factories)
57 {
58 $this->factories = $factories;
59 $this->mapperFactory = ServiceLocator::getInstance()->get('calendar.service.mappers.factory');
60
61 }
62
73 public function createEvent(Event $event, Context $context): Result
74 {
75 if ($event->getExcludedDateCollection() && $event->getExcludedDateCollection()->count())
76 {
77 $slaveEvents = $this->getSlaveEvents($event);
78 $context->add('sync', 'slaveEvents', $slaveEvents);
79 $this->prepareEventExDates($event, $slaveEvents);
80 }
81 return $this->execActionEvent('createEvent', $event, $context);
82 }
83
94 public function updateEvent(Event $event, Context $context): Result
95 {
96 if ($event->getExcludedDateCollection()->count())
97 {
98 $slaveEvents = $this->getSlaveEvents($event);
99 $context->add('sync', 'slaveEvents', $slaveEvents);
100 $this->prepareEventExDates($event, $slaveEvents);
101 }
102 return $this->execActionEvent('updateEvent', $event, $context);
103 }
104
115 public function deleteEvent(Event $event, Context $context): Result
116 {
117 return $this->execActionEvent('deleteEvent', $event, $context);
118 }
119
131 private function execActionEvent(string $method, Event $event, Context $context): Result
132 {
133 $mainResult = new Result();
134 $data = [];
135 $eventCloner = new Core\Builders\EventCloner($event);
136 $pushManager = new PushManager();
137 $push = null;
138
140 foreach ($this->factories as $factory)
141 {
142 if ($this->checkExclude($factory, $context, $method))
143 {
144 continue;
145 }
146
147 try
148 {
149
150 $clonedEvent = $eventCloner->build();
151 $vendorSync = $this->getVendorSynchronization($factory);
152 $eventContext = $this->prepareEventContext($clonedEvent, clone $context, $factory);
153
154 if ($eventContext->getSectionConnection()?->getId())
155 {
156 $push = $pushManager->getPush(PushManager::TYPE_SECTION_CONNECTION, $eventContext->getSectionConnection()->getId());
157 $pushManager->setBlockPush($push);
158 }
159
160 $vendorResult = $vendorSync->$method($clonedEvent, $eventContext);
161
162 $data[$this->getVendorCode($factory)] = $vendorResult;
163 if (!$vendorResult->isSuccess())
164 {
165 $mainResult->addErrors($vendorResult->getErrors());
166 }
167 }
168 catch (RemoteAccountException $e)
169 {
170 $mainResult->addError(new Error($e->getMessage(), $e->getCode()));
171 }
172 finally
173 {
174 $pushManager->setUnblockPush($push);
175 }
176 }
177
178 return $mainResult->setData($data);
179 }
180
194 public function reCreateRecurrence(Event $masterEvent, Context $context): Result
195 {
196 $mainResult = new Result();
197 $eventExceptionsMap = $this->getEventExceptionsMap($masterEvent);
198 $data = [];
199 $masterEvent->setVersion($masterEvent->getVersion() + 1);
200 $eventCloner = new Core\Builders\EventCloner($masterEvent);
201
203 foreach ($this->factories as $factory)
204 {
205 if ($this->checkExclude($factory, $context))
206 {
207 continue;
208 }
209 try
210 {
211 $safeEvent = $eventCloner->build();
212 $safeExceptionsMap = $this->cloneEventMap($eventExceptionsMap);
213 $vendorSyncManager = $this->getVendorSynchronization($factory);
214 $eventContext = $this->prepareEventContext($safeEvent, clone $context, $factory);
215
216 if ($factory->getServiceName() === Sync\Icloud\Factory::SERVICE_NAME)
217 {
218 $syncEvent = $this->prepareRecurrenceEvent($safeEvent, $safeExceptionsMap, $factory);
219 $vendorResult = $vendorSyncManager->updateRecurrence($syncEvent, $eventContext);
220 }
221 else
222 {
223 $syncEvent = $this->prepareRecurrenceEvent($safeEvent, $safeExceptionsMap);
224 $deleteResult = $vendorSyncManager->deleteEvent($safeEvent, clone $eventContext);
225 if ($deleteResult->isSuccess() && $syncEvent->getEventConnection())
226 {
227 $this->mapperFactory->getEventConnection()->delete($syncEvent->getEventConnection());
228 }
229 $syncEvent->setEventConnection(null);
230 $vendorResult = $vendorSyncManager->createRecurrence($syncEvent, $eventContext);
231 }
232
233 $data[$this->getVendorCode($factory)] = $vendorResult;
234 if (!$vendorResult->isSuccess())
235 {
236 $mainResult->addErrors($vendorResult->getErrors());
237 }
238 }
239 catch (RemoteAccountException $e)
240 {
241 $mainResult->addError(new Error($e->getMessage(), $e->getCode()));
242 }
243 }
244
245 return $mainResult->setData($data);
246 }
247
261 public function createRecurrence(Event $event, Context $context): Result
262 {
263 // if (empty($event->getRecurringRule()))
264 // {
265 // return $this->createEvent($event, $context);
266 // }
267
268 $eventExceptionsMap = $this->getEventExceptionsMap($event);
269 $mainResult = new Result();
270 $data = [];
271 $eventCloner = new Core\Builders\EventCloner($event);
273 foreach ($this->factories as $factory)
274 {
275 if ($this->checkExclude($factory, $context))
276 {
277 continue;
278 }
279 try
280 {
281 $safeEvent = $eventCloner->build();
282 $vendorSyncManager = $this->getVendorSynchronization($factory);
283 $eventContext = $this->prepareEventContext($safeEvent, $context, $factory);
284 $syncEvent = $this->prepareRecurrenceEvent($safeEvent, $eventExceptionsMap);
285
286 $vendorResult = $vendorSyncManager->createRecurrence($syncEvent, $eventContext);
287
288 $data[$this->getVendorCode($factory)] = $vendorResult;
289 if (!$vendorResult->isSuccess())
290 {
291 $mainResult->addErrors($vendorResult->getErrors());
292 }
293 }
294 catch (RemoteAccountException $e)
295 {
296 $mainResult->addError(new Error($e->getMessage(), $e->getCode()));
297 }
298
299 }
300
301 return $mainResult->setData($data);
302 }
303
314 public function createInstance(Event $event, Context $context): Result
315 {
316 return $this->execActionEvent('createInstance', $event, $context);
317 }
318
329 public function updateInstance(Event $event, Context $context): Result
330 {
331 return $this->execActionEvent('updateInstance', $event, $context);
332 }
333
344 public function deleteInstance(Event $event, Context $context): Result
345 {
346 $mainResult = new Result();
347
348 if (
349 !isset($context->diff['EXDATE'])
350 && !isset($context->sync['excludeDate'])
351 && !$event->getExcludedDateCollection()->count()
352 )
353 {
354 $mainResult->addError(new Error('Not found info about exclude date'));
355 return $mainResult;
356 }
357
358 if (!isset($context->sync['excludeDate']))
359 {
360 $diff = is_array($context->diff['EXDATE'])
361 ? $context->diff['EXDATE']
362 : explode(';', $context->diff['EXDATE']);
363 if (isset($context->sync['excludeDate']))
364 {
365 $excludeDate = $context->sync['excludeDate'];
366 }
367 else
368 {
369 $excludeDates = array_filter(
370 $event->getExcludedDateCollection()->getCollection(),
371 function($item) use ($diff)
372 {
373 return !in_array($item->format(CCalendar::DFormat(false)), $diff);
374 });
375 $excludeDate = $excludeDates ? reset($excludeDates) : [];
376 }
377
378 $context->add('sync', 'excludeDate', $excludeDate);
379 }
380
381 return $this->execActionEvent('deleteInstance', $event, $context);
382
383 }
384
396 private function execActionSection(
397 string $method,
398 Section $section,
399 Context $context
400 ): Result
401 {
402 $mainResult = new Result();
403 $resultData = [];
404 $pushManager = new PushManager();
405 $push = null;
406
408 foreach ($this->factories as $factory)
409 {
410 if ($this->checkExclude($factory, $context, $method))
411 {
412 continue;
413 }
414
415 if ($factory->getConnection()->getId())
416 {
417 $push = $pushManager->getPush(PushManager::TYPE_CONNECTION, $factory->getConnection()->getId());
418 $pushManager->setBlockPush($push);
419 }
420
421 try
422 {
423 $vendorSync = $this->getVendorSynchronization($factory);
424 $sectionContext = $this->prepareSectionContext($section, $context, $factory);
425 $vendorResult = $vendorSync->$method($section, $sectionContext);
426 $resultData[$this->getVendorCode($factory)] = $vendorResult;
427 if (!$vendorResult->isSuccess())
428 {
429 $mainResult->addErrors($vendorResult->getErrors());
430 }
431 }
432 catch (RemoteAccountException $e)
433 {
434 $mainResult->addError(new Error($e->getMessage(), $e->getCode()));
435 }
436 finally
437 {
438 $pushManager->setUnblockPush($push);
439 }
440 }
441
442 return $mainResult->setData($resultData);
443 }
444
455 public function createSection(Section $section, Context $context): Result
456 {
457 return $this->execActionSection('createSection', $section, $context);
458 }
459
470 public function updateSection(Section $section, Context $context): Result
471 {
472 return $this->execActionSection('updateSection', $section, $context);
473 }
474
485 public function deleteSection(Section $section, Context $context): Result
486 {
487 $result = $this->execActionSection('deleteSection', $section, $context);
488 CCalendarSect::cleanLinkTables($section->getId());
489
490 return $result;
491 }
492
505 public function upEventVersion(Event $event, Connection $connection, string $version)
506 {
508 $link = $this->mapperFactory->getEventConnection()->getMap([
509 '=CONNECTION_ID' => $connection->getId(),
510 '=EVENT_ID' => $event->getId(),
511 ])->fetch();
512
513 if ($link)
514 {
515 $link->setVersion((int)$version);
516 EventConnectionTable::update($link->getId(), [
517 'fields' => [
518 'VERSION' => $version,
519 ],
520 ]);
521 }
522 }
523
531 private function getVendorSynchronization(FactoryInterface $factory): VendorSynchronization
532 {
533 $key = $factory->getConnection()->getVendor()->getCode();
534 if (empty($this->subManagers[$key]))
535 {
536 $this->subManagers[$key] = new VendorSynchronization($factory);
537 }
538
539 return $this->subManagers[$key];
540 }
541
549 private function checkExclude(
550 FactoryInterface $factory,
551 ?Context $context,
552 string $string = ''
553 ): bool
554 {
555 if (isset($context->sync)
556 && !empty($context->sync['originalFrom'])
557 && $context->sync['originalFrom'] === $this->getVendorCode($factory)
558 )
559 {
560 return true;
561 }
562
563 return false;
564 }
565
571 private function getVendorCode(FactoryInterface $factory): string
572 {
573 return $factory->getConnection()->getVendor()->getCode();
574 }
575
587 private function prepareEventContext(Event $event, Context $context, FactoryInterface $factory): EventContext
588 {
589 $sectionLink = $context->sync['sectionLink'] ?? $this->getSectionConnection($event->getSection(), $factory);
590 $eventLink = $context->sync['eventLink'] ?? $this->getEventConnection($event, $factory);
591
592 return (new EventContext())
593 ->merge($context)
594 ->setSectionConnection($sectionLink)
595 ->setEventConnection($eventLink)
596 ;
597 }
598
610 private function prepareSectionContext(Section $section, Context $context, FactoryInterface $factory)
611 {
612 $sectionLink = $context->sync['sectionLink'] ?? $this->getSectionConnection($section, $factory);
613
614 return (new SectionContext())
615 ->merge($context)
616 ->setSectionConnection($sectionLink)
617 ;
618 }
619
630 private function getSectionConnection(Section $section, FactoryInterface $factory): ?SectionConnection
631 {
632 return $this->mapperFactory->getSectionConnection()->getMap([
633 '=SECTION_ID' => $section->getId(),
634 '=CONNECTION_ID' => $factory->getConnection()->getId(),
635 ])->fetch();
636 }
637
647 private function getEventConnection(Event $event, ?FactoryInterface $factory): ?EventConnection
648 {
649 if (!$factory)
650 {
651 return null;
652 }
653 $link = $this->mapperFactory->getEventConnection()->getMap([
654 '=EVENT_ID' => $event->getId(),
655 '=CONNECTION_ID' => $factory->getConnection()->getId(),
656 ])->fetch();
657
658 return $link;
659 }
660
669 private function getSlaveEvents(Event $event): Core\Base\Map
670 {
671 $map = $this->getEventExceptionsMap($event);
672
673 return new Core\Event\EventMap(
674 array_reduce($map->getCollection(), static function ($result, $value)
675 {
677 if ($value->getOriginalDateFrom())
678 {
679 $key = $value->getOriginalDateFrom()->format('Ymd');
680 $result[$key] = $value->getId();
681 }
682
683 return $result;
684 }, []) ?? []
685 );
686 }
687
694 private function prepareEventExDates(Event $event, Core\Base\Map $slaveEvents)
695 {
696 (new ExcludeDatesHandler())->prepareEventExcludeDates($event, $slaveEvents);
697 }
698
708 private function getEventExceptionsMap(Event $event): Core\Base\Map
709 {
710 $mapClass = Core\Event\EventMap::class;
711 $result = new $mapClass;
712
713 $queryResult = EventTable::query()
714 ->setSelect(['*'])
715 ->where('RECURRENCE_ID', $event->getParentId())
716 ->where('DELETED', 'N')
717 ->where('OWNER_ID', $event->getOwner()->getId())
718 ->where(Query::filter()
719 ->logic('or')
720 ->whereNot('MEETING_STATUS', 'N')
721 ->whereNull('MEETING_STATUS')
722 )
723 ->whereNotNull('ORIGINAL_DATE_FROM')
724 ->exec()
725 ;
727 while ($row = $queryResult->fetchObject())
728 {
729 $eventEntity = $this->mapperFactory->getEvent()->getByEntityObject($row);
730 if ($eventEntity)
731 {
732 $result->add($eventEntity, $eventEntity->getId());
733 }
734 }
735
736 return $result;
737 }
738
750 private function prepareRecurrenceEvent(
751 Event $event,
752 Core\Base\Map $eventExceptionsMap,
753 ?FactoryInterface $factory = null
754 ): SyncEvent
755 {
756 $masterLink = $this->getEventConnection($event, $factory);
757 $syncEvent = (new SyncEvent())
758 ->setEvent($event)
759 ->setAction(Dictionary::SYNC_EVENT_ACTION['create'])
760 ->setEventConnection($masterLink)
761 ->setInstanceMap(new InstanceMap());
763 foreach ($eventExceptionsMap as $exceptionEvent)
764 {
765 $instance = (new SyncEvent())
766 ->setEvent($exceptionEvent)
767 ->setAction(Dictionary::SYNC_EVENT_ACTION['create'])
768 ->setEventConnection($this->getEventConnection($exceptionEvent, $factory));
769 $syncEvent->getInstanceMap()->add($instance);
770 }
771 (new ExcludeDatesHandler())->prepareEventExcludeDates($event, $syncEvent->getInstanceMap());
772
773 return $syncEvent;
774 }
775
783 private function cloneEventMap(Core\Event\EventMap $eventExceptionsMap): Core\Event\EventMap
784 {
785 $result = new Core\Event\EventMap();
786 foreach ($eventExceptionsMap as $key => $event)
787 {
788 $result->add((new Core\Builders\EventCloner($event))->build(), $key);
789 }
790
791 return $result;
792 }
793}
createInstance(Event $event, Context $context)
updateSection(Section $section, Context $context)
deleteSection(Section $section, Context $context)
deleteInstance(Event $event, Context $context)
createEvent(Event $event, Context $context)
updateEvent(Event $event, Context $context)
deleteEvent(Event $event, Context $context)
__construct(FactoriesCollection $factories)
createSection(Section $section, Context $context)
updateInstance(Event $event, Context $context)