1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
datasyncmanager.php
См. документацию.
1<?php
2
3namespace Bitrix\Calendar\Sync\Managers;
4
5use Bitrix\Calendar\Core\Builders\EventBuilderFromArray;
6use Bitrix\Calendar\Core\Event\Event;
7use Bitrix\Calendar\Core\Managers\EventOriginalRecursion;
8use Bitrix\Calendar\Core\Mappers\Factory;
9use Bitrix\Calendar\Integration\Pull\PushCommand;
10use Bitrix\Calendar\Internals\Counter\CounterService;
11use Bitrix\Calendar\Internals\Counter\Event\EventDictionary;
12use Bitrix\Calendar\Internals\EventConnectionTable;
13use Bitrix\Calendar\Internals\EventTable;
14use Bitrix\Calendar\Sync\Builders\BuilderConnectionFromDM;
15use Bitrix\Calendar\Sync\Connection\Connection;
16use Bitrix\Calendar\Sync\Dictionary;
17use Bitrix\Calendar\Sync\Icloud;
18use Bitrix\Calendar\Rooms;
19use Bitrix\Calendar\Internals\SectionConnectionTable;
20use Bitrix\Calendar\Sync\Util\EventDescription;
21use Bitrix\Calendar\Sync\Util\RequestLogger;
22use Bitrix\Calendar\Sync\Util\Result;
23use Bitrix\Calendar\UserField\ResourceBooking;
24use Bitrix\Calendar\Util;
25use Bitrix\Dav\Internals\DavConnectionTable;
26use Bitrix\Dav\Internals\EO_DavConnection;
27use Bitrix\Dav\Internals\EO_DavConnection_Collection;
28use Bitrix\Main\ArgumentException;
29use Bitrix\Main\ArgumentNullException;
30use Bitrix\Main\DI\ServiceLocator;
31use Bitrix\Main\Loader;
32use Bitrix\Main\LoaderException;
33use Bitrix\Main\Localization\Loc;
34use Bitrix\Main\ObjectException;
35use Bitrix\Main\ObjectPropertyException;
36use Bitrix\Main\ORM\Query\Query;
37use Bitrix\Main\SystemException;
38use Bitrix\Main\Type\DateTime;
39
41{
42 private const ENTITY_TYPE = 'user';
43 private const MAX_NUMBER = 5;
44 private const TIME_SLICE = 2600000;
45
47 private $mapperFactory;
48
52 private function __construct()
53 {
54 $this->mapperFactory = ServiceLocator::getInstance()->get('calendar.service.mappers.factory');
55 }
56
65 public static function dataSyncAgent(): string
66 {
67 (new self())->dataSync();
68
69 return "\\Bitrix\\Calendar\\Sync\\Managers\\DataSyncManager::dataSyncAgent();";
70 }
71
75 public static function createInstance(): DataSyncManager
76 {
77 return new self();
78 }
79
90 public function dataSync($userId = null): bool
91 {
92 if (!Loader::includeModule('dav') || !Loader::includeModule('calendar'))
93 {
94 return true;
95 }
96
97 $userIds = [];
98 $connections = $this->getConnections($userId);
99 foreach ($connections as $connection)
100 {
101 $connection = $this->createConnectionObject($connection);
102 $ownerId = $connection->getOwner()?->getId();
103 if ($ownerId)
104 {
105 $userIds[] = $ownerId;
106 }
107 $result = $this->syncConnection($connection);
108 if ($result->isSuccess())
109 {
110 \CDavConnection::SetLastResult($connection->getId(), $result->getData()['lastResult']);
112 PushCommand::RefreshSyncStatus,
113 $connection->getOwner()->getId(),
114 [
115 'syncInfo' => [
116 $connection->getAccountType() => [
117 'status' => $result->getData()['syncStatus'],
118 'type' => $connection->getAccountType(),
119 'connected' => true,
120 'id' => $connection->getId(),
121 'syncOffset' => 0
122 ],
123 ],
124 'requestUid' => Util::getRequestUid(),
125 ]
126 );
127 }
128 }
129
130 CounterService::addEvent(EventDictionary::SYNC_CHANGED, ['user_ids' => $userIds]);
131
132 return true;
133 }
134
146 private function syncConnection(Connection $connection): Result
147 {
148 $result = new Result();
149 $logger = null;
150
151 if (
152 RequestLogger::isEnabled()
153 )
154 {
155 $logger = new RequestLogger($connection->getOwner()->getId(), $connection->getVendor()->getCode());
156 }
157
158 $client = $this->initClient($connection);
159
160 $calendarsList = $client->GetCalendarList($connection->getServer()->getBasePath(), null);
161
162 if ($client->getError())
163 {
164 $error = $this->processError($client->getError());
165 $result->setData([
166 'lastResult' => $error,
167 'syncStatus' => false,
168 ]);
169
170 return $result;
171 }
172
173 if (!$calendarsList || !is_array($calendarsList))
174 {
175 $result->setData([
176 'lastResult' => '[204] No Content',
177 'syncStatus' => true,
178 ]);
179
180 return $result;
181 }
182
183 $calendarsList = $this->syncSections($connection, $calendarsList);
184
185 foreach ($calendarsList as $calendar)
186 {
187 [$events, $eventsMap] = $this->getEventsToSync(
189 $client,
190 $calendar,
191 $logger
192 );
193
194 if ($client->getError())
195 {
196 $error = $this->processError($client->getError());
197 $result->setData([
198 'lastResult' => $error,
199 'syncStatus' => false,
200 ]);
201
202 return $result;
203 }
204
205 foreach ($events as $event)
206 {
207 $this->modifyEvent(
209 $client,
210 $event,
211 $eventsMap,
212 $calendar
213 );
214 }
215 }
216
217 $result->setData([
218 'lastResult' => '[200] OK',
219 'syncStatus' => true,
220 ]);
221
222 return $result;
223 }
224
238 private function modifyEvent(
239 Connection $connection,
240 \CDavGroupdavClientCalendar $client,
242 array $eventsMap,
243 array $calendar
244 ): void
245 {
246 if (!array_key_exists($event['href'], $eventsMap))
247 {
248 return;
249 }
250
251 $eventId = null;
252 $existEvent = $eventsMap[$event['href']];
253 if (empty($event['calendar-data']))
254 {
255 return;
256 }
257
258 [$event, $exDate] = $this->mergeExternalEventWithLocal($existEvent, $event, $client);
259
260 if (!empty($event['calendar-data']) && is_array($event['calendar-data']))
261 {
262 $eventId = $this->modifySingleEvent(
264 $event['calendar-data'],
265 [
266 'SECTION_ID' => $calendar['SECTION_ID'],
267 'VERSION' => $existEvent['VERSION'],
268 'EVENT_CONNECTION_ID' => $eventsMap[$event['href']]['EVENT_CONNECTION_ID'],
269 ]
270 );
271 }
272
273 if ($eventId && !empty($event['calendar-data-ex']) && is_array($event['calendar-data-ex']))
274 {
275 $this->modifyRecurrenceEvent(
277 $event['calendar-data-ex'],
278 [
279 'PARENT_ID' => $eventId,
280 'SECTION_ID' => $calendar['SECTION_ID'],
281 'PERIOD_UNTIL' => $event['calendar-data']['PROPERTY_PERIOD_UNTIL'] ?? null,
282 ]
283 );
284 }
285 else if ($exDate && $event['calendar-data'] && $event['calendar-data']['ID'])
286 {
287 $this->deleteDuplicateExDates(
288 $exDate,
289 $event['calendar-data']['DATE_FROM'],
290 $event['calendar-data']['ID'],
291 $connection->getOwner()->getId(),
292 );
293 }
294 }
295
308 private function deleteDuplicateExDates($exDate, $dateFrom, $eventId, $userId): void
309 {
310 global $DB;
311 $exDates = \CCalendarEvent::GetExDate($exDate);
312 $dtStartTimestamp = \CCalendar::Timestamp($dateFrom, false);
313 $needToUpdate = false;
314 foreach ($exDates as $date)
315 {
316 $dateTs = \CCalendar::Timestamp($date, false);
317 if ($dateTs < $dtStartTimestamp)
318 {
319 $needToUpdate = true;
320 break;
321 }
322 }
323
324 if ($needToUpdate)
325 {
326 $childEvents = EventConnectionTable::query()
327 ->setSelect([
328 'EVENT_ID',
329 'VERSION',
330 'DATE_FROM' => 'EVENT.DATE_FROM',
331 'EVENT_CONNECTION_ID' => 'ID',
332 ])
333 ->where('EVENT.RECURRENCE_ID', $eventId)
334 ->where('EVENT.DELETED', 'N')
335 ->where('EVENT.OWNER_ID', $userId)
336 ->exec()
337 ;
338
339 while ($child = $childEvents->fetch())
340 {
341 $eventIdList = $this->getAllEventByParentId($child['EVENT_ID']);
342
343 EventTable::updateMulti($eventIdList, ['DELETED' => 'Y']);
344
345 EventConnectionTable::delete($child['EVENT_CONNECTION_ID']);
346 }
347 }
348 }
349
362 private function modifySingleEvent(Connection $connection, array $event, array $additionalParams): ?int
363 {
364 $eventObject = $this->prepareEventParams(
365 $event,
366 $additionalParams['SECTION_ID'],
367 $connection->getOwner()->getId()
368 );
369
370 if ($eventObject->getId())
371 {
372 $result = $this->mapperFactory->getEvent()->update($eventObject, [
373 'userId' => $connection->getOwner()->getId(),
374 'bAffectToDav' => false, // Used to prevent synchro with calDav again
375 'bSilentAccessMeeting' => true,
376 'autoDetectSection' => false,
377 'originalFrom' => $connection->getVendor()->getCode(),
378 ]);
379 }
380 else
381 {
382 $result = $this->mapperFactory->getEvent()->create($eventObject, [
383 'userId' => $connection->getOwner()->getId(),
384 'bAffectToDav' => false, // Used to prevent synchro with calDav again
385 'bSilentAccessMeeting' => true,
386 'autoDetectSection' => false,
387 'originalFrom' => $connection->getVendor()->getCode(),
388 ]);
389 }
390
391 if ($result && $result->getId())
392 {
393 $data = [];
394 // Prepare Data with outer params
395 if (!empty($event['ATTENDEE']) || !empty($event['ORGANIZER_ENTITY']))
396 {
397 $this->parseInvitedAttendees($event, $data);
398 }
399 if (!empty($event['ATTACH']))
400 {
401 $this->parseAttachments($event, $data);
402 }
403 if (!empty($event['URL']))
404 {
405 $data['URL'] = $event['URL'];
406 }
407
408 if (!empty($additionalParams['EVENT_CONNECTION_ID']))
409 {
410 EventConnectionTable::update($additionalParams['EVENT_CONNECTION_ID'], [
411 'SYNC_STATUS' => Dictionary::SYNC_STATUS['success'],
412 'ENTITY_TAG' => $event['MODIFICATION_LABEL'] ?? null,
413 'VERSION' => (string)($additionalParams['VERSION'] ?? null),
414 'VENDOR_VERSION_ID' => (string)($additionalParams['VERSION'] ?? null),
415 'DATA' => $data,
416 ]);
417 }
418 else
419 {
420 EventConnectionTable::add([
421 'EVENT_ID' => (int)$result->getId(),
422 'CONNECTION_ID' => $connection->getId(),
423 'VENDOR_EVENT_ID' => $event['XML_ID'] ?? null,
424 'SYNC_STATUS' => Dictionary::SYNC_STATUS['success'],
425 'ENTITY_TAG' => $event['MODIFICATION_LABEL'] ?? null,
426 'VERSION' => (string)($additionalParams['VERSION'] ?? null),
427 'VENDOR_VERSION_ID' => (string)($additionalParams['VERSION'] ?? null),
428 'DATA' => $data,
429 ]);
430 }
431
432 return (int)$result->getId();
433 }
434
435 return null;
436 }
437
450 private function modifyRecurrenceEvent(
451 Connection $connection,
452 array $importInstances,
453 array $additionalParams
454 ): void
455 {
456 [$importInstances, $importedInstancesDates] = $this->prepareInstanceEvents($importInstances);
457 $parentEvent = \CCalendarEvent::GetById($additionalParams['PARENT_ID'], true, true);
458
459 if ($parentEvent && \CCalendarEvent::CheckRecurcion($parentEvent))
460 {
461 $exDates = \CCalendarEvent::GetExDate($parentEvent['EXDATE']);
462 $localInstances = EventConnectionTable::query()
463 ->setSelect([
464 'EVENT_ID',
465 'VERSION',
466 'DATE_FROM' => 'EVENT.DATE_FROM',
467 'EVENT_CONNECTION_ID' => 'ID',
468 'MEETING' => 'EVENT.MEETING',
469 'IS_MEETING' => 'EVENT.IS_MEETING',
470 'MEETING_HOST' => 'EVENT.MEETING_HOST',
471 'ATTENDEES_CODES' => 'EVENT.ATTENDEES_CODES',
472 'ACCESSIBILITY' => 'EVENT.ACCESSIBILITY',
473 ])
474 ->where('EVENT.RECURRENCE_ID', $additionalParams['PARENT_ID'])
475 ->where('EVENT.DELETED', 'N')
476 ->where('EVENT.OWNER_ID', $connection->getOwner()->getId())
477 ->whereNot('EVENT.MEETING_STATUS', 'N')
478 ->exec()
479 ;
480
481 $importedInstancesCount = count($importInstances);
482 while ($localInstance = $localInstances->fetch())
483 {
484 $isActive = false;
485 $localInstanceDate = \CCalendar::Date(\CCalendar::Timestamp($localInstance['DATE_FROM']), false);
486 for ($i = 0; $i < $importedInstancesCount; $i++)
487 {
488 if ($localInstanceDate === $importedInstancesDates[$i])
489 {
490 $this->mergeInstanceParams($importInstances[$i], $localInstance);
491 $isActive = true;
492
493 break;
494 }
495 }
496
497 if (!$isActive)
498 {
499 \CCalendarEvent::Delete([
500 'id' => $localInstance['EVENT_ID'],
501 'bMarkDeleted' => true,
502 'bAffectToDav' => false,
503 'originalFrom' => $connection->getVendor()->getCode(),
504 'userId' => $connection->getOwner()->getId(),
505 ]);
506
507 EventConnectionTable::delete($localInstance['EVENT_CONNECTION_ID']);
508 }
509 }
510
511 foreach ($importInstances as $instance)
512 {
513 if (
514 $additionalParams['PERIOD_UNTIL']
515 && \CCalendar::Timestamp($instance['DATE_FROM']) > \CCalendar::Timestamp($additionalParams['PERIOD_UNTIL'])
516 )
517 {
518 continue;
519 }
520 $instance = $this->addParentDataToInstance($instance, $parentEvent);
521
522 if ($instance && $instance['RECURRENCE_ID'])
523 {
524 unset($instance['RRULE'], $instance['EXDATE']);
525
526 $instanceId = $this->modifySingleEvent(
528 $instance,
529 [
530 'SECTION_ID' => $additionalParams['SECTION_ID'],
531 'VERSION' => $instance['VERSION'] ?? 1,
532 'EVENT_CONNECTION_ID' => $instance['EVENT_CONNECTION_ID'] ?? 0,
533 ]
534 );
535
536 if (!empty($instance['RECURRENCE_ID_DATE']))
537 {
538 $exDates[] = \CCalendar::Date(\CCalendar::Timestamp($instance['RECURRENCE_ID_DATE']), false);
539
540 $originalRecursionId = (int)($parentEvent['ORIGINAL_RECURSION_ID'] ?? $parentEvent['ID']);
541
542 (new EventOriginalRecursion())->add($instanceId, $originalRecursionId);
543 }
544 }
545 }
546 $exDate = \CCalendarEvent::SetExDate($exDates);
547 $eventIdList = $this->getAllEventByParentId($parentEvent['ID']);
548
549 EventTable::updateMulti($eventIdList, ['EXDATE' => $exDate]);
550 }
551 }
552
561 private function getAllEventByParentId(int $parentId): array
562 {
563 $eventIdList = EventTable::query()
564 ->setSelect(['ID'])
565 ->where('PARENT_ID', $parentId)
566 ->exec()
567 ->fetchAll()
568 ;
569
570 return array_map(static function($event){
571 return (int)$event['ID'];
572 }, $eventIdList);
573 }
574
583 private function getConnections($userId = null): EO_DavConnection_Collection
584 {
585 $query = DavConnectionTable::query()
586 ->setSelect(['*'])
587 ->whereIn('ACCOUNT_TYPE', [Icloud\Helper::ACCOUNT_TYPE])
588 ->where('ENTITY_TYPE', self::ENTITY_TYPE)
589 ->where('IS_DELETED', 'N')
590 ->setLimit(self::MAX_NUMBER)
591 ->setOrder(['SYNCHRONIZED' => 'ASC'])
592 ;
593 if ($userId)
594 {
595 $query->where('ENTITY_ID', $userId);
596 }
597
598 return $query->exec()->fetchCollection();
599 }
600
606 private function initClient(Connection $connection): \CDavGroupdavClientCalendar
607 {
608 $client = new \CDavGroupdavClientCalendar(
609 $connection->getServer()->getScheme(),
610 $connection->getServer()->getHost(),
611 $connection->getServer()->getPort(),
612 $connection->getServer()->getUserName(),
613 $connection->getServer()->getPassword()
614 );
615 if (\CDav::UseProxy())
616 {
617 $proxy = \CDav::GetProxySettings();
618 $client->SetProxy(
619 $proxy['PROXY_SCHEME'],
620 $proxy['PROXY_HOST'],
621 $proxy['PROXY_PORT'],
622 $proxy['PROXY_USERNAME'],
623 $proxy['PROXY_PASSWORD']
624 );
625 }
626
627 return $client;
628 }
629
642 private function syncCalendarSections(
643 Connection $connection,
644 array $calendars
645 ): array
646 {
647 $calendarNames = [];
648 $result = [];
649
650 foreach ($calendars as $calendar)
651 {
652 if (isset($calendar['TYPE']) && $calendar['TYPE'] === 'VEVENT')
653 {
654 $calendarNames[$calendar['XML_ID']] = $calendar;
655 }
656 }
657
658 $sectionsLink = SectionConnectionTable::query()
659 ->setSelect([
660 'SECTION_CONNECTION_ID' => 'ID',
661 'NAME' => 'SECTION.NAME',
662 'EXTERNAL_TYPE' => 'SECTION.EXTERNAL_TYPE',
663 'VENDOR_SECTION_ID',
664 'VERSION_ID',
665 'SECTION_ID',
666 ])
667 ->where('SECTION.CAL_TYPE', self::ENTITY_TYPE)
668 ->where('SECTION.OWNER_ID', $connection->getOwner()->getId())
669 ->where('CONNECTION_ID', $connection->getId())
670 ->exec()
671 ;
672
673 while ($link = $sectionsLink->fetch())
674 {
675 $xmlId = $link['VENDOR_SECTION_ID'];
676 if (empty($xmlId))
677 {
678 continue;
679 }
680
681 if (!array_key_exists($xmlId, $calendarNames))
682 {
683 $section = $this->mapperFactory->getSection()->getById($link['SECTION_ID']);
684 if ($section)
685 {
686 (new IncomingManager($connection))->deleteSection($section, $link['SECTION_CONNECTION_ID']);
687 }
688 }
689 else
690 {
691 if (($link['VERSION_ID'] ?? null) !== ($calendarNames[$xmlId]['MODIFICATION_LABEL'] ?? null))
692 {
693 $fields = [
694 'ID' => (int)($link['SECTION_ID'] ?? null),
695 'NAME' => isset($link['EXTERNAL_TYPE']) && $link['EXTERNAL_TYPE'] === 'local'
696 ? $link['NAME']
697 : $calendarNames[$xmlId]['NAME']
698 ,
699 'DESCRIPTION' => $calendarNames[$xmlId]['DESCRIPTION'] ?? null,
700 'COLOR' => $calendarNames[$xmlId]['COLOR'] ?? null,
701 ];
702
703 \CCalendarSect::Edit([
704 'arFields' => $fields,
705 'bAffectToDav' => false,
706 'originalFrom' => $connection->getVendor()->getCode(),
707 ]);
708
709 SectionConnectionTable::update((int)$link['SECTION_CONNECTION_ID'], [
710 'LAST_SYNC_DATE' => new DateTime(),
711 'LAST_SYNC_STATUS' => Dictionary::SYNC_STATUS['success'],
712 'VERSION_ID' => $calendarNames[$xmlId]['MODIFICATION_LABEL'] ?? null,
713 ]
714 );
715
716 $result[] = [
717 'XML_ID' => $xmlId,
718 'SECTION_ID' => $link['SECTION_ID'],
719 'SECTION_CONNECTION_ID' => $link['SECTION_CONNECTION_ID'],
720 'SYNC_TOKEN' => $link['VERSION_ID'] ?? null,
721 'IS_NEW' => false,
722 ];
723 }
724
725 unset($calendarNames[$xmlId]);
726 }
727 }
728
729 foreach ($calendarNames as $curXmlId => $calendar)
730 {
731 $fields = [
732 'CAL_TYPE' => self::ENTITY_TYPE,
733 'OWNER_ID' => $connection->getOwner()->getId(),
734 'CREATED_BY' => $connection->getOwner()->getId(),
735 'NAME' => $calendar['NAME'],
736 'DESCRIPTION' => $calendar['DESCRIPTION'],
737 'COLOR' => $calendar['COLOR'],
738 'EXPORT' => ['ALLOW' => false],
739 'EXTERNAL_TYPE' => $connection->getVendor()->getCode(),
740 ];
741
742 $id = (int)\CCalendarSect::Edit([
743 'arFields' => $fields,
744 'bAffectToDav' => false,
745 'originalFrom' => $connection->getVendor()->getCode(),
746 ]);
747
748 if ($id)
749 {
750 $linkId = SectionConnectionTable::add([
751 'SECTION_ID' => $id,
752 'CONNECTION_ID' => $connection->getId(),
753 'VENDOR_SECTION_ID' => $curXmlId,
754 'ACTIVE' => 'Y',
755 'LAST_SYNC_DATE' => new DateTime(),
756 'LAST_SYNC_STATUS' => Dictionary::SYNC_STATUS['success'],
757 'VERSION_ID' => $calendar['MODIFICATION_LABEL'],
758 ]);
759
760 $result[] = [
761 'XML_ID' => $curXmlId,
762 'SECTION_ID' => $id,
763 'SECTION_CONNECTION_ID' => $linkId->getId(),
764 'SYNC_TOKEN' => $calendar['MODIFICATION_LABEL'],
765 'IS_NEW' => true,
766 ];
767 }
768 }
769
770 return $result;
771 }
772
784 private function syncCalendarEvents(
785 Connection $connection,
786 int $sectionId,
787 array $events
788 ): array
789 {
790 $linksMap = [];
791 $result = [];
792
793 $eventsLink = EventConnectionTable::query()
794 ->setSelect([
795 'EVENT_CONNECTION_ID' => 'ID',
796 'VENDOR_EVENT_ID',
797 'VERSION',
798 'ENTITY_TAG',
799 'EVENT_ID',
800 'IS_MEETING' => 'EVENT.IS_MEETING',
801 'MEETING' => 'EVENT.MEETING',
802 'EXDATE' => 'EVENT.EXDATE',
803 'EVENT_PARENT_ID' => 'EVENT.PARENT_ID',
804 'EVENT_TYPE' => 'EVENT.EVENT_TYPE',
805 'EVENT_NAME' => 'EVENT.NAME',
806 'EVENT_OWNER_ID' => 'EVENT.OWNER_ID',
807 'EVENT_DATE_FROM' => 'EVENT.DATE_FROM',
808 'EVENT_DATE_TO' => 'EVENT.DATE_TO',
809 'EVENT_TZ_FROM' => 'EVENT.TZ_FROM',
810 'EVENT_TZ_TO' => 'EVENT.TZ_TO',
811 'EVENT_VERSION' => 'EVENT.VERSION',
812 'ATTENDEES_CODES' => 'EVENT.ATTENDEES_CODES',
813 'ACCESSIBILITY' => 'EVENT.ACCESSIBILITY',
814 ])
815 ->where('EVENT.CAL_TYPE', self::ENTITY_TYPE)
816 ->where('EVENT.OWNER_ID', $connection->getOwner()->getId())
817 ->where('EVENT.SECTION_ID', $sectionId)
818 ->where('EVENT.DELETED', 'N')
819 ->where(Query::filter()
820 ->logic('or')
821 ->whereNot('EVENT.MEETING_STATUS', 'N')
822 ->whereNull('EVENT.MEETING_STATUS')
823 )
824 ->whereNotNull('ENTITY_TAG')
825 ->exec()
826 ;
827
828 while ($event = $eventsLink->fetch())
829 {
830 $linksMap[$event['VENDOR_EVENT_ID']] = $event;
831 }
832
833 foreach ($events as $index => $event)
834 {
835 if (!empty($linksMap[$event['XML_ID']]))
836 {
837 $existEvent = $linksMap[$event['XML_ID']];
838
839 if (($existEvent['ENTITY_TAG'] ?? null) === ($event['SYNC_TOKEN'] ?? null))
840 {
841 continue;
842 }
843 if ($this->isBlockedChange($existEvent, $event))
844 {
845 continue;
846 }
847
848 if ((int)$event['STATUS'] === 200)
849 {
850 if (($linksMap[$event['XML_ID']]['ENTITY_TAG'] ?? null) !== ($event['SYNC_TOKEN'] ?? null))
851 {
852 $result[] = $this->prepareExistedEventParams($event['XML_ID'], $linksMap[$event['XML_ID']]);
853 }
854 else
855 {
856 unset($events[$index]);
857 }
858 }
859 else if ((int)$event['STATUS'] === 404)
860 {
861 \CCalendar::DeleteEvent(
862 $linksMap[$event['XML_ID']]['EVENT_ID'],
863 false,
864 [
865 'markDeleted' => true,
866 'originalFrom' => $connection->getVendor()->getCode(),
867 'checkPermissions' => false,
868 ]
869 );
870
871 EventConnectionTable::delete($linksMap[$event['XML_ID']]['EVENT_CONNECTION_ID']);
872 }
873 }
874 else if (!empty($event['SYNC_TOKEN']) && (int)$event['STATUS'] === 200)
875 {
876 $result[] = $this->prepareExistedEventParams($event['XML_ID']);
877 }
878 }
879
880 return $result;
881 }
882
890 private function isBlockedChange(array $existEvent, array $importedEvent): bool
891 {
892 if ($existEvent['EVENT_TYPE'] === ResourceBooking::EVENT_LABEL)
893 {
894 return true;
895 }
896
897 if ($existEvent['EVENT_ID'] !== $existEvent['EVENT_PARENT_ID'])
898 {
899 return true;
900 }
901
902 return false;
903 }
904
913 private function prepareEventParams(array $event, int $sectionId, int $entityId): Event
914 {
915 $fields = [
916 'ID' => (int)($event['ID'] ?? null),
917 'NAME' => $this->prepareName($event['NAME']),
918 'CAL_TYPE' => self::ENTITY_TYPE,
919 'DESCRIPTION' => $event['DESCRIPTION'] ?? '',
920 'OWNER_ID' => $entityId,
921 'CREATED_BY' => $entityId,
922 'ATTENDEES_CODES' => ['U' . $entityId],
923 'SECTIONS' => [$sectionId],
924 'ACCESSIBILITY' => !empty($event['ID'])
925 ? $event['ACCESSIBILITY']
926 : ($event['PROPERTY_ACCESSIBILITY'] ?? 'busy')
927 ,
928 'IS_MEETING' => $event['IS_MEETING'] ? true : null,
929 'IMPORTANCE' => $event['IMPORTANCE'] ?? 'normal',
930 'REMIND' => is_array($event['REMIND'] ?? null) ? $event['REMIND'] : [],
931 'RRULE' => is_array($event['RRULE'] ?? null) ? $event['RRULE'] : [],
932 'VERSION' => (int)$event['VERSION'],
933 'PRIVATE_EVENT' => (bool)($event['PRIVATE_EVENT'] ?? null),
934 'DATE_FROM' => $event['DATE_FROM'],
935 'DATE_TO' => $event['DATE_TO'],
936 'TZ_FROM' => $event['TZ_FROM'],
937 'TZ_TO' => $event['TZ_TO'],
938 'SKIP_TIME' => $event['SKIP_TIME'] ? 'Y' : 'N',
939 'ACTIVE' => 'Y',
940 'DELETED' => 'N',
941 'TIMESTAMP_X' => new DateTime(),
942 ];
943
944 if (!empty($event['RECURRENCE_ID']))
945 {
946 $fields['RECURRENCE_ID'] = $event['RECURRENCE_ID'];
947 }
948
949 if (!empty($event['MEETING']))
950 {
951 $fields['MEETING'] = $event['MEETING'];
952 $fields['MEETING_HOST'] = $event['MEETING']['MEETING_CREATOR'] ?? null;
953 }
954 else
955 {
956 $fields['MEETING'] = [
957 'HOST_NAME' => \CCalendar::GetUserName($entityId),
958 'NOTIFY' => true,
959 'REINVITE' => false,
960 'ALLOW_INVITE' => false,
961 'MEETING_CREATOR' => $entityId,
962 'HIDE_GUESTS' => true,
963 'LANGUAGE_ID' => \CCalendar::getUserLanguageId($entityId)
964 ];
965 $fields['MEETING_HOST'] = $entityId;
966 $fields['MEETING_STATUS'] = 'H';
967 }
968
969 if (!empty($event['ATTENDEES_CODES']))
970 {
971 $fields['ATTENDEES_CODES'] = $event['ATTENDEES_CODES'];
972 }
973
974 if (!empty($event['RECURRENCE_ID_DATE']))
975 {
976 $fields['ORIGINAL_DATE_FROM'] = $event['RECURRENCE_ID_DATE'];
977 }
978
979 if (!empty($fields['ORIGINAL_DATE_FROM']) && !empty($fields['RECURRENCE_ID']))
980 {
981 $fields['RELATIONS'] = ['COMMENT_XML_ID' => \CCalendarEvent::GetEventCommentXmlId($fields)];
982 }
983
984 if (empty($fields['TZ_TO']) && $fields['SKIP_TIME'] === 'N')
985 {
986 $currentTimezone = (new DateTime())->getTimeZone()->getName();
987 $fields['TZ_TO'] = $currentTimezone;
988 $fields['TZ_FROM'] = $currentTimezone;
989 }
990
991 if (!empty($event['SKIP_TIME']))
992 {
993 $fields['DATE_FROM'] = \CCalendar::Date(\CCalendar::Timestamp($fields['DATE_FROM'], false));
994 $fields['DATE_TO'] = \CCalendar::Date(
995 \CCalendar::Timestamp($fields['DATE_TO']) - \CCalendar::GetDayLen(),
996 false
997 );
998 }
999
1000 if (!empty($event['PROPERTY_REMIND_SETTINGS']))
1001 {
1002 if (is_array($event['PROPERTY_REMIND_SETTINGS']))
1003 {
1004 foreach ($event['PROPERTY_REMIND_SETTINGS'] as $remind)
1005 {
1006 $parsed = explode('_', $remind);
1007 $this->prepareRemind($parsed, $fields);
1008 }
1009 }
1010 else
1011 {
1012 $parsed = explode('_', $event['PROPERTY_REMIND_SETTINGS']);
1013 $this->prepareRemind($parsed, $fields);
1014 }
1015 }
1016
1017 if (!empty($event['PROPERTY_IMPORTANCE']))
1018 {
1019 $fields['IMPORTANCE'] = $event['PROPERTY_IMPORTANCE'];
1020 }
1021
1022 if (!empty($event['PROPERTY_LOCATION']))
1023 {
1024 $fields['LOCATION'] = Rooms\Util::unParseTextLocation($event['PROPERTY_LOCATION']);
1025 }
1026
1027 if (!empty($event['DETAIL_TEXT']))
1028 {
1029 $this->prepareDescription($event, $fields);
1030 }
1031
1032 //RRULE SEGMENT
1033 if (
1034 !empty($event['PROPERTY_PERIOD_TYPE'])
1035 && in_array($event['PROPERTY_PERIOD_TYPE'], ['DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'])
1036 )
1037 {
1038 $this->prepareRecurrenceRule($event, $fields);
1039 }
1040
1041 return (new EventBuilderFromArray($fields))->build();
1042 }
1043
1055 private function syncSections(Connection $connection, $calendarsList): array
1056 {
1057 $result = [];
1058 foreach ($calendarsList as $calendar)
1059 {
1060 $result[] = [
1061 'XML_ID' => $calendar['href'],
1062 'NAME' => $calendar['displayname'] ?? null,
1063 'DESCRIPTION' => $calendar['calendar-description'] ?? '',
1064 'TYPE' => $calendar['supported-calendar-component-set'] ?? '',
1065 'COLOR' => $calendar['calendar-color'] ?? null,
1066 'MODIFICATION_LABEL' => $calendar['getctag'] ?? null,
1067 ];
1068 }
1069
1070 return $this->syncCalendarSections(
1072 $result
1073 );
1074 }
1075
1087 private function getEventsToSync(
1088 Connection $connection,
1089 \CDavGroupdavClientCalendar $client,
1090 array $calendar,
1091 ?RequestLogger $logger = null
1092 ): array
1093 {
1094 $calendarEvents = [];
1095 $syncToken = !$calendar['IS_NEW'] ? $calendar['SYNC_TOKEN'] : null;
1096
1097 $calendarItems = $client->GetCalendarItemsBySyncToken($calendar['XML_ID'], $syncToken, $logger);
1098
1099 if (!$calendarItems || !is_array($calendarItems))
1100 {
1101 return $calendarEvents;
1102 }
1103
1104 foreach ($calendarItems as $item)
1105 {
1106 if (
1107 (int)$item['status'] === 404
1108 || mb_strpos($item['getcontenttype'], 'text/calendar') !== false
1109 )
1110 {
1111 $calendarEvents[] = [
1112 'XML_ID' => $client::getBasenameWithoutExtension($item['href']),
1113 'SYNC_TOKEN' => $item['getetag'] ?? null,
1114 'STATUS' => $item['status'],
1115 ];
1116 }
1117 }
1118
1119 $calendarEvents = $this->syncCalendarEvents(
1121 $calendar['SECTION_ID'],
1122 $calendarEvents
1123 );
1124
1125 $eventsToUpdate = [];
1126 $eventsMap = [];
1127 foreach ($calendarEvents as $event)
1128 {
1129 $link = $client->GetRequestEventPath($calendar['XML_ID'], $event['XML_ID']);
1130 $eventsToUpdate[] = $link;
1131 $eventsMap[$link] = $event;
1132 }
1133
1134 $calendarItems = [];
1135 if ($eventsToUpdate)
1136 {
1137 $calendarItems = $client->GetCalendarItemsList(
1138 $calendar['XML_ID'],
1139 $eventsToUpdate,
1140 true,
1141 1,
1142 [],
1143 $logger
1144 );
1145 }
1146
1147 if (!$syncToken && $calendarItems)
1148 {
1149 $calendarItems = $this->applyTimeLimitForEvents($calendarItems);
1150 }
1151
1152 return [$calendarItems, $eventsMap];
1153 }
1154
1160 private function applyTimeLimitForEvents($events): array
1161 {
1162 $timestamp = time() - self::TIME_SLICE;
1163 foreach ($events as $key => $event)
1164 {
1165 if (!empty($event['calendar-data']['PROPERTY_PERIOD_UNTIL']))
1166 {
1167 if ((int)\CCalendar::Timestamp($event['calendar-data']['PROPERTY_PERIOD_UNTIL']) - $timestamp < 0)
1168 {
1169 unset($events[$key]);
1170 }
1171 }
1172 else if (
1173 !empty($event['calendar-data']['DATE_TO'])
1174 && (int)\CCalendar::Timestamp($event['calendar-data']['DATE_TO']) - $timestamp < 0
1175 )
1176 {
1177 unset($events[$key]);
1178 }
1179 }
1180
1181 return array_values($events);
1182 }
1183
1190 private function parseInvitedAttendees(array $event, array &$data): void
1191 {
1192 if (!empty($event['ATTENDEE']))
1193 {
1195 foreach ($event['ATTENDEE'] as $attendee)
1196 {
1197 $attendeeData = [];
1198
1199 if ($attendee->Parameter('CN'))
1200 {
1201 $attendeeData['CN'] = $attendee->Parameter('CN');
1202 }
1203 if ($attendee->Parameter('CUTYPE'))
1204 {
1205 $attendeeData['CUTYPE'] = $attendee->Parameter('CUTYPE');
1206 }
1207 if ($attendee->Parameter('PARTSTAT'))
1208 {
1209 $attendeeData['PARTSTAT'] = $attendee->Parameter('PARTSTAT');
1210 }
1211 if ($attendee->Parameter('ROLE'))
1212 {
1213 $attendeeData['ROLE'] = $attendee->Parameter('ROLE');
1214 }
1215 if ($attendee->Parameter('EMAIL'))
1216 {
1217 $attendeeData['EMAIL'] = $attendee->Parameter('EMAIL');
1218 }
1219 if ($attendee->Parameter('SCHEDULE-STATUS'))
1220 {
1221 $attendeeData['SCHEDULE-STATUS'] = $attendee->Parameter('SCHEDULE-STATUS');
1222 }
1223 if ($attendee->Value())
1224 {
1225 $attendeeData['VALUE'] = $attendee->Value();
1226 }
1227
1228 $data['ATTENDEE'][] = $attendeeData;
1229 }
1230 }
1232 if ($organizer = $event['ORGANIZER_ENTITY'][0])
1233 {
1234 if ($organizer->Parameter('EMAIL'))
1235 {
1236 $data['ORGANIZER']['EMAIL'] = $organizer->Parameter('EMAIL');
1237 }
1238 if ($organizer->Parameter('CN'))
1239 {
1240 $data['ORGANIZER']['CN'] = $organizer->Parameter('CN');
1241 }
1242 if ($organizer->Value())
1243 {
1244 $data['ORGANIZER']['VALUE'] = $organizer->Value();
1245 }
1246 }
1247 }
1248
1255 private function parseAttachments(array $event, array &$data): void
1256 {
1258 foreach ($event['ATTACH'] as $attachment)
1259 {
1260 $attachmentData = [];
1261 if ($attachment->Parameter('FMTTYPE'))
1262 {
1263 $attachmentData['FMTTYPE'] = $attachment->Parameter('FMTTYPE');
1264 }
1265 if ($attachment->Parameter('SIZE'))
1266 {
1267 $attachmentData['SIZE'] = $attachment->Parameter('SIZE');
1268 }
1269 if ($attachment->Parameter('FILENAME'))
1270 {
1271 $attachmentData['FILENAME'] = $attachment->Parameter('FILENAME');
1272 }
1273 if ($attachment->Parameter('MANAGED-ID'))
1274 {
1275 $attachmentData['MANAGED-ID'] = $attachment->Parameter('MANAGED-ID');
1276 }
1277 if ($attachment->Value())
1278 {
1279 $attachmentData['VALUE'] = $attachment->Value();
1280 }
1281
1282 $data['ATTACH'][] = $attachmentData;
1283 }
1284 }
1285
1293 private function prepareRemind($parsed, array &$fields): void
1294 {
1295 $cnt = count($parsed);
1296 if ($cnt === 2 && $parsed[1] === 'date')
1297 {
1298 $fields['REMIND'][] = [
1299 'type' => $parsed[1],
1300 'value' => new DateTime($parsed[0], 'Ymd\\THis\\Z'),
1301 ];
1302 }
1303 else if ($cnt === 2 && $fields['SKIP_TIME'] === 'Y')
1304 {
1305 $fields['REMIND'][] = [
1306 'type' => 'daybefore',
1307 'before' => 1,
1308 'time' => 1440 - (int)$parsed[0] * 60,
1309 ];
1310 }
1311 else if ($cnt === 2)
1312 {
1313 $fields['REMIND'][] = [
1314 'count' => (int)$parsed[0],
1315 'type' => $parsed[1],
1316 ];
1317 }
1318 else if ($cnt === 3 && $parsed[2] === 'daybefore')
1319 {
1320 $fields['REMIND'][] = [
1321 'type' => $parsed[2],
1322 'before' => 0,
1323 'time' => (int)$parsed[0] * 60,
1324 ];
1325 }
1326 else if ($cnt === 4 && $fields['SKIP_TIME'] === 'Y')
1327 {
1328 $fields['REMIND'][] = [
1329 'type' => 'daybefore',
1330 'before' => $parsed[0] + 1,
1331 'time' => 1440 - (int)$parsed[2] * 60,
1332 ];
1333 }
1334 else if ($cnt === 4)
1335 {
1336 $fields['REMIND'][] = [
1337 'type' => $parsed[3],
1338 'count' => (int)$parsed[0] * 24 + $parsed[2],
1339 ];
1340 }
1341 }
1342
1348 private function createConnectionObject(EO_DavConnection $connection): Connection
1349 {
1350 return (new BuilderConnectionFromDM($connection))->build();
1351 }
1352
1358 private function processError(array $error): string
1359 {
1360 return '[' . $error[0] . '] ' . $error[1];
1361 }
1362
1368 private function prepareName(?string $name): string
1369 {
1370 if (!$name)
1371 {
1372 IncludeModuleLangFile($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/calendar/classes/general/calendar_js.php");
1373 $name = Loc::getMessage('EC_DEFAULT_ENTRY_NAME');
1374 }
1375
1376 return $name;
1377 }
1378
1385 private function prepareRecurrenceRule(array $event, array &$fields): void
1386 {
1387 $fields['RRULE']['FREQ'] = $event['PROPERTY_PERIOD_TYPE'];
1388 $fields['RRULE']['INTERVAL'] = $event['PROPERTY_PERIOD_COUNT'];
1389
1390 if (empty($fields['DT_LENGTH']) && !empty($event['PROPERTY_EVENT_LENGTH']))
1391 {
1392 $fields['DT_LENGTH'] = (int)$fields['PROPERTY_EVENT_LENGTH'];
1393 }
1394 else if (isset($event['DT_TO_TS'], $event['DT_FROM_TS']))
1395 {
1396 $fields['DT_LENGTH'] = $event['DT_TO_TS'] - $event['DT_FROM_TS'];
1397 }
1398 else
1399 {
1400 $fields['DT_LENGTH'] = null;
1401 }
1402
1403 if ($fields['RRULE']['FREQ'] === 'WEEKLY' && !empty($event['PROPERTY_PERIOD_ADDITIONAL']))
1404 {
1405 $fields['RRULE']['BYDAY'] = [];
1406 $days = explode(',', $event['PROPERTY_PERIOD_ADDITIONAL']);
1407 foreach ($days as $day)
1408 {
1409 $day = \CCalendar::WeekDayByInd($day, false);
1410 if ($day !== false)
1411 {
1412 $fields['RRULE']['BYDAY'][] = $day;
1413 }
1414 }
1415 $fields['RRULE']['BYDAY'] = implode(',', $fields['RRULE']['BYDAY']);
1416 }
1417
1418 if (!empty($event['PROPERTY_RRULE_COUNT']))
1419 {
1420 $fields['RRULE']['COUNT'] = $event['PROPERTY_RRULE_COUNT'];
1421 }
1422 else if (!empty($event['PROPERTY_PERIOD_UNTIL']))
1423 {
1424 $fields['RRULE']['UNTIL'] = $event['PROPERTY_PERIOD_UNTIL'];
1425 }
1426 else
1427 {
1428 $fields['RRULE']['UNTIL'] = $event['DT_TO_TS'] ?? null;
1429 }
1430
1431 if (!empty($event['EXDATE']))
1432 {
1433 $fields['EXDATE'] = $event['EXDATE'];
1434 }
1435 }
1436
1443 private function prepareDescription(array $event, array &$fields): void
1444 {
1445 if (isset($event['MEETING']) && !empty($event['MEETING']['LANGUAGE_ID']))
1446 {
1447 $languageId = $event['MEETING']['LANGUAGE_ID'];
1448 }
1449 else
1450 {
1451 $languageId = \CCalendar::getUserLanguageId((int)$fields['OWNER_ID']);
1452 }
1453
1454 $fields['DESCRIPTION'] = (new EventDescription())->prepareAfterImport($event['DETAIL_TEXT'], $languageId);
1455 }
1456
1463 private function mergeInstanceParams(array &$instance, array $localInstance): void
1464 {
1465 $instance['ID'] = (int)$localInstance['EVENT_ID'];
1466 $instance['EVENT_CONNECTION_VERSION'] = (int)$localInstance['VERSION'];
1467 $instance['EVENT_CONNECTION_ID'] = (int)$localInstance['EVENT_CONNECTION_ID'];
1468
1469 if (!empty($localInstance['MEETING']))
1470 {
1471 $instance['MEETING'] = unserialize($localInstance['MEETING'], ['allowed_classes' => false]);
1472 }
1473 if (!empty($localInstance['ATTENDEES_CODES']))
1474 {
1475 $instance['ATTENDEES_CODES'] = explode(',', $localInstance['ATTENDEES_CODES']);
1476 }
1477 if (!empty($localInstance['IS_MEETING']))
1478 {
1479 $instance['IS_MEETING'] = (bool)$localInstance['IS_MEETING'];
1480 }
1481 if (!empty($localInstance['MEETING_HOST']))
1482 {
1483 $instance['MEETING_HOST'] = $localInstance['MEETING_HOST'];
1484 }
1485 if (!empty($localInstance['ACCESSIBILITY']))
1486 {
1487 $instance['ACCESSIBILITY'] = $localInstance['ACCESSIBILITY'];
1488 }
1489 }
1490
1497 private function addParentDataToInstance(array $instance, array $parentEvent): array
1498 {
1499 if (empty($instance['IS_MEETING']))
1500 {
1501 $instance['IS_MEETING'] = $parentEvent['IS_MEETING'];
1502 }
1503 if (empty($instance['MEETING_HOST']))
1504 {
1505 $instance['MEETING_HOST'] = $parentEvent['MEETING_HOST'];
1506 }
1507 if (empty($instance['MEETING']))
1508 {
1509 $instance['MEETING'] = $parentEvent['MEETING'];
1510 }
1511 if (empty($instance['ATTENDEES_CODES']))
1512 {
1513 $instance['ATTENDEES_CODES'] = $parentEvent['ATTENDEES_CODES'];
1514 }
1515
1516 $instance['VERSION'] = !empty($instance['EVENT_CONNECTION_VERSION'])
1517 ? max($parentEvent['VERSION'], $instance['EVENT_CONNECTION_VERSION'])
1518 : $parentEvent['VERSION']
1519 ;
1520
1521 $instance['RECURRENCE_ID'] = $parentEvent['ID'];
1522
1523 return $instance;
1524 }
1525
1533 private function prepareInstanceEvents(array $events): array
1534 {
1535 $instances = [];
1536 $eventDates = [];
1537 $eventsCount = count($events);
1538
1539 for ($i = $eventsCount - 1; $i >= 0; $i--)
1540 {
1541 $eventDate = \CCalendar::Date(\CCalendar::Timestamp($events[$i]['DATE_FROM']), false);
1542 if (!in_array($eventDate, $eventDates, true))
1543 {
1544 $instances[] = $events[$i];
1545 $eventDates[] = $eventDate;
1546 }
1547 }
1548
1549 return [$instances, $eventDates];
1550 }
1551
1558 private function prepareExistedEventParams(string $xmlId, ?array $link = null): array
1559 {
1560 return [
1561 'XML_ID' => $xmlId,
1562 'ID' => (int)($link['EVENT_ID'] ?? null),
1563 'EVENT_NAME' => $link['EVENT_NAME'] ?? null,
1564 'EVENT_CONNECTION_ID' => (int)($link['EVENT_CONNECTION_ID'] ?? null),
1565 'EXDATE' => $link['EXDATE'] ?? null,
1566 'VERSION' => $link['EVENT_VERSION'] ?? $link['VERSION'] ?? 1,
1567 'MEETING' => ($link['MEETING'] ?? null)
1568 ? unserialize($link['MEETING'], ['allowed_classes' => false])
1569 : null
1570 ,
1571 'IS_MEETING' => (bool)($link['IS_MEETING'] ?? null),
1572 'ATTENDEES_CODES' => ($link['ATTENDEES_CODES'] ?? null)
1573 ? explode(',', $link['ATTENDEES_CODES'])
1574 : null
1575 ,
1576 'ACCESSIBILITY' => $link['ACCESSIBILITY'] ?? 'busy',
1577 'DATE_FROM' => $link['EVENT_DATE_FROM'] ?? null,
1578 'DATE_TO' => $link['EVENT_DATE_TO'] ?? null,
1579 'TZ_FROM' => $link['EVENT_TZ_FROM'] ?? null,
1580 'TZ_TO' => $link['EVENT_TZ_TO'] ?? null,
1581 ];
1582 }
1583
1591 private function mergeExternalEventWithLocal(
1592 array $existedEvent,
1593 array $event,
1594 \CDavGroupdavClientCalendar $client
1595 ): array
1596 {
1597 $exDate = $existedEvent['EXDATE'];
1598 $event['calendar-data'] = array_merge($event['calendar-data'], [
1599 'ID' => $existedEvent['ID'],
1600 'XML_ID' => $client::getBasenameWithoutExtension($event['href']),
1601 'MODIFICATION_LABEL' => $event['getetag'],
1602 'MEETING' => $existedEvent['MEETING'],
1603 'IS_MEETING' => $existedEvent['IS_MEETING'],
1604 'ATTENDEES_CODES' => $existedEvent['ATTENDEES_CODES'],
1605 'ACCESSIBILITY' => $existedEvent['ACCESSIBILITY'],
1606 ]);
1607
1608 return [$event, $exDate];
1609 }
1610}
$connection
Определения actionsdefinitions.php:38
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
const SYNC_STATUS
Определения dictionary.php:16
static addPullEvent(PushCommand $command, int $userId, array $params=[])
Определения util.php:385
static getRequestUid()
Определения util.php:537
$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
$query
Определения get_search.php:11
$proxy
Определения proxy.php:11
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
global $DB
Определения cron_frame.php:29
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
Определения tools.php:3778
$name
Определения menu_edit.php:35
string $sectionId
Определения columnfields.php:71
$entityId
Определения payment.php:4
$event
Определения prolog_after.php:141
$instance
Определения ps_b24_final.php:14
if(empty($signedUserToken)) $key
Определения quickway.php:257
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$error
Определения subscription_card_product.php:20
$fields
Определения yandex_run.php:501