42 private const ENTITY_TYPE =
'user';
43 private const MAX_NUMBER = 5;
44 private const TIME_SLICE = 2600000;
47 private $mapperFactory;
52 private function __construct()
54 $this->mapperFactory = ServiceLocator::getInstance()->get(
'calendar.service.mappers.factory');
69 return "\\Bitrix\\Calendar\\Sync\\Managers\\DataSyncManager::dataSyncAgent();";
92 if (!Loader::includeModule(
'dav') || !Loader::includeModule(
'calendar'))
98 $connections = $this->getConnections(
$userId);
105 $userIds[] = $ownerId;
110 \CDavConnection::SetLastResult(
$connection->getId(),
$result->getData()[
'lastResult']);
112 PushCommand::RefreshSyncStatus,
117 'status' =>
$result->getData()[
'syncStatus'],
130 CounterService::addEvent(EventDictionary::SYNC_CHANGED, [
'user_ids' => $userIds]);
152 RequestLogger::isEnabled()
160 $calendarsList = $client->GetCalendarList(
$connection->getServer()->getBasePath(),
null);
162 if ($client->getError())
164 $error = $this->processError($client->getError());
167 'syncStatus' =>
false,
173 if (!$calendarsList || !is_array($calendarsList))
176 'lastResult' =>
'[204] No Content',
177 'syncStatus' =>
true,
183 $calendarsList = $this->syncSections(
$connection, $calendarsList);
185 foreach ($calendarsList as $calendar)
187 [$events, $eventsMap] = $this->getEventsToSync(
194 if ($client->getError())
196 $error = $this->processError($client->getError());
199 'syncStatus' =>
false,
205 foreach ($events as
$event)
218 'lastResult' =>
'[200] OK',
219 'syncStatus' =>
true,
238 private function modifyEvent(
240 \CDavGroupdavClientCalendar $client,
246 if (!array_key_exists(
$event[
'href'], $eventsMap))
252 $existEvent = $eventsMap[
$event[
'href']];
253 if (empty(
$event[
'calendar-data']))
258 [
$event, $exDate] = $this->mergeExternalEventWithLocal($existEvent,
$event, $client);
260 if (!empty(
$event[
'calendar-data']) && is_array(
$event[
'calendar-data']))
262 $eventId = $this->modifySingleEvent(
266 'SECTION_ID' => $calendar[
'SECTION_ID'],
267 'VERSION' => $existEvent[
'VERSION'],
268 'EVENT_CONNECTION_ID' => $eventsMap[
$event[
'href']][
'EVENT_CONNECTION_ID'],
273 if ($eventId && !empty(
$event[
'calendar-data-ex']) && is_array(
$event[
'calendar-data-ex']))
275 $this->modifyRecurrenceEvent(
277 $event[
'calendar-data-ex'],
279 'PARENT_ID' => $eventId,
280 'SECTION_ID' => $calendar[
'SECTION_ID'],
281 'PERIOD_UNTIL' =>
$event[
'calendar-data'][
'PROPERTY_PERIOD_UNTIL'] ??
null,
285 else if ($exDate &&
$event[
'calendar-data'] &&
$event[
'calendar-data'][
'ID'])
287 $this->deleteDuplicateExDates(
289 $event[
'calendar-data'][
'DATE_FROM'],
290 $event[
'calendar-data'][
'ID'],
308 private function deleteDuplicateExDates($exDate, $dateFrom, $eventId,
$userId): void
311 $exDates = \CCalendarEvent::GetExDate($exDate);
312 $dtStartTimestamp = \CCalendar::Timestamp($dateFrom,
false);
313 $needToUpdate =
false;
314 foreach ($exDates as $date)
316 $dateTs = \CCalendar::Timestamp($date,
false);
317 if ($dateTs < $dtStartTimestamp)
319 $needToUpdate =
true;
326 $childEvents = EventConnectionTable::query()
330 'DATE_FROM' =>
'EVENT.DATE_FROM',
331 'EVENT_CONNECTION_ID' =>
'ID',
333 ->where(
'EVENT.RECURRENCE_ID', $eventId)
334 ->where(
'EVENT.DELETED',
'N')
335 ->where(
'EVENT.OWNER_ID',
$userId)
339 while ($child = $childEvents->fetch())
341 $eventIdList = $this->getAllEventByParentId($child[
'EVENT_ID']);
343 EventTable::updateMulti($eventIdList, [
'DELETED' =>
'Y']);
345 EventConnectionTable::delete($child[
'EVENT_CONNECTION_ID']);
364 $eventObject = $this->prepareEventParams(
366 $additionalParams[
'SECTION_ID'],
370 if ($eventObject->getId())
372 $result = $this->mapperFactory->getEvent()->update($eventObject, [
374 'bAffectToDav' =>
false,
375 'bSilentAccessMeeting' =>
true,
376 'autoDetectSection' =>
false,
377 'originalFrom' =>
$connection->getVendor()->getCode(),
382 $result = $this->mapperFactory->getEvent()->create($eventObject, [
384 'bAffectToDav' =>
false,
385 'bSilentAccessMeeting' =>
true,
386 'autoDetectSection' =>
false,
387 'originalFrom' =>
$connection->getVendor()->getCode(),
395 if (!empty(
$event[
'ATTENDEE']) || !empty(
$event[
'ORGANIZER_ENTITY']))
399 if (!empty(
$event[
'ATTACH']))
403 if (!empty(
$event[
'URL']))
408 if (!empty($additionalParams[
'EVENT_CONNECTION_ID']))
410 EventConnectionTable::update($additionalParams[
'EVENT_CONNECTION_ID'], [
412 'ENTITY_TAG' =>
$event[
'MODIFICATION_LABEL'] ??
null,
413 'VERSION' => (
string)($additionalParams[
'VERSION'] ??
null),
414 'VENDOR_VERSION_ID' => (
string)($additionalParams[
'VERSION'] ??
null),
420 EventConnectionTable::add([
421 'EVENT_ID' => (
int)
$result->getId(),
423 'VENDOR_EVENT_ID' =>
$event[
'XML_ID'] ??
null,
425 'ENTITY_TAG' =>
$event[
'MODIFICATION_LABEL'] ??
null,
426 'VERSION' => (
string)($additionalParams[
'VERSION'] ??
null),
427 'VENDOR_VERSION_ID' => (
string)($additionalParams[
'VERSION'] ??
null),
450 private function modifyRecurrenceEvent(
452 array $importInstances,
453 array $additionalParams
456 [$importInstances, $importedInstancesDates] = $this->prepareInstanceEvents($importInstances);
457 $parentEvent = \CCalendarEvent::GetById($additionalParams[
'PARENT_ID'],
true,
true);
459 if ($parentEvent && \CCalendarEvent::CheckRecurcion($parentEvent))
461 $exDates = \CCalendarEvent::GetExDate($parentEvent[
'EXDATE']);
462 $localInstances = EventConnectionTable::query()
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',
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')
481 $importedInstancesCount =
count($importInstances);
482 while ($localInstance = $localInstances->fetch())
485 $localInstanceDate = \CCalendar::Date(\CCalendar::Timestamp($localInstance[
'DATE_FROM']),
false);
486 for (
$i = 0;
$i < $importedInstancesCount;
$i++)
488 if ($localInstanceDate === $importedInstancesDates[
$i])
490 $this->mergeInstanceParams($importInstances[
$i], $localInstance);
499 \CCalendarEvent::Delete([
500 'id' => $localInstance[
'EVENT_ID'],
501 'bMarkDeleted' =>
true,
502 'bAffectToDav' =>
false,
503 'originalFrom' =>
$connection->getVendor()->getCode(),
507 EventConnectionTable::delete($localInstance[
'EVENT_CONNECTION_ID']);
514 $additionalParams[
'PERIOD_UNTIL']
515 && \CCalendar::Timestamp(
$instance[
'DATE_FROM']) > \CCalendar::Timestamp($additionalParams[
'PERIOD_UNTIL'])
526 $instanceId = $this->modifySingleEvent(
530 'SECTION_ID' => $additionalParams[
'SECTION_ID'],
532 'EVENT_CONNECTION_ID' =>
$instance[
'EVENT_CONNECTION_ID'] ?? 0,
536 if (!empty(
$instance[
'RECURRENCE_ID_DATE']))
538 $exDates[] = \CCalendar::Date(\CCalendar::Timestamp(
$instance[
'RECURRENCE_ID_DATE']),
false);
540 $originalRecursionId = (int)($parentEvent[
'ORIGINAL_RECURSION_ID'] ?? $parentEvent[
'ID']);
542 (
new EventOriginalRecursion())->add($instanceId, $originalRecursionId);
546 $exDate = \CCalendarEvent::SetExDate($exDates);
547 $eventIdList = $this->getAllEventByParentId($parentEvent[
'ID']);
549 EventTable::updateMulti($eventIdList, [
'EXDATE' => $exDate]);
561 private function getAllEventByParentId(
int $parentId):
array
563 $eventIdList = EventTable::query()
565 ->where(
'PARENT_ID', $parentId)
570 return array_map(
static function(
$event){
583 private function getConnections(
$userId =
null): EO_DavConnection_Collection
585 $query = DavConnectionTable::query()
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'])
598 return $query->exec()->fetchCollection();
606 private function initClient(Connection
$connection): \CDavGroupdavClientCalendar
608 $client = new \CDavGroupdavClientCalendar(
615 if (\CDav::UseProxy())
617 $proxy = \CDav::GetProxySettings();
642 private function syncCalendarSections(
650 foreach ($calendars as $calendar)
652 if (isset($calendar[
'TYPE']) && $calendar[
'TYPE'] ===
'VEVENT')
654 $calendarNames[$calendar[
'XML_ID']] = $calendar;
658 $sectionsLink = SectionConnectionTable::query()
660 'SECTION_CONNECTION_ID' =>
'ID',
661 'NAME' =>
'SECTION.NAME',
662 'EXTERNAL_TYPE' =>
'SECTION.EXTERNAL_TYPE',
667 ->where(
'SECTION.CAL_TYPE', self::ENTITY_TYPE)
668 ->where(
'SECTION.OWNER_ID',
$connection->getOwner()->getId())
673 while ($link = $sectionsLink->fetch())
675 $xmlId = $link[
'VENDOR_SECTION_ID'];
681 if (!array_key_exists($xmlId, $calendarNames))
683 $section = $this->mapperFactory->getSection()->getById($link[
'SECTION_ID']);
686 (
new IncomingManager(
$connection))->deleteSection($section, $link[
'SECTION_CONNECTION_ID']);
691 if (($link[
'VERSION_ID'] ??
null) !== ($calendarNames[$xmlId][
'MODIFICATION_LABEL'] ??
null))
694 'ID' => (int)($link[
'SECTION_ID'] ??
null),
695 'NAME' => isset($link[
'EXTERNAL_TYPE']) && $link[
'EXTERNAL_TYPE'] ===
'local'
697 : $calendarNames[$xmlId][
'NAME']
699 'DESCRIPTION' => $calendarNames[$xmlId][
'DESCRIPTION'] ??
null,
700 'COLOR' => $calendarNames[$xmlId][
'COLOR'] ??
null,
703 \CCalendarSect::Edit([
705 'bAffectToDav' =>
false,
706 'originalFrom' =>
$connection->getVendor()->getCode(),
709 SectionConnectionTable::update((
int)$link[
'SECTION_CONNECTION_ID'], [
710 'LAST_SYNC_DATE' =>
new DateTime(),
712 'VERSION_ID' => $calendarNames[$xmlId][
'MODIFICATION_LABEL'] ??
null,
718 'SECTION_ID' => $link[
'SECTION_ID'],
719 'SECTION_CONNECTION_ID' => $link[
'SECTION_CONNECTION_ID'],
720 'SYNC_TOKEN' => $link[
'VERSION_ID'] ??
null,
725 unset($calendarNames[$xmlId]);
729 foreach ($calendarNames as $curXmlId => $calendar)
732 'CAL_TYPE' => self::ENTITY_TYPE,
735 'NAME' => $calendar[
'NAME'],
736 'DESCRIPTION' => $calendar[
'DESCRIPTION'],
737 'COLOR' => $calendar[
'COLOR'],
738 'EXPORT' => [
'ALLOW' =>
false],
739 'EXTERNAL_TYPE' =>
$connection->getVendor()->getCode(),
742 $id = (int)\CCalendarSect::Edit([
744 'bAffectToDav' =>
false,
745 'originalFrom' =>
$connection->getVendor()->getCode(),
750 $linkId = SectionConnectionTable::add([
753 'VENDOR_SECTION_ID' => $curXmlId,
755 'LAST_SYNC_DATE' =>
new DateTime(),
757 'VERSION_ID' => $calendar[
'MODIFICATION_LABEL'],
761 'XML_ID' => $curXmlId,
763 'SECTION_CONNECTION_ID' => $linkId->getId(),
764 'SYNC_TOKEN' => $calendar[
'MODIFICATION_LABEL'],
784 private function syncCalendarEvents(
793 $eventsLink = EventConnectionTable::query()
795 'EVENT_CONNECTION_ID' =>
'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',
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()
821 ->whereNot(
'EVENT.MEETING_STATUS',
'N')
822 ->whereNull(
'EVENT.MEETING_STATUS')
824 ->whereNotNull(
'ENTITY_TAG')
828 while (
$event = $eventsLink->fetch())
833 foreach ($events as $index =>
$event)
835 if (!empty($linksMap[
$event[
'XML_ID']]))
837 $existEvent = $linksMap[
$event[
'XML_ID']];
839 if (($existEvent[
'ENTITY_TAG'] ??
null) === (
$event[
'SYNC_TOKEN'] ??
null))
843 if ($this->isBlockedChange($existEvent,
$event))
848 if ((
int)
$event[
'STATUS'] === 200)
850 if (($linksMap[
$event[
'XML_ID']][
'ENTITY_TAG'] ??
null) !== (
$event[
'SYNC_TOKEN'] ??
null))
852 $result[] = $this->prepareExistedEventParams(
$event[
'XML_ID'], $linksMap[
$event[
'XML_ID']]);
856 unset($events[$index]);
859 else if ((
int)
$event[
'STATUS'] === 404)
861 \CCalendar::DeleteEvent(
862 $linksMap[
$event[
'XML_ID']][
'EVENT_ID'],
865 'markDeleted' =>
true,
866 'originalFrom' =>
$connection->getVendor()->getCode(),
867 'checkPermissions' =>
false,
871 EventConnectionTable::delete($linksMap[
$event[
'XML_ID']][
'EVENT_CONNECTION_ID']);
874 else if (!empty(
$event[
'SYNC_TOKEN']) && (
int)
$event[
'STATUS'] === 200)
876 $result[] = $this->prepareExistedEventParams(
$event[
'XML_ID']);
890 private function isBlockedChange(
array $existEvent,
array $importedEvent): bool
892 if ($existEvent[
'EVENT_TYPE'] === ResourceBooking::EVENT_LABEL)
897 if ($existEvent[
'EVENT_ID'] !== $existEvent[
'EVENT_PARENT_ID'])
916 'ID' => (int)(
$event[
'ID'] ??
null),
917 'NAME' => $this->prepareName(
$event[
'NAME']),
918 'CAL_TYPE' => self::ENTITY_TYPE,
919 'DESCRIPTION' =>
$event[
'DESCRIPTION'] ??
'',
924 'ACCESSIBILITY' => !empty(
$event[
'ID'])
926 : (
$event[
'PROPERTY_ACCESSIBILITY'] ??
'busy')
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',
941 'TIMESTAMP_X' => new DateTime(),
944 if (!empty(
$event[
'RECURRENCE_ID']))
949 if (!empty(
$event[
'MEETING']))
952 $fields[
'MEETING_HOST'] =
$event[
'MEETING'][
'MEETING_CREATOR'] ??
null;
957 'HOST_NAME' => \CCalendar::GetUserName(
$entityId),
960 'ALLOW_INVITE' =>
false,
962 'HIDE_GUESTS' =>
true,
963 'LANGUAGE_ID' => \CCalendar::getUserLanguageId(
$entityId)
966 $fields[
'MEETING_STATUS'] =
'H';
969 if (!empty(
$event[
'ATTENDEES_CODES']))
974 if (!empty(
$event[
'RECURRENCE_ID_DATE']))
976 $fields[
'ORIGINAL_DATE_FROM'] =
$event[
'RECURRENCE_ID_DATE'];
979 if (!empty(
$fields[
'ORIGINAL_DATE_FROM']) && !empty(
$fields[
'RECURRENCE_ID']))
981 $fields[
'RELATIONS'] = [
'COMMENT_XML_ID' => \CCalendarEvent::GetEventCommentXmlId(
$fields)];
986 $currentTimezone = (
new DateTime())->getTimeZone()->getName();
987 $fields[
'TZ_TO'] = $currentTimezone;
988 $fields[
'TZ_FROM'] = $currentTimezone;
991 if (!empty(
$event[
'SKIP_TIME']))
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(),
1000 if (!empty(
$event[
'PROPERTY_REMIND_SETTINGS']))
1002 if (is_array(
$event[
'PROPERTY_REMIND_SETTINGS']))
1004 foreach (
$event[
'PROPERTY_REMIND_SETTINGS'] as $remind)
1006 $parsed = explode(
'_', $remind);
1007 $this->prepareRemind($parsed,
$fields);
1012 $parsed = explode(
'_',
$event[
'PROPERTY_REMIND_SETTINGS']);
1013 $this->prepareRemind($parsed,
$fields);
1017 if (!empty(
$event[
'PROPERTY_IMPORTANCE']))
1022 if (!empty(
$event[
'PROPERTY_LOCATION']))
1024 $fields[
'LOCATION'] = Rooms\Util::unParseTextLocation(
$event[
'PROPERTY_LOCATION']);
1027 if (!empty(
$event[
'DETAIL_TEXT']))
1034 !empty(
$event[
'PROPERTY_PERIOD_TYPE'])
1035 && in_array(
$event[
'PROPERTY_PERIOD_TYPE'], [
'DAILY',
'WEEKLY',
'MONTHLY',
'YEARLY'])
1041 return (
new EventBuilderFromArray(
$fields))->build();
1055 private function syncSections(Connection
$connection, $calendarsList):
array
1058 foreach ($calendarsList as $calendar)
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,
1070 return $this->syncCalendarSections(
1087 private function getEventsToSync(
1089 \CDavGroupdavClientCalendar $client,
1091 ?RequestLogger $logger =
null
1094 $calendarEvents = [];
1095 $syncToken = !$calendar[
'IS_NEW'] ? $calendar[
'SYNC_TOKEN'] :
null;
1097 $calendarItems = $client->GetCalendarItemsBySyncToken($calendar[
'XML_ID'], $syncToken, $logger);
1099 if (!$calendarItems || !is_array($calendarItems))
1101 return $calendarEvents;
1104 foreach ($calendarItems as $item)
1107 (
int)$item[
'status'] === 404
1108 || mb_strpos($item[
'getcontenttype'],
'text/calendar') !==
false
1111 $calendarEvents[] = [
1112 'XML_ID' => $client::getBasenameWithoutExtension($item[
'href']),
1113 'SYNC_TOKEN' => $item[
'getetag'] ??
null,
1114 'STATUS' => $item[
'status'],
1119 $calendarEvents = $this->syncCalendarEvents(
1121 $calendar[
'SECTION_ID'],
1125 $eventsToUpdate = [];
1127 foreach ($calendarEvents as
$event)
1129 $link = $client->GetRequestEventPath($calendar[
'XML_ID'],
$event[
'XML_ID']);
1130 $eventsToUpdate[] = $link;
1131 $eventsMap[$link] =
$event;
1134 $calendarItems = [];
1135 if ($eventsToUpdate)
1137 $calendarItems = $client->GetCalendarItemsList(
1138 $calendar[
'XML_ID'],
1147 if (!$syncToken && $calendarItems)
1149 $calendarItems = $this->applyTimeLimitForEvents($calendarItems);
1152 return [$calendarItems, $eventsMap];
1160 private function applyTimeLimitForEvents($events):
array
1162 $timestamp = time() - self::TIME_SLICE;
1165 if (!empty(
$event[
'calendar-data'][
'PROPERTY_PERIOD_UNTIL']))
1167 if ((
int)\CCalendar::Timestamp(
$event[
'calendar-data'][
'PROPERTY_PERIOD_UNTIL']) - $timestamp < 0)
1169 unset($events[
$key]);
1173 !empty(
$event[
'calendar-data'][
'DATE_TO'])
1174 && (
int)\CCalendar::Timestamp(
$event[
'calendar-data'][
'DATE_TO']) - $timestamp < 0
1177 unset($events[
$key]);
1181 return array_values($events);
1192 if (!empty(
$event[
'ATTENDEE']))
1195 foreach (
$event[
'ATTENDEE'] as $attendee)
1199 if ($attendee->Parameter(
'CN'))
1201 $attendeeData[
'CN'] = $attendee->Parameter(
'CN');
1203 if ($attendee->Parameter(
'CUTYPE'))
1205 $attendeeData[
'CUTYPE'] = $attendee->Parameter(
'CUTYPE');
1207 if ($attendee->Parameter(
'PARTSTAT'))
1209 $attendeeData[
'PARTSTAT'] = $attendee->Parameter(
'PARTSTAT');
1211 if ($attendee->Parameter(
'ROLE'))
1213 $attendeeData[
'ROLE'] = $attendee->Parameter(
'ROLE');
1215 if ($attendee->Parameter(
'EMAIL'))
1217 $attendeeData[
'EMAIL'] = $attendee->Parameter(
'EMAIL');
1219 if ($attendee->Parameter(
'SCHEDULE-STATUS'))
1221 $attendeeData[
'SCHEDULE-STATUS'] = $attendee->Parameter(
'SCHEDULE-STATUS');
1223 if ($attendee->Value())
1225 $attendeeData[
'VALUE'] = $attendee->Value();
1228 $data[
'ATTENDEE'][] = $attendeeData;
1232 if ($organizer =
$event[
'ORGANIZER_ENTITY'][0])
1234 if ($organizer->Parameter(
'EMAIL'))
1236 $data[
'ORGANIZER'][
'EMAIL'] = $organizer->Parameter(
'EMAIL');
1238 if ($organizer->Parameter(
'CN'))
1240 $data[
'ORGANIZER'][
'CN'] = $organizer->Parameter(
'CN');
1242 if ($organizer->Value())
1244 $data[
'ORGANIZER'][
'VALUE'] = $organizer->Value();
1258 foreach (
$event[
'ATTACH'] as $attachment)
1260 $attachmentData = [];
1261 if ($attachment->Parameter(
'FMTTYPE'))
1263 $attachmentData[
'FMTTYPE'] = $attachment->Parameter(
'FMTTYPE');
1265 if ($attachment->Parameter(
'SIZE'))
1267 $attachmentData[
'SIZE'] = $attachment->Parameter(
'SIZE');
1269 if ($attachment->Parameter(
'FILENAME'))
1271 $attachmentData[
'FILENAME'] = $attachment->Parameter(
'FILENAME');
1273 if ($attachment->Parameter(
'MANAGED-ID'))
1275 $attachmentData[
'MANAGED-ID'] = $attachment->Parameter(
'MANAGED-ID');
1277 if ($attachment->Value())
1279 $attachmentData[
'VALUE'] = $attachment->Value();
1282 $data[
'ATTACH'][] = $attachmentData;
1293 private function prepareRemind($parsed,
array &
$fields): void
1295 $cnt =
count($parsed);
1296 if ($cnt === 2 && $parsed[1] ===
'date')
1299 'type' => $parsed[1],
1300 'value' =>
new DateTime($parsed[0],
'Ymd\\THis\\Z'),
1303 else if ($cnt === 2 &&
$fields[
'SKIP_TIME'] ===
'Y')
1306 'type' =>
'daybefore',
1308 'time' => 1440 - (int)$parsed[0] * 60,
1311 else if ($cnt === 2)
1314 'count' => (int)$parsed[0],
1315 'type' => $parsed[1],
1318 else if ($cnt === 3 && $parsed[2] ===
'daybefore')
1321 'type' => $parsed[2],
1323 'time' => (int)$parsed[0] * 60,
1326 else if ($cnt === 4 &&
$fields[
'SKIP_TIME'] ===
'Y')
1329 'type' =>
'daybefore',
1330 'before' => $parsed[0] + 1,
1331 'time' => 1440 - (int)$parsed[2] * 60,
1334 else if ($cnt === 4)
1337 'type' => $parsed[3],
1338 'count' => (int)$parsed[0] * 24 + $parsed[2],
1348 private function createConnectionObject(EO_DavConnection
$connection): Connection
1350 return (
new BuilderConnectionFromDM(
$connection))->build();
1358 private function processError(
array $error): string
1368 private function prepareName(?
string $name): string
1373 $name = Loc::getMessage(
'EC_DEFAULT_ENTRY_NAME');
1388 $fields[
'RRULE'][
'INTERVAL'] =
$event[
'PROPERTY_PERIOD_COUNT'];
1390 if (empty(
$fields[
'DT_LENGTH']) && !empty(
$event[
'PROPERTY_EVENT_LENGTH']))
1394 else if (isset(
$event[
'DT_TO_TS'],
$event[
'DT_FROM_TS']))
1403 if (
$fields[
'RRULE'][
'FREQ'] ===
'WEEKLY' && !empty(
$event[
'PROPERTY_PERIOD_ADDITIONAL']))
1405 $fields[
'RRULE'][
'BYDAY'] = [];
1406 $days = explode(
',',
$event[
'PROPERTY_PERIOD_ADDITIONAL']);
1407 foreach ($days as $day)
1409 $day = \CCalendar::WeekDayByInd($day,
false);
1412 $fields[
'RRULE'][
'BYDAY'][] = $day;
1415 $fields[
'RRULE'][
'BYDAY'] = implode(
',',
$fields[
'RRULE'][
'BYDAY']);
1418 if (!empty(
$event[
'PROPERTY_RRULE_COUNT']))
1422 else if (!empty(
$event[
'PROPERTY_PERIOD_UNTIL']))
1424 $fields[
'RRULE'][
'UNTIL'] =
$event[
'PROPERTY_PERIOD_UNTIL'];
1431 if (!empty(
$event[
'EXDATE']))
1445 if (isset(
$event[
'MEETING']) && !empty(
$event[
'MEETING'][
'LANGUAGE_ID']))
1447 $languageId =
$event[
'MEETING'][
'LANGUAGE_ID'];
1451 $languageId = \CCalendar::getUserLanguageId((
int)
$fields[
'OWNER_ID']);
1454 $fields[
'DESCRIPTION'] = (
new EventDescription())->prepareAfterImport(
$event[
'DETAIL_TEXT'], $languageId);
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'];
1469 if (!empty($localInstance[
'MEETING']))
1471 $instance[
'MEETING'] = unserialize($localInstance[
'MEETING'], [
'allowed_classes' =>
false]);
1473 if (!empty($localInstance[
'ATTENDEES_CODES']))
1475 $instance[
'ATTENDEES_CODES'] = explode(
',', $localInstance[
'ATTENDEES_CODES']);
1477 if (!empty($localInstance[
'IS_MEETING']))
1479 $instance[
'IS_MEETING'] = (bool)$localInstance[
'IS_MEETING'];
1481 if (!empty($localInstance[
'MEETING_HOST']))
1483 $instance[
'MEETING_HOST'] = $localInstance[
'MEETING_HOST'];
1485 if (!empty($localInstance[
'ACCESSIBILITY']))
1487 $instance[
'ACCESSIBILITY'] = $localInstance[
'ACCESSIBILITY'];
1501 $instance[
'IS_MEETING'] = $parentEvent[
'IS_MEETING'];
1505 $instance[
'MEETING_HOST'] = $parentEvent[
'MEETING_HOST'];
1509 $instance[
'MEETING'] = $parentEvent[
'MEETING'];
1511 if (empty(
$instance[
'ATTENDEES_CODES']))
1513 $instance[
'ATTENDEES_CODES'] = $parentEvent[
'ATTENDEES_CODES'];
1517 ? max($parentEvent[
'VERSION'],
$instance[
'EVENT_CONNECTION_VERSION'])
1518 : $parentEvent[
'VERSION']
1521 $instance[
'RECURRENCE_ID'] = $parentEvent[
'ID'];
1533 private function prepareInstanceEvents(
array $events):
array
1537 $eventsCount =
count($events);
1539 for (
$i = $eventsCount - 1;
$i >= 0;
$i--)
1541 $eventDate = \CCalendar::Date(\CCalendar::Timestamp($events[
$i][
'DATE_FROM']),
false);
1542 if (!in_array($eventDate, $eventDates,
true))
1544 $instances[] = $events[
$i];
1545 $eventDates[] = $eventDate;
1549 return [$instances, $eventDates];
1558 private function prepareExistedEventParams(
string $xmlId, ?
array $link =
null):
array
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])
1571 'IS_MEETING' => (bool)($link[
'IS_MEETING'] ??
null),
1572 'ATTENDEES_CODES' => ($link[
'ATTENDEES_CODES'] ??
null)
1573 ? explode(
',', $link[
'ATTENDEES_CODES'])
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,
1591 private function mergeExternalEventWithLocal(
1592 array $existedEvent,
1594 \CDavGroupdavClientCalendar $client
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'],
1608 return [
$event, $exDate];