1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
outgoingmanager.php
См. документацию.
1<?php
2
3namespace Bitrix\Calendar\Sync\Managers;
4
5use Bitrix\Calendar\Core\Event\Event;
6use Bitrix\Calendar\Core;
7use Bitrix\Calendar\Sync;
8use Bitrix\Calendar\Internals\EO_Event;
9use Bitrix\Calendar\Internals\EO_Event_Collection;
10use Bitrix\Calendar\Internals\EO_EventConnection;
11use Bitrix\Calendar\Internals\EventConnectionTable;
12use Bitrix\Calendar\Internals\EventTable;
13use Bitrix\Calendar\Internals\SectionTable;
14use Bitrix\Calendar\Sync\Connection\Connection;
15use Bitrix\Calendar\Sync\Connection\EventConnection;
16use Bitrix\Calendar\Sync\Connection\SectionConnection;
17use Bitrix\Calendar\Sync\Entities\InstanceMap;
18use Bitrix\Calendar\Sync\Entities\SyncEvent;
19use Bitrix\Calendar\Sync\Exceptions\ApiException;
20use Bitrix\Calendar\Sync\Factories\FactoryBuilder;
21use Bitrix\Calendar\Sync\Factories\FactoryInterface;
22use Bitrix\Calendar\Internals\SectionConnectionTable;
23use Bitrix\Calendar\Sync\Util\Context;
24use Bitrix\Calendar\Sync\Util\EventContext;
25use Bitrix\Calendar\Sync\Util\Result;
26use Bitrix\Calendar\Sync\Util\SectionContext;
27use Bitrix\Main\ArgumentException;
28use Bitrix\Main\DB\SqlQueryException;
29use Bitrix\Main\DI\ServiceLocator;
30use Bitrix\Main\Entity\ReferenceField;
31use Bitrix\Main\Error;
32use Bitrix\Main\ObjectException;
33use Bitrix\Main\ObjectNotFoundException;
34use Bitrix\Main\ObjectPropertyException;
35use Bitrix\Main\SystemException;
36use Exception;
37use Generator;
38
40{
41 const TIME_SLICE = 2600000;
42
43 private Connection $connection;
44
45 private FactoryInterface $factory;
46
47 private VendorSynchronization $syncManager;
51 private Core\Mappers\Factory $mapperFactory;
52
56 public function __construct(Connection $connection)
57 {
58 $this->connection = $connection;
59 $context = new Context(['connection' => $connection]);
60 $this->factory = FactoryBuilder::create($connection->getVendor()->getCode(), $connection, $context);
61 $this->syncManager = new VendorSynchronization($this->factory);
62 $this->mapperFactory = ServiceLocator::getInstance()->get('calendar.service.mappers.factory');
63 }
64
74 public function exportSections(array $params = []): Result
75 {
76 $mainResult = new Result();
77 $resultData = [
78 'links' => [
79 'exported' => [],
80 'updated' => [],
81 'skipped' => [],
82 'error' => [],
83 ],
84 'exportErr' => [],
85 ];
86
87 $excludeIds = $params['excludeIds'] ?? [];
89 foreach ($this->fetchSections($excludeIds) as $section)
90 {
91 $sectionResult = $this->exportSectionSimple($section);
92 if ($sectionResult->isSuccess())
93 {
94 foreach ($sectionResult->getData() as $key => $link)
95 {
96 $resultData['links'][$key][] = $link;
97 }
98 $resultData['exported'][] = $section->getId();
99 }
100 else
101 {
102 $mainResult->addErrors($sectionResult->getErrors());
103 $resultData['exportErr'][] = $section->getId();
104 }
105 }
106
107 return $mainResult->setData($resultData);
108 }
109
121 public function exportSectionEvents(SectionConnection $sectionLink, array $excludeEventIds = []): Result
122 {
123 $result = new Result();
124 $resultData = [
125 'events' => [
126 'deleted' => [],
127 'exported' => [],
128 'updated' => [],
129 'stripped' => [],
130 'error' => [],
131 ]
132 ];
133
134 $pushResult = static function(array $result) use (&$resultData)
135 {
136 if (empty($result['entityType']) || empty($result['entity']))
137 {
138 return;
139 }
140 if ($result['entityType'] === 'link')
141 {
142 $resultData['events'][$result['action']] = $result['entity']->getEvent()->getId();
143 }
144 elseif ($result['entityType'] === 'eventId')
145 {
146 $resultData['events'][$result['action']] = $result['entity'];
147 }
148 };
149
150 foreach ($this->fetchSectionEvents($sectionLink->getSection(), $excludeEventIds) as $eventPack)
151 {
152 $exportResult = $this->exportEvent($eventPack, $sectionLink);
153 if ($exportResult->isSuccess())
154 {
155 $pushResult($exportResult->getData());
156 }
157 else
158 {
159 $id = null;
160
161 if ($eventPack['event'])
162 {
163 $id = $eventPack['event']->getId();
164 }
165 else if ($eventPack['master'])
166 {
167 $id = $eventPack['master']->getId();
168 }
169 $resultData['events']['error'] = $id;
170 }
171 }
172
173 return $result->setData($resultData);
174 }
175
185 public function export(): Result
186 {
187 $mainResult = new Result();
188 $resultData = [];
189
190 foreach ($this->fetchSections() as $section)
191 {
192 $sectionResult = $this->exportSection($section);
193 if (!$sectionResult->isSuccess())
194 {
195 continue;
196 }
197
199 $sectionLink = $sectionResult->getData()['sectionConnection'] ?? null;
200
201 if ($sectionLink)
202 {
203 foreach ($this->fetchSectionEvents($section) as $event)
204 {
205 $this->exportEvent($event, $sectionLink);
206 }
207 }
208 else
209 {
210 throw new ObjectPropertyException('Context object das not have sectionLink information');
211 }
212 }
213
214 return $mainResult->setData($resultData);
215 }
216
228 public function exportSection(Core\Section\Section $section): Result
229 {
230 $result = new Result();
231 try
232 {
233 $sectionContext = new SectionContext();
234 if ($link = $this->getSectionLink($section))
235 {
236 if ($link->isActive())
237 {
238 $result = $this->syncManager->updateSection($section, $sectionContext);
239 if ($result->isSuccess())
240 {
241 $result->setData(array_merge($result->getData(), [
242 'sectionConnection' => $link,
243 ]));
244 }
245 else if (
246 !$result->isSuccess()
247 && $result->getData()['error'] === 404
248 && $section->getExternalType() === 'local'
249 )
250 {
251 $this->deleteSectionConnection($link);
252 $sectionContext->setSectionConnection(null);
253 $result = $this->syncManager->createSection($section, $sectionContext);
254 }
255 }
256 }
257 else
258 {
259 $result = $this->syncManager->createSection($section, $sectionContext);
260 }
261 }
262 catch (Core\Base\BaseException $exception)
263 {
264 $result->addError(new Error($exception->getMessage()));
265 }
266 finally
267 {
268 return $result;
269 }
270 }
271
284 public function exportSectionSimple(Core\Section\Section $section): Result
285 {
286 $resultData = [];
287 $mainResult = new Result();
288 $sectionContext = new SectionContext();
289
290 try
291 {
292 if ($link = $this->getSectionLink($section))
293 {
294 $sectionContext->setSectionConnection($link);
295 if ($link->isActive())
296 {
297 $result = $this->syncManager->updateSection($section, $sectionContext);
298 if ($result->isSuccess())
299 {
300 $resultData['updated'] = $link;
301 }
302 else
303 {
304 $resultData['error'] = $link;
305 }
306 }
307 else
308 {
309 $resultData['skipped'] = $link;
310 }
311 }
312 elseif ($section->getExternalType() !== Core\Section\Section::LOCAL_EXTERNAL_TYPE)
313 {
314 $resultData['skipped'] = $section;
315 }
316 else
317 {
318 $result = $this->syncManager->createSection($section, $sectionContext);
319 if (!empty($result->getData()['sectionConnection']))
320 {
321 $resultData['exported'] = $result->getData()['sectionConnection'];
322 }
323 else
324 {
325 $result->addError(new Error('Error of export section'));
326 $resultData['section'] = $section;
327 }
328 }
329
330 $mainResult->setData($resultData);
331 }
332 catch (Core\Base\BaseException $exception)
333 {
334 $mainResult->addError(new Error($exception->getMessage()));
335 }
336 finally
337 {
338 return $mainResult;
339 }
340 }
341
351 private function fetchSections(array $exclude = []): Generator
352 {
353 $sectionList = SectionTable::getList([
354 'filter' => [
355 '=CAL_TYPE' => $this->connection->getOwner()->getType(),
356 'OWNER_ID' => $this->connection->getOwner()->getId(),
357 'EXTERNAL_TYPE' => ['local', $this->connection->getVendor()->getCode()],
358 '!ID' => $exclude
359 ],
360 'select' => ['*'],
361 ]);
362
363 $mapper = new Core\Mappers\Section();
364 while ($sectionDM = $sectionList->fetchObject())
365 {
366 $section = $mapper->getByEntityObject($sectionDM);
367 yield $section;
368 }
369 }
370
380 private function getSectionLink(\Bitrix\Calendar\Core\Section\Section $section): ?SectionConnection
381 {
382 $mapper = new Core\Mappers\SectionConnection();
383 $map = $mapper->getMap([
384 '=SECTION_ID' => $section->getId(),
385 '=CONNECTION_ID' => $this->connection->getId(),
386 ]);
387
388 return match ($map->count())
389 {
390 0 => null,
391 1 => $map->fetch(),
392 default => throw new Core\Base\BaseException('More than 1 SectionConnections found.'),
393 };
394 }
395
399 private function getFactory(): FactoryInterface
400 {
401 return $this->factory;
402 }
403
414 private function fetchSectionEvents(Core\Section\Section $section, array $excludeEventIds = []): Generator
415 {
416 $timestamp = time() - self::TIME_SLICE;
417 $eventList = EventTable::getList([
418 'select' => [
419 '*',
420 'LINK.*',
421 'LINK.CONNECTION',
422 ],
423 'filter' => [
424 '=SECTION_ID' => $section->getId(),
425 '=DELETED' => 'N',
426 '!ID' => $excludeEventIds,
427 '>DATE_TO_TS_UTC' => $timestamp,
428 '!=MEETING_STATUS' => 'N',
429 ],
430 'runtime' => [
431 new ReferenceField(
432 'LINK',
433 EventConnectionTable::class,
434 [
435 '=this.ID' => 'ref.EVENT_ID',
436 'ref.CONNECTION_ID' => ['?', $this->connection->getId()],
437 ],
438 ['join_type' => 'LEFT']
439 ),
440 ],
441 ])->fetchCollection();
442
443 $eventList = $this->prepareEvents($eventList);
444
445 foreach ($eventList as $eventPack)
446 {
447 yield $eventPack;
448 }
449 }
450
451 private function prepareEvents(EO_Event_Collection $events): array
452 {
453 $result = [];
454
455 foreach ($events as $event)
456 {
457 if ($event->getRrule())
458 {
459 $recId = $event->getParentId();
460 $result[$recId]['type'] = 'recurrence';
461 $result[$recId]['master'] = $event;
462 }
463 else if ($event->getRecurrenceId())
464 {
465 $result[$event->getRecurrenceId()]['instances'][] = $event;
466 }
467 else
468 {
469 $result[$event->getId()] = [
470 'type' => 'single',
471 'event' => $event,
472 ];
473 }
474 }
475
476 return $result;
477 }
478
485 private function getEventLink(EO_Event $eventEntity): ?EventConnection
486 {
488 if ($link = $eventEntity->get('LINK'))
489 {
490 $link->setEvent($eventEntity);
491
492 return $this->mapperFactory->getEventConnection()->getByEntityObject($link);
493 }
494
495 return null;
496 }
497
509 private function exportEvent(array $eventPack, SectionConnection $sectionLink): Result
510 {
511 if (!empty($eventPack['master']))
512 {
513 return $this->exportRecurrenceEvent($sectionLink, $eventPack);
514 }
515
516 if (!empty($eventPack['event']))
517 {
518 return $this->exportSimpleEvent($sectionLink, $eventPack['event']);
519 }
520
521 return (new Result())->addError(new Error('Unsupported eventpack format'));
522 }
523
537 {
538 $mainResult = new Result();
539 if ($this->syncManager->canSubscribeSection())
540 {
541 $pushManager = new Sync\Managers\PushManager();
542 $subscription = $pushManager->getPush(
544 $link->getId()
545 );
546 if ($subscription && !$subscription->isExpired())
547 {
548 $result = $this->syncManager->renewPush($subscription);
549 if ($result->isSuccess())
550 {
551 $mainResult = $pushManager->renewPush($subscription, $result->getData());
552 }
553 else
554 {
555 $mainResult->addError(new Error('Error of renew subscription.'));
556 $mainResult->addErrors($result->getErrors());
557 }
558 }
559 else
560 {
561 $subscribeResult = $this->syncManager->subscribeSection($link);
562 if ($subscribeResult->isSuccess())
563 {
564 if ($subscription)
565 {
566 $pushManager->renewPush($subscription, $subscribeResult->getData());
567 }
568 else
569 {
570 try
571 {
572 $pushManager->addPush(
573 'SECTION_CONNECTION',
574 $link->getId(),
575 $subscribeResult->getData()
576 );
577 }
578 catch(SqlQueryException $e)
579 {
580 if (
581 $e->getCode() === 400
582 && substr($e->getDatabaseMessage(), 0, 6) === '(1062)')
583 {
584 // there's a race situation
585 }
586 else
587 {
588 throw $e;
589 }
590 }
591
592 }
593 }
594 else
595 {
596 $mainResult->addError(new Error('Error of add subscription.'));
597 $mainResult->addErrors($subscribeResult->getErrors());
598 }
599 }
600 }
601
602 return $mainResult;
603 }
604
611 public function subscribeConnection(): Result
612 {
613 $mainResult = new Result();
614 if ($this->syncManager->canSubscribeConnection())
615 {
616 $pushManager = new Sync\Managers\PushManager();
617 $subscription = $pushManager->getPush(
619 $this->connection->getId()
620 );
621
622 if ($subscription && !$subscription->isExpired())
623 {
624 $result = $this->syncManager->renewPush($subscription);
625 if ($result->isSuccess())
626 {
627 $mainResult = $pushManager->renewPush($subscription, $result->getData());
628 }
629 else
630 {
631 $mainResult->addError(new Error('Error of renew subscription.'));
632 $mainResult->addErrors($result->getErrors());
633 }
634 }
635 else
636 {
637 $subscribeResult = $this->syncManager->subscribeConnection();
638 if ($subscribeResult->isSuccess())
639 {
640 if ($subscription !== null)
641 {
642 $pushManager->renewPush($subscription, $subscribeResult->getData());
643 }
644 else
645 {
646 $pushManager->addPush(
647 'CONNECTION',
648 $this->connection->getId(),
649 $subscribeResult->getData()
650 );
651 }
652 }
653 else
654 {
655 $mainResult->addError(new Error('Error of add subscription.'));
656 $mainResult->addErrors($subscribeResult->getErrors());
657 }
658 }
659 }
660
661 return $mainResult;
662 }
663
670 private function deleteSectionConnection(SectionConnection $link): void
671 {
672 global $DB;
673
674 if ($link->getConnection()->getId() && $link->getSection()->getId())
675 {
676 $DB->Query("
677 DELETE FROM b_calendar_event_connection
678 WHERE CONNECTION_ID = " . (int)$link->getConnection()->getId() ."
679 AND EVENT_ID IN (SELECT EV.ID FROM b_calendar_event EV
680 WHERE EV.SECTION_ID = " . (int)$link->getSection()->getId() . ");"
681 );
682 }
683
684 if ($link->getId())
685 {
686 SectionConnectionTable::delete($link->getId());
687 }
688 }
689
701 private function exportSimpleEvent(SectionConnection $sectionLink, EO_Event $eventData): Result
702 {
703 $result = new Result();
704 $context = new Context([]);
705 $context->add('sync', 'sectionLink', $sectionLink);
706
707 $event = $this->mapperFactory->getEvent()->getByEntityObject($eventData);
708
709 $eventLink = $this->getEventLink($eventData);
710 $eventContext = (new EventContext())
711 ->merge($context)
712 ->setSectionConnection($sectionLink)
713 ->setEventConnection($eventLink)
714 ;
715
716 if ($eventLink && !$eventLink->getVendorEventId())
717 {
718 $this->mapperFactory->getEventConnection()->delete($eventLink);
719 $eventLink = null;
720 }
721
722 if ($event && $eventLink)
723 {
724 $resultUpdate = $this->syncManager->updateEvent($event, $eventContext);
725 $resultData = [
726 'entityType' => 'link',
727 'entity' => $eventLink,
728 'action' => 'updated',
729 ];
730 if (!$resultUpdate->isSuccess())
731 {
732 $result->addErrors($resultUpdate->getErrors());
733 }
734 }
735 else
736 {
737 $resultAdd = $this->syncManager->createEvent($event, $eventContext);
738 $resultData = [
739 'entityType' => 'link',
740 'action' => 'exported',
741 ];
742 if ($resultAdd->isSuccess())
743 {
744 if (!empty($resultAdd->getData()['eventConnectionId']))
745 {
746 $resultData['entity'] = $this->mapperFactory->getEventConnection()
747 ->getById($resultAdd->getData()['eventConnectionId'])
748 ;
749 }
750 else
751 {
752 $resultData['entity'] = $this->mapperFactory->getEventConnection()->getMap([
753 '=EVENT_ID' => $event->getId(),
754 '=CONNECTION_ID' => $sectionLink->getConnection()->getId(),
755 ])->fetch();
756 }
757 }
758 else
759 {
760 $resultData = [
761 'entityType' => 'errorLink',
762 'action' => 'exported',
763 ];
764 $result->addErrors($resultAdd->getErrors());
765 }
766 }
767
768 return $result->setData($resultData);
769 }
770
778 private function exportRecurrenceEvent(SectionConnection $sectionLink, array $eventData): Result
779 {
780 $context = new EventContext();
781 $context->setSectionConnection($sectionLink);
782
783 $recurrenceEvent = $this->buildRecurrenceEvent($eventData);
784
785 if ($recurrenceEvent->getEventConnection())
786 {
787 $context->setEventConnection($recurrenceEvent->getEventConnection());
788 $result = $this->syncManager->updateRecurrence($recurrenceEvent, $context);
789 }
790 else
791 {
792 $result = $this->syncManager->createRecurrence($recurrenceEvent, $context);
793 }
794
795 $resultData = [
796 'entityType' => 'link',
797 'action' => 'updated',
798 ];
799
800 return $result->setData($resultData);
801 }
802
809 private function buildRecurrenceEvent(array $eventData): SyncEvent
810 {
811 $masterEvent = $this->mapperFactory->getEvent()->getByEntityObject($eventData['master']);
812 $masterLink = $this->getEventLink($eventData['master']);
813 $masterSyncEvent = (new SyncEvent())
814 ->setEvent($masterEvent)
815 ->setEventConnection($masterLink)
816 ;
817 if ($masterSyncEvent->getEventConnection() && !$masterSyncEvent->getEventConnection()->getVendorEventId())
818 {
819 $this->mapperFactory->getEventConnection()->delete($masterSyncEvent->getEventConnection());
820 $masterSyncEvent->setEventConnection(null);
821 }
822
823 $instancesCollection = new InstanceMap();
824 $instances = $eventData['instances'] ?? [];
825 foreach ($instances as $instance)
826 {
827 $instanceEvent = $this->mapperFactory->getEvent()->getByEntityObject($instance);
828 $instanceLink = $this->getEventLink($instance);
829 $instanceSyncEvent = (new SyncEvent())
830 ->setEvent($instanceEvent)
831 ->setEventConnection($instanceLink)
832 ;
833
834 if ($instanceSyncEvent->getEventConnection() && !$instanceSyncEvent->getEventConnection()->getVendorEventId())
835 {
836 $this->mapperFactory->getEventConnection()->delete($instanceSyncEvent->getEventConnection());
837 $instanceSyncEvent->setEventConnection(null);
838 }
839 $instancesCollection->add($instanceSyncEvent);
840 }
841
842 $masterSyncEvent->setInstanceMap($instancesCollection);
843
844 return $masterSyncEvent;
845 }
846}
__construct(Connection $connection)
Определения outgoingmanager.php:56
exportSection(Core\Section\Section $section)
Определения outgoingmanager.php:228
exportSectionEvents(SectionConnection $sectionLink, array $excludeEventIds=[])
Определения outgoingmanager.php:121
subscribeSection(SectionConnection $link)
Определения outgoingmanager.php:536
exportSectionSimple(Core\Section\Section $section)
Определения outgoingmanager.php:284
getDatabaseMessage()
Определения exception.php:37
Определения error.php:15
</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
global $DB
Определения cron_frame.php:29
$context
Определения csv_new_setup.php:223
$map
Определения config.php:5
Определения culture.php:9
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
if(empty($signedUserToken)) $key
Определения quickway.php:257
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799