1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
incomingmanager.php
См. документацию.
1<?php
2
3namespace Bitrix\Calendar\Sync\Managers;
4
5use Bitrix\Calendar\Core\Event\Event;
6use Bitrix\Calendar\Internals\EventConnectionTable;
7use Bitrix\Calendar\Internals\SectionConnectionTable;
8use Bitrix\Calendar\Sync\Connection\Connection;
9use Bitrix\Calendar\Sync\Connection\SectionConnection;
10use Bitrix\Calendar\Sync\Dictionary;
11use Bitrix\Calendar\Sync\Entities\SyncSection;
12use Bitrix\Calendar\Sync\Factories\EventConnectionFactory;
13use Bitrix\Calendar\Sync\Factories\FactoryBuilder;
14use Bitrix\Calendar\Sync\Factories\FactoryInterface;
15use Bitrix\Calendar\Sync\Factories\SectionConnectionFactory;
16use Bitrix\Calendar\Sync\Util\Context;
17use Bitrix\Calendar\Sync\Util\Result;
18use Bitrix\Main\ArgumentException;
19use Bitrix\Main\DI\ServiceLocator;
20use Bitrix\Main\Error;
21use Bitrix\Main\ObjectException;
22use Bitrix\Main\ObjectNotFoundException;
23use Bitrix\Main\ObjectPropertyException;
24use Bitrix\Main\SystemException;
25use Bitrix\Calendar\Core\Section\Section;
26use Bitrix\Main\Type\DateTime;
27use CCalendar;
28use Bitrix\Calendar\Core;
29use CCalendarEvent;
30use CCalendarSect;
31use Exception;
32
37{
38 private Connection $connection;
39
40 private FactoryInterface $factory;
41
42 private VendorSynchronization $syncManager;
44 private Core\Mappers\Factory $mapperFactory;
45
51 public function __construct(Connection $connection)
52 {
53 $this->connection = $connection;
54
55 $this->mapperFactory = ServiceLocator::getInstance()->get('calendar.service.mappers.factory');
56 }
57
63 public function importSections(): Result
64 {
65 // In this case we can guess, they were deleted on the vendor side.
66 $resultData = [
67 'links' => [
68 'updated' => [],
69 'imported' => [],
70 'skipped' => [],
71 ],
72 'vendorSections' => [],
73 ];
74 $result = new Result();
75
76 $getResult = $this->getFactory()->getIncomingSectionManager()->getSections();
77 if ($getResult->isSuccess())
78 {
79 $sections = $getResult->getData()['externalSyncSectionMap'];
80
81 $mapper = $this->mapperFactory->getSection();
83 foreach ($sections as $syncSection)
84 {
85 $link = null;
86 try {
87 if ($link = $this->getSectionLinkByVendorId($syncSection->getSectionConnection()->getVendorSectionId()))
88 {
89 $resultData['linkedSectionIds'][] = $link->getSection()->getId();
90 if (!$link->isActive())
91 {
92 $resultData['links']['skipped'][] = $link;
93 continue;
94 }
95 if (
96 !empty($syncSection->getAction() === 'updated')
97 && $syncSection->getSection()->getDateModified() > $link->getSection()->getDateModified()
98 )
99 {
100 $section = $this->mergeSections($link->getSection(), $syncSection->getSection());
101 $mapper->update($section, [
102 'originalFrom' => $this->connection->getVendor()->getCode(),
103 ]);
104 (new Core\Mappers\SectionConnection())->update($link);
105
106 $resultData['importedSectionIds'][] = $link->getSection()->getId();
107 $resultData['links']['updated'][] = $link;
108 }
109 }
110 else
111 {
112 $link = $this->importSectionSimple(
113 $syncSection->getSection(),
114 $syncSection->getSectionConnection()->getVendorSectionId(),
115 $syncSection->getSectionConnection()->getVersionId() ?? null,
116 $syncSection->getSectionConnection()->isPrimary() ?? null,
117 );
118 $resultData['links']['imported'][] = $link;
119 }
120 if (!empty($link))
121 {
122 $resultData['importedSectionIds'][] = $link->getSection()->getId();
123 }
124
125 }
126 catch (SystemException $e)
127 {
128 $resultData['sections'][$syncSection['id']] = Dictionary::SYNC_STATUS['failed'];
129 }
130 }
131
132 return $result->setData($resultData);
133 }
134 else
135 {
136 $result->addErrors($getResult->getErrors());
137 return $result;
138 }
139 }
140
141
151 private function getSectionLinkByVendorId(string $sectionId): ?SectionConnection
152 {
153 $mapper = $this->mapperFactory->getSectionConnection();
154 $map = $mapper->getMap([
155 '=CONNECTION_ID' => $this->connection->getId(),
156 '=VENDOR_SECTION_ID' => $sectionId,
157 ]);
158
159 return $map->fetch();
160 }
161
170 public function import(): Result
171 {
172 $resultData = [];
173 $result = new Result();
175 $sections = $this->getFactory()->getSectionManager()->getSections($this->connection);
176
182 foreach ($sections as $vendorSectionPack)
183 {
184 try {
185 $sectionLink = $this->importSection(
186 $vendorSectionPack['section'],
187 $vendorSectionPack['id'],
188 $vendorSectionPack['version'] ?? null,
189 $vendorSectionPack['is_primary'] ?? null,
190
191 );
192 $resultData['importedSectionIds'][] = $sectionLink->getSection()->getId();
193 if ($sectionLink->isActive())
194 {
195 $eventsResult = $this->importSectionEvents($sectionLink);
196 $resultData['events'][$vendorSectionPack['id']] = $eventsResult->getData();
197 $resultData['sections'][$vendorSectionPack['id']] = Dictionary::SYNC_STATUS['success'];
198 }
199 else
200 {
201 $resultData['sections'][$vendorSectionPack['id']] = Dictionary::SYNC_STATUS['inactive'];
202 $resultData['events'][$vendorSectionPack['id']] = null;
203 }
204 } catch (SystemException $e) {
205 $resultData['sections'][$vendorSectionPack['id']] = Dictionary::SYNC_STATUS['failed'];
206 }
207 }
208
209 return $result->setData($resultData);
210 }
211
215 private function getFactory(): FactoryInterface
216 {
217 if (empty($this->factory))
218 {
219 $this->factory = FactoryBuilder::create(
220 $this->connection->getVendor()->getCode(),
221 $this->connection,
222 new Context()
223 );
224 }
225
226 return $this->factory;
227 }
228
247 private function importSection(
248 Section $section,
249 string $vendorId,
250 string $vendorVersion = null,
251 bool $isPrimary = false
252 ): SectionConnection
253 {
254 $sectionFactory = new SectionConnectionFactory();
255 $link = $sectionFactory->getSectionConnection([
256 'filter' => [
257 'CONNECTION_ID' => $this->connection->getId(),
258 'VENDOR_SECTION_ID' => $vendorId,
259 ],
260 'connectionObject' => $this->connection,
261 ]);
262 if (!$link)
263 {
264 $fields = [
265 'NAME' => $section->getName(),
266 'ACTIVE' => 'Y',
267 'DESCRIPTION' => $section->getDescription() ?? '',
268 'COLOR' => $section->getColor() ?? '',
269 'CAL_TYPE' => $this->connection->getOwner()
270 ? $this->connection->getOwner()->getType()
271 : Core\Role\User::TYPE
272 ,
273 'OWNER_ID' => $this->connection->getOwner()
274 ? $this->connection->getOwner()->getId()
275 : null
276 ,
277 'CREATED_BY' => $this->connection->getOwner()->getId(),
278 'DATE_CREATE' => new DateTime(),
279 'TIMESTAMP_X' => new DateTime(),
280 'EXTERNAL_TYPE' => $this->connection->getVendor()->getCode(),
281 ];
282 if ($sectionId = CCalendar::SaveSection([
283 'arFields' => $fields,
284 'originalFrom' => $this->connection->getVendor()->getCode(),
285
286 ]))
287 {
288 $section = $this->mapperFactory->getSection()->resetCacheById($sectionId)->getById($sectionId);
289
290 $protoLink = (new SectionConnection())
291 ->setSection($section)
292 ->setConnection($this->connection)
293 ->setVendorSectionId($vendorId)
294 ->setVersionId($vendorVersion)
295 ->setPrimary($isPrimary)
296 ;
297 $link = $this->mapperFactory->getSectionConnection()->create($protoLink);
298 }
299 else
300 {
301 throw new SystemException("Can't create Bitrix Calendar Section", 500, __FILE__, __LINE__ );
302 }
303 }
304
305 if ($link)
306 {
307 $this->subscribeSection($link);
308 }
309
310 return $link;
311 }
312
326 private function importSectionSimple(
327 Section $section,
328 string $vendorId,
329 string $vendorVersion = null,
330 bool $isPrimary = null
331 ): SectionConnection
332 {
333
334 if ($section = $this->saveSection($section))
335 {
336 if ($link = $this->createSectionConnection($section, $vendorId, $vendorVersion, $isPrimary))
337 {
338 return $link;
339 }
340 else
341 {
342 throw new SystemException("Can't create Section Connection link.", 500, __FILE__, __LINE__ );
343 }
344 }
345 else
346 {
347 throw new SystemException("Can't create Bitrix Calendar Section.", 500, __FILE__, __LINE__ );
348 }
349 }
350
362 private function createSectionConnection(
363 Section $section,
364 string $vendorId,
365 string $vendorVersion = null,
366 bool $isPrimary = null
367 ): ?SectionConnection
368 {
369 $entity = (new SectionConnection())
370 ->setSection($section)
371 ->setConnection($this->connection)
372 ->setVendorSectionId($vendorId)
373 ->setVersionId($vendorVersion)
374 ->setPrimary($isPrimary ?? false)
375 ;
377 $result = (new Core\Mappers\SectionConnection())->create($entity);
378
379 return $result;
380 }
381
392 private function subscribeSection(SectionConnection $link): Result
393 {
394 $mainResult = new Result();
395 if ($this->getSyncManager()->canSubscribeSection())
396 {
397 $pushManager = new PushManager();
398 $subscription = $pushManager->getPush(
400 $link->getId()
401 );
402 if ($subscription && !$subscription->isExpired())
403 {
404 $result = $this->getSyncManager()->renewPush($subscription);
405 if ($result->isSuccess())
406 {
407 $mainResult = $pushManager->renewPush($subscription, $result->getData());
408 }
409 else
410 {
411 $mainResult->addError(new Error('Error of renew subscription.'));
412 $mainResult->addErrors($result->getErrors());
413 }
414 }
415 else
416 {
417 $subscribeResult = $this->getSyncManager()->subscribeSection($link);
418 if ($subscribeResult->isSuccess())
419 {
420 if ($subscription !== null)
421 {
422 $pushManager->renewPush($subscription, $subscribeResult->getData());
423 }
424 else
425 {
426 $pushManager->addPush(
427 'SECTION_CONNECTION',
428 $link->getId(),
429 $subscribeResult->getData()
430 );
431 }
432 }
433 else
434 {
435 $mainResult->addError(new Error('Error of add subscription.'));
436 $mainResult->addErrors($subscribeResult->getErrors());
437 }
438 }
439 }
440
441 return $mainResult;
442 }
443
452 public function importSectionEvents(SectionConnection $sectionLink): Result
453 {
454 $mainResult = new Result();
455 $resultData = [
456 'events' => [
457 'deleted' => [],
458 'imported' => [],
459 'updated' => [],
460 'stripped' => [],
461 'error' => [],
462 ],
463 ];
464
465 $pushResult = static function(array $result) use (&$resultData)
466 {
467 if (empty($result['entityType']))
468 {
469 return;
470 }
471 if ($result['entityType'] === 'link')
472 {
473 $resultData['events'][$result['action']] = $result['entity']->getEvent()->getId();
474 }
475 elseif ($result['entityType'] === 'eventId')
476 {
477 $resultData['events'][$result['action']] = $result['entity'];
478 }
479 };
480
481 $vendorManager = $this->getFactory()->getEventManager();
482 foreach ($vendorManager->fetchSectionEvents($sectionLink) as $eventPack)
483 {
484 $masterId = null;
485 foreach ($eventPack as $eventData)
486 {
488 if ($event = $eventData['event'])
489 {
490 $event->setSection($sectionLink->getSection());
491 }
492 if ($eventData['type'] === 'deleted')
493 {
494 $result = $this->deleteInstance($eventData['id']);
495 if ($result->isSuccess())
496 {
497 $resultData['events']['deleted'][] = $result->getData()['eventId'];
498 $resultData[$eventData['id']] = 'delete success';
499 }
500 else
501 {
502 $resultData['events']['error'][] = $result->getData()['eventId'];
503 $resultData[$eventData['id']] = 'delete fail';
504 }
505 }
506 elseif ($eventData['type'] === 'single')
507 {
508 $result = $this->importEvent(
509 $eventData['event'],
510 $eventData['id'],
511 $eventData['version'],
512 [
513 'data' => $eventData['data'] ?? null,
514 ]
515 );
516 if ($result->isSuccess())
517 {
518 $pushResult($result->getData());
519 $resultData[$eventData['id']] = 'create success';
520 }
521 else
522 {
523 $resultData[$eventData['id']] = 'create fail';
524 }
525 }
526 elseif ($eventData['type'] === 'master')
527 {
528 $result = $this->importEvent(
529 $eventData['event'],
530 $eventData['id'],
531 $eventData['version'],
532 [
533 'recursionEditMode' => 'all',
534 'data' => $eventData['data'] ?? null,
535 ]);
536 if ($result->isSuccess())
537 {
538 $masterId = $result->getData()['id'];
539 $resultData[$eventData['id']] = 'create success';
540 }
541 else
542 {
543 $resultData[$eventData['id']] = 'create fail';
544 }
545 }
546 elseif ($eventData['type'] === 'exception')
547 {
548 if ($masterId === null)
549 {
550 $resultData[$eventData['id']] = 'create fail: master event not found';
551 continue;
552 }
553 $eventData['event']->setRecurrenceId($masterId);
554 $result = $this->importEvent(
555 $eventData['event'],
556 $eventData['id'],
557 $eventData['version'],
558 [
559 'data' => $eventData['data'] ?? null,
560 ]
561 );
562 if ($result->isSuccess())
563 {
564 $masterId = $result->getData()['id'];
565 $resultData[$eventData['id']] = 'create success';
566 }
567 else
568 {
569 $resultData[$eventData['id']] = 'create fail';
570 }
571 }
572 }
573 }
574
575 return $mainResult->setData($resultData);
576 }
577
586 private function deleteInstance(string $vendorId): Result
587 {
588 $result = new Result();
589 try
590 {
591 $linkData = EventConnectionTable::query()
592 ->setSelect([
594 'EVENT',
595 ])
596 ->addFilter('CONNECTION_ID', $this->connection->getId())
597 ->addFilter('=VENDOR_EVENT_ID', $vendorId)
598 ->exec()->fetchObject()
599 ;
600 }
601 catch (\Bitrix\Main\ArgumentException $exception)
602 {
603 $result->addError(new Error('Probably corrupted data'));
604
605 return $result;
606 }
607
608 if ($linkData)
609 {
610 if (!CCalendarEvent::Delete([
611 'id' => $linkData->getEventId(),
612 'userId' => $this->connection->getOwner()->getId(),
613 'bMarkDeleted' => true,
614 'originalFrom' => $this->connection->getVendor()->getCode(),
615 ]))
616 {
617 $result->addError(new Error('Error of delete event'));
618 $result->setData(['eventId' => $linkData->getEventId()]);
619 }
620 }
621 else
622 {
623 $result->addError(new Error('Event not found'));
624 }
625
626 return $result;
627 }
628
641 private function importEvent(
642 Event $event,
643 string $vendorId,
644 string $vendorVersion = null,
645 array $params = []
646 ): Result
647 {
648 $prepareResult = function (string $action, string $entityType, $entity)
649 {
650 return [
651 'type' => $entityType,
652 'action' => $action,
653 'value' => $entity,
654 ];
655 };
656
657 $result = new Result();
658
659 $mapper = new Core\Mappers\Event();
660
661 $link = (new EventConnectionFactory())->getEventConnection([
662 'filter' => [
663 'CONNECTION_ID' => $this->connection->getId(),
664 '=VENDOR_EVENT_ID' => $vendorId,
665 ],
666 'connection' => $this->connection,
667 ]);
668 // TODO: explode to 2 methods
669 if ($link)
670 {
671 if ($vendorVersion && $link->getEntityTag() === $vendorVersion)
672 {
673 $resultData = $prepareResult('skipped', 'link', $link);
674 }
675 elseif (
676 empty($event->getDateModified())
677 || empty($link->getEvent()->getDateModified())
678 || ($link->getEvent()->getDateModified()->getTimestamp() >= $event->getDateModified()->getTimestamp())
679 )
680 {
681 $resultData = $prepareResult('skipped', 'link', $link);
682 }
683 else
684 {
685 try {
686 $event->setId($link->getEvent()->getId());
687 $event->setOwner(Core\Role\Helper::getRole(
688 $this->connection->getOwner()->getId(),
689 $this->connection->getOwner()->getType(),
690 ));
691 $this->mergeReminders($event, $link->getEvent());
692 $mapper->update($event, [
693 'originalFrom' => $this->connection->getVendor()->getCode(),
694 'userId' => $this->connection->getOwner()->getId(),
695 ]);
696 EventConnectionTable::update($link->getId(), [
697 'ENTITY_TAG' => $vendorVersion,
698 'DATA' => $params['data'] ?? null,
699 ]);
700 $resultData = $prepareResult('updated', 'link', $link);
701
702 }
703 catch (Exception $e) {
704 $resultData = $prepareResult('error', 'link', $link);
705 $result->addError(new Error($e->getMessage(), $e->getCode()));
706 }
707 }
708 }
709 else
710 {
711 try
712 {
713 $event->setOwner(Core\Role\Helper::getRole(
714 $this->connection->getOwner()->getId(),
715 $this->connection->getOwner()->getType(),
716 ));
717 $newEvent = $mapper->create($event, [
718 'originalFrom' => $this->connection->getVendor()->getCode(),
719 'userId' => $this->connection->getOwner()->getId(),
720 ]);
721 $linkResult = EventConnectionTable::add([
722 'EVENT_ID' => $newEvent->getId(),
723 'CONNECTION_ID' => $this->connection->getId(),
724 'VENDOR_EVENT_ID' => $vendorId,
725 'SYNC_STATUS' => Dictionary::SYNC_STATUS['success'],
726 'ENTITY_TAG' => $vendorVersion,
727 'VERSION' => 1,
728 'DATA' => $params['data'] ?? null,
729 ]);
730 if ($linkResult->isSuccess())
731 {
732 $resultData = $prepareResult('imported', 'eventId', $newEvent->getId());
733 }
734 else
735 {
736 $resultData = $prepareResult('error', 'eventId', $newEvent->getId());
737 $result->addError(reset($linkResult->getErrors()));
738 }
739 }
740 catch (Exception $e)
741 {
742 $resultData = $prepareResult('error', 'vendorId', $vendorId);
743 $result->addError(new Error($e->getMessage()));
744 }
745 }
746
747 return $result->setData($resultData ?? []);
748 }
749
755 private function getSyncManager(): VendorSynchronization
756 {
757 if (empty($this->syncManager))
758 {
759 $this->syncManager = new VendorSynchronization($this->getFactory());
760 }
761
762 return $this->syncManager;
763 }
764
765 private function mergeSections(Section $baseSection, Section $newSection): Section
766 {
767 $baseSection
768 ->setId($newSection->getId() ?: $baseSection->getId())
769 ->setName($newSection->getName() ?: $baseSection->getName())
770 ->setDescription($newSection->getDescription())
771 ->setIsActive($newSection->isActive())
772 ->setColor($newSection->getColor())
773 ;
774 return $baseSection;
775 }
776
786 private function saveSection(Section $section): ?Section
787 {
788 if ($this->connection->getOwner() === null)
789 {
790 throw new Core\Base\BaseException('The connection must have an owner');
791 }
792
793 $fields = [
794 'NAME' => $section->getName(),
795 'ACTIVE' => 'Y',
796 'DESCRIPTION' => $section->getDescription() ?? '',
797 'COLOR' => $section->getColor() ?? '',
798 'CAL_TYPE' => $this->connection->getOwner()->getType(),
799 'OWNER_ID' => $this->connection->getOwner()->getId(),
800 'CREATED_BY' => $this->connection->getOwner()->getId(),
801 'DATE_CREATE' => new DateTime(),
802 'TIMESTAMP_X' => new DateTime(),
803 'EXTERNAL_TYPE' => $this->connection->getVendor()->getCode(),
804 ];
806 'arFields' => $fields,
807 'originalFrom' => $this->connection->getVendor()->getCode(),
808 ]);
809
810 if ($sectionId)
811 {
812 return $this->mapperFactory->getSection()
813 ->resetCacheById($sectionId)
814 ->getById($sectionId)
815 ;
816 }
817
818 return null;
819 }
820
827 private function mergeReminders(Event $targetEvent, Event $sourceEvent)
828 {
829 foreach ($sourceEvent->getRemindCollection()->getCollection() as $item)
830 {
831 $targetEvent->getRemindCollection()->add($item);
832 }
833 }
834
847 public function deleteSection(Section $section, int $linkId)
848 {
849 $this->deleteSectionConnection($section->getId(), $linkId);
850
851 if ($section->getExternalType() !== 'local')
852 {
853 CCalendarSect::Delete($section->getId(), false, [
854 'originalFrom' => $this->connection->getVendor()->getCode(),
855 ]);
856 }
857 else
858 {
859 $this->exportLocalSection($section);
860 }
861 }
862
870 private function deleteSectionConnection(int $sectionId, int $sectionConnectionId)
871 {
872 global $DB;
873
874 if ($this->connection->getId() && $sectionId)
875 {
876 $DB->Query("
877 DELETE FROM b_calendar_event_connection
878 WHERE CONNECTION_ID = " . $this->connection->getId() . "
879 AND EVENT_ID IN (SELECT EV.ID FROM b_calendar_event EV
880 WHERE EV.SECTION_ID = " . $sectionId . " );"
881 );
882 }
883
884 if ($sectionConnectionId)
885 {
886 SectionConnectionTable::delete($sectionConnectionId);
887 }
888 }
889
899 private function exportLocalSection(Section $section): void
900 {
901 $manager = new OutgoingManager($this->connection);
902 $sectionResult = $manager->exportSectionSimple($section);
903 if ($sectionResult->isSuccess() && $sectionResult->getData()['exported'])
904 {
906 $sectionLink = $sectionResult->getData()['exported'];
907 $manager->exportSectionEvents($sectionLink);
908 }
909 }
910}
const SYNC_STATUS
Определения dictionary.php:16
__construct(Connection $connection)
Определения incomingmanager.php:51
deleteSection(Section $section, int $linkId)
Определения incomingmanager.php:847
static SaveSection($params)
Определения calendar.php:3443
</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
$entity
global $DB
Определения cron_frame.php:29
$map
Определения config.php:5
setSection(?string $value)
Определения columnfields.php:308
string $sectionId
Определения columnfields.php:71
trait Error
Определения error.php:11
$manager
Определения office365push.php:39
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$action
Определения file_dialog.php:21
$fields
Определения yandex_run.php:501