55 $eventUserFields = [],
56 $attendeeBelongingToEvent = [],
57 $collabIdByParent = [],
58 $isAddIcalFailEmailError =
false,
95 if (is_array($recRule) && isset($recRule[
'FREQ']) && $recRule[
'FREQ'] !==
'WEEKLY' && isset($recRule[
'BYDAY']))
97 unset($recRule[
'BYDAY']);
124 $entryFields =
$params[
'arFields'] ?? [];
125 $arAffectedSections = [];
128 $sendInvitations = (
$params[
'sendInvitations'] ??
null) !==
false;
129 $sendEditNotification = (
$params[
'sendEditNotification'] ??
null) !==
false;
130 $checkLocationOccupancy = (
$params[
'checkLocationOccupancy'] ??
null) ===
true;
136 : CCalendar::GetCurUserId();
138 if (!
$userId && isset($entryFields[
'CREATED_BY']))
140 $userId = (int)$entryFields[
'CREATED_BY'];
143 $isNewEvent = !isset($entryFields[
'ID']) || !$entryFields[
'ID'];
144 $entryFields[
'TIMESTAMP_X'] = CCalendar::Date(time(),
true,
false);
148 if (!empty($entryFields[
'IS_MEETING']) && !isset($entryFields[
'ATTENDEES']) && isset($entryFields[
'ATTENDEES_CODES']))
150 $entryFields[
'ATTENDEES'] = \CCalendar::getDestinationUsers($entryFields[
'ATTENDEES_CODES']);
155 $currentEvent =
$params[
'currentEvent'] ?? self::GetById($entryFields[
'ID'],
$params[
'checkPermission'] ??
true);
157 if (!isset($entryFields[
'LOCATION']) || !is_array($entryFields[
'LOCATION']))
159 $entryFields[
'LOCATION'] = [
160 'NEW' => $entryFields[
'LOCATION'] ??
null,
165 isset($entryFields[
'MEETING'])
166 && is_array($entryFields[
'MEETING'])
167 && is_array($currentEvent[
'MEETING'])
168 && !isset($entryFields[
'MEETING'][
'CHAT_ID'])
169 && isset($currentEvent[
'MEETING'][
'CHAT_ID'])
172 $entryFields[
'MEETING'][
'CHAT_ID'] = $currentEvent[
'MEETING'][
'CHAT_ID'];
175 if (empty($entryFields[
'LOCATION'][
'OLD']))
177 $entryFields[
'LOCATION'][
'OLD'] = $currentEvent[
'LOCATION'] ??
null;
181 !empty($currentEvent[
'IS_MEETING'])
182 && !isset($entryFields[
'ATTENDEES'])
183 && $currentEvent[
'PARENT_ID'] === $currentEvent[
'ID']
184 && !empty($entryFields[
'IS_MEETING'])
187 $entryFields[
'ATTENDEES'] = [];
188 $attendees = self::GetAttendees($currentEvent[
'PARENT_ID']);
189 if (!empty($attendees[$currentEvent[
'PARENT_ID']]))
191 foreach ($attendees[$currentEvent[
'PARENT_ID']] as $attendee)
193 $entryFields[
'ATTENDEES'][] = $attendee[
'USER_ID'];
198 if (!empty($currentEvent[
'PARENT_ID']))
200 $entryFields[
'PARENT_ID'] = (int)$currentEvent[
'PARENT_ID'];
204 if (self::CheckFields($entryFields, $currentEvent,
$userId))
206 $attendees = (isset($entryFields[
'ATTENDEES']) && is_array($entryFields[
'ATTENDEES']))
207 ? $entryFields[
'ATTENDEES']
212 && (empty($entryFields[
'PARENT_ID']) || $entryFields[
'PARENT_ID'] === $entryFields[
'ID'])
215 $fromTs = $entryFields[
'DATE_FROM_TS_UTC'] ??
null;
216 $toTs = $entryFields[
'DATE_TO_TS_UTC'] ??
null;
217 if (($entryFields[
'DT_SKIP_TIME'] ??
null) !==
"Y")
219 $fromTs += (int)date(
'Z', $fromTs);
220 $toTs += (int)date(
'Z', $toTs);
223 $entryFields[
'LOCATION'] = self::checkLocationField($entryFields[
'LOCATION'] ??
null, $isNewEvent);
225 if ($checkLocationOccupancy)
227 self::checkLocationOccupancy($entryFields,
$params, $currentEvent,
$userId);
230 $entryFields[
'LOCATION'] = Bitrix\Calendar\Rooms\Util::setLocation(
231 $entryFields[
'LOCATION'][
'OLD'],
232 $entryFields[
'LOCATION'][
'NEW'],
235 'dateFrom' => CCalendar::Date($fromTs, $entryFields[
'DT_SKIP_TIME'] !==
"Y"),
236 'dateTo' => CCalendar::Date($toTs, $entryFields[
'DT_SKIP_TIME'] !==
"Y"),
238 'name' => $entryFields[
'NAME'],
239 'persons' =>
count($attendees),
240 'attendees' => $attendees,
241 'bRecreateReserveMeetings' => ($entryFields[
'LOCATION'][
'RE_RESERVE'] ??
null) !==
'N',
242 'checkPermission' =>
$params[
'checkPermission'] ??
null,
248 $entryFields[
'LOCATION'] = self::checkLocationField($entryFields[
'LOCATION'], $isNewEvent);
249 $entryFields[
'LOCATION'] = $entryFields[
'LOCATION'][
'NEW'];
253 if (isset($entryFields[
'SECTION_ID']))
255 $sectionId = (int)$entryFields[
'SECTION_ID'];
259 $sectionId = !empty($entryFields[
'SECTIONS'][0])
260 ? (int)$entryFields[
'SECTIONS'][0]
266 $sectionId = self::tryingToFindEventSection($isNewEvent, $entryFields,
$userId, $currentEvent);
269 $entryFields[
'SECTION_ID'] = $sectionId;
270 $arAffectedSections[] = $sectionId;
272 $section = CCalendarSect::GetList([
'arFilter' => [
'ID' => $sectionId],
273 'checkPermissions' =>
false,
274 'getPermissions' =>
false,
280 $entryFields[
'CAL_TYPE'] = $section[
'CAL_TYPE'];
281 $entryFields[
'OWNER_ID'] = $section[
'OWNER_ID'] ??
'';
284 $section[
'CAL_TYPE'] === Dictionary::CALENDAR_TYPE[
'group']
285 &&
Collab\
Entity\SectionEntityHelper::getIfCollab($sectionId)
286 && $entryFields[
'EVENT_TYPE'] !== Dictionary::EVENT_TYPE[
'shared_collab']
289 $entryFields[
'EVENT_TYPE'] = Dictionary::EVENT_TYPE[
'collab'];
293 if (($entryFields[
'CAL_TYPE'] ??
null) ===
'user')
295 $CACHE_MANAGER->ClearByTag(
'calendar_user_'.$entryFields[
'OWNER_ID']);
300 if (!isset($entryFields[
'CREATED_BY']))
302 $entryFields[
'CREATED_BY'] = (
303 !empty($entryFields[
'IS_MEETING'])
304 && ($entryFields[
'CAL_TYPE'] ??
null) ===
'user'
305 && !empty($entryFields[
'OWNER_ID'])
306 ) ? $entryFields[
'OWNER_ID'] :
$userId;
309 if (!isset($entryFields[
'DATE_CREATE']))
311 $entryFields[
'DATE_CREATE'] = $entryFields[
'TIMESTAMP_X'];
316 $arAffectedSections[] = $currentEvent[
'SECTION_ID'] ?? $currentEvent[
'SECT_ID'];
320 !isset($entryFields[
'IS_MEETING'])
321 && isset($entryFields[
'ATTENDEES'])
322 && is_array($entryFields[
'ATTENDEES'])
323 && empty($entryFields[
'ATTENDEES'])
326 $entryFields[
'IS_MEETING'] =
false;
328 if (!empty($entryFields[
'IS_MEETING']) && !$isNewEvent)
330 $entryChanges = self::CheckEntryChanges($entryFields, $currentEvent);
331 $changedFields = array_column($entryChanges,
'fieldKey');
334 !empty($entryFields[
'IS_MEETING'])
335 && !empty(
$params[
'editInstance'])
336 && !empty(
$params[
'instanceChanges'])
339 $entryChanges =
$params[
'instanceChanges'];
340 $changedFields = array_column($entryChanges,
'fieldKey');
343 $attendeesCodes = $entryFields[
'ATTENDEES_CODES'] ??
null;
344 if (is_array($attendeesCodes))
346 $entryFields[
'ATTENDEES_CODES'] = implode(
',', $attendeesCodes);
349 if ($entryFields[
'CAL_TYPE'] === Dictionary::CALENDAR_TYPE[
'open_event'])
352 $entryFields[
'MEETING_STATUS'] =
'N';
355 !isset($entryFields[
'MEETING_STATUS'])
356 && !empty($entryFields[
'MEETING_HOST'])
357 && (
int)$entryFields[
'MEETING_HOST'] === (
int)($entryFields[
'CREATED_BY'] ??
null)
360 $entryFields[
'MEETING_STATUS'] =
'H';
362 else if (!isset($entryFields[
'MEETING_STATUS']) && !$currentEvent)
364 $entryFields[
'MEETING_STATUS'] =
'Y';
367 if (isset($entryFields[
'MEETING']) && is_array($entryFields[
'MEETING']))
369 $entryFields[
'~MEETING'] = $entryFields[
'MEETING'];
370 $entryFields[
'MEETING'][
'REINVITE'] =
false;
372 $entryFields[
'MEETING'][
'MAIL_FROM'] =
373 $entryFields[
'MEETING'][
'MAIL_FROM']
374 ?? $meetingHostSettings[
'sendFromEmail']
377 $entryFields[
'MEETING'] = serialize($entryFields[
'MEETING']);
380 if (isset($entryFields[
'RELATIONS']) && is_array($entryFields[
'RELATIONS']))
382 $entryFields[
'~RELATIONS'] = $entryFields[
'RELATIONS'];
383 $entryFields[
'RELATIONS'] = serialize($entryFields[
'RELATIONS']);
387 isset($entryFields[
'REMIND'])
390 || !$entryFields[
'IS_MEETING']
391 || (
int)$entryFields[
'CREATED_BY'] ===
$userId
392 || (
$params[
'updateReminders'] ??
null) ===
true
398 elseif (!empty($currentEvent[
'REMIND']))
406 $entryFields[
'REMIND'] = serialize($reminderList);
409 isset($entryFields[
'SYNC_STATUS'])
413 $entryFields[
'SYNC_STATUS'] =
null;
416 if (isset($entryFields[
'EXDATE']) && is_array($entryFields[
'EXDATE']))
418 $entryFields[
'EXDATE'] = implode(
';', $entryFields[
'EXDATE']);
420 $entryFields[
'EXDATE'] = !empty($entryFields[
'EXDATE'])
421 ? self::convertExDatesToInternalFormat($entryFields[
'EXDATE'])
425 $entryFields[
'RRULE'] = self::convertRuleUntilToInternalFormat($entryFields[
'RRULE'] ??
null);
427 $AllFields = self::GetFields();
430 foreach($entryFields as $field =>
$val)
433 isset($AllFields[$field])
438 $dbFields[$field] =
$val;
442 if (!empty($dbFields[
'NAME']))
446 if (!empty($dbFields[
'DESCRIPTION']))
448 $dbFields[
'DESCRIPTION'] =
Emoji::encode($dbFields[
'DESCRIPTION']);
450 if (!empty($dbFields[
'LOCATION']))
452 $dbFields[
'LOCATION'] =
Emoji::encode($dbFields[
'LOCATION']);
455 CTimeZone::Disable();
457 $isOpenEvent = ($entryFields[
'CAL_TYPE'] ??
null) === Dictionary::CALENDAR_TYPE[
'open_event'];
461 $eventId =
$DB->Add(
"b_calendar_event", $dbFields, [
'DESCRIPTION',
'MEETING',
'EXDATE']);
465 $eventId = $entryFields[
'ID'];
466 $strUpdate =
$DB->PrepareUpdate(
"b_calendar_event", $dbFields);
468 "UPDATE b_calendar_event SET ".
470 " WHERE ID=". (int)$eventId;
473 'DESCRIPTION' =>
Emoji::encode($entryFields[
'DESCRIPTION'] ??
''),
474 'MEETING' => $entryFields[
'MEETING'] ??
null,
475 'EXDATE' => $entryFields[
'EXDATE'] ??
null,
480 self::updateEventAttendee($eventId,
$userId, $changedFields, $dbFields);
489 && (
$params[
'overSaving'] ??
null) !==
true
494 $loggerParams[
'arFields'] = $entryFields;
495 $loggerParams[
'loggerUuid'] = $eventId;
497 (new \Bitrix\Calendar\Sync\Util\RequestLogger(
$userId,
'portal_edit'))->write($loggerParams);
521 if (!empty($arAffectedSections))
523 CCalendarSect::UpdateModificationLabel($arAffectedSections);
527 !empty($entryFields[
'IS_MEETING'])
528 || (!$isNewEvent && !empty($currentEvent[
'IS_MEETING']))
531 if (empty($entryFields[
'PARENT_ID']))
533 Internals\EventTable::update((
int)$eventId, [
534 'PARENT_ID' => (
int)$eventId,
539 (empty($entryFields[
'PARENT_ID']) || $entryFields[
'PARENT_ID'] === $eventId)
543 self::CreateChildEvents($eventId, $entryFields,
$params, $entryChanges);
546 if ((empty($entryFields[
'PARENT_ID']) || $entryFields[
'PARENT_ID'] === $eventId) && !empty($entryFields[
'RECURRENCE_ID']))
548 self::UpdateParentEventExDate($entryFields[
'RECURRENCE_ID'], $entryFields[
'ORIGINAL_DATE_FROM'], $entryFields[
'ATTENDEES']);
551 if (empty($entryFields[
'PARENT_ID']))
553 $entryFields[
'PARENT_ID'] = (int)$eventId;
556 else if (($isNewEvent && empty($entryFields[
'PARENT_ID'])) || (!$isNewEvent && empty($currentEvent[
'PARENT_ID'])))
558 Internals\EventTable::update((
int)$eventId, [
559 'PARENT_ID' => (
int)$eventId,
562 if (empty($entryFields[
'PARENT_ID']))
564 $entryFields[
'PARENT_ID'] = (int)$eventId;
571 'reminders' => $reminderList,
572 'arFields' => $entryFields,
576 if ((empty($entryFields[
'PARENT_ID']) || $entryFields[
'PARENT_ID'] === $eventId))
578 self::updateSearchIndex((
int)$eventId, [
580 'updateAllByParent' =>
true,
581 'isNew' => $isNewEvent,
582 'changedFields' => $changedFields,
586 $nowUtc = time() - date(
'Z');
589 !empty($entryFields[
'IS_MEETING'])
590 && (
$params[
'overSaving'] ??
null) !==
true
593 $fromTo = self::GetEventFromToForUser($entryFields, $entryFields[
'OWNER_ID'] ??
null);
597 if (isset($entryFields[
'DATE_TO_TS_UTC']) && $entryFields[
'DATE_TO_TS_UTC'] > $nowUtc)
600 $sendEditNotification
601 && (
int)($entryFields[
'PARENT_ID'] ??
null) !== (
int)$eventId
602 && !empty($entryChanges)
604 ($entryFields[
'MEETING_STATUS'] ??
null) ===
'Y'
605 || ($entryFields[
'MEETING_STATUS'] ??
null) ===
'H'
612 (!empty($entryFields[
'MEETING_HOST']) && (
int)$entryFields[
'MEETING_HOST'] === (
int)
$userId)
613 || self::checkAttendeeBelongsToEvent($entryFields[
'PARENT_ID'] ??
null,
$userId)
616 $CACHE_MANAGER->ClearByTag(
'calendar_user_'.$entryFields[
'OWNER_ID']);
617 CCalendarNotify::Send([
618 'mode' =>
'change_notify',
619 'name' => $entryFields[
'NAME'] ??
null,
620 "from" => $fromTo[
'DATE_FROM'] ??
null,
621 "to" => $fromTo[
'DATE_TO'] ??
null,
622 "location" => CCalendar::GetTextLocation($entryFields[
"LOCATION"] ??
null),
623 "guestId" => $entryFields[
'OWNER_ID'] ??
null,
624 "eventId" => $entryFields[
'PARENT_ID'] ??
null,
625 "userId" => \CCalendar::GetUserId(),
626 "fields" => $entryFields,
627 "isSharing" => ($entryFields[
'EVENT_TYPE'] ??
null) === Dictionary::EVENT_TYPE[
'shared'],
628 "entryChanges" => $entryChanges,
633 (
int)($entryFields[
'PARENT_ID'] ??
null) !== $eventId
634 && ($entryFields[
'MEETING_STATUS'] ??
null) ===
'Q'
638 $CACHE_MANAGER->ClearByTag(
'calendar_user_'.$entryFields[
'OWNER_ID'] ??
'');
639 CCalendarNotify::Send(
array(
641 "name" => $entryFields[
'NAME'] ??
null,
642 "from" => $fromTo[
'DATE_FROM'] ??
null,
643 "to" => $fromTo[
'DATE_TO'] ??
null,
644 "location" => CCalendar::GetTextLocation($entryFields[
"LOCATION"] ??
null),
645 "guestId" => $entryFields[
'OWNER_ID'] ??
null,
646 "eventId" => $entryFields[
'PARENT_ID'] ??
null,
647 "userId" => $entryFields[
'MEETING_HOST'] ?? \CCalendar::GetUserId(),
648 "isSharing" => ($entryFields[
'EVENT_TYPE'] ??
null) === Dictionary::EVENT_TYPE[
'shared'],
649 "fields" => $entryFields,
656 !empty($entryFields[
'IS_MEETING'])
657 && !empty($entryFields[
'ATTENDEES_CODES'])
658 && (
int)($entryFields[
'PARENT_ID'] ??
null) === (
int)$eventId
659 && (
$params[
'overSaving'] ??
null) !==
true
660 && isset($entryFields[
'DATE_TO_TS_UTC'])
661 && $entryFields[
'DATE_TO_TS_UTC'] > $nowUtc
665 'eventId' => $eventId,
666 'arFields' => $entryFields,
667 'attendeesCodes' => $attendeesCodes,
671 CCalendar::ClearCache(
'event_list');
673 if (($entryFields[
'ACCESSIBILITY'] ??
'') ===
'absent')
675 (new \Bitrix\Calendar\Integration\Intranet\Absence())->cleanCache();
680 if (!empty($entryFields[
'LOCATION']))
699 if (($entryFields[
'PARENT_ID'] ??
null) === $eventId && $entryFields[
'CAL_TYPE'] !==
'location')
713 $currentEvent[
'ATTENDEE_LIST'] ??
null,
719 $pullUserId = (isset($entryFields[
'OWNER_ID']) && (int)$entryFields[
'OWNER_ID'] > 0)
720 ? (int)$entryFields[
'OWNER_ID']
726 && (
$params[
'overSaving'] ??
null) !==
true
731 $parentEventId = $entryFields[
'PARENT_ID'] ??
$result;
733 $entryFields = self::calculateUserOffset($pullUserId, $entryFields);
735 if (isset(
$params[
'attendeeStatuses']))
737 $attendeeList =
$params[
'attendeeStatuses'];
742 $attendeeListResult = $dbFields[
'CAL_TYPE'] === Dictionary::CALENDAR_TYPE[
'open_event']
743 ? self::getAttendeeList([], [$parentEventId])
744 : self::getAttendeeList([$parentEventId])
746 $attendeeList = $attendeeListResult[
'attendeeList'][$parentEventId] ?? [];
749 $entryFields = self::PreHandleEvent($entryFields);
750 $skipTime = $entryFields[
'DT_SKIP_TIME'] ===
'Y';
751 $dateFromFormatted = self::getDateInJsFormat(
752 CCalendar::createDateTimeObjectFromString($entryFields[
'DATE_FROM']),
755 $dateToFormatted = self::getDateInJsFormat(
756 CCalendar::createDateTimeObjectFromString($entryFields[
'DATE_TO']),
759 $isDaylightSavingTimezone = !empty($entryFields[
'TZ_FROM'])
760 ? CCalendar::isDaylightSavingTimezone($entryFields[
'TZ_FROM'])
767 !empty($entryFields[
'EVENT_TYPE'])
769 $entryFields[
'EVENT_TYPE'],
770 [Dictionary::EVENT_TYPE[
'collab'], Dictionary::EVENT_TYPE[
'shared_collab']],
775 $collabId = self::getCollabIdByParentId($entryFields[
'PARENT_ID']);
779 PushCommand::EditEvent,
782 'fields' => array_merge($entryFields, [
783 'ATTENDEE_LIST' => $attendeeList,
784 'DATE_FROM_FORMATTED' => $dateFromFormatted,
785 'DATE_TO_FORMATTED' => $dateToFormatted,
786 'IS_DAYLIGHT_SAVING_TZ' => $isDaylightSavingTimezone,
787 'COLLAB_ID' => $collabId,
788 'permissions' => self::getEventPermissions($entryFields, $pullUserId),
790 'newEvent' => $isNewEvent,
791 'requestUid' =>
$params[
'requestUid'] ??
null,
806 public static function GetById($id,
bool $checkPermissions =
true, $loadOriginalRecursion =
false)
815 'parseRecursion' =>
false,
816 'fetchAttendees' => $checkPermissions,
817 'checkPermissions' => $checkPermissions,
818 'setDefaultLimit' =>
false,
819 'loadOriginalRecursion' => $loadOriginalRecursion,
834 $isIntranetEnabled = CCalendar::IsIntranetEnabled();
835 $checkPermissions = (
$params[
'checkPermissions'] ??
null) !==
false;
836 $bCache = CCalendar::CacheTime() > 0;
837 $params[
'setDefaultLimit'] = (
$params[
'setDefaultLimit'] ??
null) ===
true;
839 $params[
'parseDescription'] =
$params[
'parseDescription'] ??
true;
840 $params[
'fetchAttendees'] = (
$params[
'fetchAttendees'] ??
null) !==
false;
841 $resultEntryList =
null;
844 CTimeZone::Disable();
847 $cache =
new CPHPCache;
848 $cacheId =
'eventlist'.md5(serialize(
$params)).CCalendar::GetOffset();
849 if ($checkPermissions)
851 $cacheId .=
'perm' . CCalendar::GetCurUserId() .
'|';
853 if (CCalendar::IsSocNet() && CCalendar::IsSocnetAdmin())
855 $cacheId .=
'socnetAdmin|';
857 $cachePath = CCalendar::CachePath().
'event_list';
859 if ($cache->InitCache(CCalendar::CacheTime(), $cacheId, $cachePath))
861 $cachedData = $cache->GetVars();
862 if (isset($cachedData[
'dateTimeFormat']) && $cachedData[
'dateTimeFormat'] ===
FORMAT_DATETIME)
864 $resultEntryList = $cachedData[
"resultEntryList"];
865 $userIndex = $cachedData[
"userIndex"];
870 if (!$bCache || !isset($resultEntryList))
873 $resultEntryList = [];
875 $listResult = self::getListOrm(
$params);
876 [$eventList, $parentMeetingIdList, $involvedUsersIdList, $openEventParentIdList] = $listResult;
878 if (!empty(
$params[
'fetchAttendees']) && !empty($parentMeetingIdList))
880 $attendeeListData = self::getAttendeeList($parentMeetingIdList, $openEventParentIdList);
881 $attendeeList = $attendeeListData[
'attendeeList'];
882 $involvedUsersIdList = array_unique(array_merge($involvedUsersIdList, $attendeeListData[
'userIdList']));
884 $userIndex = self::getUsersDetails($involvedUsersIdList);
886 $parentCollabConnections = self::getParentCollabConnections($eventList);
888 foreach ($eventList as
$event)
890 $isOpenEvent =
$event[
'CAL_TYPE'] === Dictionary::CALENDAR_TYPE[
'open_event'];
893 && isset($attendeeList[
$event[
'PARENT_ID']])
894 && $isIntranetEnabled
901 $event[
'ATTENDEE_LIST'] = array_filter(
902 $attendeeList[
$event[
'PARENT_ID']],
903 static fn (
array $a) =>
$a[
'status'] ===
'Y'
908 $event[
'ATTENDEE_LIST'] = $attendeeList[
$event[
'PARENT_ID']];
911 else if (!empty(
$params[
'fetchAttendees']))
916 $event[
'ATTENDEE_LIST'] = [
918 'id' => (int)
$event[
'MEETING_HOST'],
919 'entryId' =>
$event[
'ID'],
920 'status' => in_array(
$event[
'MEETING_STATUS'], [
'Y',
'N',
'Q',
'H'])
921 ?
$event[
'MEETING_STATUS']
930 $event[
'ATTENDEE_LIST'] = [];
933 if ($checkPermissions)
935 $checkPermissionsForEvent =
$userId !== (int)(
$event[
'CREATED_BY'] ??
null);
939 $checkPermissionsForEvent
940 && (
$event[
'CAL_TYPE'] ??
null) ===
'user'
944 $checkPermissionsForEvent =
false;
948 $checkPermissionsForEvent
950 && is_array(
$event[
'ATTENDEE_LIST'] ??
null)
951 && !empty(
$event[
'ATTENDEE_LIST'])
954 foreach(
$event[
'ATTENDEE_LIST'] as $attendee)
956 if ((
int)$attendee[
'id'] ===
$userId)
958 $checkPermissionsForEvent =
false;
964 if ($checkPermissionsForEvent)
972 $event[
'COLLAB_ID'] = self::getCollabIdByEvent(
$event, $parentCollabConnections);
975 'parseDescription' =>
$params[
'parseDescription'],
978 if (!empty(
$params[
'parseRecursion']) && self::CheckRecurcion(
$event))
980 self::ParseRecursion($resultEntryList,
$event, [
982 'fromLimit' =>
$arFilter[
"FROM_LIMIT"] ??
null,
983 'toLimit' =>
$arFilter[
"TO_LIMIT"] ??
null,
984 'loadLimit' =>
$params[
"limit"] ??
null,
985 'instanceCount' =>
$params[
'maxInstanceCount'] ??
false,
986 'preciseLimits' =>
$params[
'preciseLimits'] ??
false,
998 $cache->StartDataCache(CCalendar::CacheTime(), $cacheId, $cachePath);
1002 $cache->EndDataCache(
1004 'resultEntryList' => $resultEntryList,
1005 'userIndex' => $userIndex,
1013 $logger = new \Bitrix\Main\Diag\EventLogger(
'calendar',
'REMIND_DEBUG');
1015 $logger->debug(
'Found Closure. Message: ' . $e->getMessage());
1022 'resultEntryList' => $resultEntryList,
1023 'userIndex' => $userIndex,
1034 if (is_array($userIndex))
1036 foreach($userIndex as $userIndexId => $userIndexDetails)
1038 self::$userIndex[$userIndexId] = $userIndexDetails;
1042 CTimeZone::Enable();
1044 return $resultEntryList;
1047 private static function getListOrm(
$params = [])
1052 $fetchSection =
$params[
'fetchSection'] ??
null;
1053 $orderFields =
$params[
'arOrder'] ?? [];
1056 $getUf = (
$params[
'getUserfields'] ??
null) !==
false;
1057 $eventFields = self::getEventFields();
1068 if ((
$params[
'setDefaultLimit'] ??
null) !==
false)
1081 $query = Internals\EventTable::query();
1082 $attendeesQuery = Internals\EventTable::query();
1084 $joinOpenEvents = !empty(
1085 array_intersect([
'ID',
'PARENT_ID',
'RECURRENCE_ID',
'CAL_TYPE',
'SECTION'], array_keys(
$filterFields))
1092 if (is_string($value) && !$value)
1100 $timestamp = (int)CCalendar::Timestamp($value,
false);
1103 $query->where(
'DATE_TO_TS_UTC',
'>=', $timestamp);
1104 $attendeesQuery->where(
'DATE_TO_TS_UTC',
'>=', $timestamp);
1108 $timestamp = (int)CCalendar::Timestamp($value,
false);
1112 $query->where(
'DATE_FROM_TS_UTC',
'<=', $toTimestamp);
1113 $attendeesQuery->where(
'DATE_FROM_TS_UTC',
'<=', $toTimestamp);
1116 case 'MEETING_HOST':
1118 if (is_array($value))
1120 $value = array_map(
static function($item) {
1131 else if ((
int)$value)
1138 case 'RECURRENCE_ID':
1139 if (!is_array($value))
1144 $value = array_map(
'intval', $value);
1152 $attendeesQuery->whereIn(
$key, $value);
1156 if (!is_array($value))
1161 $value = array_map(
'intval', $value);
1172 $query->whereIn(
'OWNER_ID', $value);
1178 $query->where(
'ID',
'>', $value);
1182 if (!is_array($value))
1187 $joinOpenEvents = $joinOpenEvents && in_array(Dictionary::CALENDAR_TYPE[
'open_event'], $value,
true);
1189 $calTypes = array_diff($value, [Dictionary::CALENDAR_TYPE[
'open_event']]);
1190 if (!empty($calTypes))
1192 $query->whereIn(
'CAL_TYPE', $calTypes);
1197 if (!is_array($value))
1202 if (is_array($value))
1205 foreach ($value as $item)
1209 $sections[] = (int)$item;
1213 if (!empty($sections))
1216 $joinOpenEvents = $joinOpenEvents && in_array($openEventSection?->
getId(), $sections,
true);
1217 $sections = array_diff($sections, [$openEventSection?->
getId()]);
1219 if (Util::isSectionStructureConverted())
1221 $query->whereIn(
'SECTION_ID', $sections);
1225 $query->whereIn(
'EVENT_SECT.SECT_ID', $sections);
1230 case 'ACTIVE_SECTION':
1231 if ($value ===
'Y' && Util::isSectionStructureConverted())
1233 $query->where(
'SECTION.ACTIVE', $value);
1236 case '*SEARCHABLE_CONTENT':
1237 $searchText = \Bitrix\Main\ORM\Query\Filter\Helper::matchAgainstWildcard($value);
1238 $query->whereMatch(
'SEARCHABLE_CONTENT', $searchText);
1240 case '*%SEARCHABLE_CONTENT':
1241 $query->whereLike(
'SEARCHABLE_CONTENT',
'%' . $value .
'%');
1243 case '=UF_CRM_CAL_EVENT':
1244 $query->where(
'UF_CRM_CAL_EVENT', $value);
1247 if (in_array(
$key, $eventFields,
true))
1249 if (is_array($value))
1268 $attendeesQuery->setSelect([
1274 $joinSection = $fetchSection
1276 && Util::isSectionStructureConverted()
1281 $selectFields[
'SECTION_DAV_XML_ID'] =
'SECTION.CAL_DAV_CAL';
1290 ->registerRuntimeField(
1293 OpenEvents\Internals\OpenEventOptionTable::getEntity(),
1294 Join::on(
'this.ID',
'ref.EVENT_ID'),
1304 if (in_array(
$key, $eventFields,
true))
1306 $orderList[
$key] = (mb_strtoupper(
$order) ===
'DESC') ?
'DESC' :
'ASC';
1310 if (!empty($orderList))
1312 $query->setOrder($orderList);
1313 $attendeesQuery->setOrder($orderList);
1319 $attendeesQuery->setLimit((
int)
$params[
'limit']);
1322 if ((
$params[
'loadOriginalRecursion'] ??
null) ===
true)
1324 self::applyLoadOriginalRecursionLogic(
$query);
1327 $queryResult =
$query->exec();
1328 $hasAttendees =
$query->getEntity()->hasField(
'ATTENDEES');
1329 $hasOriginalRecursion =
$query->getEntity()->hasField(
'ORIGINAL_RECURSION');
1331 $parentMeetingIdList = [];
1332 $involvedUsersIdList = [];
1334 $openEventList = [];
1336 while ($eventObject = $queryResult->fetchObject())
1338 [
$event, $parentMeetings, $involvedUsers] = self::prepareEventObject(
1342 $hasOriginalRecursion
1345 $parentMeetingIdList = [...$parentMeetingIdList, ...$parentMeetings];
1346 $involvedUsersIdList = [...$involvedUsersIdList, ...$involvedUsers];
1352 if (!empty(
$event[
'CAL_TYPE']) &&
$event[
'CAL_TYPE'] === Dictionary::CALENDAR_TYPE[
'open_event'])
1354 $openEventList[] = (int)
$event[
'ID'];
1358 if (!$joinOpenEvents)
1360 return [$eventList, array_unique($parentMeetingIdList), array_unique($involvedUsersIdList), []];
1364 ->registerRuntimeField(
1365 (
new ReferenceField(
1367 Internals\EventAttendeeTable::getEntity(),
1368 Join::on(
'this.ID',
'ref.EVENT_ID')
1369 ->whereIn(
'ref.OWNER_ID', $attendeeIds)
1370 ->where(
'ref.MEETING_STATUS',
'Y')
1371 ->where(
'ref.DELETED',
'N')
1374 ->configureJoinType(Join::TYPE_INNER)
1377 ->registerRuntimeField(
1380 OpenEvents\Internals\OpenEventOptionTable::getEntity(),
1381 Join::on(
'this.ID',
'ref.EVENT_ID'),
1384 ->where(
'DELETED',
'N')
1385 ->where(
'CAL_TYPE', Dictionary::CALENDAR_TYPE[
'open_event'])
1388 $attendeesQueryResult = $attendeesQuery->exec();
1389 $hasAttendees = $attendeesQuery->getEntity()->hasField(
'ATTENDEES');
1390 $openEventParentMeetingIdList = [];
1392 while ($eventObject = $attendeesQueryResult->fetchObject())
1394 [
$event, $parentMeetings, $involvedUsers] = self::prepareEventObject(
$userId, $eventObject, $hasAttendees);
1396 $parentMeetingIdList = [...$parentMeetingIdList, ...$parentMeetings];
1397 $involvedUsersIdList = [...$involvedUsersIdList, ...$involvedUsers];
1398 $openEventParentMeetingIdList = [...$openEventParentMeetingIdList, ...$parentMeetings];
1402 if ((
$key = array_search((
int)
$event[
'ID'], $openEventList,
true)) !==
false)
1414 array_unique($parentMeetingIdList),
1415 array_unique($involvedUsersIdList),
1416 array_unique($openEventParentMeetingIdList),
1420 private static function prepareEventObject(
1422 Internals\EO_Event $eventObject,
1424 bool $hasOriginalRecursion =
false
1427 $event = $eventObject->collectValues();
1430 unset(
$event[
'UTS_OBJECT']);
1431 unset(
$event[
'SECTION']);
1447 foreach ($stringFields as $sField)
1449 if (!isset(
$event[$sField]))
1461 foreach ($boolFields as $bField)
1463 if (!isset(
$event[$bField]))
1467 $event[$bField] =
$event[$bField] ===
true ?
'Y' :
'N';
1472 'SYNC_STATUS' =>
'',
1475 'ATTENDEES_CODES' =>
'',
1476 'RECURRENCE_ID' => 0,
1478 foreach ($notEmptyFields as $nesField => $emptyValue)
1480 if (!isset(
$event[$nesField]))
1487 $event[
'SECTION_DAV_XML_ID'] = $eventObject->getSection()?->getCalDavCal();
1489 if (isset(
$event[
'PARENT_ID']))
1494 $isFullDay = (
$event[
'DT_SKIP_TIME'] ??
null) ===
'Y';
1496 if (!empty(
$event[
'DATE_FROM']))
1498 $event[
'DATE_FROM_FORMATTED'] = self::getDateInJsFormat(
$event[
'DATE_FROM'], $isFullDay);
1502 if (!empty(
$event[
'DATE_TO']))
1504 $event[
'DATE_TO_FORMATTED'] = self::getDateInJsFormat(
$event[
'DATE_TO'], $isFullDay);
1508 if (!empty(
$event[
'ORIGINAL_DATE_FROM']))
1510 $event[
'ORIGINAL_DATE_FROM'] = (string)
$event[
'ORIGINAL_DATE_FROM'];
1513 if (!empty(
$event[
'TIMESTAMP_X']))
1518 if (!empty(
$event[
'DATE_CREATE']))
1523 $event[
'IS_DAYLIGHT_SAVING_TZ'] = !empty(
$event[
'TZ_FROM'])
1530 (
int)(
$event[
'IS_MEETING'] ?? 0) > 0
1531 || (
$event[
'CAL_TYPE'] ??
null) === Dictionary::CALENDAR_TYPE[
'open_event']
1534 $event[
'IS_MEETING'] =
true;
1538 $event[
'IS_MEETING'] =
false;
1541 if (empty(
$event[
'NAME']))
1543 $event[
'NAME'] = Loc::getMessage(
'EC_T_NEW_EVENT');
1550 if (!empty(
$event[
'DESCRIPTION']))
1552 $event[
'DESCRIPTION'] = Emoji::decode(
$event[
'DESCRIPTION']);
1555 if (!empty(
$event[
'LOCATION']))
1557 $event[
'LOCATION'] = Emoji::decode(
$event[
'LOCATION']);
1560 if (!empty(
$event[
'DT_LENGTH']) && is_numeric(
$event[
'DT_LENGTH']))
1565 $parentMeetingIdList = [];
1566 $involvedUsersIdList = [];
1568 if (!empty(
$event[
'IS_MEETING']) && !empty(
$event[
'PARENT_ID']) && CCalendar::IsIntranetEnabled())
1570 $parentMeetingIdList[] =
$event[
'PARENT_ID'];
1573 if (!empty(
$event[
'CREATED_BY']))
1575 $involvedUsersIdList[] =
$event[
'CREATED_BY'];
1579 !empty(
$event[
'IS_MEETING'])
1580 &&
$event[
'CAL_TYPE'] === Dictionary::CALENDAR_TYPE[
'user']
1586 if (!$defaultMeetingSection || !CCalendarSect::GetById($defaultMeetingSection,
false))
1589 $defaultMeetingSection = $sectRes[
'sectionId'];
1593 $event[
'SECT_ID'] = (string)$defaultMeetingSection;
1594 $event[
'SECTION_ID'] = (string)$defaultMeetingSection;
1597 $eventOptions = $eventObject->get(
'EVENT_OPTIONS');
1598 $event[
'OPTIONS'] = $eventOptions ? $eventOptions->collectValues() :
null;
1602 if ($hasAttendees && ($currentAttendee = $eventObject->get(
'ATTENDEES')))
1615 if (isset(
$event[
'MEETING_STATUS']))
1617 $event[
'MEETING_STATUS'] = $currentAttendee->getMeetingStatus();
1619 if (isset(
$event[
'SECTION_ID']))
1622 $event[
'SECTION_ID'] = (string)$currentAttendee->getSectionId();
1623 $event[
'SECT_ID'] = (string)$currentAttendee->getSectionId();
1625 if (!empty($currentAttendee->getColor()))
1627 $event[
'COLOR'] = $currentAttendee->getColor();
1629 if (isset(
$event[
'REMIND']))
1631 $event[
'REMIND'] = $currentAttendee->getRemind();
1633 if (isset(
$event[
'SYNC_STATUS']))
1635 $event[
'SYNC_STATUS'] = $currentAttendee->getSyncStatus();
1637 $event[
'CURRENT_ATTENDEE_ID'] = $currentAttendee->getId();
1638 $parentMeetingIdList[] =
$event[
'ID'];
1643 $hasOriginalRecursion
1644 && $originalRecursion = $eventObject->get(
'ORIGINAL_RECURSION')
1647 $event[
'ORIGINAL_RECURSION_ID'] = $originalRecursion->getOriginalRecursionEventId();
1650 return [
$event, $parentMeetingIdList, $involvedUsersIdList];
1653 private static function applyLoadOriginalRecursionLogic(Query
$query): void
1655 $query->registerRuntimeField(
1657 'ORIGINAL_RECURSION',
1658 Internals\EventOriginalRecursionTable::class,
1659 Join::on(
'this.PARENT_ID',
'ref.PARENT_EVENT_ID'),
1661 ->configureJoinType(Join::TYPE_LEFT)
1666 [
'ORIGINAL_RECURSION_ID' =>
'ORIGINAL_RECURSION.ORIGINAL_RECURSION_EVENT_ID']
1672 private static function getDateInJsFormat(DateTime|Bitrix\Main\Type\DateTime $date, $isFullDay): string
1676 return $date->format(
'D M d Y');
1679 return $date->format(
'D M d Y H:i:s');
1682 private static function GetFields()
1685 if (!
count(self::$fields))
1688 self::$fields =
array(
1689 "ID" => Array(
"FIELD_NAME" =>
"CE.ID",
"FIELD_TYPE" =>
"int"),
1690 "PARENT_ID" => Array(
"FIELD_NAME" =>
"CE.PARENT_ID",
"FIELD_TYPE" =>
"int"),
1691 "DELETED" => Array(
"FIELD_NAME" =>
"CE.DELETED",
"FIELD_TYPE" =>
"string"),
1692 "CAL_TYPE" => Array(
"FIELD_NAME" =>
"CE.CAL_TYPE",
"FIELD_TYPE" =>
"string"),
1693 "SYNC_STATUS" => Array(
"FIELD_NAME" =>
"CE.SYNC_STATUS",
"FIELD_TYPE" =>
"string"),
1694 "OWNER_ID" => Array(
"FIELD_NAME" =>
"CE.OWNER_ID",
"FIELD_TYPE" =>
"int"),
1695 "EVENT_TYPE" => Array(
"FIELD_NAME" =>
"CE.EVENT_TYPE",
"FIELD_TYPE" =>
"string"),
1696 "CREATED_BY" => Array(
"FIELD_NAME" =>
"CE.CREATED_BY",
"FIELD_TYPE" =>
"int"),
1697 "NAME" => Array(
"FIELD_NAME" =>
"CE.NAME",
"FIELD_TYPE" =>
"string"),
1698 "DATE_FROM" => Array(
"FIELD_NAME" =>
$DB->DateToCharFunction(
"CE.DATE_FROM").
' as DATE_FROM',
"FIELD_TYPE" =>
"date"),
1699 "DATE_TO" => Array(
"FIELD_NAME" =>
$DB->DateToCharFunction(
"CE.DATE_TO").
' as DATE_TO',
"FIELD_TYPE" =>
"date"),
1700 "TZ_FROM" => Array(
"FIELD_NAME" =>
"CE.TZ_FROM",
"FIELD_TYPE" =>
"string"),
1701 "TZ_TO" => Array(
"FIELD_NAME" =>
"CE.TZ_TO",
"FIELD_TYPE" =>
"string"),
1702 "ORIGINAL_DATE_FROM" => Array(
"FIELD_NAME" =>
$DB->DateToCharFunction(
"CE.ORIGINAL_DATE_FROM").
' as ORIGINAL_DATE_FROM',
"FIELD_TYPE" =>
"date"),
1703 "TZ_OFFSET_FROM" => Array(
"FIELD_NAME" =>
"CE.TZ_OFFSET_FROM",
"FIELD_TYPE" =>
"int"),
1704 "TZ_OFFSET_TO" => Array(
"FIELD_NAME" =>
"CE.TZ_OFFSET_TO",
"FIELD_TYPE" =>
"int"),
1705 "DATE_FROM_TS_UTC" => Array(
"FIELD_NAME" =>
"CE.DATE_FROM_TS_UTC",
"FIELD_TYPE" =>
"int"),
1706 "DATE_TO_TS_UTC" => Array(
"FIELD_NAME" =>
"CE.DATE_TO_TS_UTC",
"FIELD_TYPE" =>
"int"),
1708 "TIMESTAMP_X" => Array(
"FIELD_NAME" =>
$DB->DateToCharFunction(
"CE.TIMESTAMP_X").
' as TIMESTAMP_X',
"FIELD_TYPE" =>
"date"),
1709 "DATE_CREATE" => Array(
"FIELD_NAME" =>
$DB->DateToCharFunction(
"CE.DATE_CREATE").
' as DATE_CREATE',
"FIELD_TYPE" =>
"date"),
1710 "DESCRIPTION" => Array(
"FIELD_NAME" =>
"CE.DESCRIPTION",
"FIELD_TYPE" =>
"string"),
1711 "DT_SKIP_TIME" => Array(
"FIELD_NAME" =>
"CE.DT_SKIP_TIME",
"FIELD_TYPE" =>
"string"),
1712 "DT_LENGTH" => Array(
"FIELD_NAME" =>
"CE.DT_LENGTH",
"FIELD_TYPE" =>
"int"),
1713 "PRIVATE_EVENT" => Array(
"FIELD_NAME" =>
"CE.PRIVATE_EVENT",
"FIELD_TYPE" =>
"string"),
1714 "ACCESSIBILITY" => Array(
"FIELD_NAME" =>
"CE.ACCESSIBILITY",
"FIELD_TYPE" =>
"string"),
1715 "IMPORTANCE" => Array(
"FIELD_NAME" =>
"CE.IMPORTANCE",
"FIELD_TYPE" =>
"string"),
1716 "IS_MEETING" => Array(
"FIELD_NAME" =>
"CE.IS_MEETING",
"FIELD_TYPE" =>
"string"),
1717 "MEETING_HOST" => Array(
"FIELD_NAME" =>
"CE.MEETING_HOST",
"FIELD_TYPE" =>
"int"),
1718 "MEETING_STATUS" => Array(
"FIELD_NAME" =>
"CE.MEETING_STATUS",
"FIELD_TYPE" =>
"string"),
1719 "MEETING" => Array(
"FIELD_NAME" =>
"CE.MEETING",
"FIELD_TYPE" =>
"string"),
1720 "LOCATION" => Array(
"FIELD_NAME" =>
"CE.LOCATION",
"FIELD_TYPE" =>
"string"),
1721 "REMIND" => Array(
"FIELD_NAME" =>
"CE.REMIND",
"FIELD_TYPE" =>
"string"),
1722 "COLOR" => Array(
"FIELD_NAME" =>
"CE.COLOR",
"FIELD_TYPE" =>
"string"),
1723 "RRULE" => Array(
"FIELD_NAME" =>
"CE.RRULE",
"FIELD_TYPE" =>
"string"),
1724 "EXDATE" => Array(
"FIELD_NAME" =>
"CE.EXDATE",
"FIELD_TYPE" =>
"string"),
1725 "ATTENDEES_CODES" => Array(
"FIELD_NAME" =>
"CE.ATTENDEES_CODES",
"FIELD_TYPE" =>
"string"),
1726 "DAV_XML_ID" => Array(
"FIELD_NAME" =>
"CE.DAV_XML_ID",
"FIELD_TYPE" =>
"string"),
1727 "DAV_EXCH_LABEL" => Array(
"FIELD_NAME" =>
"CE.DAV_EXCH_LABEL",
"FIELD_TYPE" =>
"string"),
1728 "G_EVENT_ID" => Array(
"FIELD_NAME" =>
"CE.G_EVENT_ID",
"FIELD_TYPE" =>
"string"),
1729 "CAL_DAV_LABEL" => Array(
"FIELD_NAME" =>
"CE.CAL_DAV_LABEL",
"FIELD_TYPE" =>
"string"),
1730 "VERSION" => Array(
"FIELD_NAME" =>
"CE.VERSION",
"FIELD_TYPE" =>
"string"),
1731 "RECURRENCE_ID" => Array(
"FIELD_NAME" =>
"CE.RECURRENCE_ID",
"FIELD_TYPE" =>
"int"),
1732 "RELATIONS" => Array(
"FIELD_NAME" =>
"CE.RELATIONS",
"FIELD_TYPE" =>
"int"),
1733 "SEARCHABLE_CONTENT" => Array(
"FIELD_NAME" =>
"CE.SEARCHABLE_CONTENT",
"FIELD_TYPE" =>
"string"),
1734 "SECTION_ID" => Array(
"FIELD_NAME" =>
"CE.SECTION_ID",
"FIELD_TYPE" =>
"int"),
1738 return self::$fields;
1741 private static function getEventFields():
array
1757 'ORIGINAL_DATE_FROM',
1798 "DELETE FROM b_calendar_event_sect WHERE EVENT_ID=". (
int)$eventId);
1801 "INSERT INTO b_calendar_event_sect(EVENT_ID, SECT_ID) ".
1802 "SELECT ". (
int)$eventId .
", ID ".
1803 "FROM b_calendar_section ".
1804 "WHERE ID=". (
int)$sectionId);
1815 if (!CCalendar::IsSocNet())
1818 'attendeeList' => $attendeeList,
1819 'userIdList' => $userIdList,
1823 $entryIdList = is_array($entryIdList)
1824 ? array_map(
'intval', array_unique($entryIdList))
1825 : [(int)$entryIdList]
1828 if (empty($entryIdList) && empty($openEventIdList))
1831 'attendeeList' => $attendeeList,
1832 'userIdList' => $userIdList,
1837 if (!empty($entryIdList))
1839 $queries[] = Internals\EventTable::query()
1841 'USER_ID' =>
'OWNER_ID',
1844 'EVENT_MEETING_STATUS' =>
'MEETING_STATUS',
1847 ->where(
'ACTIVE',
'Y')
1848 ->where(
'CAL_TYPE',
'user')
1849 ->where(
'DELETED',
'N')
1850 ->whereIn(
'PARENT_ID', $entryIdList)
1855 if (!empty($openEventIdList))
1858 $queries[] = Internals\EventTable::query()
1859 ->registerRuntimeField(
1860 (
new ReferenceField(
1863 Join::on(
'this.ID',
'ref.EVENT_ID')
1867 'USER_ID' =>
'EVENT_ATTENDEE.OWNER_ID',
1870 'EVENT_MEETING_STATUS' =>
'EVENT_ATTENDEE.MEETING_STATUS',
1872 'ATTENDEE_ID' =>
'EVENT_ATTENDEE.ID',
1874 ->where(
'ACTIVE',
'Y')
1875 ->where(
'DELETED',
'N')
1876 ->whereIn(
'PARENT_ID', $openEventIdList)
1879 ->logic(ConditionTree::LOGIC_OR)
1880 ->where(
'EVENT_ATTENDEE.MEETING_STATUS',
'Y')
1881 ->whereNot(
'CAL_TYPE', Dictionary::CALENDAR_TYPE[
'open_event'])
1888 $queryCombineGenerator =
static function (
array $queryResults): \Generator
1890 foreach ($queryResults as $queryResult)
1892 while($item = $queryResult->fetch())
1900 $combinedQuery = $queryCombineGenerator($queries);
1902 while($entry = $combinedQuery->current())
1904 $combinedQuery->next();
1905 $entry[
'USER_ID'] = (int)$entry[
'USER_ID'];
1906 if (!isset($attendeeList[$entry[
'PARENT_ID']]))
1908 $attendeeList[$entry[
'PARENT_ID']] = [];
1910 $entry[
"STATUS"] = trim($entry[
'EVENT_MEETING_STATUS']);
1912 ($entry[
'PARENT_ID'] === $entry[
'ID'] || $entry[
'USER_ID'] === $entry[
'MEETING_HOST'])
1913 && !isset($entry[
'ATTENDEE_ID'])
1916 $entry[
"STATUS"] =
"H";
1918 if (!isset($attendeeList[$entry[
'PARENT_ID']][$entry[
'USER_ID']])) {
1919 $attendeeList[$entry[
'PARENT_ID']][$entry[
'USER_ID']] = [
1920 'id' => $entry[
'USER_ID'],
1921 'entryId' => $entry[
'ID'],
1922 'status' => $entry[
"STATUS"],
1926 if (!in_array($entry[
'USER_ID'], $userIdList,
true))
1928 $userIdList[] = $entry[
'USER_ID'];
1933 $attendeeList = array_map(
static fn($itemList) => array_values($itemList), $attendeeList);
1936 'attendeeList' => $attendeeList,
1937 'userIdList' => $userIdList,
1945 if (!empty($userIdList))
1947 $userIdList = array_unique(array_map(
static fn(
$userId) => (
int)
$userId, $userIdList));
1949 $userList = CCalendar::GetUserList($userIdList);
1954 $id = (int)$userData[
'ID'];
1955 if (!in_array($id, $userIdList,
true))
1960 $users[$userData[
'ID']] = [
1961 'ID' => $userData[
'ID'],
1962 'DISPLAY_NAME' => CCalendar::GetUserName($userData),
1963 'URL' => CCalendar::GetUserUrl($userData[
'ID']),
1964 'AVATAR' => CCalendar::GetUserAvatarSrc($userData,
$params),
1965 'EMAIL_USER' => $userData[
'EXTERNAL_AUTH_ID'] ===
'email',
1967 'COLLAB_USER' => $userData[
'COLLAB_USER'],
1974 public static function GetAttendees($eventIdList = [], $checkDeleted =
true)
1979 if (CCalendar::IsSocNet())
1981 $eventIdList = is_array($eventIdList) ? array_map(
'intval', array_unique($eventIdList)) : [(int)$eventIdList];
1983 if (!empty($eventIdList))
1985 $deletedCondition = $checkDeleted ?
"CE.DELETED = 'N' AND" :
'';
1988 CE.OWNER_ID AS USER_ID,
1989 CE.ID, CE.PARENT_ID, CE.MEETING_STATUS, CE.MEETING_HOST,
1990 U.LOGIN, U.NAME, U.LAST_NAME, U.SECOND_NAME, U.EMAIL, U.PERSONAL_PHOTO, U.WORK_POSITION, U.EXTERNAL_AUTH_ID,
1994 LEFT JOIN b_user U ON (U.ID=CE.OWNER_ID)
1995 LEFT JOIN b_uts_user BUF ON (BUF.VALUE_ID = CE.OWNER_ID)
1998 CE.CAL_TYPE = 'user' AND
2000 CE.PARENT_ID in (".implode(
',', $eventIdList).
")";
2003 while($entry =
$res->Fetch())
2005 $parentId = (int)$entry[
'PARENT_ID'];
2006 $attendeeId = (int)$entry[
'USER_ID'];
2007 if (!isset($attendees[$parentId]))
2009 $attendees[$parentId] = [];
2011 $entry[
"STATUS"] = trim($entry[
"MEETING_STATUS"]);
2012 if ($parentId === (
int)$entry[
'ID'])
2014 $entry[
"STATUS"] =
"H";
2017 CCalendar::SetUserDepartment($attendeeId, (empty($entry[
'UF_DEPARTMENT'])
2019 : unserialize($entry[
'UF_DEPARTMENT'], [
'allowed_classes' =>
false])));
2020 $entry[
'DISPLAY_NAME'] = CCalendar::GetUserName($entry);
2021 $entry[
'URL'] = CCalendar::GetUserUrl($attendeeId);
2022 $entry[
'AVATAR'] = CCalendar::GetUserAvatarSrc($entry);
2023 $entry[
'EVENT_ID'] = $entry[
'ID'];
2025 unset($entry[
'ID'], $entry[
'PARENT_ID'], $entry[
'UF_DEPARTMENT'], $entry[
'LOGIN']);
2026 $attendees[$parentId][] = $entry;
2036 $eventIdList = array_map(
'intval', array_unique($eventIdList));
2038 if (empty($eventIdList))
2044 $entries = Internals\EventTable::query()
2045 ->setSelect([
'PARENT_ID',
'CAL_TYPE',
'OWNER_ID',
'ACTIVE',
'DELETED'])
2046 ->whereIn(
'PARENT_ID', $eventIdList)
2047 ->where(
'CAL_TYPE', Dictionary::CALENDAR_TYPE[
'user'])
2048 ->where(
'ACTIVE',
'Y')
2049 ->where(
'DELETED',
'N')
2054 foreach ($entries as $entry)
2056 $parentId = $entry[
'PARENT_ID'];
2057 $attendeeIds[$parentId] ??= [];
2058 $attendeeIds[$parentId][] = (int)$entry[
'OWNER_ID'];
2061 return $attendeeIds;
2068 $userId = CCalendar::GetUserId();
2071 $sectId = (int)
$event[
'SECT_ID'];
2072 if (empty(
$event[
'ACCESSIBILITY']))
2074 $event[
'ACCESSIBILITY'] =
'busy';
2077 $private = !empty(
$event[
'PRIVATE_EVENT']) && (
$event[
'CAL_TYPE'] ??
null) ===
'user';
2078 $isAttendee =
false;
2080 if (is_array(
$event[
'ATTENDEE_LIST'] ??
null))
2082 foreach(
$event[
'ATTENDEE_LIST'] as $attendee)
2084 if ((
int)$attendee[
'id'] === (
int)
$userId)
2093 (
$event[
'CAL_TYPE'] ??
null) ===
'user'
2094 && !empty(
$event[
'IS_MEETING'])
2099 $sectId = (int)CCalendar::GetMeetingSection(
$userId);
2102 $accessResult = self::checkEventAccessFromGetList(
$event, $sectId,
$userId);
2104 if ($private || (!$accessResult[ActionDictionary::ACTION_EVENT_VIEW_FULL] && !$isAttendee))
2108 $event[
'NAME'] =
'[' . Loc::getMessage(
'EC_ACCESSIBILITY_' . mb_strtoupper(
$event[
'ACCESSIBILITY'])) .
']';
2109 $event[
'IS_ACCESSIBLE_TO_USER'] =
false;
2111 if (!$accessResult[ActionDictionary::ACTION_EVENT_VIEW_TIME])
2116 else if (!$accessResult[ActionDictionary::ACTION_EVENT_VIEW_TITLE])
2118 if ($accessResult[ActionDictionary::ACTION_EVENT_VIEW_TIME])
2120 $event[
'NAME'] =
'[' . Loc::getMessage(
'EC_ACCESSIBILITY_' . mb_strtoupper(
$event[
'ACCESSIBILITY'])) .
']';
2121 $event[
'IS_ACCESSIBLE_TO_USER'] =
false;
2130 $event[
'NAME'] .=
' [' . Loc::getMessage(
'EC_ACCESSIBILITY_' . mb_strtoupper(
$event[
'ACCESSIBILITY'])) .
']';
2139 $event[
'ATTENDEES_CODES'],
2140 $event[
'UF_CRM_CAL_EVENT'],
2141 $event[
'UF_WEBDAV_CAL_EVENT'],
2150 if (CCalendar::DFormat(
false) !== ExcludedDatesCollection::EXCLUDED_DATE_FORMAT)
2152 if (preg_match_all(
"/(\d{2})\.(\d{2})\.(\d{4})/",
$str,
$matches))
2154 foreach (
$matches[0] as $index => $match)
2156 $newValue = CCalendar::Date(
2167 $str = str_replace($match, $newValue,
$str);
2175 private static function convertExDatesToInternalFormat(
string $exDateString): string
2177 if (!empty($exDateString))
2179 $exDates = explode(
';', $exDateString);
2181 foreach ($exDates as $exDate)
2183 $result[] = self::convertDateToRecurrenceFormat($exDate);
2185 $exDateString = implode(
';',
$result);
2188 return $exDateString;
2190 private static function convertRuleUntilToInternalFormat(?
string $untilString): ?string
2192 if (!empty($untilString) && preg_match(
'/UNTIL=(.+)[;$]/U', $untilString,
$matches))
2194 $internalFormatedDate = self::convertDateToRecurrenceFormat(
$matches[1]);
2195 $untilString = str_replace(
$matches[1], $internalFormatedDate, $untilString);
2198 return $untilString;
2201 private static function convertDateToRecurrenceFormat(
string $date =
''): string
2203 if (CCalendar::DFormat(
false) !== ExcludedDatesCollection::EXCLUDED_DATE_FORMAT)
2206 ExcludedDatesCollection::EXCLUDED_DATE_FORMAT,
2207 CCalendar::Timestamp($date)
2214 private static function PreHandleEvent($item,
$params = [])
2216 if (!empty($item[
'LOCATION']))
2218 $item[
'LOCATION'] = trim($item[
'LOCATION']);
2221 if (!empty($item[
'MEETING']))
2223 $item[
'MEETING'] = unserialize($item[
'MEETING'], [
'allowed_classes' =>
false]);
2225 if (!is_array($item[
'MEETING']))
2227 $item[
'MEETING'] = [];
2231 if (!empty($item[
'RELATIONS']))
2233 $item[
'RELATIONS'] = unserialize($item[
'RELATIONS'], [
'allowed_classes' =>
false]);
2235 if (!is_array($item[
'RELATIONS']))
2237 $item[
'RELATIONS'] = [];
2241 if (!empty($item[
'REMIND']))
2243 $item[
'REMIND'] = unserialize($item[
'REMIND'], [
'allowed_classes' =>
false]);
2245 if (!is_array($item[
'REMIND']))
2247 $item[
'REMIND'] = [];
2251 if (!empty($item[
'IS_MEETING']) && !empty($item[
'MEETING']) && !is_array($item[
'MEETING']))
2253 $item[
'MEETING'] = unserialize($item[
'MEETING'], [
'allowed_classes' =>
false]);
2255 if (!is_array($item[
'MEETING']))
2257 $item[
'MEETING'] = [];
2261 if (self::CheckRecurcion($item))
2263 $item[
'EXDATE'] = !empty($item[
'EXDATE']) ? self::convertDateToCulture($item[
'EXDATE']) :
'';
2264 $item[
'RRULE'] = self::ParseRRULE(self::convertDateToCulture($item[
'RRULE']));
2265 $item[
'~RRULE_DESCRIPTION'] = self::GetRRULEDescription($item);
2268 if (($tsTo - $tsFrom) > $item[
'DT_LENGTH'] + CCalendar::DAY_LENGTH)
2270 $toTS = $tsFrom + $item[
'DT_LENGTH'];
2271 if (isset($item[
'DT_SKIP_TIME']) && $item[
'DT_SKIP_TIME'] ===
'Y')
2279 if (!empty($item[
'ATTENDEES_CODES']) && is_string($item[
'ATTENDEES_CODES']))
2281 $item[
'ATTENDEES_CODES'] = explode(
',', $item[
'ATTENDEES_CODES']);
2282 $item[
'attendeesEntityList'] = Util::convertCodesToEntities($item[
'ATTENDEES_CODES'] ??
null);
2286 !empty($item[
'IS_MEETING'])
2287 && (
int)$item[
'ID'] === (
int)$item[
'PARENT_ID']
2288 && !($item[
'CURRENT_ATTENDEE_ID'] ??
null)
2291 $item[
'MEETING_STATUS'] =
'H';
2294 $item[
'DT_SKIP_TIME'] = ($item[
'DT_SKIP_TIME'] ??
null) ===
'Y' ?
'Y' :
'N';
2297 if (empty($item[
'IMPORTANCE']))
2299 $item[
'IMPORTANCE'] =
'normal';
2302 $item[
'PRIVATE_EVENT'] = trim((
string)($item[
'PRIVATE_EVENT'] ??
null));
2304 $item[
'DESCRIPTION'] = trim((
string)($item[
'DESCRIPTION'] ??
null));
2306 if (!empty(
$params[
'parseDescription']))
2308 $item[
'~DESCRIPTION'] = self::ParseText(
2309 $item[
'DESCRIPTION'],
2310 !empty($item[
'PARENT_ID']) ? $item[
'PARENT_ID'] : $item[
'ID'],
2311 $item[
'UF_WEBDAV_CAL_EVENT'] ??
null
2315 if (isset($item[
'UF_CRM_CAL_EVENT']) && is_array($item[
'UF_CRM_CAL_EVENT']) && empty($item[
'UF_CRM_CAL_EVENT']))
2317 $item[
'UF_CRM_CAL_EVENT'] =
'';
2320 unset($item[
'SEARCHABLE_CONTENT']);
2327 return !empty(
$event[
'RRULE']);
2334 if (!is_object(self::$TextParser))
2337 self::$TextParser->allow =
array(
2350 "CUT_ANCHOR" =>
"N",
2356 self::$TextParser->allow[
"USERFIELDS"] = self::getUFForParseText($eventId, $arUFWDValue);
2357 $text = self::$TextParser->convertText(
$text);
2358 $text = preg_replace(
"/<br \/>/i",
"<br>",
$text);
2365 $userFields = self::GetEventUserFields([
'ID' => $eventId]);
2367 'UF_WEBDAV_CAL_EVENT' => $userFields[
'UF_WEBDAV_CAL_EVENT'] ?? [],
2370 if (empty($arUFWDValue))
2372 $arUFWDValue = $userFields[
'UF_WEBDAV_CAL_EVENT'][
'VALUE'] ?? [];
2375 $userFields[
'UF_WEBDAV_CAL_EVENT'][
'VALUE'] = $arUFWDValue;
2376 $userFields[
'UF_WEBDAV_CAL_EVENT'][
'ENTITY_VALUE_ID'] = $eventId;
2385 $length =
$event[
'DT_LENGTH'];
2386 $skipTime =
$event[
'DT_SKIP_TIME'] ===
'Y';
2388 $rrule =
$event[
'RRULE'];
2389 $exDate = self::GetExDate(
$event[
'EXDATE'] ??
null);
2390 $tsFrom = CCalendar::Timestamp(
$event[
'DATE_FROM']);
2391 $tsTo = CCalendar::Timestamp(
$event[
'DATE_TO']);
2393 if (($tsTo - $tsFrom) >
$event[
'DT_LENGTH'] + CCalendar::DAY_LENGTH)
2395 $toTS = $tsFrom + $length;
2398 $toTS -= CCalendar::GetDayLen();
2401 $event[
'DATE_TO'] = CCalendar::Date($toTS);
2404 $h24 = CCalendar::GetDayLen();
2405 $instanceCount = (
$params[
'instanceCount'] &&
$params[
'instanceCount'] > 0) ?
$params[
'instanceCount'] :
false;
2408 $preciseLimits =
$params[
'preciseLimits'];
2416 if (isset(
$params[
'fromLimitTs']))
2418 $limitFromTS = (int)
$params[
'fromLimitTs'];
2420 else if (!empty(
$params[
'fromLimit']))
2422 $limitFromTS = CCalendar::Timestamp(
$params[
'fromLimit']);
2426 $limitFromTS = CCalendar::Timestamp(CCalendar::GetMinDate());
2429 if (isset(
$params[
'toLimitTs']))
2431 $limitToTS = (int)
$params[
'toLimitTs'];
2433 else if (!empty(
$params[
'toLimit']))
2435 $limitToTS = CCalendar::Timestamp(
$params[
'toLimit']);
2439 $limitToTS = CCalendar::Timestamp(CCalendar::GetMaxDate());
2442 $evFromTS = CCalendar::Timestamp(
$event[
'DATE_FROM']);
2444 $limitFromTS +=
$event[
'TZ_OFFSET_FROM'];
2445 $limitToTS +=
$event[
'TZ_OFFSET_TO'] + CCalendar::GetDayLen();
2446 $limitFromTSReal = $limitFromTS;
2449 if ($skipTime && $length > CCalendar::GetDayLen())
2451 $limitFromTSReal += $length - CCalendar::GetDayLen();
2454 if ($limitFromTS <
$event[
'DATE_FROM_TS_UTC'])
2456 $limitFromTS =
$event[
'DATE_FROM_TS_UTC'];
2459 $eventDateToTsUtc =
$event[
'DATE_TO_TS_UTC'];
2460 if (isset($rrule[
'COUNT']))
2462 $eventDateToTsUtc = self::calculateUntilForCountRRule(
$event);
2465 if ($limitToTS > $eventDateToTsUtc)
2467 $limitToTS = $eventDateToTsUtc;
2470 $fromTS = self::getClosestRepetitionTs($limitFromTS, $evFromTS, $rrule);
2473 if (isset($rrule[
'COUNT']) && in_array($rrule[
'FREQ'], [
'DAILY',
'MONTHLY',
'YEARLY'],
true))
2475 $lastRepetitionTs = $eventDateToTsUtc;
2478 $lastRepetitionTs -= $length;
2481 $untilDiff = self::calculateUntilForCountRRule(
$event, $fromTS - $evFromTS) - $lastRepetitionTs;
2482 $freqDuration = self::getFreqDuration($rrule[
'FREQ']);
2483 $countOffset = (int)($untilDiff / ($freqDuration * $rrule[
'INTERVAL']));
2488 $event[
'~DATE_FROM'] = CCalendar::Date(CCalendar::Timestamp(
$event[
'DATE_FROM']),
false);
2489 $event[
'~DATE_TO'] = CCalendar::Date(CCalendar::Timestamp(
$event[
'DATE_TO']),
false);
2497 $hour = (int)date(
'H', $fromTS);
2498 $min = (int)date(
'i', $fromTS);
2499 $sec = (int)date(
's', $fromTS);
2501 $orig_d = (int)date(
'd', $fromTS);
2502 $orig_m = (int)date(
'm', $fromTS);
2503 $orig_y = (int)date(
'Y', $fromTS);
2510 $d = (int)date(
'd', $fromTS);
2511 $m = (int)date(
'm', $fromTS);
2512 $y = (int)date(
'Y', $fromTS);
2513 $toTS = mktime($hour, $min, $sec + $length, $m, $d, $y);
2516 (isset($rrule[
'COUNT']) && $rrule[
'COUNT'] > 0 && ($realCount + $countOffset) >= $rrule[
'COUNT'])
2517 || ($loadLimit && $dispCount >= $loadLimit)
2518 || ($fromTS > $limitToTS)
2519 || ($instanceCount && $dispCount >= $instanceCount)
2520 || (!$fromTS || $fromTS < $evFromTS - CCalendar::GetDayLen())
2527 $event[
'DATE_FROM'] = CCalendar::Date($fromTS, !$skipTime,
false);
2528 $event[
'DATE_FROM_FORMATTED'] = self::getDateInJsFormat(
2529 CCalendar::createDateTimeObjectFromString(
$event[
'DATE_FROM']),
2533 $event[
'RRULE'] = $rrule;
2534 $event[
'RINDEX'] = $realCount > 0 || $countOffset > 0
2535 ? $realCount + $countOffset
2536 : self::getFirstInstanceIndex($fromTS,
$event[
'~DATE_FROM'])
2541 if (!empty($exDate))
2543 $fromDate = CCalendar::Date($fromTS,
false);
2544 $exclude = in_array($fromDate, $exDate,
true);
2547 if ($rrule[
'FREQ'] ===
'WEEKLY')
2549 $weekDay = CCalendar::WeekDayByInd(date(
"w", $fromTS));
2551 if (!empty($rrule[
'BYDAY'][$weekDay]))
2555 if (($preciseLimits && $toTS >= $limitFromTSReal) || (!$preciseLimits && $toTS > $limitFromTS - $h24))
2557 if ((
$event[
'DT_SKIP_TIME'] ??
null) ===
'Y')
2559 $toTS -= CCalendar::GetDayLen();
2562 $event[
'DATE_TO'] = CCalendar::Date($toTS - (
$event[
'TZ_OFFSET_FROM'] -
$event[
'TZ_OFFSET_TO']), !$skipTime,
false);
2563 $event[
'DATE_TO_FORMATTED'] = self::getDateInJsFormat(
2564 CCalendar::createDateTimeObjectFromString(
$event[
'DATE_TO']),
2576 if (isset($weekDay) && $weekDay ===
'SU')
2578 $delta = ($rrule[
'INTERVAL'] - 1) * 7 + 1;
2585 $fromTS = mktime($hour, $min, $sec, $m, $d +
$delta, $y);
2591 if ((
$event[
'DT_SKIP_TIME'] ??
null) ===
'Y')
2593 $toTS -= CCalendar::GetDayLen();
2597 ($preciseLimits && $toTS >= $limitFromTSReal)
2598 || (!$preciseLimits && $toTS > $limitFromTS - $h24)
2601 $event[
'DATE_TO'] = CCalendar::Date($toTS - (
$event[
'TZ_OFFSET_FROM'] -
$event[
'TZ_OFFSET_TO']), !$skipTime,
false);
2602 $event[
'DATE_TO_FORMATTED'] = self::getDateInJsFormat(
2603 CCalendar::createDateTimeObjectFromString(
$event[
'DATE_TO']),
2615 switch ($rrule[
'FREQ'])
2618 $fromTS = mktime($hour, $min, $sec, $m, $d + $rrule[
'INTERVAL'], $y);
2621 $durOffset = $realCount * $rrule[
'INTERVAL'];
2624 $month = $orig_m + $durOffset;
2629 $delta_y = floor($month / 12);
2630 $delta_m = $month - $delta_y * 12;
2633 $year = $orig_y + $delta_y;
2637 if ($orig_d > 28 && $orig_d > date(
"t", mktime($hour, $min, $sec, $month, 1,
$year)))
2643 $fromTS = mktime($hour, $min, $sec, $month, $day,
$year);
2646 $fromTS = mktime($hour, $min, $sec, $orig_m, $orig_d, $y + $rrule[
'INTERVAL']);
2653 private static function getFirstInstanceIndex($fromTs, $eventFromDate): int
2655 $eventTs = (int)CCalendar::Timestamp($eventFromDate);
2657 return (($fromTs - $eventTs) / 86400) > 1 ? 1 : 0;
2662 $interval = (int)$rrule[
'INTERVAL'];
2664 $hour = (int)date(
'H', $evFromTS);
2665 $min = (int)date(
'i', $evFromTS);
2666 $sec = (int)date(
's', $evFromTS);
2668 $orig_d = (int)date(
'd', $evFromTS);
2669 $orig_m = (int)date(
'm', $evFromTS);
2670 $orig_y = (int)date(
'Y', $evFromTS);
2672 $closestDateTs = $limitFromTS;
2674 $freqDuration = self::getFreqDuration($rrule[
'FREQ']);
2676 if ($rrule[
'FREQ'] ===
'DAILY')
2678 $dayDifference = round(($limitFromTS - $evFromTS) / $freqDuration);
2679 $dayDifference -= $dayDifference % $interval;
2680 $closestDateTs = mktime($hour, $min, $sec, $orig_m, $orig_d + $dayDifference, $orig_y);
2683 if ($rrule[
'FREQ'] ===
'MONTHLY')
2685 $monthDifference = round(($limitFromTS - $evFromTS) / $freqDuration);
2686 $monthDifference -= $monthDifference % $interval;
2687 $closestDateTs = mktime($hour, $min, $sec, $orig_m + $monthDifference, $orig_d, $orig_y);
2690 if ($rrule[
'FREQ'] ===
'YEARLY')
2692 $yearDifference = round(($limitFromTS - $evFromTS) / $freqDuration);
2693 $yearDifference -= $yearDifference % $interval;
2694 $closestDateTs = mktime($hour, $min, $sec, $orig_m, $orig_d, $orig_y + $yearDifference);
2697 if ($rrule[
'FREQ'] ===
'WEEKLY')
2699 $dayDifference = round(($limitFromTS - $evFromTS) / $freqDuration);
2700 $count = round($dayDifference / (
count($rrule[
'BYDAY']) * 7 * $interval) + 1);
2701 $daysCount = round((
$count - 1) /
count($rrule[
'BYDAY']) * 7 * $interval);
2702 $closestDateTs = mktime($hour, $min, $sec, $orig_m, $orig_d + $daysCount, $orig_y);
2705 $closestRepetitionDate = (int)date(
'd', $closestDateTs);
2706 $closestRepetitionMonth = (int)date(
'm', $closestDateTs);
2707 $closestRepetitionYear = (int)date(
'Y', $closestDateTs);
2709 return mktime($hour, $min, $sec, $closestRepetitionMonth, $closestRepetitionDate, $closestRepetitionYear);
2714 if ($freq ===
'DAILY' || $freq ===
'WEEKLY')
2716 return 60 * 60 * 24;
2719 if ($freq ===
'MONTHLY')
2721 return 60 * 60 * 24 * 30;
2724 if ($freq ===
'YEARLY')
2726 return 60 * 60 * 24 * 365;
2740 if (is_array($rule))
2742 return isset($rule[
'FREQ'])
2747 $arRule = explode(
";", $rule);
2748 if (!is_array($arRule))
2753 foreach($arRule as $par)
2755 $arPar = explode(
"=", $par);
2756 if (!empty($arPar[0]))
2761 if (in_array($arPar[1], [
'DAILY',
'WEEKLY',
'MONTHLY',
'YEARLY']))
2763 $res[
'FREQ'] = $arPar[1];
2769 if ((
int)$arPar[1] > 0)
2771 $res[$arPar[0]] = (int)$arPar[1];
2777 CCalendar::DFormat(
false) !== ExcludedDatesCollection::EXCLUDED_DATE_FORMAT
2778 && $arPar[1][2] ===
'.'
2779 && $arPar[1][5] ===
'.'
2782 $arPar[1] = self::convertDateToCulture($arPar[1]);
2784 $res[
'UNTIL'] = CCalendar::Timestamp($arPar[1])
2786 : CCalendar::Date((
int)$arPar[1],
false,
false)
2791 $res[$arPar[0]] = [];
2792 foreach(explode(
',', $arPar[1]) as $day)
2795 if (preg_match(
'/((\-|\+)?\d+)?(MO|TU|WE|TH|FR|SA|SU)/', $day,
$matches))
2803 if (empty(
$res[$arPar[0]]))
2805 unset(
$res[$arPar[0]]);
2810 $res[$arPar[0]] = [];
2811 foreach(explode(
',', $arPar[1]) as $day)
2813 if (abs($day) > 0 && abs($day) <= 31)
2815 $res[$arPar[0]][(int)$day] = (
int)$day;
2818 if (empty(
$res[$arPar[0]]))
2820 unset(
$res[$arPar[0]]);
2826 $res[$arPar[0]] = [];
2827 foreach(explode(
',', $arPar[1]) as $day)
2829 if (abs($day) > 0 && abs($day) <= 366)
2831 $res[$arPar[0]][(int)$day] = (
int)$day;
2834 if (empty(
$res[$arPar[0]]))
2836 unset(
$res[$arPar[0]]);
2841 $res[$arPar[0]] = [];
2842 foreach(explode(
',', $arPar[1]) as $day)
2844 if (abs($day) > 0 && abs($day) <= 53)
2846 $res[$arPar[0]][(int)$day] = (
int)$day;
2849 if (empty(
$res[$arPar[0]]))
2851 unset(
$res[$arPar[0]]);
2856 $res[$arPar[0]] = [];
2857 foreach(explode(
',', $arPar[1]) as $m)
2859 if ($m > 0 && $m <= 12)
2861 $res[$arPar[0]][(int)$m] = (
int)$m;
2864 if (empty(
$res[$arPar[0]]))
2866 unset(
$res[$arPar[0]]);
2875 $res[
'FREQ'] ===
'WEEKLY'
2877 empty(
$res[
'BYDAY'])
2878 || !is_array(
$res[
'BYDAY'])
2882 $res[
'BYDAY'] = [
'MO' =>
'MO'];
2885 if (
$res[
'FREQ'] !==
'WEEKLY' && isset(
$res[
'BYDAY']))
2887 unset(
$res[
'BYDAY']);
2890 $res[
'INTERVAL'] = (int)(
$res[
'INTERVAL'] ??
null);
2891 if (
$res[
'INTERVAL'] <= 1)
2893 $res[
'INTERVAL'] = 1;
2896 $res[
'~UNTIL'] =
$res[
'UNTIL'] ??
null;
2897 if ((
$res[
'UNTIL'] ??
null) === CCalendar::GetMaxDate())
2899 $res[
'~UNTIL'] =
'';
2902 $res[
'UNTIL_TS'] = !empty(
$res[
'UNTIL']) && is_string(
$res[
'UNTIL'])
2903 ? CCalendar::TimestampUTC(
$res[
'UNTIL'])
2917 if (is_string($exDate))
2919 $result = $exDate ===
'' ? [] : explode(
';', $exDate);
2932 private static function calculateUserOffset(
$userId,
$event = [])
2934 if ((
$event[
'DT_SKIP_TIME'] ??
null) ===
'N')
2936 $currentUserTimezone = \CCalendar::GetUserTimezoneName(
$userId);
2938 $fromTs = \CCalendar::Timestamp(
$event[
'DATE_FROM']);
2939 $toTs = $fromTs + (
$event[
'DT_LENGTH'] ??
null);
2942 - \CCalendar::GetTimezoneOffset($currentUserTimezone, $fromTs);
2945 - \CCalendar::GetTimezoneOffset($currentUserTimezone, $toTs);
2949 $event[
'~USER_OFFSET_FROM'] = 0;
2950 $event[
'~USER_OFFSET_TO'] = 0;
2964 $arFields[
'TIMESTAMP_X'] = CCalendar::Date(time(),
true,
false);
2969 $userId = CCalendar::GetCurUserId();
2972 if (!isset(
$arFields[
'DT_SKIP_TIME']) && isset($currentEvent[
'DT_SKIP_TIME']))
2974 $arFields[
'DT_SKIP_TIME'] = $currentEvent[
'DT_SKIP_TIME'];
2976 if (!isset(
$arFields[
'DATE_FROM']) && isset($currentEvent[
'DATE_FROM']))
2978 $arFields[
'DATE_FROM'] = $currentEvent[
'DATE_FROM'];
2980 if (!isset(
$arFields[
'DATE_TO']) && isset($currentEvent[
'DATE_TO']))
2982 $arFields[
'DATE_TO'] = $currentEvent[
'DATE_TO'];
2986 if (!isset(
$arFields[
'DATE_CREATE']) && $isNewEvent)
3005 $fromTs = CCalendar::Timestamp(
$arFields[
'DATE_FROM'],
false,
$arFields[
'DT_SKIP_TIME'] !==
'Y');
3006 $toTs = CCalendar::Timestamp(
$arFields[
'DATE_TO'],
false,
$arFields[
'DT_SKIP_TIME'] !==
'Y');
3008 $arFields[
'DATE_FROM'] = CCalendar::Date($fromTs);
3009 $arFields[
'DATE_TO'] = CCalendar::Date($toTs);
3014 $fromTs = CCalendar::Timestamp(
$arFields[
'DATE_FROM'],
false,
false);
3028 if ((
$arFields[
'DT_SKIP_TIME'] ??
null) !==
'Y')
3031 if (!isset(
$arFields[
'TZ_FROM']) && isset($currentEvent[
'TZ_FROM']))
3033 $arFields[
'TZ_FROM'] = $currentEvent[
'TZ_FROM'];
3035 if (!isset(
$arFields[
'TZ_TO']) && isset($currentEvent[
'TZ_TO']))
3037 $arFields[
'TZ_TO'] = $currentEvent[
'TZ_TO'];
3042 $userTimezoneName = CCalendar::GetUserTimezoneName(
$userId,
true);
3043 $arFields[
'TZ_FROM'] = $userTimezoneName;
3047 if (!isset(
$arFields[
'TZ_OFFSET_FROM']))
3049 $arFields[
'TZ_OFFSET_FROM'] = CCalendar::GetTimezoneOffset(
$arFields[
'TZ_FROM'], $fromTs);
3057 if (!isset(
$arFields[
'TZ_OFFSET_FROM']))
3066 if (!isset(
$arFields[
'DATE_FROM_TS_UTC']))
3070 if (!isset(
$arFields[
'DATE_TO_TS_UTC']))
3083 $h24 = 60 * 60 * 24;
3084 if ((
$arFields[
'DT_SKIP_TIME'] ??
null) ===
'Y')
3090 if ((
int)$fromTs === (
int)$toTs && date(
'H:i', $fromTs) ===
'00:00' &&
$arFields[
'DT_SKIP_TIME'] ===
'Y')
3097 if ((
$arFields[
'DT_SKIP_TIME'] ??
null) ===
"Y")
3110 if (!in_array(
$arFields[
'ACCESSIBILITY'], [
'busy',
'quest',
'free',
'absent'],
true))
3117 if (!in_array(
$arFields[
'IMPORTANCE'], [
'high',
'normal',
'low']))
3135 self::checkRecurringRuleField(
$arFields, $toTs, ($currentEvent[
'EXDATE'] ??
null));
3141 "NEW" => is_string(
$arFields[
'LOCATION'] ??
null)
3166 foreach ($fieldList as $fieldKey)
3168 if ($fieldKey ===
'LOCATION')
3171 is_array($newFields[$fieldKey] ??
null)
3172 && ($newFields[$fieldKey][
'NEW'] ??
null) !== ($currentFields[$fieldKey] ??
null)
3173 && (CCalendar::GetTextLocation($newFields[$fieldKey][
'NEW'] ??
'')) !== (CCalendar::GetTextLocation($currentFields[$fieldKey] ??
''))
3177 'fieldKey' => $fieldKey,
3178 'oldValue' => $currentFields[$fieldKey] ??
null,
3179 'newValue' => $newFields[$fieldKey][
'NEW'] ??
null,
3183 !is_array($newFields[$fieldKey] ??
null)
3184 && ($newFields[$fieldKey] ??
null) !== ($currentFields[$fieldKey] ??
null)
3185 && (CCalendar::GetTextLocation($newFields[$fieldKey] ??
'')) !== (CCalendar::GetTextLocation($currentFields[$fieldKey] ??
''))
3189 'fieldKey' => $fieldKey,
3190 'oldValue' => $currentFields[$fieldKey],
3191 'newValue' => $newFields[$fieldKey],
3195 else if ($fieldKey ===
'DATE_FROM')
3198 $newFields[$fieldKey] !== $currentFields[$fieldKey]
3199 || ($newFields[
'TZ_FROM'] ??
null) !== ($currentFields[
'TZ_FROM'] ??
null)
3203 'fieldKey' => $fieldKey,
3204 'oldValue' => $currentFields[$fieldKey],
3205 'newValue' => $newFields[$fieldKey],
3209 else if ($fieldKey ===
'DATE_TO')
3213 $newFields[
'DATE_FROM'] === $currentFields[
'DATE_FROM']
3214 && ($newFields[
'TZ_FROM'] ??
null) === ($currentFields[
'TZ_FROM'] ??
null)
3218 $newFields[$fieldKey] !== $currentFields[$fieldKey]
3219 || ($newFields[
'TZ_TO'] ??
null) !== ($currentFields[
'TZ_TO'] ??
null)
3224 'fieldKey' => $fieldKey,
3225 'oldValue' => $currentFields[$fieldKey],
3226 'newValue' => $newFields[$fieldKey],
3230 else if ($fieldKey ===
'IMPORTANCE')
3233 $newFields[$fieldKey] !== $currentFields[$fieldKey]
3234 && $newFields[$fieldKey] ===
'high'
3238 'fieldKey' => $fieldKey,
3239 'oldValue' => $currentFields[$fieldKey],
3240 'newValue' => $newFields[$fieldKey],
3244 else if ($fieldKey ===
'DESCRIPTION')
3246 if (mb_strtolower(trim($newFields[$fieldKey])) !== mb_strtolower(trim($currentFields[$fieldKey])))
3249 'fieldKey' => $fieldKey,
3250 'oldValue' => $currentFields[$fieldKey],
3251 'newValue' => $newFields[$fieldKey],
3255 else if ($fieldKey ===
'RRULE')
3257 $newRule = self::ParseRRULE($newFields[$fieldKey] ??
null);
3258 $oldRule = self::ParseRRULE($currentFields[$fieldKey] ??
null);
3261 (($newRule[
'FREQ'] ??
null) !== ($oldRule[
'FREQ'] ??
null))
3262 || (($newRule[
'INTERVAL'] ??
null) !== ($oldRule[
'INTERVAL'] ??
null))
3263 || (($newRule[
'BYDAY'] ??
null) !== ($oldRule[
'BYDAY'] ??
null))
3267 'fieldKey' => $fieldKey,
3268 'oldValue' => $oldRule,
3269 'newValue' => $newRule,
3274 else if (($newFields[$fieldKey] ??
null) !== ($currentFields[$fieldKey] ??
null))
3277 'fieldKey' => $fieldKey,
3278 'oldValue' => $currentFields[$fieldKey],
3279 'newValue' => $newFields[$fieldKey],
3285 is_array($newFields[
'ATTENDEES_CODES'] ??
null)
3286 && is_array($currentFields[
'ATTENDEES_CODES'] ??
null)
3287 && (
count(array_diff($newFields[
'ATTENDEES_CODES'], $currentFields[
'ATTENDEES_CODES']))
3288 ||
count(array_diff($currentFields[
'ATTENDEES_CODES'], $newFields[
'ATTENDEES_CODES'])))
3292 'fieldKey' =>
'ATTENDEES',
3293 'oldValue' => $currentFields[
'ATTENDEES_CODES'],
3294 'newValue' => $newFields[
'ATTENDEES_CODES'],
3301 private static function PackRRule($RRule = [])
3304 if (is_array($RRule))
3308 $strRes .=
$key .
'=' .
$val .
';';
3312 return trim($strRes,
', ');
3325 private static function CreateChildEvents($parentId,
$arFields,
$params, $changeFields)
3328 $parentId = (int)$parentId;
3331 $involvedAttendees = [];
3333 $isPastEvent = (
$arFields[
'DATE_TO_TS_UTC'] ??
null) < (time() - (
int)date(
'Z'));
3338 $eventWithEmailGuestEnabled = Bitrix24Manager::isFeatureEnabled(FeatureDictionary::CALENDAR_EVENTS_WITH_EMAIL_GUESTS);
3340 unset(
$params[
'dontSyncParent']);
3342 if ($chatId > 0 && Loader::includeModule(
'im'))
3344 $chat = new \CIMChat(
$userId);
3349 $attendees[] = (int)
$arFields[
'CREATED_BY'];
3352 foreach($attendees as $userKey)
3354 $involvedAttendees[] = (int)$userKey;
3357 $currentAttendeesIndex = [];
3358 $deletedAttendees = [];
3359 $affectedEventIds = [$parentId];
3362 $curAttendees = self::GetAttendees($parentId);
3363 $curAttendees = is_array(($curAttendees[$parentId] ??
null)) ? $curAttendees[$parentId] : [];
3364 foreach($curAttendees as
$user)
3366 $currentAttendeesIndex[
$user[
'USER_ID']] =
$user;
3375 $deletedAttendees[
$user[
'USER_ID']] = (int)
$user[
'USER_ID'];
3376 $involvedAttendees[] = (int)
$user[
'USER_ID'];
3380 $involvedAttendees = array_unique($involvedAttendees);
3381 $userIndex = self::generateUserIndex($involvedAttendees,
$arFields[
'MEETING_HOST']);
3383 $attendeeStatuses = self::getAttendeeStatuses(
3387 $currentAttendeesIndex,
3390 $params[
'attendeeStatuses'] = $attendeeStatuses;
3392 foreach ($attendees as $userKey)
3395 $attendeeId = (int)$userKey;
3396 $isNewAttendee = !empty($clonedParams[
'currentEvent'][
'ATTENDEE_LIST'])
3397 && is_array($clonedParams[
'currentEvent'][
'ATTENDEE_LIST'])
3398 && self::isNewAttendee($clonedParams[
'currentEvent'][
'ATTENDEE_LIST'], $attendeeId)
3405 && ((
$arFields[
'CAL_TYPE'] ??
null) !==
'user' || (
int)(
$arFields[
'OWNER_ID'] ??
null) !== $attendeeId)
3408 $childParams = $clonedParams;
3409 $childParams[
'arFields'][
'CAL_TYPE'] =
'user';
3410 $childParams[
'arFields'][
'PARENT_ID'] = $parentId;
3411 $childParams[
'arFields'][
'OWNER_ID'] = $attendeeId;
3412 $childParams[
'arFields'][
'CREATED_BY'] = $attendeeId;
3413 $childParams[
'arFields'][
'CREATED'] =
$arFields[
'DATE_CREATE'] ??
null;
3414 $childParams[
'arFields'][
'MODIFIED'] =
$arFields[
'TIMESTAMP_X'] ??
null;
3415 $childParams[
'arFields'][
'ACCESSIBILITY'] =
$arFields[
'ACCESSIBILITY'] ??
null;
3416 $childParams[
'arFields'][
'MEETING'] =
$arFields[
'~MEETING'] ??
null;
3418 $childParams[
'arFields'][
'MEETING_STATUS'] = $attendeeStatuses[$attendeeId][
'status'];
3419 $childParams[
'arFields'][
'EVENT_TYPE'] =
$arFields[
'EVENT_TYPE'] ??
null;
3420 $childParams[
'sendInvitations'] = $clonedParams[
'sendInvitations'] ??
null;
3423 $childParams[
'arFields'][
'SECTIONS'],
3424 $childParams[
'arFields'][
'SECTION_ID'],
3425 $childParams[
'currentEvent'],
3426 $childParams[
'updateReminders'],
3427 $childParams[
'arFields'][
'ID'],
3428 $childParams[
'arFields'][
'DAV_XML_ID'],
3429 $childParams[
'arFields'][
'G_EVENT_ID'],
3430 $childParams[
'arFields'][
'SYNC_STATUS']
3436 isset($userIndex[$attendeeId])
3437 && $userIndex[$attendeeId][
'EXTERNAL_AUTH_ID'] ===
'email'
3439 && !$eventWithEmailGuestEnabled
3447 if (!empty($currentAttendeesIndex[$attendeeId]))
3449 $childParams[
'arFields'][
'ID'] = $currentAttendeesIndex[$attendeeId][
'EVENT_ID'];
3451 if (empty(
$arFields[
'~MEETING'][
'REINVITE']))
3453 $childParams[
'arFields'][
'MEETING_STATUS'] = $currentAttendeesIndex[$attendeeId][
'STATUS'];
3455 $childParams[
'sendInvitations'] = $childParams[
'sendInvitations'] && $currentAttendeesIndex[$attendeeId][
'STATUS'] !==
'Q';
3458 if ($attendeeStatuses[$attendeeId][
'sendInvitations'])
3460 $childParams[
'sendInvitations'] =
true;
3464 ($isExchangeEnabled || $isCalDavEnabled)
3465 && ($childParams[
'overSaving'] ??
false) !==
true
3468 self::prepareArFieldBeforeSyncEvent($childParams);
3469 $childParams[
'currentEvent'] = self::GetById($childParams[
'arFields'][
'ID'],
false);
3472 'bCalDav' => $isCalDavEnabled,
3473 'bExchange' => $isExchangeEnabled,
3474 'sectionId' => (int)$childParams[
'currentEvent'][
'SECTION_ID'],
3475 'modeSync' => $clonedParams[
'modeSync'],
3476 'editInstance' => $clonedParams[
'editInstance'],
3477 'originalDavXmlId' => $childParams[
'currentEvent'][
'G_EVENT_ID'],
3478 'instanceTz' => $childParams[
'currentEvent'][
'TZ_FROM'],
3479 'editParentEvents' => $clonedParams[
'editParentEvents'],
3480 'editNextEvents' => $clonedParams[
'editNextEvents'],
3481 'syncCaldav' => $clonedParams[
'syncCaldav'],
3482 'parentDateFrom' => $childParams[
'currentEvent'][
'DATE_FROM'],
3483 'parentDateTo' => $childParams[
'currentEvent'][
'DATE_TO'],
3493 $childParams[
'arFields'][
'SECTIONS'] = [$childSectId];
3496 if (!$clonedParams[
'editInstance'])
3498 $childParams[
'arFields'][
'DAV_XML_ID'] = self::getUidForChildEvent($childParams[
'arFields']);
3502 if (!empty($childParams[
'arFields'][
'RECURRENCE_ID']))
3504 $parentEvent = Internals\EventTable::query()
3505 ->where(
'PARENT_ID' , (
int)$childParams[
'arFields'][
'RECURRENCE_ID'])
3506 ->where(
'OWNER_ID' , (
int)($childParams[
'arFields'][
'OWNER_ID'] ?? 0))
3520 $childParams[
'arFields'][
'DAV_XML_ID'] = $parentEvent[
'DAV_XML_ID'] ??
null;
3525 $childParams[
'arFields'][
'ORIGINAL_DATE_FROM'],
3526 $childParams[
'arFields'][
'RECURRENCE_ID'],
3527 $clonedParams[
'recursionEditMode']
3530 $childParams[
'arFields'][
'DAV_XML_ID'] = UidGenerator::createInstance()
3531 ->setPortalName(Util::getServerName())
3532 ->setDate(
new Date(Util::getDateObject(
3533 $childParams[
'arFields'][
'DATE_FROM'] ??
null,
3535 ($childParams[
'arFields'][
'TZ_FROM'] ??
null) ?:
null
3537 ->setUserId((
int)($childParams[
'arFields'][
'OWNER_ID'] ??
null))
3544 ($isExchangeEnabled || $isCalDavEnabled)
3545 && ($childParams[
'overSaving'] ??
false) !==
true
3549 'bCalDav' => $isCalDavEnabled,
3550 'bExchange' => $isExchangeEnabled,
3551 'sectionId' => $childSectId,
3552 'modeSync' => $clonedParams[
'modeSync'] ??
null,
3553 'editInstance' => $clonedParams[
'editInstance'] ??
null,
3554 'instanceTz' => $parentEvent[
'TZ_FROM'] ??
null,
3555 'editParentEvents' => $clonedParams[
'editParentEvents'] ??
null,
3556 'editNextEvents' => $clonedParams[
'editNextEvents'] ??
null,
3557 'syncCaldav' => $clonedParams[
'syncCaldav'] ??
null,
3558 'parentDateFrom' => $parentEvent[
'DATE_FROM'] ??
null,
3559 'parentDateTo' => $parentEvent[
'DATE_TO'] ??
null,
3566 if (!empty($childParams[
'arFields'][
'ID']))
3568 $curEvent = self::GetList([
3570 "ID" => (
int)$childParams[
'arFields'][
'ID'],
3573 'checkPermissions' =>
false,
3574 'parseRecursion' =>
false,
3575 'fetchAttendees' =>
true,
3576 'fetchMeetings' =>
false,
3582 $curEvent = $curEvent[0];
3585 if (!empty($curEvent[
'COLOR']))
3588 empty($childParams[
'arFields'][
'COLOR'])
3589 || ($curEvent[
'COLOR'] !== $childParams[
'arFields'][
'COLOR'])
3592 $childParams[
'arFields'][
'COLOR'] = $curEvent[
'COLOR'];
3596 $id = self::Edit($childParams);
3597 $affectedEventIds[] = $id;
3600 $userIndex[$attendeeId]
3601 && $userIndex[$attendeeId][
'EXTERNAL_AUTH_ID'] ===
'email'
3602 && ((!($clonedParams[
'fromWebservice'] ??
false)) || !empty($changeFields))
3604 && ($childParams[
'overSaving'] ??
false) !==
true
3608 $sender = self::getSenderForIcal($userIndex, $childParams[
'arFields'][
'MEETING_HOST']);
3610 if (empty($sender) || !$sender[
'ID'])
3615 if (!empty(
$email = self::getSenderEmailForIcal(
$arFields[
'MEETING'])) && !self::$isAddIcalFailEmailError)
3617 $sender[
'EMAIL'] =
$email;
3622 self::$isAddIcalFailEmailError =
true;
3627 $invitationInfo = [];
3629 if (!empty($currentAttendeesIndex[$attendeeId]))
3631 $mailChangeFields = array_filter($changeFields,
3632 static fn (
array $field) => !in_array(
3634 [
'ATTENDEES',
'IMPORTANCE'],
3638 if (!empty($mailChangeFields))
3640 $invitationInfo = (
new InvitationInfo(
3644 InvitationInfo::TYPE_EDIT,
3651 $invitationInfo = (
new InvitationInfo(
3655 InvitationInfo::TYPE_REQUEST
3659 SendingEmailNotification::sendMessageToQueue($invitationInfo);
3666 && $userIndex[$attendeeId]
3667 && $userIndex[$attendeeId][
'EXTERNAL_AUTH_ID'] !==
'email'
3668 && $userIndex[$attendeeId][
'EXTERNAL_AUTH_ID'] !== Sharing\SharingUser::EXTERNAL_AUTH_ID
3669 && $childParams[
'arFields'][
'MEETING_STATUS'] !==
'N'
3672 $chat->AddUser($chatId, $attendeeId, $hideHistory =
true, $skipMessage =
false);
3677 CCalendar::syncChange($id, $childParams[
'arFields'], $clonedParams, $curEvent);
3680 unset($deletedAttendees[$attendeeId]);
3685 $eventIdToDelete = [];
3686 if (!$isNewEvent && !empty($deletedAttendees))
3688 $isSharing = in_array(
3689 $arFields[
'EVENT_TYPE'] ??
'', Sharing\SharingEventManager::getSharingEventTypes(),
true
3697 $notifyUserId =
$arFields[
'MEETING_HOST'] ??
null;
3699 foreach($deletedAttendees as $attendeeId)
3701 if ($chatId > 0 && $chat)
3703 $chat->DeleteUser($chatId, $attendeeId,
false);
3706 $att = $currentAttendeesIndex[$attendeeId];
3708 (
$params[
'sendInvitations'] ??
null) !==
false
3709 && ($att[
'STATUS'] ??
null) ===
'Y'
3714 $fromTo = self::GetEventFromToForUser(
$arFields, $att[
"USER_ID"]);
3718 "from" => $fromTo[
'DATE_FROM'] ??
null,
3719 "to" => $fromTo[
'DATE_TO'] ??
null,
3720 "location" => CCalendar::GetTextLocation(
$arFields[
"LOCATION"] ??
null),
3721 "guestId" => $att[
"USER_ID"] ??
null,
3722 "eventId" => $parentId,
3723 "userId" => $notifyUserId,
3728 $pullUserId = (int)$attendeeId;
3735 PushCommand::DeleteEvent,
3739 'requestUid' =>
$params[
'userId'],
3744 $affectedEventIds[] = $att[
'EVENT_ID'] ?? 0;
3746 if ((
int)($att[
'EVENT_ID'] ??
null))
3748 $eventIdToDelete[] = (int)$att[
'EVENT_ID'];
3751 $currentEvent = self::GetList([
3753 "PARENT_ID" => $parentId,
3754 "OWNER_ID" => $attendeeId,
3758 'parseRecursion' =>
false,
3759 'fetchAttendees' =>
true,
3760 'fetchMeetings' =>
true,
3761 'checkPermissions' =>
false,
3762 'setDefaultLimit' =>
false,
3764 $currentEvent = $currentEvent[0];
3767 if (($isExchangeEnabled || $isCalDavEnabled) && $currentEvent)
3770 'bCalDav' => $isCalDavEnabled,
3771 'bExchangeEnabled' => $isExchangeEnabled,
3772 'sectionId' => $currentEvent[
'SECT_ID'] ??
null,
3778 self::onEventDelete($currentEvent,
$params);
3781 if (isset($att[
'EXTERNAL_AUTH_ID']) && $att[
'EXTERNAL_AUTH_ID'] ===
'email' && !$isPastEvent)
3783 if (empty($receiver[
'EMAIL']))
3788 $sender = self::getSenderForIcal($currentAttendeesIndex,
$arFields[
'MEETING_HOST']);
3791 $sender[
'EMAIL'] =
$email;
3795 $meetingHostSettings = UserSettings::get(
$arFields[
'MEETING_HOST']);
3796 $sender[
'EMAIL'] = $meetingHostSettings[
'sendFromEmail'];
3798 if (empty($sender[
'ID']) && isset($sender[
'USER_ID']))
3800 $sender[
'ID'] = (int)$sender[
'USER_ID'];
3803 $invitationInfo = (
new InvitationInfo(
3806 (
int)$receiver[
'ID'],
3807 InvitationInfo::TYPE_CANCEL
3810 SendingEmailNotification::sendMessageToQueue($invitationInfo);
3816 if (!empty($eventIdToDelete))
3818 Internals\EventTable::updateByFilter(
3820 'ID' => $eventIdToDelete,
3821 '=PARENT_ID' => (
int)$parentId,
3826 self::safeDeleteEventAttendees($parentId, $deletedAttendees);
3829 if (!empty($involvedAttendees))
3831 $involvedAttendees = array_unique($involvedAttendees);
3840 $event = Internals\EventTable::query()
3841 ->setSelect([
'PARENT_ID',
'EXDATE'])
3842 ->where(
'PARENT_ID', $recurrenceId)
3846 $exDates = self::GetExDate(
$event[
'EXDATE']);
3848 ExcludedDatesCollection::EXCLUDED_DATE_FORMAT,
3849 \CCalendar::Timestamp($exDate)
3851 $exDates = array_unique($exDates);
3852 $strExDates = implode(
';', $exDates);
3854 Internals\EventTable::updateByFilter(
3855 [
'=PARENT_ID' => (
int)$recurrenceId],
3856 [
'EXDATE' => $strExDates],
3859 if (is_array($attendeeIds))
3861 foreach ($attendeeIds as $id)
3872 $skipTime =
$params[
'DT_SKIP_TIME'] !==
'N';
3874 $fromTs = CCalendar::Timestamp(
$params[
'DATE_FROM'],
false, !$skipTime);
3875 $toTs = CCalendar::Timestamp(
$params[
'DATE_TO'],
false, !$skipTime);
3879 $fromTs -= (CCalendar::GetTimezoneOffset(
$params[
'TZ_FROM']) - CCalendar::GetCurrentOffsetUTC(
$userId));
3880 $toTs -= (CCalendar::GetTimezoneOffset(
$params[
'TZ_TO']) - CCalendar::GetCurrentOffsetUTC(
$userId));
3883 $dateFrom = CCalendar::Date($fromTs, !$skipTime);
3884 $dateTo = CCalendar::Date($toTs, !$skipTime);
3887 "DATE_FROM" => $dateFrom,
3888 "DATE_TO" => $dateTo,
3889 "TS_FROM" => $fromTs,
3899 if ((
$arFields[
'LOCATION'] ??
null) !==
'')
3912 $arFields[
'REMIND'] = unserialize(
$arFields[
'REMIND'], [
'allowed_classes' =>
false]);
3914 if (!is_array(
$arFields[
'REMIND'] ??
null))
3926 $eventId = (int)$eventId;
3938 foreach(
GetModuleEvents(
"calendar",
"OnAfterCalendarEventUserFieldsUpdate",
true) as $arEvent)
3943 if ($updateSearchIndex)
3945 self::updateSearchIndex($eventId);
3951 public static function Delete(
$params)
3954 $bCalDav = CCalendar::IsCalDAVEnabled();
3956 $sendNotification = (
$params[
'sendNotification'] ??
null) !==
false;
3963 : CCalendar::GetCurUserId()
3966 $arAffectedSections = [];
3967 $entry =
$params[
'Event'] ??
null;
3969 if (!isset($entry) || !is_array($entry))
3971 CCalendar::SetOffset();
3972 $res = self::GetList([
3976 'parseRecursion' =>
false,
3978 $entry =
$res[0] ??
null;
3983 $entry[
'PARENT_ID'] = $entry[
'PARENT_ID'] ??
null;
3984 if (!empty($entry[
'IS_MEETING']) && $entry[
'PARENT_ID'] !== $entry[
'ID'])
3986 $parentEvent = self::GetList([
3988 "ID" => $entry[
'PARENT_ID'],
3990 'parseRecursion' =>
false,
3992 $parentEvent = $parentEvent[0];
3996 $eventModel = self::getEventModelForPermissionCheck(
3997 (
int)($entry[
'ID'] ?? 0),
4005 if (in_array($entry[
'MEETING_STATUS'] ??
null,
4007 Dictionary::MEETING_STATUS[
'Yes'],
4008 Dictionary::MEETING_STATUS[
'Question'],
4012 self::SetMeetingStatus([
4014 'eventId' => $entry[
'ID'],
4016 'doSendMail' =>
false,
4028 foreach(
GetModuleEvents(
"calendar",
"OnBeforeCalendarEventDelete",
true) as $arEvent)
4033 if (!empty($entry[
'PARENT_ID']))
4042 $sharingOwnerId = -1;
4044 $eventType = $entry[
'EVENT_TYPE'] ??
'';
4046 if (in_array($eventType, Sharing\SharingEventManager::getSharingEventTypes(),
true))
4048 $eventId = (int)($entry[
'ID'] ?? 0);
4054 Sharing\SharingEventManager::onSharingEventDeleted(
4060 $linkFactory = (
new Sharing\Link\Factory());
4063 $eventLink = $linkFactory->getEventLinkByEventId((
int)($entry[
'PARENT_ID'] ?? $eventId));
4066 $sharingOwnerId = $eventLink->getOwnerId();
4070 $arAffectedSections[] = $entry[
'SECT_ID'];
4072 if (!empty($entry[
'LOCATION']))
4074 $loc = Rooms\Util::parseLocation($entry[
'LOCATION']);
4075 if ($loc[
'mrevid'] || $loc[
'room_event_id'])
4077 Rooms\Util::releaseLocation($loc);
4081 if ($entry[
'CAL_TYPE'] ===
'user')
4086 if (!empty($entry[
'IS_MEETING']))
4088 $isPastEvent = (int)$entry[
'DATE_TO_TS_UTC'] < (time() - (
int)$entry[
'TZ_OFFSET_TO']);;
4091 if (Loader::includeModule(
"im"))
4097 $involvedAttendees = [];
4100 $childEvents = self::GetList([
4104 'parseRecursion' =>
false,
4105 'checkPermissions' =>
false,
4106 'setDefaultLimit' =>
false,
4109 foreach($childEvents as $chEvent)
4111 $CACHE_MANAGER->ClearByTag(
'calendar_user_'.$chEvent[
"OWNER_ID"]);
4113 $chEvent[
"MEETING_STATUS"] !==
"N"
4114 && $sendNotification
4116 && $sharingOwnerId !== (
int)$chEvent[
"OWNER_ID"]
4119 $fromTo = self::GetEventFromToForUser($entry, $chEvent[
"OWNER_ID"]);
4121 if (
$userId === 0 && ($eventLink instanceof Sharing\Link\EventLink))
4123 $sendCancelUserId = $eventLink->getHostId();
4126 (!empty($chEvent[
'MEETING_HOST']) && (
int)$chEvent[
'MEETING_HOST'] === $sendCancelUserId)
4127 || self::checkAttendeeBelongsToEvent($id, $sendCancelUserId)
4130 if (($eventLink instanceof Sharing\Link\EventLink))
4132 \CCalendarNotify::Send([
4133 'mode' =>
'cancel_sharing',
4134 'userId' => $sendCancelUserId,
4135 'guestId' => $chEvent[
'OWNER_ID'],
4137 'name' => $chEvent[
'NAME'],
4138 'from' => $fromTo[
'DATE_FROM'],
4139 'to' => $fromTo[
'DATE_TO'],
4140 'isSharing' =>
true,
4146 \CCalendarNotify::Send([
4148 'name' => $chEvent[
'NAME'],
4149 "from" => $fromTo[
"DATE_FROM"],
4150 "to" => $fromTo[
"DATE_TO"],
4151 "location" => CCalendar::GetTextLocation($chEvent[
"LOCATION"]),
4152 "guestId" => $chEvent[
"OWNER_ID"],
4154 "userId" => $sendCancelUserId,
4160 if ($chEvent[
"MEETING_STATUS"] ===
"Q")
4162 $involvedAttendees[] = $chEvent[
"OWNER_ID"];
4166 if ($bExchange || $bCalDav)
4170 'bCalDav' => $bCalDav,
4171 'bExchangeEnabled' => $bExchange,
4172 'sectionId' => $chEvent[
'SECT_ID'],
4178 self::onEventDelete($chEvent,
$params);
4180 $isParent = $chEvent[
'ID'] === $chEvent[
'PARENT_ID'];
4184 && ICalUtil::isMailUser($chEvent[
'OWNER_ID'])
4187 if (!empty($chEvent[
'ATTENDEE_LIST']) && is_array($chEvent[
'ATTENDEE_LIST']))
4190 foreach ($chEvent[
'ATTENDEE_LIST'] as $attendee)
4192 $attendeeIds[] = $attendee[
'id'];
4196 if (!empty($attendeeIds))
4198 $attendees = ICalUtil::getIndexUsersById($attendeeIds);
4201 $sender = self::getSenderForIcal($attendees, $chEvent[
'MEETING_HOST']);
4202 if (!empty($chEvent[
'MEETING'][
'MAIL_FROM']))
4204 $sender[
'EMAIL'] = $chEvent[
'MEETING'][
'MAIL_FROM'];
4205 $sender[
'MAIL_FROM'] = $chEvent[
'MEETING'][
'MAIL_FROM'];
4213 $chEvent[
'VERSION'] = (int)$chEvent[
'VERSION'] + 1;
4215 $invitationInfo = (
new InvitationInfo(
4216 (
int)$chEvent[
'ID'],
4218 (
int)$chEvent[
'OWNER_ID'],
4219 InvitationInfo::TYPE_CANCEL
4222 SendingEmailNotification::sendMessageToQueue($invitationInfo);
4225 $pullUserId = (int)$chEvent[
'OWNER_ID'] > 0 ? (
int)$chEvent[
'OWNER_ID'] :
$userId;
4232 PushCommand::DeleteEvent,
4235 'fields' => $chEvent,
4236 'requestUid' =>
$params[
'requestUid'] ??
null,
4243 if (!empty(
$params[
'bMarkDeleted']))
4245 Internals\EventTable::updateByFilter(
4246 [
'=PARENT_ID' => $id],
4252 Internals\EventTable::deleteByFilter([
'PARENT_ID' => $id]);
4255 if (!empty($involvedAttendees))
4261 if (!$entry[
'IS_MEETING'] && $entry[
'CAL_TYPE'] ===
'user')
4263 self::onEventDelete($entry,
$params);
4269 && \Bitrix\Calendar\Sync\Util\RequestLogger::isEnabled()
4273 unset($loggerData[
'Event']);
4274 $loggerData[
'loggerUuid'] = $id;
4275 (new \Bitrix\Calendar\Sync\Util\RequestLogger(
$userId,
'portal_delete'))->write($loggerData);
4278 if (!empty(
$params[
'bMarkDeleted']))
4280 Internals\EventTable::update($id, [
'DELETED' =>
'Y']);
4285 Internals\EventTable::delete($id);
4288 if (!empty($arAffectedSections))
4293 foreach(EventManager::getInstance()->findEventHandlers(
"calendar",
"OnAfterCalendarEventDelete") as
$event)
4300 if (($entry[
'ACCESSIBILITY'] ??
'') ===
'absent')
4302 (new \Bitrix\Calendar\Integration\Intranet\Absence())->cleanCache();
4305 $pullUserId = (int)$entry[
'OWNER_ID'] > 0 ? (
int)$entry[
'OWNER_ID'] :
$userId;
4312 PushCommand::DeleteEvent,
4316 'requestUid' =>
$params[
'requestUid'] ??
null,
4321 (new \Bitrix\Calendar\Event\Event\AfterCalendarEventDeleted($id))->emit();
4332 $doSendMail =
$params[
'doSendMail'] ??
true;
4333 $reccurentMode = isset(
$params[
'reccurentMode'])
4334 && in_array(
$params[
'reccurentMode'], [
'this',
'next',
'all'])
4338 $currentDateFrom = CCalendar::Date(CCalendar::Timestamp(
$params[
'currentDateFrom']),
false);
4339 if ($reccurentMode && $currentDateFrom)
4342 $recurrenceId =
$event[
'RECURRENCE_ID'] ??
$event[
'ID'];
4344 if ($reccurentMode !==
'all')
4346 $res = CCalendar::SaveEventEx([
4350 'silentErrorMode' =>
false,
4351 'recursionEditMode' => $reccurentMode,
4352 'userId' =>
$event[
'MEETING_HOST'],
4353 'checkPermission' =>
false,
4354 'currentEventDateFrom' => $currentDateFrom,
4355 'sendEditNotification' =>
false,
4360 && isset(
$res[
'recEventId'])
4361 &&
$res[
'recEventId']
4364 self::SetMeetingStatus([
4365 'userId' =>
$params[
'attendeeId'],
4366 'eventId' =>
$res[
'recEventId'],
4367 'status' =>
$params[
'status'],
4368 'personalNotification' =>
true,
4369 'doSendMail' => $doSendMail,
4374 if ($reccurentMode ===
'all' || $reccurentMode ===
'next')
4376 $recRelatedEvents = self::GetEventsByRecId($recurrenceId,
false);
4378 if ($reccurentMode ===
'next')
4380 $untilTimestamp = CCalendar::Timestamp($currentDateFrom);
4384 $untilTimestamp =
false;
4385 self::SetMeetingStatus([
4386 'userId' =>
$params[
'attendeeId'],
4387 'eventId' =>
$params[
'eventId'],
4388 'status' =>
$params[
'status'],
4389 'personalNotification' =>
true,
4390 'doSendMail' => $doSendMail,
4394 foreach($recRelatedEvents as $ev)
4396 if ((
int)$ev[
'ID'] === (
int)(
$params[
'eventId'] ?? 0))
4401 if ($reccurentMode ===
'all'
4404 && CCalendar::Timestamp($ev[
'DATE_FROM']) > $untilTimestamp
4408 self::SetMeetingStatus([
4409 'userId' =>
$params[
'attendeeId'],
4410 'eventId' => $ev[
'ID'],
4411 'status' =>
$params[
'status'],
4412 'doSendMail' => $doSendMail,
4420 self::SetMeetingStatus([
4421 'userId' =>
$params[
'attendeeId'] ??
null,
4422 'eventId' =>
$params[
'eventId'] ??
null,
4423 'status' =>
$params[
'status'] ??
null,
4424 'doSendMail' => $doSendMail,
4437 CTimeZone::Disable();
4441 $doSendMail =
$params[
'doSendMail'] ??
true;
4443 if (!in_array(
$status, [
"Q",
"Y",
"N",
"H",
"M"],
true))
4454 'parseRecursion' =>
false,
4455 'fetchAttendees' =>
true,
4456 'fetchMeetings' =>
true,
4457 'checkPermissions' =>
false,
4458 'setDefaultLimit' =>
false,
4464 $prevStatus =
$event[
'MEETING_STATUS'];
4471 $userEventForSharing = self::GetList([
4473 'PARENT_ID' =>
$event[
'PARENT_ID'],
4478 'checkPermissions' =>
false,
4481 if (!empty($userEventForSharing))
4483 $userEventForSharing = $userEventForSharing[0];
4487 if (ICalUtil::isMailUser(
$event[
'MEETING_HOST']))
4495 if ($doSendMail && $prevStatus !==
$status)
4497 IncomingEventManager::rehandleRequest([
4505 if (
$event[
'CURRENT_ATTENDEE_ID'] ??
null)
4507 self::updateEventAttendeeMeetingStatus((
int)
$event[
'CURRENT_ATTENDEE_ID'],
$status);
4510 Internals\EventTable::updateByFilter(
4512 '=PARENT_ID' => (
int)
$event[
'PARENT_ID'],
4514 '=CAL_TYPE' => Dictionary::CALENDAR_TYPE[
'user'],
4516 [
'MEETING_STATUS' =>
$status],
4524 CCalendarSect::UpdateModificationLabel(
$event[
'SECT_ID']);
4527 CCalendarNotify::ClearNotifications(
$event[
'PARENT_ID'],
$userId);
4530 if (!empty(
$params[
'personalNotification']) && CCalendar::getCurUserId() ===
$userId)
4533 CCalendarNotify::Send([
4534 'mode' =>
$status ===
"Y" ?
'status_accept' :
'status_decline',
4535 'name' =>
$event[
'NAME'],
4536 "from" => $fromTo[
"DATE_FROM"],
4538 "eventId" =>
$event[
'PARENT_ID'],
4545 $shouldNotifyExtranetInCollab =
$status ===
'Y'
4546 &&
$event[
'EVENT_TYPE'] === Dictionary::EVENT_TYPE[
'collab']
4550 if ($shouldNotifyExtranetInCollab)
4553 CCalendarNotify::Send([
4554 'mode' =>
'ics_link',
4555 'name' =>
$event[
'NAME'],
4556 'from' => $fromTo[
'DATE_FROM'],
4558 'eventId' =>
$event[
'PARENT_ID'],
4567 && (
$params[
'sharingAutoAccept'] ??
null) ===
true
4572 CCalendarNotify::Send([
4573 'mode' =>
'status_accept',
4574 'name' =>
$event[
'NAME'],
4575 "from" => $fromTo[
"DATE_FROM"],
4576 "guestId" => (
int)(
$event[
'MEETING_HOST'] ??
null),
4577 "eventId" =>
$event[
'PARENT_ID'],
4580 'isSharing' =>
true,
4584 $addedPullUserList = [];
4585 if (isset(
$event[
'ATTENDEE_LIST']) && is_array(
$event[
'ATTENDEE_LIST']))
4587 foreach (
$event[
'ATTENDEE_LIST'] as $attendee)
4592 'PARENT_ID' =>
$event[
'PARENT_ID'] ?? $eventId,
4593 'ATTENDEES' =>
$event[
'ATTENDEES'] ??
null,
4594 'CAL_TYPE' =>
$event[
'CAL_TYPE'] ??
null,
4598 PushCommand::SetMeetingStatus,
4601 'fields' => $fields,
4602 'requestUid' =>
$params[
'requestUid'] ??
null,
4605 $addedPullUserList[] = (int)$attendee[
'id'];
4610 if ($pullUserId && !in_array($pullUserId, $addedPullUserList,
true))
4615 'PARENT_ID' =>
$event[
'PARENT_ID'] ?? $eventId,
4616 'ATTENDEES' =>
$event[
'ATTENDEES'] ??
null,
4617 'CAL_TYPE' =>
$event[
'CAL_TYPE'] ??
null,
4621 PushCommand::SetMeetingStatus,
4624 'fields' => $fields,
4625 'requestUid' =>
$params[
'requestUid'] ??
null,
4632 $event[
'MEETING'][
'NOTIFY']
4633 && (
int)
$event[
'MEETING_HOST']
4635 && (
$params[
'hostNotification'] ??
null) !==
false
4638 if (self::checkAttendeeBelongsToEvent(
$event[
'PARENT_ID'],
$userId))
4641 $fromTo = self::GetEventFromToForUser(
$event,
$event[
'MEETING_HOST']);
4642 CCalendarNotify::Send([
4643 'mode' =>
$status ===
"Y" ?
'accept' :
'decline',
4644 'name' =>
$event[
'NAME'],
4645 "from" => $fromTo[
"DATE_FROM"],
4646 "to" => $fromTo[
"DATE_TO"],
4647 "location" => CCalendar::GetTextLocation(
$event[
"LOCATION"] ??
null),
4649 "eventId" =>
$event[
'PARENT_ID'],
4650 "userId" =>
$event[
'MEETING'][
'MEETING_CREATOR'] ??
$event[
'MEETING_HOST'],
4656 !empty($userEventForSharing)
4660 Sharing\SharingEventManager::onSharingEventMeetingStatusChange(
4663 $userEventForSharing,
4664 $params[
'sharingAutoAccept'] ??
false
4669 CCalendarSect::UpdateModificationLabel([
$event[
'SECTIONS'][0] ??
null]);
4673 $childEvent = self::GetList([
4675 "PARENT_ID" =>
$event[
'PARENT_ID'],
4680 'parseRecursion' =>
false,
4681 'fetchAttendees' =>
true,
4682 'checkPermissions' =>
false,
4683 'setDefaultLimit' =>
false,
4686 if ($childEvent && $childEvent[0])
4688 $childEvent = $childEvent[0];
4689 $bCalDav = CCalendar::IsCalDAVEnabled();
4690 $bExchange = CCalendar::IsExchangeEnabled(
$userId);
4692 if ($bExchange || $bCalDav)
4695 'bCalDav' => $bCalDav,
4696 'bExchangeEnabled' => $bExchange,
4697 'sectionId' => $childEvent[
'SECT_ID'],
4701 self::onEventDelete($childEvent);
4707 if ((
$params[
'affectRecRelatedEvents'] ??
null) !==
false)
4715 'parseRecursion' =>
false,
4716 'fetchAttendees' =>
true,
4717 'fetchMeetings' =>
true,
4718 'checkPermissions' =>
false,
4719 'setDefaultLimit' =>
false,
4727 if (!empty(
$event[
'RECURRENCE_ID']))
4729 $masterEvent = self::GetList([
4731 'PARENT_ID' =>
$event[
'RECURRENCE_ID'],
4735 'parseRecursion' =>
false,
4736 'fetchAttendees' =>
false,
4737 'checkPermissions' =>
false,
4738 'setDefaultLimit' =>
false,
4740 if (!empty($masterEvent))
4742 $masterEvent = $masterEvent[0];
4745 if (($masterEvent[
'MEETING_STATUS'] ??
null) !==
'Y')
4747 self::SetMeetingStatus([
4749 'eventId' => $masterEvent[
'ID'],
4751 'personalNotification' =>
true,
4752 'hostNotification' =>
true,
4753 'affectRecRelatedEvents' =>
false,
4754 'updateDescription' =>
$params[
'updateDescription'] ??
null,
4757 self::SetMeetingStatusForRecurrenceEvents(
4762 $params[
'updateDescription'] ??
null,
4767 if (!empty(
$event[
'RRULE']) && in_array($prevStatus, [
'N',
'Q',
'H']))
4769 self::SetMeetingStatusForRecurrenceEvents(
4774 $params[
'updateDescription'] ??
null,
4779 if ($prevStatus ===
'N')
4781 CCalendar::syncChange(
4788 'originalFrom' =>
null,
4795 if ((
$params[
'updateDescription'] ??
null) !==
false)
4797 if (!empty(
$event[
'RECURRENCE_ID']))
4801 if (!empty(
$event[
'PARENT_ID']) && (
int)
$event[
'PARENT_ID'] !== (
int)
$event[
'RECURRENCE_ID'])
4809 'eventId' => $eventId,
4814 CCalendar::UpdateCounter([
$userId], [$eventId]);
4818 $createdBy = (int)
$event[
'CREATED_BY'];
4824 if ((
$event[
'ACCESSIBILITY'] ??
'') ===
'absent')
4826 (new \Bitrix\Calendar\Integration\Intranet\Absence())->cleanCache();
4831 CCalendarNotify::ClearNotifications($eventId);
4834 CTimeZone::Enable();
4835 CCalendar::ClearCache([
'attendees_list',
'event_list']);
4837 (new \Bitrix\Calendar\Event\Event\AfterMeetingStatusChanged($eventId,
$userId,
$status))->emit();
4840 private static function updateEventAttendeeMeetingStatus(
int $eventAttendeeId,
string $meetingStatus): void
4842 $updateResult = Internals\EventAttendeeTable::update(
4845 'MEETING_STATUS' => $meetingStatus,
4848 if ($updateResult->isSuccess())
4853 private static function updateEventAttendeeColor(
int $eventAttendeeId,
string $color): void
4855 $updateResult = Internals\EventAttendeeTable::update(
4861 if ($updateResult->isSuccess())
4866 private static function getEventAttendeeIdByEventIdAndUserId(
int $eventId,
int $userId): ?int
4868 $eventAttendee = Internals\EventAttendeeTable::getRow([
4871 'EVENT_ID' => $eventId,
4876 return $eventAttendee ? (int)$eventAttendee[
'ID'] : null;
4879 private static function safeDeleteEventAttendees(
int $eventId,
array $attendeeUserIds): void
4881 $eventAttendeesResult = Internals\EventAttendeeTable::getList([
4884 'EVENT_ID' => $eventId,
4885 'OWNER_ID' => $attendeeUserIds,
4889 $eventAttendees = $eventAttendeesResult->fetchAll();
4890 if (empty($eventAttendees))
4895 $eventAttendeeIds = array_column($eventAttendees,
'ID');
4897 $updateResult = Internals\EventAttendeeTable::updateMulti($eventAttendeeIds, [
4901 if ($updateResult->isSuccess())
4906 private static function updateEventAttendee(
4909 array $changedFields,
4913 if (in_array(
'COLOR', $changedFields,
true) && isset($dbFields[
'COLOR']))
4916 $allChildEventAttendees = Internals\EventAttendeeTable::getList([
4919 'EVENT_ID' => $parentEventId,
4922 $eventAttendeeIds = array_column($allChildEventAttendees->fetchAll(),
'ID');
4924 if (!$eventAttendeeIds)
4928 $colorUpdateResult = Internals\EventAttendeeTable::updateMulti(
4931 'COLOR' => $dbFields[
'COLOR'],
4934 if ($colorUpdateResult->isSuccess())
4940 if (in_array(
'REMIND', $changedFields,
true) && isset($dbFields[
'REMIND']))
4942 $eventAttendeeId = self::getEventAttendeeIdByEventIdAndUserId($parentEventId,
$userId);
4944 if (!$eventAttendeeId)
4949 $remindUpdateResult = Internals\EventAttendeeTable::update(
4952 'REMIND' => $dbFields[
'REMIND'],
4955 if ($remindUpdateResult->isSuccess())
4961 protected static function ShowEventSection(
int $parentId,
int $userId): void
4963 $eventEO = Internals\EventTable::query()
4965 ->where(
'PARENT_ID', $parentId)
4967 ->exec()->fetchObject();
4969 if (is_null($eventEO))
4975 $acceptedEvent = (
new Mappers\Event())->getByEntityObject($eventEO);
4977 if (is_null($acceptedEvent))
4982 $acceptedSectionId = $acceptedEvent->getSection()->getId();
4983 $hiddenSectionIds = UserSettings::getHiddenSections(
$userId, [
'isPersonalCalendarContext' =>
true ]);
4984 $newHiddenSectionIds = array_filter($hiddenSectionIds,
static function($hiddenSectionId) use ($acceptedSectionId) {
4985 return !is_numeric($hiddenSectionId) || (int)$hiddenSectionId !== $acceptedSectionId;
4988 if (
count($hiddenSectionIds) ===
count($newHiddenSectionIds))
4993 UserSettings::saveHiddenSections(
$userId, $newHiddenSectionIds);
4995 PushCommand::HiddenSectionsUpdated,
4998 'hiddenSections' => $newHiddenSectionIds,
5008 ?
bool $updateDescription =
null,
5011 $recRelatedEvents = self::GetEventsByRecId($recurrenceId,
false,
$userId);
5012 foreach ($recRelatedEvents as $ev)
5014 if ((
int)$ev[
'ID'] === $eventId)
5019 self::SetMeetingStatus([
5021 'eventId' => $ev[
'ID'],
5023 'personalNotification' =>
false,
5024 'hostNotification' =>
false,
5025 'affectRecRelatedEvents' =>
false,
5026 'updateDescription' => $updateDescription,
5039 $eventId = (int)$eventId;
5042 $event = self::GetById($eventId,
false);
5047 $event = Internals\EventTable::query()
5048 ->setSelect([
'MEETING_STATUS'])
5049 ->where(
'PARENT_ID', (
int)
$event[
'PARENT_ID'])
5077 $curEventId = (int)
$params[
'curEventId'];
5078 $curUserId = isset(
$params[
'userId']) ? (int)
$params[
'userId'] : CCalendar::GetCurUserId();
5084 if (!isset(
$params[
'checkPermissions']))
5086 $params[
'checkPermissions'] =
true;
5090 $accessibility = [];
5106 $events = self::GetList([
5108 "FROM_LIMIT" =>
$params[
'from'],
5110 "CAL_TYPE" =>
'user',
5111 "OWNER_ID" => $users,
5112 "ACTIVE_SECTION" =>
"Y",
5114 'arSelect' => self::$defaultSelectEvent,
5115 'parseRecursion' =>
true,
5116 'fetchAttendees' =>
false,
5117 'fetchSection' =>
true,
5118 'parseDescription' =>
false,
5119 'setDefaultLimit' =>
false,
5120 'checkPermissions' =>
$params[
'checkPermissions'],
5123 foreach ($events as
$event)
5125 if ($curEventId && ((
int)
$event[
"ID"] === $curEventId || (
int)
$event[
"PARENT_ID"] === $curEventId))
5129 if (
$event[
"ACCESSIBILITY"] ===
'free')
5133 if (
$event[
"IS_MEETING"] &&
$event[
"MEETING_STATUS"] ===
"N")
5137 if (CCalendarSect::CheckGoogleVirtualSection(
$event[
'SECTION_DAV_XML_ID']))
5144 $eventModel = EventModel::createFromArray(
$event);
5147 (
$event[
'PRIVATE_EVENT'] &&
$event[
'CAL_TYPE'] ===
'user' &&
$event[
'OWNER_ID'] !== $curUserId)
5148 || !
$accessController->check(ActionDictionary::ACTION_EVENT_VIEW_TITLE, $eventModel)
5151 $name =
'[' . Loc::getMessage(
'EC_ACCESSIBILITY_' . mb_strtoupper(
$event[
'ACCESSIBILITY'])) .
']';
5154 $accessibility[
$event[
'OWNER_ID']][] = [
5157 "DATE_FROM" =>
$event[
"DATE_FROM"],
5158 "DATE_TO" =>
$event[
"DATE_TO"],
5159 "DATE_FROM_TS_UTC" =>
$event[
"DATE_FROM_TS_UTC"],
5160 "DATE_TO_TS_UTC" =>
$event[
"DATE_TO_TS_UTC"],
5161 "~USER_OFFSET_FROM" =>
$event[
"~USER_OFFSET_FROM"],
5162 "~USER_OFFSET_TO" =>
$event[
"~USER_OFFSET_TO"],
5163 "DT_SKIP_TIME" =>
$event[
"DT_SKIP_TIME"],
5164 "TZ_FROM" =>
$event[
"TZ_FROM"],
5165 "TZ_TO" =>
$event[
"TZ_TO"],
5166 "ACCESSIBILITY" =>
$event[
"ACCESSIBILITY"],
5167 "IMPORTANCE" =>
$event[
"IMPORTANCE"],
5168 "EVENT_TYPE" =>
$event[
"EVENT_TYPE"],
5216 return $accessibility;
5222 $tempUser = CCalendar::TempUser(
false,
true);
5223 $checkPermissions =
$params[
'checkPermissions'] !==
false;
5224 $curUserId = isset(
$params[
'userId']) ? (int)
$params[
'userId'] : CCalendar::GetCurUserId();
5227 if (is_array($users) && !empty($users))
5229 foreach($users as $id)
5233 $arUsers[] = (int)$id;
5236 if (empty($arUsers))
5244 'ACCESSIBILITY' =>
'absent',
5245 'CAL_TYPE' => Dictionary::CALENDAR_TYPE[
'user'],
5253 if (isset(
$params[
'fromLimit']))
5255 $arFilter[
'FROM_LIMIT'] = CCalendar::Date(CCalendar::Timestamp(
$params[
'fromLimit'],
false),
true,
false);
5258 if (isset(
$params[
'toLimit']))
5260 $arFilter[
'TO_LIMIT'] = CCalendar::Date(CCalendar::Timestamp(
$params[
'toLimit'],
false),
true,
false);
5263 $eventList = self::GetList([
5265 'arSelect' => self::$defaultSelectEvent,
5266 'parseRecursion' =>
true,
5267 'getUserfields' =>
false,
5268 'fetchAttendees' =>
false,
5269 'userId' => $curUserId,
5270 'preciseLimits' =>
true,
5271 'checkPermissions' =>
false,
5272 'parseDescription' =>
false,
5273 'skipDeclined' =>
true,
5279 foreach($eventList as
$event)
5282 if (!empty($users) && !in_array(
$userId, $arUsers,
true))
5287 if (
$event[
'IS_MEETING'] &&
$event[
"MEETING_STATUS"] ===
'N')
5294 && (
$event[
'CAL_TYPE'] !==
'user' || $curUserId !== (
int)
$event[
'OWNER_ID'])
5295 && $curUserId !== (
int)
$event[
'CREATED_BY']
5298 $sectId = (int)
$event[
'SECTION_ID'];
5299 if (empty(
$event[
'ACCESSIBILITY']))
5301 $event[
'ACCESSIBILITY'] =
'busy';
5308 $private =
$event[
'PRIVATE_EVENT'] &&
$event[
'CAL_TYPE'] ===
'user';
5311 $eventModel = EventModel::createFromArray(
$event);
5312 $eventModel->setSectionId((
int)$sectId);
5314 if ($private || !
$accessController->check(ActionDictionary::ACTION_EVENT_VIEW_FULL, $eventModel))
5320 $skipTime =
$event[
'DT_SKIP_TIME'] ===
'Y';
5321 $fromTs = CCalendar::Timestamp(
$event[
'DATE_FROM'],
false, !$skipTime);
5322 $toTs = CCalendar::Timestamp(
$event[
'DATE_TO'],
false, !$skipTime);
5323 if (
$event[
'DT_SKIP_TIME'] !==
'Y')
5325 $fromTs -=
$event[
'~USER_OFFSET_FROM'];
5326 $toTs -=
$event[
'~USER_OFFSET_TO'];
5331 'NAME' =>
$event[
'NAME'],
5332 'DATE_FROM' => CCalendar::Date($fromTs, !$skipTime,
false),
5333 'DATE_TO' => CCalendar::Date($toTs, !$skipTime,
false),
5334 'DT_FROM_TS' => $fromTs,
5335 'DT_TO_TS' => $toTs,
5337 'DETAIL_TEXT' =>
'',
5344 if (
$a[
'DT_FROM_TS'] === $b[
'DT_FROM_TS'])
5348 return $a[
'DT_FROM_TS'] < $b[
'DT_FROM_TS'] ? -1 : 1;
5351 CCalendar::TempUser($tempUser,
false);
5363 $query = Internals\EventTable::query()
5364 ->setSelect([
'ID',
'LOCATION',
'SECTION_ID'])
5365 ->where(
'SECTION_ID', $sectionId)
5371 $loc =
$event[
'LOCATION'] ??
null;
5372 if ($loc && mb_strlen($loc) > 5 && mb_strpos($loc,
'ECMR_') === 0)
5374 $loc = Rooms\Util::parseLocation($loc);
5375 if ($loc[
'mrid'] !==
false && $loc[
'mrevid'] !==
false)
5377 Rooms\Util::releaseLocation($loc);
5380 else if ($loc && mb_strlen($loc) > 9 && mb_strpos($loc,
'calendar_') === 0)
5382 $loc = Rooms\Util::parseLocation($loc);
5383 if ($loc[
'room_id'] !==
false && $loc[
'room_event_id'] !==
false)
5385 Rooms\Util::releaseLocation($loc);
5388 $itemIds[] = (int)
$event[
'ID'];
5392 if (!empty($itemIds))
5394 Internals\EventTable::deleteByFilter([
5399 CCalendar::ClearCache([
5408 $strSql =
"SELECT PARENT_ID from b_calendar_event where PARENT_ID in (SELECT ID from b_calendar_event where MEETING_STATUS='H' and DELETED='Y') AND DELETED='N'";
5414 $strItems .=
",". (int)
$result[
'ID'];
5417 if ($strItems !=
"0")
5420 "UPDATE b_calendar_event SET ".
5421 $DB->PrepareUpdate(
"b_calendar_event",
array(
"DELETED" =>
"Y")).
5422 " WHERE PARENT_ID in (".$strItems.
")";
5423 $DB->Query($strSql);
5425 CCalendar::ClearCache([
'section_list',
'event_list']);
5430 if ((
int)$eventId > 0)
5435 'arFilter' =>
array(
5438 'parseRecursion' =>
false,
5439 'fetchAttendees' =>
true,
5440 'checkPermissions' =>
true,
5449 'arFilter' =>
array(
5450 "PARENT_ID" => $eventId,
5453 'parseRecursion' =>
false,
5454 'fetchAttendees' =>
true,
5455 'checkPermissions' =>
true,
5465 && (
$event[0][
'IS_ACCESSIBLE_TO_USER'] ??
null) !==
false
5467 isset(
$event[0][
'DESCRIPTION'])
5468 || isset(
$event[0][
'IS_MEETING'])
5469 || isset(
$event[0][
'LOCATION'])
5485 $eventId = !empty(
$event[
'PARENT_ID']) ? (int)
$event[
'PARENT_ID'] : (int)(
$event[
'ID'] ??
null);
5487 if (isset(self::$eventUserFields[$eventId]))
5489 return self::$eventUserFields[$eventId];
5492 self::$eventUserFields[$eventId] =
$USER_FIELD_MANAGER->GetUserFields(
'CALENDAR_EVENT', $eventId, LANGUAGE_ID);
5494 return self::$eventUserFields[$eventId];
5497 public static function SetExDate($exDate = [], $untilTimestamp =
false)
5499 if ($untilTimestamp && !empty($exDate) && is_array($exDate))
5503 foreach($exDate as $date)
5505 if (CCalendar::Timestamp($date) <= $untilTimestamp)
5507 $exDateRes[] = $date;
5511 $exDate = $exDateRes;
5514 $exDate = array_unique($exDate);
5516 return implode(
';', $exDate);
5521 if ($recurrenceId > 0)
5524 'RECURRENCE_ID' => $recurrenceId,
5532 return self::GetList([
5534 'parseRecursion' =>
false,
5535 'fetchAttendees' =>
false,
5536 'checkPermissions' => $checkPermissions,
5537 'setDefaultLimit' =>
false,
5554 if (self::IsBrokenEventOfSeries(
$event))
5556 $commentXmlId =
$event[
'RELATIONS'][
'COMMENT_XML_ID'];
5557 $doesntHaveCommentsOrWereCommentsMoved = self::MoveCommentsToFirstRecurrence(
$event, $commentXmlId);
5558 if ($doesntHaveCommentsOrWereCommentsMoved)
5560 self::CleanUpBrokenRecursiveExclusion($commentXmlId);
5563 $xmlId =
$event[
'RECURRENCE_ID'] ??
null;
5564 preg_match(
'/EVENT_\d+_(.*)/', $commentXmlId, $matchesDate);
5565 $xmlDate = $matchesDate[1] ??
null;
5567 $doesntHaveCommentsOrWereCommentsMovedAnother =
false;
5568 if (!is_null($xmlId) && !is_null($xmlDate))
5570 $anotherCommentXmlId =
"EVENT_{$xmlId}_{$xmlDate}";
5571 $doesntHaveCommentsOrWereCommentsMovedAnother = self::MoveCommentsToFirstRecurrence(
$event, $anotherCommentXmlId);
5572 if ($doesntHaveCommentsOrWereCommentsMovedAnother)
5574 self::CleanUpBrokenRecursiveExclusion($anotherCommentXmlId);
5578 if ($doesntHaveCommentsOrWereCommentsMoved && $doesntHaveCommentsOrWereCommentsMovedAnother)
5580 self::CleanUpBrokenRecursiveEvent(
$event);
5583 unset(
$event[
'ORIGINAL_DATE_FROM'],
$event[
'RELATIONS']);
5585 \CCalendar::ClearCache(
'event_list');
5594 return !empty(
$event[
'RRULE']) && !empty(
$event[
'RELATIONS'][
'COMMENT_XML_ID']);
5599 $rows = Internals\EventTable::query()
5601 ->whereNot(
'RRULE',
'')
5602 ->where(
'PARENT_ID',
$event[
'PARENT_ID'])
5605 $events =
$rows->fetchAll();
5611 $eventIds = array_map(
'intval', array_column($events,
'ID'));
5612 Internals\EventTable::updateMulti($eventIds, [
5613 'ORIGINAL_DATE_FROM' =>
null,
5614 'RELATIONS' =>
null,
5620 $rows = Internals\EventTable::query()
5621 ->setSelect([
'ID',
'PARENT_ID',
'RECURRENCE_ID',
'ORIGINAL_DATE_FROM'])
5622 ->where(
'RRULE',
'')
5623 ->where(
'RELATIONS', serialize([
5624 'COMMENT_XML_ID' => $commentXmlId,
5628 $events =
$rows->fetchAll();
5629 foreach ($events as $brokenEvent)
5631 Internals\EventTable::update($brokenEvent[
'ID'], [
5632 'RELATIONS' => serialize([
5633 'COMMENT_XML_ID' => self::GetEventCommentXmlId($brokenEvent),
5646 $forumId = \CCalendar::GetSettings()[
'forum_id'];
5648 $newEntityXmlId =
"EVENT_$eventEntityId";
5650 $tsFrom =
$event[
'DATE_FROM_TS_UTC'];
5651 if (!empty(
$event[
'~DATE_FROM']))
5653 $tsFrom = \CCalendar::TimestampUTC(
$event[
'~DATE_FROM']);
5657 if (str_contains(
$event[
'EXDATE'], $firstRecurrenceDate))
5659 $newEntityXmlId =
"EVENT_{$event['RECURRENCE_ID']}_{$firstRecurrenceDate}";
5662 $currentFeed = new \Bitrix\Forum\Comments\Feed($forumId, [
5664 'id' => $eventEntityId,
5665 'xml_id' => $currentEntityXmlId,
5668 return $currentFeed->moveEventCommentsToNewXmlId($newEntityXmlId);
5673 if (isset(
$event[
'RELATIONS'][
'ORIGINAL_RECURSION_ID']))
5675 $date = CCalendar::Date(CCalendar::Timestamp(
$event[
'DATE_FROM']),
false);
5676 return "EVENT_{$event['RELATIONS']['ORIGINAL_RECURSION_ID']}_$date";
5678 if (isset(
$event[
'ORIGINAL_DATE_FROM'],
$event[
'RECURRENCE_ID']))
5680 $date = CCalendar::Date(CCalendar::Timestamp(
$event[
'ORIGINAL_DATE_FROM']),
false);
5681 return "EVENT_{$event['RECURRENCE_ID']}_$date";
5684 $commentXmlId =
"EVENT_" . (
$event[
'PARENT_ID'] ??
$event[
'ID']);
5687 self::CheckRecurcion(
$event)
5689 && (CCalendar::Date(CCalendar::Timestamp(
$event[
'DATE_FROM']),
false)
5690 !== CCalendar::Date(CCalendar::Timestamp(
$event[
'~DATE_FROM'] ??
null),
false))
5693 $commentXmlId .=
'_'.CCalendar::Date(CCalendar::Timestamp(
$event[
'DATE_FROM']),
false);
5696 return $commentXmlId;
5704 $xmlAr = explode(
'_', $xmlId);
5705 if (is_array($xmlAr) && isset($xmlAr[2]) && $xmlAr[2])
5707 $date = CCalendar::Date(CCalendar::Timestamp($xmlAr[2]),
false);
5716 if (!empty(
$event[
'RRULE']))
5720 if (!empty(
$event[
'RRULE'][
'BYDAY']))
5722 $event[
'RRULE'][
'BYDAY'] = self::sortByDay(
$event[
'RRULE'][
'BYDAY']);
5725 switch(
$event[
'RRULE'][
'FREQ'])
5728 if ((
int)
$event[
'RRULE'][
'INTERVAL'] === 1)
5730 $res = Loc::getMessage(
'EC_RRULE_EVERY_DAY',
null, $languageId);
5734 $res = Loc::getMessage(
5735 'EC_RRULE_EVERY_DAY_1',
5736 [
'#DAY#' =>
$event[
'RRULE'][
'INTERVAL']],
5743 foreach (
$event[
'RRULE'][
'BYDAY'] as $day)
5745 $daysList[] = Loc::getMessage(
'EC_'.$day,
null, $languageId);
5748 $daysList = implode(
', ', $daysList);
5749 if ((
int)
$event[
'RRULE'][
'INTERVAL'] === 1)
5751 $res = Loc::getMessage(
5752 'EC_RRULE_EVERY_WEEK',
5753 [
'#DAYS_LIST#' => $daysList],
5759 $res = Loc::getMessage(
5760 'EC_RRULE_EVERY_WEEK_1',
5762 '#WEEK#' =>
$event[
'RRULE'][
'INTERVAL'],
5763 '#DAYS_LIST#' => $daysList,
5770 if ((
int)
$event[
'RRULE'][
'INTERVAL'] === 1)
5772 $res = Loc::getMessage(
'EC_RRULE_EVERY_MONTH',
null, $languageId);
5776 $res = Loc::getMessage(
5777 'EC_RRULE_EVERY_MONTH_1',
5779 '#MONTH#' =>
$event[
'RRULE'][
'INTERVAL'],
5786 $fromTs = CCalendar::Timestamp(
$event[
'DATE_FROM']);
5787 if (
$event[
'DT_SKIP_TIME'] !==
"Y")
5789 $fromTs -=
$event[
'~USER_OFFSET_FROM'] ?? 0;
5792 if ((
int)
$event[
'RRULE'][
'INTERVAL'] === 1)
5794 $res = Loc::getMessage(
5795 'EC_RRULE_EVERY_YEAR',
5797 '#DAY#' =>
FormatDate(
'j', $fromTs,
false, $languageId),
5798 '#MONTH#' =>
FormatDate(
'n', $fromTs,
false, $languageId),
5805 $res = Loc::getMessage(
5806 'EC_RRULE_EVERY_YEAR_1',
5808 '#YEAR#' =>
$event[
'RRULE'][
'INTERVAL'],
5809 '#DAY#' =>
FormatDate(
'j', $fromTs,
false, $languageId),
5810 '#MONTH#' =>
FormatDate(
'n', $fromTs,
false, $languageId),
5827 if (isset(
$event[
'~DATE_FROM']))
5829 $res .= Loc::getMessage(
5831 [
'#FROM_DATE#' => CCalendar::Date(CCalendar::Timestamp(
$event[
'~DATE_FROM']),
false)],
5837 $res .= Loc::getMessage(
5839 [
'#FROM_DATE#' => CCalendar::Date(CCalendar::Timestamp(
$event[
'DATE_FROM']),
false)],
5844 if ($showUntil && (
$event[
'RRULE'][
'UNTIL'] ??
null) != CCalendar::GetMaxDate())
5846 $res .=
' ' . Loc::getMessage(
5848 [
'#UNTIL_DATE#' => CCalendar::Date(CCalendar::Timestamp(
$event[
'RRULE'][
'UNTIL']),
false)],
5852 elseif ($showUntil && ((
$event[
'RRULE'][
'COUNT'] ??
null) > 0))
5854 $res .=
', ' . Loc::getMessage(
5856 [
'#COUNT#' =>
$event[
'RRULE'][
'COUNT']],
5869 $userId = \CCalendar::getCurUserId();
5870 $eventId = (int)$eventId;
5871 $excludeDateTs = CCalendar::Timestamp($excludeDate);
5872 $excludeDate = CCalendar::Date($excludeDateTs,
false);
5879 'parseRecursion' =>
false,
5880 'fetchAttendees' =>
true,
5881 'setDefaultLimit' =>
false,
5888 if (
$event && $excludeDate && self::CheckRecurcion(
$event))
5890 $excludeDates = self::GetExDate(
$event[
'EXDATE']);
5891 $excludeDates[] = $excludeDate;
5893 $id = CCalendar::SaveEvent([
5896 'DATE_FROM' =>
$event[
'DATE_FROM'],
5897 'DATE_TO' =>
$event[
'DATE_TO'],
5898 'EXDATE' => self::SetExDate($excludeDates),
5900 'silentErrorMode' =>
false,
5901 'recursionEditMode' =>
'skip',
5902 'editParentEvents' =>
true,
5905 if (!empty(
$event[
'ATTENDEE_LIST']) && is_array(
$event[
'ATTENDEE_LIST']))
5907 foreach(
$event[
'ATTENDEE_LIST'] as $attendee)
5909 if ($attendee[
'status'] ===
'Y')
5911 if (
$event[
'DT_SKIP_TIME'] !==
'Y')
5913 $excludeDate = CCalendar::Date(CCalendar::DateWithNewTime(CCalendar::Timestamp(
$event[
'DATE_FROM']), $excludeDateTs));
5918 $meetingHost =
$event[
'MEETING'][
'MEETING_CREATOR'] ??
$event[
'MEETING_HOST'];
5919 CCalendarNotify::Send([
5920 "mode" =>
'cancel_this',
5921 "name" =>
$event[
'NAME'],
5922 "from" => $excludeDate,
5923 "guestId" => $attendee[
"id"],
5924 "eventId" =>
$event[
'PARENT_ID'],
5940 && is_array($valueList)
5944 $attachedIdList = [];
5945 foreach($valueList as $value)
5947 [
$type, $realValue] = FileUserType::detectType($value);
5948 if (
$type === FileUserType::TYPE_NEW_OBJECT)
5950 $file = \Bitrix\Disk\File::loadById($realValue,
array(
'STORAGE'));
5951 $result[] = strip_tags($file->getName());
5955 $attachedIdList[] = $realValue;
5959 if (!empty($attachedIdList))
5961 $attachedObjects = AttachedObject::getModelList(
array(
5962 'with' =>
array(
'OBJECT'),
5964 'ID' => $attachedIdList,
5967 foreach($attachedObjects as $attachedObject)
5969 $file = $attachedObject->getFile();
5970 $result[] = strip_tags($file->getName());
5981 if ((
int)$eventId > 0)
5983 $event = Internals\EventTable::query()
5984 ->setSelect([
'ID',
'DELETED',
'SEARCHABLE_CONTENT'])
5985 ->where(
'ID', (
int)$eventId)
5986 ->where(
'DELETED',
'N')
5990 $res = !empty(
$event[
'SEARCHABLE_CONTENT']) ?
$event[
'SEARCHABLE_CONTENT'] :
'';
6000 $targetChangedFields = [
'NAME',
'DESCRIPTION',
'LOCATION'];
6003 isset(
$params[
'changedFields'])
6004 && empty(array_intersect(
$params[
'changedFields'], $targetChangedFields))
6017 $events = self::getList([
6019 'ID' => $eventIdList,
6022 'parseRecursion' =>
false,
6023 'fetchAttendees' =>
true,
6024 'checkPermissions' =>
false,
6025 'setDefaultLimit' =>
false,
6026 'userId' =>
$params[
'userId'] ??
null,
6030 if (is_array($events))
6032 $event = current($events);
6033 $eventId = (int)
$event[
'ID'];
6036 if (!empty(
$params[
'updateAllByParent']) && $eventId === (
int)
$event[
'PARENT_ID'])
6038 Internals\EventTable::updateByFilter(
6039 [
'=PARENT_ID' => $eventId],
6045 Internals\EventTable::update($eventId, [
6046 [
'SEARCHABLE_CONTENT' =>
$content],
6062 .
Emoji::encode(\CCalendar::GetTextLocation($entry[
'LOCATION'] ??
''))
6065 if ($entry[
'IS_MEETING'])
6067 $attendeesWereHandled =
false;
6068 if (!empty($entry[
'ATTENDEE_LIST']) && is_array($entry[
'ATTENDEE_LIST']))
6070 foreach($entry[
'ATTENDEE_LIST'] as $attendee)
6072 if (isset(self::$userIndex[$attendee[
'id']]))
6074 $content .=
' '.static::prepareToken(self::$userIndex[$attendee[
'id']][
'DISPLAY_NAME']);
6077 $attendeesWereHandled =
true;
6080 if (!empty($entry[
'ATTENDEES_CODES']))
6082 if ($attendeesWereHandled)
6084 $attendeesCodes = [];
6085 foreach($entry[
'ATTENDEES_CODES'] as
$code)
6087 if (!str_starts_with(
$code,
'U'))
6089 $attendeesCodes[] =
$code;
6095 $attendeesCodes = $entry[
'ATTENDEES_CODES'];
6097 $content .=
' ' . static::prepareToken(
6107 $content .=
' ' . static::prepareToken(CCalendar::GetUserName($entry[
'CREATED_BY']));
6114 $fileNameList = self::getDiskUFFileNameList($entry[
'UF_WEBDAV_CAL_EVENT']);
6115 if (!empty($fileNameList))
6117 $content .=
' '.static::prepareToken(implode(
' ', $fileNameList));
6121 catch (RuntimeException $e)
6129 $uf = $entry[
'UF_CRM_CAL_EVENT'];
6131 foreach ($uf as $item)
6133 $title = self::getCrmElementTitle($item);
6138 catch (RuntimeException $e)
6148 $crmElement = explode(
'_', $item);
6149 $type = $crmElement[0];
6150 $typeId = \CCrmOwnerType::ResolveID(\CCrmOwnerTypeAbbr::ResolveName(
$type));
6152 return \CCrmOwnerType::GetCaption($typeId, $crmElement[1]);
6159 $res =
$DB->Query(
'select count(*) as c from b_calendar_event');
6171 Internals\EventTable::update((
int)$eventId, [
'COLOR' => $color]);
6173 $eventAttendeeId = self::getEventAttendeeIdByEventIdAndUserId($eventId, CCalendar::GetCurUserId());
6174 if ($eventAttendeeId)
6176 self::updateEventAttendeeColor($eventAttendeeId, $color);
6187 return str_rot13($token);
6192 return COption::GetOptionString(
"calendar",
"~ft_b_calendar_event",
false);
6197 return self::$userIndex;
6206 $fromTs = \CCalendar::Timestamp(
$params[
'eventDate'],
true,
false) -
$params[
'timezoneOffset'];
6207 $userDateFrom = \CCalendar::Date($fromTs);
6209 $entry = self::GetList([
6213 "FROM_LIMIT" => $userDateFrom,
6214 "TO_LIMIT" => $userDateFrom,
6216 'parseRecursion' =>
true,
6217 'maxInstanceCount' => 1,
6218 'preciseLimits' =>
true,
6219 'fetchAttendees' =>
true,
6220 'checkPermissions' =>
true,
6221 'setDefaultLimit' =>
false,
6222 'getUserfields' =>
true,
6228 && is_array($entry[0])
6229 && $entry[0][
'RRULE']
6230 && $entry[0][
'EXDATE']
6231 && in_array(
$params[
'eventDate'], self::GetExDate($entry[0][
'EXDATE']))
6234 $entry = self::GetList([
6236 "RECURRENCE_ID" => $entryId,
6238 "FROM_LIMIT" =>
$params[
'eventDate'],
6239 "TO_LIMIT" =>
$params[
'eventDate'],
6241 'parseRecursion' =>
true,
6242 'maxInstanceCount' => 1,
6243 'preciseLimits' =>
true,
6244 'fetchAttendees' =>
true,
6245 'checkPermissions' =>
true,
6246 'setDefaultLimit' =>
false,
6247 'getUserfields' =>
true,
6251 if (!$entry || !is_array($entry[0]))
6253 $entry = self::GetList([
6258 'parseRecursion' =>
true,
6259 'maxInstanceCount' => 1,
6260 'fetchAttendees' =>
true,
6261 'checkPermissions' =>
true,
6262 'setDefaultLimit' =>
false,
6263 'getUserfields' =>
true,
6268 if (!$entry || !is_array($entry[0]))
6270 $entry = self::GetList([
6275 'parseRecursion' =>
false,
6276 'fetchAttendees' =>
true,
6277 'checkPermissions' =>
true,
6278 'setDefaultLimit' =>
false,
6279 'getUserfields' =>
true,
6283 if ($entry && is_array($entry[0]))
6286 if ($entry[
'IS_MEETING'] && (
int)$entry[
'PARENT_ID'] !== (
int)$entry[
'ID'])
6288 $parentEntry =
false;
6289 $parentEntryList = self::GetList([
6291 "ID" => (
int)$entry[
'PARENT_ID'],
6292 "FROM_LIMIT" => $userDateFrom,
6293 "TO_LIMIT" => $userDateFrom,
6295 'parseRecursion' =>
true,
6296 'maxInstanceCount' => 1,
6297 'preciseLimits' =>
true,
6298 'fetchAttendees' =>
true,
6299 'checkPermissions' =>
true,
6300 'setDefaultLimit' =>
false,
6301 'getUserfields' =>
true,
6305 if (!empty($parentEntryList[0]) && is_array($parentEntryList[0]))
6307 $parentEntry = $parentEntryList[0];
6312 if ($parentEntry[
'DELETED'] ===
'Y')
6314 self::CleanEventsWithDeadParents();
6318 if ((
int)$parentEntry[
'MEETING_HOST'] === (
int)
$params[
'userId'])
6320 $entry = $parentEntry;
6326 isset($entry[
'UF_WEBDAV_CAL_EVENT'])
6327 && is_array($entry[
'UF_WEBDAV_CAL_EVENT'])
6328 && empty($entry[
'UF_WEBDAV_CAL_EVENT'])
6331 $entry[
'UF_WEBDAV_CAL_EVENT'] =
null;
6336 ($entry[
'IS_MEETING'] ??
null)
6337 && !empty($entry[
'ATTENDEE_LIST'])
6338 && is_array($entry[
'ATTENDEE_LIST'])
6339 && (
int)$entry[
'CREATED_BY'] !==
$params[
'userId']
6340 && (
int)$entry[
'OWNER_ID'] !==
$params[
'userId']
6341 && (
$params[
'recursion'] ??
null) !==
false
6342 && ($entry[
'CAL_TYPE'] !== Dictionary::CALENDAR_TYPE[
'open_event'])
6345 foreach($entry[
'ATTENDEE_LIST'] as $attendee)
6347 if ((
int)$attendee[
'id'] === (
int)
$params[
'userId'])
6349 $entry = self::GetList([
6351 'PARENT_ID' => $entry[
'PARENT_ID'],
6352 'OWNER_ID' =>
$params[
'userId'],
6355 'parseRecursion' =>
false,
6356 'maxInstanceCount' => 1,
6357 'preciseLimits' =>
false,
6358 'fetchAttendees' =>
false,
6359 'checkPermissions' =>
true,
6360 'setDefaultLimit' =>
false,
6361 'getUserfields' =>
true,
6364 if ($entry && is_array($entry[0]) && $entry[0][
'CAL_TYPE'] ===
'location')
6367 $entry = self::getEventForViewInterface($entry[0][
'PARENT_ID'],
$params);
6369 else if ($entry && is_array($entry[0]))
6372 $entry = self::getEventForViewInterface($entry[0][
'ID'],
$params);
6383 $entry = self::GetList(
6388 "FROM_LIMIT" =>
$params[
'eventDate'] ??
null,
6389 "TO_LIMIT" =>
$params[
'eventDate'] ??
null,
6391 'parseRecursion' =>
true,
6392 'maxInstanceCount' => 1,
6393 'preciseLimits' =>
true,
6394 'fetchAttendees' =>
true,
6395 'checkPermissions' =>
true,
6396 'setDefaultLimit' =>
false,
6400 if (!$entry || !is_array($entry[0]))
6402 $entry = self::GetList(
6408 'parseRecursion' =>
true,
6409 'maxInstanceCount' => 1,
6410 'fetchAttendees' =>
true,
6411 'checkPermissions' =>
true,
6412 'setDefaultLimit' =>
false,
6418 if (!$entry || !is_array($entry[0]))
6420 $entry = self::GetList(
6426 'parseRecursion' =>
false,
6427 'fetchAttendees' =>
true,
6428 'checkPermissions' =>
true,
6429 'setDefaultLimit' =>
false,
6434 $entry = is_array($entry) ? $entry[0] :
null;
6436 if (is_array($entry) && $entry[
'ID'] !== $entry[
'PARENT_ID'])
6438 return self::getEventForEditInterface($entry[
'PARENT_ID'],
$params);
6446 $accessCodes = is_array($accessCodes) ? $accessCodes : [];
6449 if (empty($accessCodes))
6451 $accessCodes[] =
'U'.$userId;
6454 $accessCodes = array_unique($accessCodes);
6456 return $accessCodes;
6469 mixed $currentEvent,
6473 $entryFields[
'TZ_FROM'] ??=
null;
6474 $entryFields[
'TZ_TO'] ??=
null;
6475 $entryFields[
'TZ_OFFSET_FROM'] ??= 0;
6476 $entryFields[
'TZ_OFFSET_TO'] ??= 0;
6478 $fieldsToCheckOccupancy = $entryFields;
6479 if (!empty(
$params[
'checkLocationOccupancyFields']))
6481 $fieldsToCheckOccupancy =
$params[
'checkLocationOccupancyFields'];
6482 $fieldsToCheckOccupancy[
'LOCATION'] = [
6483 'NEW' => $fieldsToCheckOccupancy[
'LOCATION'] ??
'',
6485 self::CheckFields($fieldsToCheckOccupancy, $currentEvent,
$userId);
6489 if (!$occupancyCheckResult->isSuccess())
6491 $disturbingEventsFormatted = $occupancyCheckResult->getData()[
'disturbingEventsFormatted'];
6492 if ($occupancyCheckResult->getData()[
'isDisturbingEventsAmountOverShowLimit'])
6494 $message = Loc::getMessage(
'EC_LOCATION_REPEAT_BUSY_TOO_MANY', [
6495 '#DATES#' => $disturbingEventsFormatted,
6500 $message = Loc::getMessage(
'EC_LOCATION_REPEAT_BUSY', [
6501 '#DATES#' => $disturbingEventsFormatted,
6527 !empty($entryFields[
'IS_MEETING'])
6528 && !empty($entryFields[
'PARENT_ID'])
6529 && ($entryFields[
'CAL_TYPE'] ??
null) ===
'user'
6532 $sectionId = CCalendar::GetMeetingSection($entryFields[
'OWNER_ID'] ??
null);
6536 $sectionId = CCalendarSect::GetLastUsedSection(
6537 $entryFields[
'CAL_TYPE'] ??
null, $entryFields[
'OWNER_ID'] ??
null,
$userId
6543 $res = CCalendarSect::GetList([
6545 'CAL_TYPE' => $entryFields[
'CAL_TYPE'] ??
null,
'OWNER_ID' => $entryFields[
'OWNER_ID'] ??
null,
6560 if (empty($sectionId))
6562 $sectRes = CCalendarSect::GetSectionForOwner($entryFields[
'CAL_TYPE'], $entryFields[
'OWNER_ID'],
true);
6563 $sectionId = $sectRes[
'sectionId'];
6568 $sectionId = $currentEvent[
'SECTION_ID'] ?? $currentEvent[
'SECT_ID'];
6590 '=ID' => $involvedAttendees,
'=ACTIVE' =>
'Y',
6592 'ID',
'EXTERNAL_AUTH_ID',
'NAME',
'LAST_NAME',
'SECOND_NAME',
'LOGIN',
'EMAIL',
'TITLE',
6597 while (
$user = $orm->fetch())
6599 if (
$user[
'ID'] === ($meetingHost ??
null))
6601 $user[
'STATUS'] =
'accepted';
6605 $user[
'STATUS'] =
'needs_action';
6618 private static function getSenderEmailForIcal(
string $serializedMeetingInfo =
null): ?string
6620 $meetingInfo = unserialize($serializedMeetingInfo, [
'allowed_classes' =>
false]);
6622 return !empty($meetingInfo) && !empty($meetingInfo[
'MAIL_FROM'])
6623 ? $meetingInfo[
'MAIL_FROM']
6635 private static function getSenderForIcal($userIndex, $organizerId): ?
array
6637 if (!empty($userIndex) && !empty($userIndex[$organizerId]))
6639 return $userIndex[$organizerId];
6642 $userOrm = UserTable::getList([
6644 '=ID' => $organizerId,
6660 if (
$user = $userOrm->fetch())
6682 CTimeZone::Disable();
6684 $strSql =
"UPDATE b_calendar_event SET ".
6685 $DB->PrepareUpdate(
"b_calendar_event", $fields)
6686 .
" WHERE ID=" . (int)
$event[
'ID'] .
"; ";
6687 $DB->Query($strSql);
6690 CTimeZone::Enable();
6705 "UPDATE b_calendar_event"
6706 .
" SET " .
$DB->PrepareUpdate(
'b_calendar_event', [
'SYNC_STATUS' =>
$status])
6707 .
" WHERE ID = " . $eventId .
";"
6714 $parsedNew = Bitrix\Calendar\Rooms\Util::parseLocation(
$location[
'NEW']);
6715 if (!empty($parsedNew[
'room_event_id']))
6717 $location[
'NEW'] =
'calendar_' . $parsedNew[
'room_id'];
6734 if (is_string($childParams[
'arFields'][
'MEETING']))
6736 $childParams[
'arFields'][
'MEETING'] = unserialize($childParams[
'arFields'][
'MEETING'], [
'allowed_classes' =>
false]);
6739 $childParams[
'arFields'][
'MEETING'][
'LANGUAGE_ID'] = CCalendar::getUserLanguageId((
int)$childParams[
'arFields'][
'OWNER_ID']);
6747 private static function getUidForChildEvent(
array $event): string
6749 return UidGenerator::createInstance()
6750 ->setPortalName(Util::getServerName())
6752 Util::getDateObject(
6754 (
$event[
'SKIP_TIME'] ??
false),
6755 $event[
'TZ_FROM'] ??
null,
6758 ->setUserId((
int)
$event[
'OWNER_ID'])
6776 private static function onEventDelete(
6782 $mapperFactory = \Bitrix\Main\DI\ServiceLocator::getInstance()->get(
'calendar.service.mappers.factory');
6784 $originalDate =
null;
6785 if (empty($eventData))
6790 $section = $mapperFactory->getSection()->getById((
int)$eventData[
'SECTION_ID']);
6792 if ($section ===
null)
6797 $factories = FactoriesCollection::createBySection($section);
6799 if ($factories->count() === 0)
6804 $recId = $eventData[
'RECURRENCE_ID'];
6807 $exDate = $eventData[
'DATE_FROM'];
6808 $originalDate = $eventData[
'ORIGINAL_DATE_FROM'];
6809 $originalEventData = $eventData;
6810 $eventData = Internals\EventTable::getRow([
6812 '=PARENT_ID' => $eventData[
'RECURRENCE_ID'],
6813 '=OWNER_ID' => $eventData[
'OWNER_ID'],
6820 $event = (new \Bitrix\Calendar\Core\Builders\EventBuilderFromArray($eventData))->build();
6827 $syncManager =
new Synchronization($factories);
6832 !empty(
$params[
'originalFrom'])
6833 && (
int)(
$params[
'userId'] ??
null) === (
int)$eventData[
'OWNER_ID']
6837 $connection = $mapperFactory->getConnection()->getMap([
6838 '=ACCOUNT_TYPE' =>
$params[
'originalFrom'],
6839 '=ENTITY_TYPE' =>
$event->getCalendarType(),
6840 '=ENTITY_ID' =>
$event->getOwner()?->getId(),
6844 $syncManager->upEventVersion(
6854 $exDate = new \Bitrix\Main\Type\Date(CCalendar::Date(CCalendar::Timestamp($exDate),
false));
6855 $context->add(
'sync',
'excludeDate',
new \Bitrix\Calendar\Core\Base\Date($exDate));
6860 $originalDate = new \Bitrix\Main\Type\DateTime(CCalendar::Date(CCalendar::Timestamp($originalDate)));
6861 $context->add(
'sync',
'originalDate',
new \Bitrix\Calendar\Core\Base\Date($originalDate));
6867 if (!empty($originalEventData) && !
$result->isSuccess())
6869 $originalEvent = (new \Bitrix\Calendar\Core\Builders\EventBuilderFromArray($originalEventData))->build();
6870 $syncManager->deleteEvent($originalEvent,
$context);
6879 private static function isNewAttendee($attendees, $currentId): bool
6881 foreach ($attendees as $attendee)
6883 if ((
int)$attendee[
'id'] === (
int)$currentId)
6894 uasort($byDay,
function(
$a, $b){
6918 private static function checkRecurringRuleField(
array &
$arFields,
int $toTs, $currentExDate): void
6923 && in_array(
$arFields[
'RRULE'][
'FREQ'], [
'HOURLY',
'DAILY',
'MONTHLY',
'YEARLY',
'WEEKLY'])
6927 if (isset(
$arFields[
'RRULE'][
'INTERVAL']) && (
int)
$arFields[
'RRULE'][
'INTERVAL'] > 1)
6932 $untilTs = CCalendar::Timestamp(
$arFields[
'RRULE'][
'UNTIL'] ??
null,
false,
false);
6943 elseif ($untilTs + CCalendar::GetDayLen() < $toTs)
6959 if (is_array(
$arFields[
'RRULE'][
'BYDAY']))
6966 $days = [
'SU',
'MO',
'TU',
'WE',
'TH',
'FR',
'SA'];
6967 $bydays = explode(
',',
$arFields[
'RRULE'][
'BYDAY']);
6968 foreach ($bydays as $day)
6970 $day = mb_strtoupper($day);
6971 if (in_array($day, $days,
true))
6977 $arFields[
'RRULE'][
'BYDAY'] = implode(
',', $BYDAY);
6982 $excludeDates = self::GetExDate(
$arFields[
"EXDATE"]);
6986 $excludeDates = self::GetExDate($currentExDate);
6989 if (!empty($excludeDates) && $untilTs)
6991 $arFields[
"EXDATE"] = self::SetExDate($excludeDates, $untilTs);
7005 $rrule = self::ParseRRULE(
$arFields[
'RRULE']);
7006 $interval = (int)$rrule[
'INTERVAL'];
7007 $count = (int)$rrule[
'COUNT'];
7009 $fromTS = CCalendar::Timestamp(
$arFields[
'DATE_FROM']) + $offsetTs;
7010 $toTS = CCalendar::Timestamp(
$arFields[
'DATE_TO']) + $offsetTs;
7011 $length = $toTS - $fromTS;
7013 $h = (int)date(
'H', $toTS);
7014 $min = (int)date(
'i', $toTS);
7015 $d = (int)date(
'd', $toTS);
7016 $m = (int)date(
'm', $toTS);
7017 $y = (int)date(
'Y', $toTS);
7019 if ($rrule[
'FREQ'] ===
'DAILY')
7021 return mktime($h, $min, 0, $m, $d +
$count * $interval, $y);
7023 if ($rrule[
'FREQ'] ===
'MONTHLY')
7025 return mktime($h, $min, 0, $m +
$count * $interval, $d, $y);
7027 if ($rrule[
'FREQ'] ===
'YEARLY')
7029 return mktime($h, $min, 0, $m, $d, $y +
$count * $interval);
7031 if ($rrule[
'FREQ'] ===
'WEEKLY')
7033 $byDay = $rrule[
'BYDAY'];
7035 if (!is_array($byDay))
7038 $weekDays = [
'SU',
'MO',
'TU',
'WE',
'TH',
'FR',
'SA'];
7040 foreach (explode(
',', $byDay) as $day)
7042 if (in_array($day, $weekDays,
true))
7056 return mktime($h, $min, $length, $m, $d + round((
$count - 1) /
count($byDay) * 7 * $interval), $y);
7073 private static function pushUpdateDescriptionToQueue($PARENT_ID,
$userId,
$status): void
7075 $message = (new \Bitrix\Calendar\Core\Queue\Message\Message())
7077 'parentId' => $PARENT_ID,
7081 ->setRoutingKey(
'calendar:update_meeting_status');
7082 (new \Bitrix\Calendar\Core\Queue\Producer\Producer())->send(
$message);
7089 $userId = CCalendar::GetUserId();
7092 $eventModel = self::getEventModelForPermissionCheck((
int)(
$event[
'ID'] ?? 0),
$event,
$userId);
7095 ActionDictionary::ACTION_EVENT_VIEW_FULL => [],
7096 ActionDictionary::ACTION_EVENT_VIEW_TIME => [],
7097 ActionDictionary::ACTION_EVENT_VIEW_TITLE => [],
7098 ActionDictionary::ACTION_EVENT_VIEW_COMMENTS => [],
7099 ActionDictionary::ACTION_EVENT_EDIT => [],
7100 ActionDictionary::ACTION_EVENT_EDIT_LOCATION => [],
7101 ActionDictionary::ACTION_EVENT_EDIT_ATTENDEES => [],
7102 ActionDictionary::ACTION_EVENT_DELETE => [],
7107 'view_full' => $accessResult[ActionDictionary::ACTION_EVENT_VIEW_FULL],
7108 'view_time' => $accessResult[ActionDictionary::ACTION_EVENT_VIEW_TIME],
7109 'view_title' => $accessResult[ActionDictionary::ACTION_EVENT_VIEW_TITLE],
7110 'view_comments' => $accessResult[ActionDictionary::ACTION_EVENT_VIEW_COMMENTS],
7111 'edit' => $accessResult[ActionDictionary::ACTION_EVENT_EDIT],
7112 'editLocation' => $accessResult[ActionDictionary::ACTION_EVENT_EDIT_LOCATION],
7113 'editAttendees' => $accessResult[ActionDictionary::ACTION_EVENT_EDIT_ATTENDEES],
7114 'delete' => $accessResult[ActionDictionary::ACTION_EVENT_DELETE],
7122 $userId = CCalendar::GetUserId();
7128 $userEventResult = self::GetList([
7130 'PARENT_ID' => $eventId,
7132 'CAL_TYPE' => [Dictionary::CALENDAR_TYPE[
'user'], Dictionary::CALENDAR_TYPE[
'open_event']],
7135 'arSelect' => self::$defaultSelectEvent,
7136 'parseRecursion' =>
false,
7137 'fetchMeetings' =>
false,
7139 'checkPermissions' =>
false,
7140 'getPermissions' =>
false,
7143 if ($userEventResult)
7145 $userEvent = $userEventResult[0];
7153 || ((
int)(
$event[
'ID'] ?? 0) !== $eventId)
7157 $currentEventResult = self::GetList([
7162 'arSelect' => self::$defaultSelectEvent,
7163 'parseRecursion' =>
false,
7164 'fetchMeetings' =>
false,
7166 'checkPermissions' =>
false,
7167 'getPermissions' =>
false,
7170 if ($currentEventResult)
7172 $event = $currentEventResult[0];
7176 return EventModel::createFromArray($userEvent ?:
$event ?: []);
7181 if (empty($eventId) || empty(
$userId))
7186 if (isset(self::$attendeeBelongingToEvent[$eventId][
$userId]))
7188 return self::$attendeeBelongingToEvent[$eventId][
$userId];
7191 $event = Internals\EventTable::query()
7193 ->where(
'PARENT_ID', $eventId)
7198 if (!isset(self::$attendeeBelongingToEvent[$eventId]))
7200 self::$attendeeBelongingToEvent[$eventId] = [];
7202 if (!isset(self::$attendeeBelongingToEvent[$eventId][
$userId]))
7204 self::$attendeeBelongingToEvent[$eventId][
$userId] = !empty(
$event);
7207 return self::$attendeeBelongingToEvent[$eventId][
$userId];
7212 $userTimezoneName = \CCalendar::GetUserTimezoneName(\CCalendar::GetUserId());
7216 'from' => \CCalendar::Date(mktime(0, 0, 0, $monthFrom, 1, $yearFrom) - $offset,
false),
7217 'to' => \CCalendar::Date(mktime(0, 0, 0, $monthTo, 1, $yearTo) - $offset,
false),
7221 private static function getOpenEventSection(?
int $userId =
null): ?
Section
7228 if (self::$openEventSection !==
false)
7230 return self::$openEventSection;
7234 $mapperFactory = ServiceLocator::getInstance()->get(
'calendar.service.mappers.factory');
7237 '=CAL_TYPE' => Dictionary::CALENDAR_TYPE[
'open_event'],
7245 private static function getParentCollabConnections(
array $eventList):
array
7248 foreach ($eventList as
$event)
7250 $collabEventTypes = [
7251 Dictionary::EVENT_TYPE[
'collab'],
7252 Dictionary::EVENT_TYPE[
'shared_collab'],
7256 !empty(
$event[
'EVENT_TYPE'])
7257 && in_array(
$event[
'EVENT_TYPE'], $collabEventTypes,
true)
7260 if (!$isCollabEvent ||
$event[
'ID'] ===
$event[
'PARENT_ID'])
7265 $parentIds[
$event[
'PARENT_ID']] = (int)
$event[
'PARENT_ID'];
7268 if (empty($parentIds))
7273 $cachedCollabIdsByParent = array_intersect_key(self::$collabIdByParent, $parentIds);
7274 $cachedParentIds = array_keys($cachedCollabIdsByParent);
7276 $nonCachedParentIds = array_diff($parentIds, $cachedParentIds);
7278 if (empty($nonCachedParentIds))
7280 return $cachedCollabIdsByParent;
7284 Internals\EventTable::query()
7285 ->whereIn(
'ID', $nonCachedParentIds)
7286 ->where(
'CAL_TYPE', Dictionary::CALENDAR_TYPE[
'group'])
7287 ->setSelect([
'ID',
'PARENT_ID',
'OWNER_ID'])
7291 $nonCachedCollabIdsByParent = array_combine(
7292 $eventCollection->getParentIdList(),
7293 $eventCollection->getOwnerIdList(),
7296 self::$collabIdByParent += $nonCachedCollabIdsByParent;
7298 return $cachedCollabIdsByParent + $nonCachedCollabIdsByParent;
7301 private static function getCollabIdByEvent(
array $event,
array $parentCollabConnections): ?int
7304 !(
$event[
'EVENT_TYPE'] ??
null)
7307 [Dictionary::EVENT_TYPE[
'collab'], Dictionary::EVENT_TYPE[
'shared_collab']],
7316 $parentId = (int)
$event[
'PARENT_ID'];
7317 if ((
int)
$event[
'ID'] !== $parentId && !empty($parentCollabConnections[$parentId]))
7319 $collabId = $parentCollabConnections[$parentId];
7321 else if (
$event[
'CAL_TYPE'] === Dictionary::CALENDAR_TYPE[
'group'])
7323 $collabId = (int)
$event[
'OWNER_ID'];
7337 private static function getCollabIdByParentId(
int $parentId): int
7339 if (isset(self::$collabIdByParent[$parentId]))
7341 return self::$collabIdByParent[$parentId];
7344 $result = Internals\EventTable::query()
7345 ->setSelect([
'ID',
'CAL_TYPE',
'OWNER_ID'])
7346 ->where(
'CAL_TYPE', Dictionary::CALENDAR_TYPE[
'group'])
7347 ->where(
'ID', $parentId)
7352 self::$collabIdByParent[$parentId] = (int)(
$result[
'OWNER_ID'] ?? 0);
7354 return self::$collabIdByParent[$parentId];
7367 if (isset(self::$getListAccessCheck[$sectionId]))
7369 return self::$getListAccessCheck[
$sectionId];
7372 $eventModel = EventModel::createFromArray(
$event);
7373 $eventModel->setSectionId($sectionId);
7377 ActionDictionary::ACTION_EVENT_VIEW_FULL => [],
7378 ActionDictionary::ACTION_EVENT_VIEW_TIME => [],
7379 ActionDictionary::ACTION_EVENT_VIEW_TITLE => [],
7384 return self::$getListAccessCheck[
$sectionId];
7387 private static function getAttendeeStatuses(
7391 $currentAttendeesIndex,
7397 foreach ($attendees as $attendee)
7399 $attendeeId = (int)$attendee;
7400 $meetingStatus =
'Q';
7401 $sendInvitations =
false;
7404 (
int)
$fields[
'OWNER_ID'] === $attendeeId
7405 || (
int)
$fields[
'CREATED_BY'] === $attendeeId
7408 $meetingStatus =
'H';
7410 elseif ($isNewEvent && (
int)(
$fields[
'~MEETING'][
'MEETING_CREATOR'] ??
null) === $attendeeId)
7412 $meetingStatus =
'Y';
7415 !empty(
$params[
'saveAttendeesStatus'])
7416 && !empty(
$params[
'currentEvent'][
'ATTENDEE_LIST'])
7417 && is_array(
$params[
'currentEvent'][
'ATTENDEE_LIST'])
7420 foreach(
$params[
'currentEvent'][
'ATTENDEE_LIST'] as $currentAttendee)
7422 if ((
int)$currentAttendee[
'id'] === $attendeeId)
7424 $meetingStatus = $currentAttendee[
'status'];
7431 !empty($currentAttendeesIndex[$attendeeId])
7432 &&
$params[
'sendInvitesToDeclined']
7433 && $meetingStatus ===
'N'
7436 $meetingStatus =
'Q';
7437 $sendInvitations =
true;
7441 'id' => $attendeeId,
7442 'status' => $meetingStatus,
7443 'sendInvitations' => $sendInvitations