• Модуль: calendar
  • Путь к файлу: ~/bitrix/modules/calendar/classes/general/calendar_event.php
  • Класс: CCalendarEvent
  • Вызов: CCalendarEvent::CreateChildEvents
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); } }