static function CreateChildEvents($parentId, $arFields, $params, $changeFields)
{
global $DB, $CACHE_MANAGER;
$parentId = (int) $parentId;
$isNewEvent = !isset($arFields['ID']) || $arFields['ID'] <= 0;
$chatId = (int) ($arFields['~MEETING']['CHAT_ID'] ?? null) ;
$involvedAttendees = []; // List of all attendees to invite or to exclude from event
$isMailAvailable = Loader::includeModule("mail");
$isCalDavEnabled = CCalendar::IsCalDAVEnabled();
$isPastEvent = ($arFields['DATE_TO_TS_UTC'] ?? null) < (time() - (int)date('Z'));
$userId = $params['userId'];
$attendees = is_array($arFields['ATTENDEES']) ? $arFields['ATTENDEES'] : []; // List of attendees for event
$eventManagersCollection = [];
$attaches = [];
$chat = null;
$isIncreaseMailLimit = false;
unset($params['dontSyncParent']);
if ($chatId > 0 && Loader::includeModule('im'))
{
$chat = new CIMChat($userId);
}
if (empty($attendees) && !($arFields['CAL_TYPE'] === 'user' && $arFields['OWNER_ID'] === $userId))
{
$attendees[] = (int)$arFields['CREATED_BY'];
}
foreach($attendees as $userKey)
{
$involvedAttendees[] = (int)$userKey;
}
$currentAttendeesIndex = [];
$deletedAttendees = [];
if (!$isNewEvent)
{
$curAttendees = self::GetAttendees($parentId);
$curAttendees = is_array(($curAttendees[$parentId] ?? null)) ? $curAttendees[$parentId] : [];
foreach($curAttendees as $user)
{
$currentAttendeesIndex[$user['USER_ID']] = $user;
if (
$user['USER_ID'] !== $arFields['MEETING_HOST'] &&
($user['USER_ID'] !== $arFields['OWNER_ID'] || $arFields['CAL_TYPE'] !== 'user')
)
{
$deletedAttendees[$user['USER_ID']] = (int)$user['USER_ID'];
$involvedAttendees[] = (int)$user['USER_ID'];
}
}
}
$involvedAttendees = array_unique($involvedAttendees);
$meetingInfo = unserialize($arFields['MEETING'], ['allowed_classes' => false]);
$userIndex = [];
if ($isMailAvailable)
{
// Here we collecting information about EXTERNAL_AUTH_ID to
// know if some of the users are external
$orm = UserTable::getList([
'filter' => [
'=ID' => $involvedAttendees,
'=ACTIVE' => 'Y'
],
'select' => [
'ID',
'EXTERNAL_AUTH_ID',
'NAME',
'LAST_NAME',
'SECOND_NAME',
'LOGIN',
'EMAIL',
'TITLE',
'UF_DEPARTMENT',
]
]);
while ($user = $orm->fetch())
{
if ($user['ID'] === ($arFields['MEETING_HOST'] ?? null))
{
$user['STATUS'] = 'accepted';
}
else
{
$user['STATUS'] = 'needs_action';
}
$userIndex[$user['ID']] = $user;
}
}
foreach($attendees as $userKey)
{
$clonedParams = $params;
$attendeeId = (int)$userKey;
$isNewAttendee = !empty($clonedParams['currentEvent']['ATTENDEE_LIST'])
&& is_array($clonedParams['currentEvent']['ATTENDEE_LIST'])
&& self::isNewAttendee($clonedParams['currentEvent']['ATTENDEE_LIST'], $attendeeId)
;
$CACHE_MANAGER->ClearByTag('calendar_user_'.$attendeeId);
// Skip creation of child event if it's event inside his own user calendar
if (
$attendeeId
&& (($arFields['CAL_TYPE'] ?? null) !== 'user' || (int)($arFields['OWNER_ID'] ?? null) !== $attendeeId)
)
{
$childParams = $clonedParams;
$childParams['arFields']['CAL_TYPE'] = 'user';
$childParams['arFields']['PARENT_ID'] = $parentId;
$childParams['arFields']['OWNER_ID'] = $attendeeId;
$childParams['arFields']['CREATED_BY'] = $attendeeId;
$childParams['arFields']['CREATED'] = $arFields['DATE_CREATE'] ?? null;
$childParams['arFields']['MODIFIED'] = $arFields['TIMESTAMP_X'] ?? null;
$childParams['arFields']['ACCESSIBILITY'] = $arFields['ACCESSIBILITY'] ?? null;
$childParams['arFields']['MEETING'] = $arFields['~MEETING'] ?? null;
$childParams['arFields']['TEXT_LOCATION'] = CCalendar::GetTextLocation($arFields["LOCATION"] ?? null);
$childParams['arFields']['MEETING_STATUS'] = 'Q';
$childParams['sendInvitations'] = $clonedParams['sendInvitations'] ?? null;
if ((int)$arFields['CREATED_BY'] === $attendeeId)
{
$childParams['arFields']['MEETING_STATUS'] = 'Y';
}
elseif ($isNewEvent && (int)($arFields['~MEETING']['MEETING_CREATOR'] ?? null) === $attendeeId)
{
$childParams['arFields']['MEETING_STATUS'] = 'Y';
}
elseif (
!empty($clonedParams['saveAttendeesStatus'])
&& !empty($clonedParams['currentEvent']['ATTENDEE_LIST'])
&& is_array($clonedParams['currentEvent']['ATTENDEE_LIST'])
)
{
foreach($clonedParams['currentEvent']['ATTENDEE_LIST'] as $currentAttendee)
{
if ((int)$currentAttendee['id'] === $attendeeId)
{
$childParams['arFields']['MEETING_STATUS'] = $currentAttendee['status'];
break;
}
}
}
else
{
$childParams['arFields']['MEETING_STATUS'] = 'Q';
}
unset(
$childParams['arFields']['SECTIONS'],
$childParams['arFields']['SECTION_ID'],
$childParams['currentEvent'],
$childParams['updateReminders'],
$childParams['arFields']['ID'],
$childParams['arFields']['DAV_XML_ID'],
$childParams['arFields']['G_EVENT_ID'],
$childParams['arFields']['SYNC_STATUS']
);
$isExchangeEnabled = CCalendar::IsExchangeEnabled($attendeeId);
if (
$userIndex[$attendeeId]
&& $userIndex[$attendeeId]['EXTERNAL_AUTH_ID'] === 'email'
&& $isNewEvent
&& !$isIncreaseMailLimit
)
{
if (Bitrix24Manager::isEventWithEmailGuestAllowed())
{
Bitrix24Manager::increaseEventWithEmailGuestAmount();
$isIncreaseMailLimit = true;
}
else
{
// Just skip external emil users if they are not allowed
// We will show warning on the client's side
continue;
}
}
if (!empty($currentAttendeesIndex[$attendeeId]))
{
$childParams['arFields']['ID'] = $currentAttendeesIndex[$attendeeId]['EVENT_ID'];
if (empty($arFields['~MEETING']['REINVITE']))
{
$childParams['arFields']['MEETING_STATUS'] = $currentAttendeesIndex[$attendeeId]['STATUS'];
$childParams['sendInvitations'] = $childParams['sendInvitations'] && $currentAttendeesIndex[$attendeeId]['STATUS'] !== 'Q';
}
if (
$clonedParams['sendInvitesToDeclined']
&& $childParams['arFields']['MEETING_STATUS'] === 'N'
)
{
$childParams['arFields']['MEETING_STATUS'] = 'Q';
$childParams['sendInvitations'] = true;
}
if (
($isExchangeEnabled || $isCalDavEnabled)
&& ($childParams['overSaving'] ?? false) !== true
)
{
self::prepareArFieldBeforeSyncEvent($childParams);
$childParams['currentEvent'] = self::GetById($childParams['arFields']['ID'], false);
$davParams = [
'bCalDav' => $isCalDavEnabled,
'bExchange' => $isExchangeEnabled,
'sectionId' => (int)$childParams['currentEvent']['SECTION_ID'],
'modeSync' => $clonedParams['modeSync'],
'editInstance' => $clonedParams['editInstance'],
'originalDavXmlId' => $childParams['currentEvent']['G_EVENT_ID'],
'instanceTz' => $childParams['currentEvent']['TZ_FROM'],
'editParentEvents' => $clonedParams['editParentEvents'],
'editNextEvents' => $clonedParams['editNextEvents'],
'syncCaldav' => $clonedParams['syncCaldav'],
'parentDateFrom' => $childParams['currentEvent']['DATE_FROM'],
'parentDateTo' => $childParams['currentEvent']['DATE_TO'],
];
CCalendarSync::DoSaveToDav($childParams['arFields'], $davParams, $childParams['currentEvent']);
}
}
else
{
$childSectId = CCalendar::GetMeetingSection($attendeeId, true);
if ($childSectId)
{
$childParams['arFields']['SECTIONS'] = [$childSectId];
}
if (empty($childParams['arFields']['DAV_XML_ID']) && !$clonedParams['editInstance'])
{
$childParams['arFields']['DAV_XML_ID'] = self::getUidForChildEvent($childParams['arFields']);
}
$parentEvent = InternalsEventTable::query()
->where('PARENT_ID' , (int)($childParams['arFields']['RECURRENCE_ID'] ?? 0))
->where('OWNER_ID' , (int)($childParams['arFields']['OWNER_ID'] ?? 0))
->setSelect(['*'])
->exec()
->fetch() ?: [];
if ($parentEvent)
{
$childParams['arFields']['DAV_XML_ID'] = $parentEvent['DAV_XML_ID'] ?? null;
}
else
{
unset(
$childParams['arFields']['ORIGINAL_DATE_FROM'],
$childParams['arFields']['RECURRENCE_ID'],
$clonedParams['recursionEditMode']
);
$childParams['arFields']['DAV_XML_ID'] = UidGenerator::createInstance()
->setPortalName(Util::getServerName())
->setDate(new Date(Util::getDateObject(
$childParams['arFields']['DATE_FROM'] ?? null,
false,
($childParams['arFields']['TZ_FROM'] ?? null) ?: null
)))
->setUserId((int)($childParams['arFields']['OWNER_ID'] ?? null))
->getUidWithDate();
}
// CalDav & Exchange
if (
($isExchangeEnabled || $isCalDavEnabled)
&& ($childParams['overSaving'] ?? false) !== true
)
{
$davParams = [
'bCalDav' => $isCalDavEnabled,
'bExchange' => $isExchangeEnabled,
'sectionId' => $childSectId,
'modeSync' => $clonedParams['modeSync'] ?? null,
'editInstance' => $clonedParams['editInstance'] ?? null,
'originalDavXmlId' => $parentEvent['G_EVENT_ID'] ?? null,
'instanceTz' => $parentEvent['TZ_FROM'] ?? null,
'editParentEvents' => $clonedParams['editParentEvents'] ?? null,
'editNextEvents' => $clonedParams['editNextEvents'] ?? null,
'syncCaldav' => $clonedParams['syncCaldav'] ?? null,
'parentDateFrom' => $parentEvent['DATE_FROM'] ?? null,
'parentDateTo' => $parentEvent['DATE_TO'] ?? null,
];
CCalendarSync::DoSaveToDav($childParams['arFields'], $davParams);
}
}
if ($isNewAttendee && !empty($childParams['arFields']['RECURRENCE_ID']))
{
$childParams['arFields']['RECURRENCE_ID'] = '';
}
$curEvent = null;
if (!empty($childParams['arFields']['ID']))
{
$curEvent = self::GetList([
'arFilter' => [
"ID" => (int)$childParams['arFields']['ID'],
"DELETED" => 'N',
],
'checkPermissions' => false,
'parseRecursion' => false,
'fetchAttendees' => true,
'fetchMeetings' => false,
'userId' => $userId,
]);
}
if ($curEvent)
{
$curEvent = $curEvent[0];
}
$id = self::Edit($childParams);
if (
$userIndex[$attendeeId]
&& $userIndex[$attendeeId]['EXTERNAL_AUTH_ID'] === 'email'
&& ((!($clonedParams['fromWebservice'] ?? false)) || !empty($changeFields))
&& !$isPastEvent
&& ($childParams['overSaving'] ?? false) !== true
)
{
if (empty($attaches))
{
$isChangeFiles = false;
$attaches = Helper::getMailAttaches($clonedParams['UF'] ?? null, $arFields['MEETING_HOST'], $parentId, $isChangeFiles);
if ($isChangeFiles)
{
$changeFields[] = [
'fieldKey' => 'FILES',
];
}
}
$sender = self::getSenderForIcal($userIndex, $childParams['arFields']['MEETING_HOST']);
if (empty($sender) || !$sender['ID'])
{
continue;
}
if (!empty($email = self::getSenderEmailForIcal($arFields['MEETING'])) && !self::$isAddIcalFailEmailError)
{
$sender['EMAIL'] = $email;
}
else
{
CCalendar::ThrowError(GetMessage("EC_ICAL_NOTICE_DO_NOT_SET_EMAIL"));
self::$isAddIcalFailEmailError = true;
continue;
}
$additionalChildArFields['ATTACHES'] = $attaches;
$additionalChildArFields['ID'] = $id;
$additionalChildArFields['ICAL_ORGANIZER'] = self::getOrganizerForIcal($userIndex, (int)$childParams['arFields']['MEETING_HOST'], $sender['EMAIL']);
$additionalChildArFields['ICAL_ATTENDEES'] = self::createMailAttendeesCollection(
$userIndex,
$childParams['arFields']['MEETING']['HIDE_GUESTS'] ?? true,
[$attendeeId, $childParams['arFields']['MEETING_HOST']],
$attendees
);
$additionalChildArFields['ICAL_ATTACHES'] = $attaches;
if (count($eventManagersCollection) <= 3)
{
if (!empty($currentAttendeesIndex[$attendeeId]))
{
if (
count($changeFields) !== 1
|| $changeFields[0]['fieldKey'] !== 'ATTENDEES'
|| !$meetingInfo['HIDE_GUESTS']
)
{
$eventManagersCollection[] = SenderEditInvitation::createInstance(
array_merge(
self::prepareChildParamsForIcalInvitation($childParams['arFields']),
$additionalChildArFields
),
IcalMailContext::createInstance(
self::getMailAddresser($sender, $meetingInfo['MAIL_FROM']),
self::getMailReceiver($userIndex[$attendeeId])
)->setChangeFields($changeFields)
);
}
}
else
{
$eventManagersCollection[] = SenderRequestInvitation::createInstance(
array_merge(
self::prepareChildParamsForIcalInvitation($childParams['arFields']),
$additionalChildArFields
),
IcalMailContext::createInstance(
self::getMailAddresser($sender, $meetingInfo['MAIL_FROM']),
self::getMailReceiver($userIndex[$attendeeId])
)
);
}
}
else
{
MailInvitationManager::createAgentSent($eventManagersCollection);
$eventManagersCollection = [];
}
unset($additionalChildArFields);
}
if (
$chatId > 0
&& $chat
&& $isNewAttendee
&& $userIndex[$attendeeId]
&& $userIndex[$attendeeId]['EXTERNAL_AUTH_ID'] !== 'email'
&& $userIndex[$attendeeId]['EXTERNAL_AUTH_ID'] !== 'calendar_sharing'
&& $childParams['arFields']['MEETING_STATUS'] !== 'N'
)
{
$chat->AddUser($chatId, $attendeeId, $hideHistory = true, $skipMessage = false);
}
if ($id)
{
CCalendar::syncChange($id, $childParams['arFields'], $clonedParams, $curEvent);
}
unset($deletedAttendees[$attendeeId]);
}
}
if (!empty($eventManagersCollection))
{
MailInvitationManager::createAgentSent($eventManagersCollection);
$eventManagersCollection = [];
}
// Delete
$delIdStr = '';
if (!$isNewEvent && !empty($deletedAttendees))
{
foreach($deletedAttendees as $attendeeId)
{
if ($chatId > 0 && $chat)
{
$chat->DeleteUser($chatId, $attendeeId, false);
}
$att = $currentAttendeesIndex[$attendeeId];
if (
($params['sendInvitations'] ?? null) !== false
&& ($att['STATUS'] ?? null) === 'Y'
&& !$isPastEvent
)
{
$CACHE_MANAGER->ClearByTag('calendar_user_'.$att["USER_ID"]);
$fromTo = self::GetEventFromToForUser($arFields, $att["USER_ID"]);
CCalendarNotify::Send([
"mode" => 'cancel',
"name" => $arFields['NAME'] ?? null,
"from" => $fromTo['DATE_FROM'] ?? null,
"to" => $fromTo['DATE_TO'] ?? null,
"location" => CCalendar::GetTextLocation($arFields["LOCATION"] ?? null),
"guestId" => $att["USER_ID"] ?? null,
"eventId" => $parentId,
"userId" => $arFields['MEETING_HOST'] ?? null,
"fields" => $arFields
]);
}
//add pull event to update calendar grid after event delete
$pullUserId = (int)$attendeeId;
if ($pullUserId > 0)
{
Util::addPullEvent(
'delete_event',
$pullUserId,
[
'fields' => $arFields,
'requestUid' => $params['userId']
]
);
}
CCalendarNotify::ClearNotifications($arFields['PARENT_ID'], $pullUserId);
$delIdStr .= ','.(int)($att['EVENT_ID'] ?? null);
$currentEvent = self::GetList(
array(
'arFilter' => array(
"PARENT_ID" => $parentId,
"OWNER_ID" => $attendeeId,
"IS_MEETING" => 1,
"DELETED" => "N"
),
'parseRecursion' => false,
'fetchAttendees' => true,
'fetchMeetings' => true,
'checkPermissions' => false,
'setDefaultLimit' => false
)
);
$currentEvent = $currentEvent[0];
$isExchangeEnabled = CCalendar::IsExchangeEnabled($attendeeId);
if (($isExchangeEnabled || $isCalDavEnabled) && $currentEvent)
{
CCalendarSync::DoDeleteToDav([
'bCalDav' => $isCalDavEnabled,
'bExchangeEnabled' => $isExchangeEnabled,
'sectionId' => $currentEvent['SECT_ID'] ?? null
], $currentEvent);
}
if ($currentEvent)
{
self::onEventDelete($currentEvent, $params);
}
if (isset($att['EXTERNAL_AUTH_ID']) && $att['EXTERNAL_AUTH_ID'] === 'email' && !$isPastEvent)
{
$declinedUser = $receiver = $userIndex[$attendeeId];
if (empty($receiver['EMAIL']))
{
continue;
}
$sender = self::getSenderForIcal($currentAttendeesIndex, $arFields['MEETING_HOST']);
if ($email = self::getSenderEmailForIcal($arFields['MEETING']))
{
$sender['EMAIL'] = $email;
}
else
{
$meetingHostSettings = UserSettings::get($arFields['MEETING_HOST']);
$sender['EMAIL'] = $meetingHostSettings['sendFromEmail'];
}
if (empty($sender['ID']) && isset($sender['USER_ID']))
{
$sender['ID'] = (int)$sender['USER_ID'];
}
// $sender = $currentAttendeesIndex[$arFields['MEETING_HOST']];
$declinedUser['STATUS'] = 'declined';
$additionalChildArFields['ICAL_ORGANIZER'] = self::getOrganizerForIcal($currentAttendeesIndex, (int)$arFields['MEETING_HOST'], $sender['EMAIL']);
$additionalChildArFields['ICAL_ATTENDEES'] = self::createMailAttendeesCollection([$declinedUser['ID'] => $declinedUser], false);
if (count($eventManagersCollection) <= 3)
{
$eventManagersCollection[] = SenderCancelInvitation::createInstance(
array_merge(self::prepareChildParamsForIcalInvitation($arFields), $additionalChildArFields),
IcalMailContext::createInstance(self::getMailAddresser($sender, $meetingInfo['MAIL_FROM']), self::getMailReceiver($receiver))
);
}
else
{
MailInvitationManager::createAgentSent($eventManagersCollection);
}
}
}
if (!empty($eventManagersCollection))
{
MailInvitationManager::createAgentSent($eventManagersCollection);
}
}
$delIdStr = trim($delIdStr, ', ');
if ($delIdStr !== '')
{
$strSql =
"UPDATE b_calendar_event SET ".
$DB->PrepareUpdate("b_calendar_event", ["DELETED" => "Y"]).
" WHERE PARENT_ID=". (int)$parentId ." AND ID IN (" . $delIdStr . ")";
$DB->Query($strSql, false, "FILE: ".__FILE__."
LINE: ".__LINE__);
}
if (!empty($involvedAttendees))
{
$involvedAttendees = array_unique($involvedAttendees);
CCalendar::UpdateCounter($involvedAttendees);
}
}