static function Edit($params = [])
{
global $DB, $CACHE_MANAGER;
$entryFields = $params['arFields'] ?? [];
$arAffectedSections = [];
$entryChanges = [];
$sendInvitations = ($params['sendInvitations'] ?? null) !== false;
$sendEditNotification = ($params['sendEditNotification'] ?? null) !== false;
$checkLocationOccupancy = ($params['checkLocationOccupancy'] ?? null) === true;
$checkLocationOccupancyFields = $params['checkLocationOccupancyFields'] ?? null;
$result = false;
// Get current user id
$userId = (isset($params['userId']) && (int)$params['userId'] > 0)
? (int)$params['userId']
: CCalendar::GetCurUserId();
if (!$userId && isset($entryFields['CREATED_BY']))
{
$userId = (int)$entryFields['CREATED_BY'];
}
$isNewEvent = !isset($entryFields['ID']) || !$entryFields['ID'];
$entryFields['TIMESTAMP_X'] = CCalendar::Date(time(), true, false);
// Current event
$currentEvent = [];
if (!empty($entryFields['IS_MEETING']) && !isset($entryFields['ATTENDEES']) && isset($entryFields['ATTENDEES_CODES']))
{
$entryFields['ATTENDEES'] = CCalendar::getDestinationUsers($entryFields['ATTENDEES_CODES']);
}
if (!$isNewEvent)
{
$currentEvent = $params['currentEvent'] ?? self::GetById($entryFields['ID'], $params['checkPermission'] ?? true);
if (!isset($entryFields['LOCATION']) || !is_array($entryFields['LOCATION']))
{
$entryFields['LOCATION'] = [
'NEW' => $entryFields['LOCATION'] ?? null
];
}
if (
isset($entryFields['MEETING'])
&& is_array($entryFields['MEETING'])
&& is_array($currentEvent['MEETING'])
&& !isset($entryFields['MEETING']['CHAT_ID'])
&& isset($currentEvent['MEETING']['CHAT_ID'])
)
{
$entryFields['MEETING']['CHAT_ID'] = $currentEvent['MEETING']['CHAT_ID'];
}
if (empty($entryFields['LOCATION']['OLD']))
{
$entryFields['LOCATION']['OLD'] = $currentEvent['LOCATION'] ?? null;
}
if (
!empty($currentEvent['IS_MEETING']) && !isset($entryFields['ATTENDEES'])
&& $currentEvent['PARENT_ID'] === $currentEvent['ID']
&& !empty($entryFields['IS_MEETING'])
)
{
$entryFields['ATTENDEES'] = [];
$attendees = self::GetAttendees($currentEvent['PARENT_ID']);
if (!empty($attendees[$currentEvent['PARENT_ID']]))
{
$attendeesCount = count($attendees[$currentEvent['PARENT_ID']]);
for ($i = 0; $i < $attendeesCount; $i++)
{
$entryFields['ATTENDEES'][] = $attendees[$currentEvent['PARENT_ID']][$i]['USER_ID'];
}
}
}
if (!empty($currentEvent['PARENT_ID']))
{
$entryFields['PARENT_ID'] = (int)$currentEvent['PARENT_ID'];
}
}
if (self::CheckFields($entryFields, $currentEvent, $userId))
{
$attendees = (isset($entryFields['ATTENDEES']) && is_array($entryFields['ATTENDEES']))
? $entryFields['ATTENDEES']
: [];
if (
($entryFields['CAL_TYPE'] ?? null) !== RoomsManager::TYPE
&& (empty($entryFields['PARENT_ID']) || $entryFields['PARENT_ID'] === $entryFields['ID'])
)
{
$fromTs = $entryFields['DATE_FROM_TS_UTC'] ?? null;
$toTs = $entryFields['DATE_TO_TS_UTC'] ?? null;
if (($entryFields['DT_SKIP_TIME'] ?? null) !== "Y")
{
$fromTs += (int)date('Z', $fromTs);
$toTs += (int)date('Z', $toTs);
}
$entryFields['LOCATION'] = self::checkLocationField($entryFields['LOCATION'] ?? null, $isNewEvent);
if ($checkLocationOccupancy)
{
$fieldsToCheckOccupancy = $entryFields;
if (!empty($params['checkLocationOccupancyFields']))
{
$fieldsToCheckOccupancy = $params['checkLocationOccupancyFields'];
$fieldsToCheckOccupancy['LOCATION'] = [
'NEW' => $checkLocationOccupancyFields['LOCATION'] ?? ''
];
self::CheckFields($fieldsToCheckOccupancy, $currentEvent, $userId);
}
$occupancyCheckResult = (new RoomsOccupancyChecker())->check($fieldsToCheckOccupancy);
if (!$occupancyCheckResult->isSuccess())
{
$disturbingEventsFormatted = $occupancyCheckResult->getData()['disturbingEventsFormatted'];
if ($occupancyCheckResult->getData()['isDisturbingEventsAmountOverShowLimit'])
{
$message = Loc::getMessage(
'EC_LOCATION_REPEAT_BUSY_TOO_MANY',
['#DATES#' => $disturbingEventsFormatted]
);
}
else
{
$message = Loc::getMessage(
'EC_LOCATION_REPEAT_BUSY',
['#DATES#' => $disturbingEventsFormatted]
);
}
throw new RoomsOccupancyCheckerException($message);
}
}
$entryFields['LOCATION'] = BitrixCalendarRoomsUtil::setLocation(
$entryFields['LOCATION']['OLD'],
$entryFields['LOCATION']['NEW'],
[
// UTC timestamp + date('Z', $timestamp) /*offset of the server*/
'dateFrom' => CCalendar::Date($fromTs, $entryFields['DT_SKIP_TIME'] !== "Y"),
'dateTo' => CCalendar::Date($toTs, $entryFields['DT_SKIP_TIME'] !== "Y"),
'parentParams' => $params,
'name' => $entryFields['NAME'],
'persons' => count($attendees),
'attendees' => $attendees,
'bRecreateReserveMeetings' => ($entryFields['LOCATION']['RE_RESERVE'] ?? null) !== 'N',
'checkPermission' => $params['checkPermission'] ?? null,
]
);
}
else
{
$entryFields['LOCATION'] = self::checkLocationField($entryFields['LOCATION'], $isNewEvent);
$entryFields['LOCATION'] = $entryFields['LOCATION']['NEW'];
}
// Section
if (isset($entryFields['SECTION_ID']))
{
$sectionId = (int)$entryFields['SECTION_ID'];
}
else
{
$sectionId = !empty($entryFields['SECTIONS'][0])
? (int)$entryFields['SECTIONS'][0]
: false;
}
if (!$sectionId)
{
// It's new event we have to find section where to put it automatically
if ($isNewEvent)
{
if (
!empty($entryFields['IS_MEETING'])
&& !empty($entryFields['PARENT_ID'])
&& ($entryFields['CAL_TYPE'] ?? null) === 'user'
)
{
$sectionId = CCalendar::GetMeetingSection($entryFields['OWNER_ID'] ?? null);
}
else
{
$sectionId = CCalendarSect::GetLastUsedSection(
$entryFields['CAL_TYPE'] ?? null,
$entryFields['OWNER_ID'] ?? null,
$userId);
}
if ($sectionId)
{
$res = CCalendarSect::GetList([
'arFilter' => [
'CAL_TYPE' => $entryFields['CAL_TYPE'] ?? null,
'OWNER_ID' => $entryFields['OWNER_ID'] ?? null,
'ID'=> $sectionId
]
]);
if (empty($res[0]))
{
$sectionId = false;
}
}
else
{
$sectionId = false;
}
if (empty($sectionId))
{
$sectRes = CCalendarSect::GetSectionForOwner($entryFields['CAL_TYPE'], $entryFields['OWNER_ID'], true);
$sectionId = $sectRes['sectionId'];
}
}
else
{
$sectionId = $currentEvent['SECTION_ID'] ?? $currentEvent['SECT_ID'];
}
}
$entryFields['SECTION_ID'] = $sectionId;
$arAffectedSections[] = $sectionId;
$section = CCalendarSect::GetList(['arFilter' => ['ID' => $sectionId],
'checkPermissions' => false,
'getPermissions' => false
])[0] ?? null;
// Here we take type and owner parameters from section data
if ($section)
{
$entryFields['CAL_TYPE'] = $section['CAL_TYPE'];
$entryFields['OWNER_ID'] = $section['OWNER_ID'] ?? '';
}
if (($entryFields['CAL_TYPE'] ?? null) === 'user')
{
$CACHE_MANAGER->ClearByTag('calendar_user_'.$entryFields['OWNER_ID']);
}
if ($isNewEvent)
{
if (!isset($entryFields['CREATED_BY']))
{
$entryFields['CREATED_BY'] = (
!empty($entryFields['IS_MEETING'])
&& ($entryFields['CAL_TYPE'] ?? null) === 'user'
&& !empty($entryFields['OWNER_ID'])
) ? $entryFields['OWNER_ID'] : $userId;
}
if (!isset($entryFields['DATE_CREATE']))
{
$entryFields['DATE_CREATE'] = $entryFields['TIMESTAMP_X'];
}
}
else
{
$arAffectedSections[] = $currentEvent['SECTION_ID'] ?? $currentEvent['SECT_ID'];
}
if (
!isset($entryFields['IS_MEETING'])
&& isset($entryFields['ATTENDEES'])
&& is_array($entryFields['ATTENDEES'])
&& empty($entryFields['ATTENDEES'])
)
{
$entryFields['IS_MEETING'] = false;
}
if (!empty($entryFields['IS_MEETING']) && !$isNewEvent)
{
$entryChanges = self::CheckEntryChanges($entryFields, $currentEvent);
}
$attendeesCodes = $entryFields['ATTENDEES_CODES'] ?? null;
if (is_array($attendeesCodes))
{
$entryFields['ATTENDEES_CODES'] = implode(',', $attendeesCodes);
}
if (
!isset($entryFields['MEETING_STATUS'])
&& !empty($entryFields['MEETING_HOST'])
&& (int)$entryFields['MEETING_HOST'] === (int)($entryFields['CREATED_BY'] ?? null)
)
{
$entryFields['MEETING_STATUS'] = 'H';
}
else if (!isset($entryFields['MEETING_STATUS']) && !$currentEvent)
{
$entryFields['MEETING_STATUS'] = 'Y';
}
if (isset($entryFields['MEETING']) && is_array($entryFields['MEETING']))
{
$entryFields['~MEETING'] = $entryFields['MEETING'];
$entryFields['MEETING']['REINVITE'] = false;
$meetingHostSettings = UserSettings::get($entryFields['MEETING_HOST'] ?? null);
$entryFields['MEETING']['MAIL_FROM'] = $meetingHostSettings['sendFromEmail'] ?? null;
$entryFields['MEETING'] = serialize($entryFields['MEETING']);
}
if (isset($entryFields['RELATIONS']) && is_array($entryFields['RELATIONS']))
{
$entryFields['~RELATIONS'] = $entryFields['RELATIONS'];
$entryFields['RELATIONS'] = serialize($entryFields['RELATIONS']);
}
if (
isset($entryFields['REMIND'])
&& (
$isNewEvent
|| !$entryFields['IS_MEETING']
|| (int)$entryFields['CREATED_BY'] === $userId
|| ($params['updateReminders'] ?? null) === true
)
)
{
$reminderList = CCalendarReminder::prepareReminder($entryFields['REMIND']);
}
elseif (!empty($currentEvent['REMIND']))
{
$reminderList = CCalendarReminder::prepareReminder($currentEvent['REMIND']);
}
else
{
$reminderList = [];
}
$entryFields['REMIND'] = serialize($reminderList);
if (
isset($entryFields['SYNC_STATUS'])
&& !in_array($entryFields['SYNC_STATUS'],BitrixCalendarSyncGoogleDictionary::SYNC_STATUS, true)
)
{
$entryFields['SYNC_STATUS'] = null;
}
if (isset($entryFields['EXDATE']) && is_array($entryFields['EXDATE']))
{
$entryFields['EXDATE'] = implode(';', $entryFields['EXDATE']);
}
$entryFields['EXDATE'] = !empty($entryFields['EXDATE'])
? self::convertExDatesToInternalFormat($entryFields['EXDATE'])
: ''
;
$entryFields['RRULE'] = self::convertRuleUntilToInternalFormat($entryFields['RRULE'] ?? null);
$AllFields = self::GetFields();
$dbFields = [];
foreach($entryFields as $field => $val)
{
if (
isset($AllFields[$field])
&& $field !== "ID"
&& is_scalar($val)
)
{
$dbFields[$field] = $val;
}
}
if (!empty($dbFields['NAME']))
{
$dbFields['NAME'] = Emoji::encode($dbFields['NAME']);
}
if (!empty($dbFields['DESCRIPTION']))
{
$dbFields['DESCRIPTION'] = Emoji::encode($dbFields['DESCRIPTION']);
}
if (!empty($dbFields['LOCATION']))
{
$dbFields['LOCATION'] = Emoji::encode($dbFields['LOCATION']);
}
CTimeZone::Disable();
if ($isNewEvent) // Add
{
$eventId = $DB->Add("b_calendar_event", $dbFields, ['DESCRIPTION', 'MEETING', 'EXDATE']);
}
else // Update
{
$eventId = $entryFields['ID'];
$strUpdate = $DB->PrepareUpdate("b_calendar_event", $dbFields);
$strSql =
"UPDATE b_calendar_event SET ".
$strUpdate.
" WHERE ID=". (int)$eventId;
$DB->QueryBind($strSql, array(
'DESCRIPTION' => Emoji::encode($entryFields['DESCRIPTION'] ?? ''),
'MEETING' => $entryFields['MEETING'] ?? null,
'EXDATE' => $entryFields['EXDATE'] ?? null
));
}
CTimeZone::Enable();
if (
$userId
&& $params
&& ($params['overSaving'] ?? null) !== true
&& BitrixCalendarSyncUtilRequestLogger::isEnabled()
)
{
$loggerParams = $params;
$loggerParams['arFields'] = $entryFields;
$loggerParams['loggerUuid'] = $eventId;
(new BitrixCalendarSyncUtilRequestLogger($userId, 'portal_edit'))->write($loggerParams);
}
if ($isNewEvent && !isset($dbFields['DAV_XML_ID']))
{
$strSql =
"UPDATE b_calendar_event SET ".
$DB->PrepareUpdate("b_calendar_event", ['DAV_XML_ID' => (int)$eventId]).
" WHERE ID = ". (int)$eventId;
$DB->Query($strSql, false, "File: ".__FILE__."
Line: ".__LINE__);
}
// Deprecated. Now connection saved in the table
if (
!Util::isSectionStructureConverted() &&
($isNewEvent || $sectionId !== $currentEvent['SECTION_ID']))
{
self::ConnectEventToSection($eventId, $sectionId);
}
if (!empty($arAffectedSections))
{
CCalendarSect::UpdateModificationLabel($arAffectedSections);
}
if (
!empty($entryFields['IS_MEETING'])
|| (!$isNewEvent && !empty($currentEvent['IS_MEETING']))
)
{
if (empty($entryFields['PARENT_ID']))
{
$DB->Query("UPDATE b_calendar_event SET ".$DB->PrepareUpdate("b_calendar_event", array("PARENT_ID" => $eventId))." WHERE ID=". (int)$eventId, false, "FILE: ".__FILE__."
LINE: ".__LINE__);
}
$mailEvent = ICalUtil::isMailUser($entryFields['MEETING_HOST'] ?? null);
if ((empty($entryFields['PARENT_ID']) || $entryFields['PARENT_ID'] === $eventId) && !$mailEvent)
{
self::CreateChildEvents($eventId, $entryFields, $params, $entryChanges);
}
if ((empty($entryFields['PARENT_ID']) || $entryFields['PARENT_ID'] === $eventId) && !empty($entryFields['RECURRENCE_ID']))
{
self::UpdateParentEventExDate($entryFields['RECURRENCE_ID'], $entryFields['ORIGINAL_DATE_FROM'], $entryFields['ATTENDEES']);
}
if (empty($entryFields['PARENT_ID']))
{
$entryFields['PARENT_ID'] = (int)$eventId;
}
}
else if (($isNewEvent && empty($entryFields['PARENT_ID'])) || (!$isNewEvent && empty($currentEvent['PARENT_ID'])))
{
$DB->Query("UPDATE b_calendar_event SET ".$DB->PrepareUpdate("b_calendar_event", array("PARENT_ID" => $eventId))." WHERE ID=".intval($eventId), false, "FILE: ".__FILE__."
LINE: ".__LINE__);
if (empty($entryFields['PARENT_ID']))
{
$entryFields['PARENT_ID'] = (int)$eventId;
}
}
// Update reminders for event
CCalendarReminder::updateReminders([
'id' => $eventId,
'reminders' => $reminderList,
'arFields' => $entryFields,
'userId' => $userId,
'path' => $params['path'] ?? null
]);
// Update search index
self::updateSearchIndex($eventId, ['userId' => $userId]);
$nowUtc = time() - date('Z');
// Send invitations and notifications
if (
!empty($entryFields['IS_MEETING'])
&& ($params['overSaving'] ?? null) !== true
)
{
$fromTo = self::GetEventFromToForUser($entryFields, $entryFields['OWNER_ID'] ?? null);
// If it's event in the past we're skipping notifications.
// The past is the past...
if (isset($entryFields['DATE_TO_TS_UTC']) && $entryFields['DATE_TO_TS_UTC'] > $nowUtc)
{
if (
$sendEditNotification
&& (int)($entryFields['PARENT_ID'] ?? null) !== (int)$eventId
&& !empty($entryChanges)
&& (
($entryFields['MEETING_STATUS'] ?? null) === 'Y'
|| ($entryFields['MEETING_STATUS'] ?? null) === 'H'
)
)
{
// third problematic place
if (
(!empty($entryFields['MEETING_HOST']) && (int)$entryFields['MEETING_HOST'] === (int)$userId)
|| self::checkAttendeeBelongsToEvent($entryFields['PARENT_ID'] ?? null, $userId)
)
{
$CACHE_MANAGER->ClearByTag('calendar_user_'.$entryFields['OWNER_ID']);
CCalendarNotify::Send([
'mode' => 'change_notify',
'name' => $entryFields['NAME'] ?? null,
"from" => $fromTo['DATE_FROM'] ?? null,
"to" => $fromTo['DATE_TO'] ?? null,
"location" => CCalendar::GetTextLocation($entryFields["LOCATION"] ?? null),
"guestId" => $entryFields['OWNER_ID'] ?? null,
"eventId" => $entryFields['PARENT_ID'] ?? null,
"userId" => $userId,
"fields" => $entryFields,
"isSharing" => ($entryFields['EVENT_TYPE'] ?? null) === Dictionary::EVENT_TYPE['shared'],
"entryChanges" => $entryChanges
]);
}
}
elseif (
(int)($entryFields['PARENT_ID'] ?? null) !== $eventId
&& ($entryFields['MEETING_STATUS'] ?? null) === 'Q'
&& $sendInvitations
)
{
$CACHE_MANAGER->ClearByTag('calendar_user_'.$entryFields['OWNER_ID'] ?? '');
CCalendarNotify::Send(array(
"mode" => 'invite',
"name" => $entryFields['NAME'] ?? null,
"from" => $fromTo['DATE_FROM'] ?? null,
"to" => $fromTo['DATE_TO'] ?? null,
"location" => CCalendar::GetTextLocation($entryFields["LOCATION"] ?? null),
"guestId" => $entryFields['OWNER_ID'] ?? null,
"eventId" => $entryFields['PARENT_ID'] ?? null,
"userId" => $userId,
"isSharing" => ($entryFields['EVENT_TYPE'] ?? null) === Dictionary::EVENT_TYPE['shared'],
"fields" => $entryFields
));
}
}
}
if (
!empty($entryFields['IS_MEETING'])
&& !empty($entryFields['ATTENDEES_CODES'])
&& (int)($entryFields['PARENT_ID'] ?? null) === (int)$eventId
&& ($params['overSaving'] ?? null) !== true
&& isset($entryFields['DATE_TO_TS_UTC'])
&& $entryFields['DATE_TO_TS_UTC'] > $nowUtc
)
{
CCalendarLiveFeed::OnEditCalendarEventEntry([
'eventId' => $eventId,
'arFields' => $entryFields,
'attendeesCodes' => $attendeesCodes
]);
}
CCalendar::ClearCache('event_list');
$result = $eventId;
if (!empty($entryFields['LOCATION']))
{
RoomsManager::setEventIdForLocation($eventId);
}
if ($isNewEvent)
{
foreach(EventManager::getInstance()->findEventHandlers("calendar", "OnAfterCalendarEntryAdd") as $event)
{
ExecuteModuleEventEx(
$event,
[
$eventId,
$entryFields,
[]
]
);
}
if (($entryFields['PARENT_ID'] ?? null) === $eventId && $entryFields['CAL_TYPE'] !== 'location')
{
Bitrix24Manager::increaseEventsAmount();
}
}
else
{
foreach(EventManager::getInstance()->findEventHandlers("calendar", "OnAfterCalendarEntryUpdate") as $event)
{
ExecuteModuleEventEx(
$event,
[
$eventId,
$entryFields,
$currentEvent['ATTENDEE_LIST'] ?? null
]
);
}
}
$pullUserId = (isset($entryFields['CREATED_BY']) && (int)($entryFields['CREATED_BY'] ?? null) > 0)
? (int)$entryFields['CREATED_BY'] : $userId;
if (
$pullUserId > 0
&& ($params['overSaving'] ?? null) !== true
)
{
$entryFields = self::calculateUserOffset($pullUserId, $entryFields);
Util::addPullEvent(
'edit_event',
$pullUserId,
[
'fields' => $entryFields,
'newEvent' => $isNewEvent,
'requestUid' => $params['requestUid'] ?? null
]
);
}
}
return $result;
}