static function SaveEventEx($params = [])
{
$arFields = $params['arFields'];
if (self::$type && !isset($arFields['CAL_TYPE']))
{
$arFields['CAL_TYPE'] = self::$type;
}
elseif (isset($arFields['SECTION_CAL_TYPE']) && !isset($arFields['CAL_TYPE']))
{
$arFields['CAL_TYPE'] = $arFields['SECTION_CAL_TYPE'];
}
if (self::$bOwner && !isset($arFields['OWNER_ID']))
{
$arFields['OWNER_ID'] = self::$ownerId;
}
elseif (isset($arFields['SECTION_OWNER_ID']) && !isset($arFields['OWNER_ID']))
{
$arFields['OWNER_ID'] = $arFields['SECTION_OWNER_ID'];
}
if (!isset($arFields['SKIP_TIME']) && isset($arFields['DT_SKIP_TIME']))
{
$arFields['SKIP_TIME'] = $arFields['DT_SKIP_TIME'] === 'Y';
}
//flags for synchronize the instance of a recurring event
//modeSync - edit mode instance for avoid unnecessary request (patch)
//editParentEvents - editing the parent event of the following
$params['modeSync'] = true;
$params['editInstance'] = $params['editInstance'] ?? false;
$params['editNextEvents'] = $params['editNextEvents'] ?? false;
$params['editParentEvents'] = $params['editParentEvents'] ?? false;
$params['editEntryUntil'] = $params['editEntryUntil'] ?? false;
$params['originalDavXmlId'] = $params['originalDavXmlId'] ?? null;
$params['originalFrom'] = $params['originalFrom'] ?? null;
$params['instanceTz'] = $params['instanceTz'] ?? null;
$params['syncCaldav'] = $params['syncCaldav'] ?? false;
$params['sendInvitesToDeclined'] = $params['sendInvitesToDeclined'] ?? false;
$params['autoDetectSection'] = $params['autoDetectSection'] ?? false;
$userId = $params['userId'] ?? self::getCurUserId();
$accessController = new EventAccessController($userId);
$result = [];
$sectionId =
(!empty($arFields['SECTIONS']) && is_array($arFields['SECTIONS']))
? $arFields['SECTIONS'][0]
: (int)($arFields['SECTIONS'] ?? null);
$bPersonal = self::IsPersonal($arFields['CAL_TYPE'] ?? null, $arFields['OWNER_ID'] ?? null, $userId);
$checkPermission = !isset($params['checkPermission']) || $params['checkPermission'] !== false;
$silentErrorModePrev = self::$silentErrorMode;
self::SetSilentErrorMode();
if (
isset($arFields['DT_FROM'], $arFields['DT_TO'])
&& !isset($arFields['DATE_FROM'])
&& !isset($arFields['DATE_TO'])
)
{
$arFields['DATE_FROM'] = $arFields['DT_FROM'];
$arFields['DATE_TO'] = $arFields['DT_TO'];
unset($arFields['DT_FROM'], $arFields['DT_TO']);
}
// Fetch current event
$curEvent = false;
$bNew = !isset($arFields['ID']) || !$arFields['ID'];
if (!$bNew)
{
$curEvent = CCalendarEvent::GetList(
[
'arFilter' => [
"ID" => (int)$arFields['ID'],
"DELETED" => 'N',
],
'parseRecursion' => false,
'fetchAttendees' => true,
'fetchMeetings' => false,
'userId' => $userId,
'checkPermissions' => $checkPermission,
]
);
if ($curEvent)
{
$curEvent = $curEvent[0];
}
$canChangeDateRecurrenceEvent = isset($params['recursionEditMode'])
&& in_array($params['recursionEditMode'], ['all', ''], true)
&& (($arFields['DATE_FROM'] ?? null) !== ($curEvent['DATE_FROM'] ?? null))
&& ($arFields['RRULE']['FREQ'] ?? null) !== 'NONE'
;
if ($canChangeDateRecurrenceEvent)
{
$arFields['DATE_FROM'] = self::GetOriginalDate(
$arFields['DATE_FROM'],
$curEvent['DATE_FROM'],
$arFields['TZ_FROM'] ?? null
);
$arFields['DATE_TO'] = self::GetOriginalDate(
$arFields['DATE_TO'],
$curEvent['DATE_TO'],
$arFields['TZ_TO'] ?? null
);
}
$bPersonal = $bPersonal && self::IsPersonal($curEvent['CAL_TYPE'], $curEvent['OWNER_ID'], $userId);
$arFields['CAL_TYPE'] = $curEvent['CAL_TYPE'];
$arFields['OWNER_ID'] = $curEvent['OWNER_ID'];
$arFields['CREATED_BY'] = $curEvent['CREATED_BY'];
$arFields['ACTIVE'] = $curEvent['ACTIVE'] ?? null;
$eventModel = CCalendarEvent::getEventModelForPermissionCheck((int)($curEvent['ID'] ?? 0), $curEvent, $userId);
$accessCheckResult = $accessController->check(ActionDictionary::ACTION_EVENT_EDIT, $eventModel);
$bChangeMeeting = !$checkPermission || $accessCheckResult;
if (!$bChangeMeeting)
{
return Loc::getMessage('EC_ACCESS_DENIED');
}
if (!isset($arFields['NAME']))
{
$arFields['NAME'] = $curEvent['NAME'];
}
if (!isset($arFields['DESCRIPTION']))
{
$arFields['DESCRIPTION'] = $curEvent['DESCRIPTION'];
}
if (!isset($arFields['COLOR']) && $curEvent['COLOR'])
{
$arFields['COLOR'] = $curEvent['COLOR'];
}
if (!isset($arFields['TEXT_COLOR']) && !empty($curEvent['TEXT_COLOR']))
{
$arFields['TEXT_COLOR'] = $curEvent['TEXT_COLOR'];
}
if (!isset($arFields['SECTIONS']))
{
$arFields['SECTIONS'] = [$curEvent['SECT_ID']];
$sectionId = !empty($arFields['SECTIONS']) ? $arFields['SECTIONS'][0] : 0;
}
if (!isset($arFields['IS_MEETING']))
{
$arFields['IS_MEETING'] = $curEvent['IS_MEETING'];
}
if (!isset($arFields['MEETING_HOST']))
{
$arFields['MEETING_HOST'] = $curEvent['MEETING_HOST'];
}
if (!isset($arFields['MEETING_STATUS']))
{
$arFields['MEETING_STATUS'] = $curEvent['MEETING_STATUS'];
}
if (!isset($arFields['ACTIVE']) && isset($curEvent['ACTIVE']))
{
$arFields['ACTIVE'] = $curEvent['ACTIVE'];
}
if (!isset($arFields['PRIVATE_EVENT']))
{
$arFields['PRIVATE_EVENT'] = $curEvent['PRIVATE_EVENT'];
}
if (!isset($arFields['ACCESSIBILITY']))
{
$arFields['ACCESSIBILITY'] = $curEvent['ACCESSIBILITY'];
}
if (!isset($arFields['IMPORTANCE']))
{
$arFields['IMPORTANCE'] = $curEvent['IMPORTANCE'];
}
if (!isset($arFields['SKIP_TIME']))
{
$arFields['SKIP_TIME'] = $curEvent['DT_SKIP_TIME'] === 'Y';
}
if (!isset($arFields['DATE_FROM']) && isset($curEvent['DATE_FROM']))
{
$arFields['DATE_FROM'] = $curEvent['DATE_FROM'];
}
if (!isset($arFields['DATE_TO']) && isset($curEvent['DATE_TO']))
{
$arFields['DATE_TO'] = $curEvent['DATE_TO'];
}
if (!isset($arFields['TZ_FROM']))
{
$arFields['TZ_FROM'] = $curEvent['TZ_FROM'];
}
if (!isset($arFields['TZ_TO']))
{
$arFields['TZ_TO'] = $curEvent['TZ_TO'];
}
if (!isset($arFields['RELATIONS']))
{
$arFields['RELATIONS'] = $curEvent['RELATIONS'];
}
if (!isset($arFields['MEETING']))
{
$arFields['MEETING'] = $curEvent['MEETING'];
}
if (!isset($arFields['SYNC_STATUS']) && $curEvent['SYNC_STATUS'])
{
$arFields['SYNC_STATUS'] = $curEvent['SYNC_STATUS'];
}
$arFields['MEETING']['LANGUAGE_ID'] = self::getUserLanguageId((int)$userId);
if (
!isset($arFields['ATTENDEES']) && !isset($arFields['ATTENDEES_CODES'])
&& $arFields['IS_MEETING']
&& !empty($curEvent['ATTENDEE_LIST'])
&& is_array($curEvent['ATTENDEE_LIST'])
)
{
$arFields['ATTENDEES'] = [];
foreach ($curEvent['ATTENDEE_LIST'] as $attendee)
{
$arFields['ATTENDEES'][] = $attendee['id'];
}
}
if (!isset($arFields['ATTENDEES_CODES']) && $arFields['IS_MEETING'])
{
$arFields['ATTENDEES_CODES'] = $curEvent['ATTENDEES_CODES'];
}
if (!isset($arFields['LOCATION']) && $curEvent['LOCATION'] !== "")
{
$arFields['LOCATION'] = [
'OLD' => $curEvent['LOCATION'],
'NEW' => $curEvent['LOCATION'],
];
//if location wasn't change when updating event
$parsedLoc = BitrixCalendarRoomsUtil::parseLocation($curEvent['LOCATION']);
if ($parsedLoc['room_event_id'])
{
$arFields['LOCATION']['NEW'] = 'calendar_' . $parsedLoc['room_id'];
}
}
if (!$bChangeMeeting)
{
$arFields['IS_MEETING'] = $curEvent['IS_MEETING'];
}
if ($arFields['IS_MEETING'] && !$bPersonal && $arFields['CAL_TYPE'] === 'user')
{
$arFields['SECTIONS'] = [$curEvent['SECT_ID']];
}
// If it's attendee but modifying called from CalDav methods
if (
(!empty($params['bSilentAccessMeeting'])
|| (isset($params['fromWebservice']) && $params['fromWebservice'] === true)
)
&& !empty($curEvent['IS_MEETING'])
&& ($curEvent['PARENT_ID'] !== $curEvent['ID'])
)
{
// TODO: It called when changes caused in google/webservise side but can't be
// TODO: implemented because user is only attendee, not the owner of the event
//Todo: we have to update such events back to revert changes from google
return true; // CalDav will return 204
}
if (!isset($arFields["RRULE"]) && $curEvent["RRULE"] !== '' && ($params['fromWebservice'] ?? null) !== true)
{
$arFields["RRULE"] = CCalendarEvent::ParseRRULE($curEvent["RRULE"]);
}
if (
(($params['fromWebservice'] ?? null) === true)
&& $arFields["RRULE"] === -1
&& CCalendarEvent::CheckRecurcion($curEvent)
)
{
$arFields["RRULE"] = CCalendarEvent::ParseRRULE($curEvent['RRULE']);
}
if (!isset($arFields['EXDATE']) && !empty($arFields["RRULE"]))
{
$arFields['EXDATE'] = $curEvent['EXDATE'];
}
else if (
isset($arFields['EXDATE'], $curEvent['EXDATE'])
&& $arFields['EXDATE']
&& $curEvent['EXDATE']
&& !empty($arFields["RRULE"])
)
{
$arFields['EXDATE'] = self::mergeExcludedDates($curEvent['EXDATE'], $arFields['EXDATE']);
}
if ($curEvent)
{
$params['currentEvent'] = $curEvent;
}
}
elseif ($checkPermission && $sectionId > 0 && !$bPersonal)
{
$section = CCalendarSect::GetList(['arFilter' => ['ID' => $sectionId],
'checkPermissions' => false,
'getPermissions' => false
])[0] ?? null;
if ($section)
{
$arFields['CAL_TYPE'] = $section['CAL_TYPE'];
}
else
{
return self::ThrowError(Loc::getMessage('EC_ACCESS_DENIED'));
}
$newEventModel =
EventModel::createNew()
->setOwnerId((int)$arFields['OWNER_ID'])
->setSectionId((int)$sectionId)
->setSectionType($arFields['CAL_TYPE'])
;
if (!$accessController->check(ActionDictionary::ACTION_EVENT_ADD, $newEventModel))
{
return self::ThrowError(Loc::getMessage('EC_ACCESS_DENIED'));
}
}
if ($params['autoDetectSection'] && $sectionId <= 0)
{
$sectionId = false;
if ($arFields['CAL_TYPE'] === 'user')
{
$sectionId = self::GetMeetingSection($arFields['OWNER_ID'], true);
if ($sectionId)
{
$res = CCalendarSect::GetList(
[
'arFilter' => [
'CAL_TYPE' => $arFields['CAL_TYPE'],
'OWNER_ID' => $arFields['OWNER_ID'],
'ID' => $sectionId,
],
]
);
if (!$res || !$res[0] || CCalendarSect::CheckGoogleVirtualSection($res[0]['GAPI_CALENDAR_ID']))
{
$sectionId = false;
}
}
else
{
$sectionId = false;
}
if ($sectionId)
{
$arFields['SECTIONS'] = [$sectionId];
}
}
if (!$sectionId)
{
if (empty($arFields['CAL_TYPE']) || empty($arFields['OWNER_ID']))
{
return false;
}
$sectRes = CCalendarSect::GetSectionForOwner(
$arFields['CAL_TYPE'],
$arFields['OWNER_ID'],
$params['autoCreateSection']
);
if ($sectRes['sectionId'] > 0)
{
$sectionId = $sectRes['sectionId'];
$arFields['SECTIONS'] = [$sectionId];
if ($sectRes['autoCreated'])
{
$params['bAffectToDav'] = false;
}
}
else
{
return false;
}
}
}
if (isset($arFields["RRULE"]))
{
$arFields["RRULE"] = CCalendarEvent::CheckRRULE($arFields["RRULE"]);
}
if ($bNew && !$params['editInstance'] && !($arFields['DAV_XML_ID'] ?? null))
{
$arFields['DAV_XML_ID'] = UidGenerator::createInstance()
->setDate(
new Date(
Util::getDateObject(
$arFields['DATE_FROM'],
$arFields['SKIP_TIME'] ?? null,
$arFields['TZ_FROM'] ?? null,
)
)
)
->setPortalName(Util::getServerName())
->setUserId((int)$arFields['OWNER_ID'])
->getUidWithDate();
}
elseif ($params['editInstance'])
{
$arFields['DAV_XML_ID'] = $params['currentEvent']['DAV_XML_ID'];
}
// Set version
if (!isset($arFields['VERSION']) || ($arFields['VERSION'] <= ($curEvent['VERSION'] ?? null)))
{
$arFields['VERSION'] = ($curEvent['VERSION'] ?? null)
? $curEvent['VERSION'] + 1
: 1
;
}
if ($params['autoDetectSection'] && $sectionId <= 0 && $arFields['OWNER_ID'] > 0)
{
$res = CCalendarSect::GetList(
[
'arFilter' => [
'CAL_TYPE' => $arFields['CAL_TYPE'],
'OWNER_ID' => $arFields['OWNER_ID'],
],
'checkPermissions' => false,
]
);
if ($res && is_array($res) && isset($res[0]))
{
$sectionId = $res[0]['ID'];
}
else
{
$defCalendar = CCalendarSect::CreateDefault(array(
'type' => $arFields['CAL_TYPE'],
'ownerId' => $arFields['OWNER_ID'],
));
$sectionId = $defCalendar['ID'];
self::SetCurUserMeetingSection($defCalendar['ID']);
$params['bAffectToDav'] = false;
}
if ($sectionId > 0)
{
$arFields['SECTIONS'] = [$sectionId];
}
else
{
return false;
}
}
$bExchange = self::IsExchangeEnabled() && $arFields['CAL_TYPE'] === 'user';
$bCalDav = self::IsCalDAVEnabled() && $arFields['CAL_TYPE'] === 'user';
if (
(($params['editNextEvents'] ?? null) === false && ($params['recursionEditMode'] ?? null) === 'next')
|| (in_array($params['recursionEditMode'] ?? null, ['this', 'skip'])
&& ($params['editInstance'] ?? null) === false)
)
{
$params['modeSync'] = false;
if (($params['editParentEvents'] ?? null) === true)
{
$params['modeSync'] = true;
}
}
if (
(
($params['bAffectToDav'] ?? null) !== false
&& ($bExchange || $bCalDav)
&& $sectionId > 0
&& !(isset($params['dontSyncParent']) && $params['dontSyncParent'])
&& ($params['overSaving'] ?? false) !== true
)
|| $params['syncCaldav']
)
{
$davParams = [
'bCalDav' => $bCalDav,
'bExchange' => $bExchange,
'sectionId' => $sectionId,
'modeSync' => $params['modeSync'],
'editInstance' => $params['editInstance'],
'originalDavXmlId' => $params['originalDavXmlId'],
'instanceTz' => $params['instanceTz'],
'editParentEvents' => $params['editParentEvents'],
'editNextEvents' => $params['editNextEvents'],
'syncCaldav' => $params['syncCaldav'],
'parentDateFrom' => ($params['parentDateFrom'] ?? null),
'parentDateTo' => ($params['parentDateTo'] ?? null),
];
$res = CCalendarSync::DoSaveToDav( $arFields, $davParams, $curEvent);
if ($res !== true && self::$silentErrorMode === true)
{
self::ThrowError($res);
}
}
$params['arFields'] = $arFields;
$params['userId'] = $userId;
$params['path'] = self::GetPath($arFields['CAL_TYPE'], $arFields['OWNER_ID'], 1);
$isSharingEvent =
isset($curEvent['EVENT_TYPE'])
&& in_array($curEvent['EVENT_TYPE'], SharingEventManager::getSharingEventTypes(), true)
;
if (!empty($arFields['ID']) && $isSharingEvent)
{
SharingEventManager::onSharingEventEdit($arFields);
}
if (
$curEvent
&& in_array(($params['recursionEditMode'] ?? null), ['this', 'next'], true)
&& CCalendarEvent::CheckRecurcion($curEvent)
)
{
// Edit only current instance of the set of recurrent events
if ($params['recursionEditMode'] === 'this')
{
// 1. Edit current reccurent event: exclude current date
$excludeDates = CCalendarEvent::GetExDate($curEvent['EXDATE']);
$excludeDate = self::Date(
self::Timestamp($params['currentEventDateFrom'] ?? $arFields['DATE_FROM']),
false
);
$excludeDates[] = $excludeDate;
$saveEventData = [
'recursionEditMode' => 'skip',
'silentErrorMode' => $params['silentErrorMode'],
'sendInvitesToDeclined' => $params['sendInvitesToDeclined'],
'sendInvitations' => false,
'sendEditNotification' => false,
'userId' => $userId,
'requestUid' => $params['requestUid'] ?? null,
];
$arFieldsCurrent = [
'ID' => $curEvent["ID"],
'EXDATE' => CCalendarEvent::SetExDate($excludeDates),
];
if (
!empty($params['arFields']['SECTIONS'][0])
&& (int)$curEvent['SECTION_ID'] !== (int)$params['arFields']['SECTIONS'][0]
)
{
$arFieldsCurrent['SECTIONS'] = $params['arFields']['SECTIONS'];
$arFieldsCurrent['CAL_TYPE'] = $params['arFields']['CAL_TYPE'];
$arFieldsCurrent['OWNER_ID'] = $userId;
}
$saveEventData['arFields'] = $arFieldsCurrent;
// Save current event
$id = self::SaveEvent($saveEventData);
// 2. Copy event with new changes, but without recursion
$newParams = $params;
$newParams['sendEditNotification'] = false;
if (!$newParams['arFields']['MEETING']['REINVITE'])
{
$newParams['saveAttendeesStatus'] = true;
}
$newParams['arFields']['RECURRENCE_ID'] = $curEvent['RECURRENCE_ID'] ?: $newParams['arFields']['ID'];
unset(
$newParams['arFields']['ID'],
$newParams['arFields']['DAV_XML_ID'],
$newParams['arFields']['G_EVENT_ID'],
$newParams['arFields']['SYNC_STATUS'],
$newParams['arFields']['CAL_DAV_LABEL'],
$newParams['arFields']['RRULE'],
$newParams['arFields']['EXDATE'],
$newParams['recursionEditMode'],
);
$newParams['arFields']['REMIND'] = $params['currentEvent']['REMIND'];
$fromTs = self::Timestamp($newParams['currentEventDateFrom']);
$currentFromTs = self::Timestamp($newParams['arFields']['DATE_FROM']);
$length = self::Timestamp($newParams['arFields']['DATE_TO']) - self::Timestamp($newParams['arFields']['DATE_FROM']);
if (!isset($newParams['arFields']['DATE_FROM'], $newParams['arFields']['DATE_TO']))
{
$length = $curEvent['DT_LENGTH'];
$currentFromTs = self::Timestamp($curEvent['DATE_FROM']);
}
$instanceDate = !isset($newParams['arFields']['DATE_FROM'])
||self::Date(self::Timestamp($curEvent['DATE_FROM']), false) === self::Date($currentFromTs, false);
if ($newParams['arFields']['SKIP_TIME'])
{
if ($instanceDate)
{
$newParams['arFields']['DATE_FROM'] = self::Date($fromTs, false);
$newParams['arFields']['DATE_TO'] = self::Date($fromTs + $length - self::GetDayLen(), false);
}
else
{
$newParams['arFields']['DATE_FROM'] = self::Date($currentFromTs, false);
$newParams['arFields']['DATE_TO'] = self::Date($currentFromTs + $length - self::GetDayLen(), false);
}
}
elseif ($instanceDate)
{
$newFromTs = self::DateWithNewTime($currentFromTs, $fromTs);
$newParams['arFields']['DATE_FROM'] = self::Date($newFromTs);
$newParams['arFields']['DATE_TO'] = self::Date($newFromTs + $length);
}
$eventMod = $curEvent;
if (!isset($eventMod['~DATE_FROM']))
{
$eventMod['~DATE_FROM'] = $eventMod['DATE_FROM'];
}
$eventMod['DATE_FROM'] = $newParams['currentEventDateFrom'];
$commentXmlId = CCalendarEvent::GetEventCommentXmlId($eventMod);
$newParams['arFields']['RELATIONS'] = array('COMMENT_XML_ID' => $commentXmlId);
$newParams['editInstance'] = true;
//original instance date start
$newParams['arFields']['ORIGINAL_DATE_FROM'] = self::GetOriginalDate(
$params['currentEvent']['DATE_FROM'],
$eventMod['DATE_FROM'] ?? $newParams['currentEventDateFrom'],
$newParams['arFields']['TZ_FROM']
);
$newParams['originalDavXmlId'] = $params['currentEvent']['G_EVENT_ID'];
$newParams['instanceTz'] = $params['currentEvent']['TZ_FROM'];
$newParams['parentDateFrom'] = $params['currentEvent']['DATE_FROM'];
$newParams['parentDateTo'] = $params['currentEvent']['DATE_TO'];
$newParams['requestUid'] = $params['requestUid'] ?? null;
$newParams['sendInvitesToDeclined'] = $params['sendInvitesToDeclined'] ?? null;
$result['recEventId'] = self::SaveEvent($newParams);
}
// Edit all next instances of the set of recurrent events
elseif(($params['recursionEditMode']) === 'next')
{
$currentDateTimestamp = self::Timestamp($params['currentEventDateFrom'] ?? null);
// Copy event with new changes
$newParams = $params;
$recId = $curEvent['RECURRENCE_ID'] ?: $newParams['arFields']['ID'];
$newParams['arFields']['RELATIONS'] ??= [];
$newParams['arFields']['RELATIONS'] = [
'ORIGINAL_RECURSION_ID' => $curEvent['RELATIONS']['ORIGINAL_RECURSION_ID'] ?? $recId,
];
if (empty($newParams['arFields']['MEETING']['REINVITE']))
{
$newParams['saveAttendeesStatus'] = true;
}
$currentFromTs = self::Timestamp($newParams['arFields']['DATE_FROM'] ?? null);
$length = self::Timestamp($newParams['arFields']['DATE_TO']) - self::Timestamp($newParams['arFields']['DATE_FROM']);
if (!isset($newParams['arFields']['DATE_FROM']) || !isset($newParams['arFields']['DATE_TO']))
{
$length = $curEvent['DT_LENGTH'];
$currentFromTs = self::Timestamp($curEvent['DATE_FROM']);
}
$instanceDate = !isset($newParams['arFields']['DATE_FROM'])
||self::Date(self::Timestamp($curEvent['DATE_FROM']), false) === self::Date($currentFromTs, false);
if ($newParams['arFields']['SKIP_TIME'])
{
if ($instanceDate)
{
$newParams['arFields']['DATE_FROM'] = self::Date($currentDateTimestamp, false);
$newParams['arFields']['DATE_TO'] = self::Date($currentDateTimestamp + $length, false);
}
else
{
$newParams['arFields']['DATE_FROM'] = self::Date($currentFromTs, false);
$newParams['arFields']['DATE_TO'] = self::Date($currentFromTs + $length, false);
}
}
elseif ($instanceDate)
{
$newFromTs = self::DateWithNewTime($currentFromTs, $currentDateTimestamp);
$newParams['arFields']['DATE_FROM'] = self::Date($newFromTs);
$newParams['arFields']['DATE_TO'] = self::Date($newFromTs + $length);
}
if (isset($curEvent['EXDATE']) && $curEvent['EXDATE'] !== '')
{
$newParams['arFields']['EXDATE'] = $curEvent['EXDATE'];
}
if (isset($newParams['arFields']['RRULE']['COUNT']) && $newParams['arFields']['RRULE']['COUNT'] > 0)
{
$countParams = [
'rrule' => $newParams['arFields']['RRULE'],
'dateFrom' => $curEvent['DATE_FROM'],
'dateTo' => $newParams['arFields']['DATE_FROM'],
'timeZone' => $curEvent['TZ_FROM'],
];
$newParams['arFields']['RRULE']['COUNT'] = self::CountNumberFollowEvents($countParams);
unset($newParams['arFields']['RRULE']['UNTIL'], $newParams['arFields']['RRULE']['~UNTIL']);
}
if (
isset($newParams['arFields']['RRULE']['FREQ'])
&& $newParams['arFields']['RRULE']['FREQ'] === 'WEEKLY'
&& isset($curEvent['RRULE']['FREQ'])
&& $curEvent['RRULE']['FREQ'] === 'WEEKLY'
&& $newParams['arFields']['RRULE']['BYDAY'] === $curEvent['RRULE']['BYDAY']
)
{
$currentDate = new TypeDate($params['currentEventDateFrom']);
$currentFromDate = new TypeDate($newParams['arFields']['DATE_FROM']);
$currentDateWeekday = self::WeekDayByInd($currentDate->format('N'));
$currentFromDateWeekday = self::WeekDayByInd($currentFromDate->format('N'));
if (isset($newParams['arFields']['RRULE']['BYDAY'][$currentDateWeekday]))
{
unset($newParams['arFields']['RRULE']['BYDAY'][$currentDateWeekday]);
}
$newParams['arFields']['RRULE']['BYDAY'][$currentFromDateWeekday] = $currentFromDateWeekday;
}
// Check if it's first instance of the series, so we shouldn't create another event
if (self::Date(self::Timestamp($curEvent['DATE_FROM']), false) === self::Date($currentDateTimestamp, false))
{
$newParams['recursionEditMode'] = 'skip';
}
else
{
// 1. Edit current recurrent event: set finish date with date of current instance
$arFieldsCurrent = [
"ID" => $curEvent["ID"],
"RRULE" => CCalendarEvent::ParseRRULE($curEvent['RRULE']),
];
$arFieldsCurrent['RRULE']['UNTIL'] = self::Date($currentDateTimestamp - self::GetDayLen(), false);
unset($arFieldsCurrent['RRULE']['~UNTIL'], $arFieldsCurrent['RRULE']['COUNT']);
if (
!empty($params['arFields']['SECTIONS'][0])
&& (int)$curEvent['SECTION_ID'] !== (int)$params['arFields']['SECTIONS'][0]
)
{
$arFieldsCurrent['SECTIONS'] = $params['arFields']['SECTIONS'];
}
// Save current event
$id = self::SaveEvent([
'arFields' => $arFieldsCurrent,
'silentErrorMode' => $params['silentErrorMode'] ?? null,
'recursionEditMode' => 'skip',
'sendInvitations' => false,
'sendEditNotification' => false,
'sendInvitesToDeclined' => $params['sendInvitesToDeclined'] ?? null,
'userId' => $userId,
'editNextEvents' => true,
'editParentEvents' => true,
'checkPermission' => $checkPermission,
'requestUid' => $params['requestUid'] ?? null,
'checkLocationOccupancyFields' => $newParams['arFields'],
'checkLocationOccupancy' => $params['checkLocationOccupancy'] ?? false,
]);
unset($newParams['arFields']['ID'],
$newParams['arFields']['DAV_XML_ID'],
$newParams['arFields']['G_EVENT_ID'],
$newParams['recursionEditMode']
);
}
if (empty($newParams['arFields']['DAV_XML_ID']))
{
$newParams['arFields']['DAV_XML_ID'] = UidGenerator::createInstance()
->setPortalName(Util::getServerName())
->setDate(new Date(
Util::getDateObject(
$newParams['arFields']['ORIGINAL_DATE_FROM'] ?? null,
$newParams['arFields']['SKIP_TIME'] ?? null,
$newParams['arFields']['TZ_FROM'] ?? null
)
))
->setUserId((int)($newParams['arFields']['OWNER_ID'] ?? null))
->getUidWithDate()
;
}
$newParams['sendInvitesToDeclined'] = $params['sendInvitesToDeclined'];
$newParams['editNextEvents'] = true;
$result = self::SaveEvent($newParams);
if (!is_array($result))
{
$result = [
'id' => $result,
'recEventId' => $result,
];
}
if ($recId)
{
$recRelatedEvents = CCalendarEvent::GetEventsByRecId($recId, false);
foreach($recRelatedEvents as $ev)
{
if ($ev['ID'] === $result['id'])
{
continue;
}
$evFromTs = self::Timestamp($ev['DATE_FROM']);
if ($evFromTs > $currentDateTimestamp)
{
$newParams['arFields']['ID'] = $ev['ID'];
$newParams['arFields']['RRULE'] = CCalendarEvent::ParseRRULE($ev['RRULE']);
if ($newParams['arFields']['SKIP_TIME'])
{
$newParams['arFields']['DATE_FROM'] = self::Date($evFromTs, false);
$newParams['arFields']['DATE_TO'] = self::Date(self::Timestamp($ev['DATE_TO']), false);
}
else
{
$newFromTs = self::DateWithNewTime($currentFromTs, $evFromTs);
$newParams['arFields']['DATE_FROM'] = self::Date($newFromTs);
$newParams['arFields']['DATE_TO'] = self::Date($newFromTs + $length);
}
$newParams['arFields']['RECURRENCE_ID'] = $result['id'];
$newParams['originalDavXmlId'] = $result['originalDavXmlId'];
$newParams['arFields']['ORIGINAL_DATE_FROM'] = self::GetOriginalDate(
$result['originalDateFrom'] ?? null,
$ev['ORIGINAL_DATE_FROM'] ?? $newParams['currentEventDateFrom'],
$result['instanceTz']
);
$newParams['instanceTz'] = $result['instanceTz'];
$newParams['editInstance'] = true;
unset($newParams['arFields']['EXDATE']);
if (isset($newParams['arFields']['RELATIONS']['ORIGINAL_RECURSION_ID']))
{
unset($newParams['arFields']['RELATIONS']);
}
self::SaveEvent($newParams);
}
}
}
}
}
else
{
if (($params['recursionEditMode'] ?? null) !== 'all')
{
$params['recursionEditMode'] = 'skip';
}
else
{
$params['recursionEditMode'] = '';
}
$id = CCalendarEvent::Edit($params);
if ($id)
{
$UFs = $params['UF'] ?? null;
if(!empty($UFs) && is_array($UFs))
{
CCalendarEvent::UpdateUserFields($id, $UFs);
if (!empty($arFields['IS_MEETING']) && !empty($UFs['UF_WEBDAV_CAL_EVENT']))
{
$UF = $GLOBALS['USER_FIELD_MANAGER']->GetUserFields("CALENDAR_EVENT", $id, LANGUAGE_ID);
self::UpdateUFRights(
$UFs['UF_WEBDAV_CAL_EVENT'],
$arFields['ATTENDEES_CODES'] ?? null,
$UF['UF_WEBDAV_CAL_EVENT'] ?? null
);
}
}
}
if ($params['editNextEvents'] === true && $params['editParentEvents'] === false)
{
$result['originalDate'] = $params['arFields']['DATE_FROM'];
$result['originalDavXmlId'] = $params['arFields']['DAV_XML_ID'];
$result['instanceTz'] = $params['arFields']['TZ_FROM'];
$result['recEventId'] = $id;
}
// Here we should select all events connected with edited via RECURRENCE_ID:
// It could be original source event (without RECURRENCE_ID) or sibling events
if (
$curEvent
&& !$params['recursionEditMode']
&& !($params['arFields']['RECURRENCE_ID'] ?? null)
&& CCalendarEvent::CheckRecurcion($curEvent)
)
{
$events = [];
$recId = $curEvent['RECURRENCE_ID'] ?: $curEvent['ID'];
if ($curEvent['RECURRENCE_ID'] && $curEvent['RECURRENCE_ID'] !== $curEvent['ID'])
{
$masterEvent = CCalendarEvent::GetById($curEvent['RECURRENCE_ID']);
if ($masterEvent)
{
$events[] = $masterEvent;
}
}
if ($recId)
{
$instances = CCalendarEvent::GetList([
'arFilter' => [
'RECURRENCE_ID' => $recId,
],
'parseRecursion' => false,
'setDefaultLimit' => false,
]);
if ($instances)
{
$events = array_merge($events, $instances);
}
}
foreach($events as $ev)
{
if ($ev['ID'] !== $curEvent['ID'])
{
$newParams = $params;
$newParams['arFields']['ID'] = $ev['ID'];
$newParams['arFields']['RECURRENCE_ID'] = $ev['RECURRENCE_ID'];
$newParams['arFields']['DAV_XML_ID'] = $ev['DAV_XML_ID'];
$newParams['arFields']['G_EVENT_ID'] = $ev['G_EVENT_ID'];
$newParams['arFields']['ORIGINAL_DATE_FROM'] = self::GetOriginalDate($arFields['DATE_FROM'], $ev['ORIGINAL_DATE_FROM'], $arFields['TZ_FROM']);
$newParams['arFields']['CAL_DAV_LABEL'] = $ev['CAL_DAV_LABEL'];
$newParams['arFields']['RRULE'] = CCalendarEvent::ParseRRULE($ev['RRULE']);
$newParams['recursionEditMode'] = 'skip';
$newParams['currentEvent'] = $ev;
$eventFromTs = self::Timestamp($ev['DATE_FROM']);
$currentFromTs = self::Timestamp($newParams['arFields']['DATE_FROM']);
$length = self::Timestamp($newParams['arFields']['DATE_TO']) - self::Timestamp($newParams['arFields']['DATE_FROM']);
if ($newParams['arFields']['SKIP_TIME'])
{
$newParams['arFields']['DATE_FROM'] = $ev['DATE_FROM'];
$newParams['arFields']['DATE_TO'] = self::Date($eventFromTs + $length, false);
}
else
{
$newFromTs = self::DateWithNewTime($currentFromTs, $eventFromTs);
$newParams['arFields']['DATE_FROM'] = self::Date($newFromTs);
$newParams['arFields']['DATE_TO'] = self::Date($newFromTs + $length);
}
if (isset($ev['EXDATE']) && $ev['EXDATE'])
{
$newParams['arFields']['EXDATE'] = $ev['EXDATE'];
}
if (isset($newParams['arFields']['RELATIONS']['ORIGINAL_RECURSION_ID']))
{
unset($newParams['arFields']['RELATIONS']);
}
self::SaveEvent($newParams);
}
}
}
if ($id)
{
self::syncChange($id, $arFields, $params, $curEvent ?: null);
}
$arFields['ID'] = $id;
if (($params['overSaving'] ?? false) !== true)
{
foreach(GetModuleEvents("calendar", "OnAfterCalendarEventEdit", true) as $arEvent)
{
ExecuteModuleEventEx($arEvent, array($arFields, $bNew, $userId));
}
}
}
self::SetSilentErrorMode($silentErrorModePrev);
$result['id'] = $id ?? null;
return $result;
}