403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/calendar/lib/sharing/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/calendar/lib/sharing/sharingeventmanager.php
<?php

namespace Bitrix\Calendar\Sharing;

use Bitrix\Calendar\Core\Base\Result;
use Bitrix\Calendar\Core\Builders\EventBuilderFromArray;
use Bitrix\Calendar\Core\Event\Event;
use Bitrix\Calendar\Core\Event\Tools\Dictionary;
use Bitrix\Calendar\Core\Mappers;
use Bitrix\Calendar\Integration\Crm\DealHandler;
use Bitrix\Calendar\Internals\EventTable;
use Bitrix\Calendar\Sharing;
use Bitrix\Calendar\Util;
use Bitrix\Crm;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\Loader;
use Bitrix\Main\LoaderException;
use Bitrix\Main\ObjectPropertyException;
use Bitrix\Main\PhoneNumber;
use Bitrix\Main\Error;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\SystemException;
use Bitrix\Main\Type\DateTime;
use CUser;

class SharingEventManager
{
	public const SHARED_EVENT_TYPE = Dictionary::EVENT_TYPE['shared'];
	public const SHARED_EVENT_CRM_TYPE = Dictionary::EVENT_TYPE['shared_crm'];
	public const SHARED_EVENT_COLLAB_TYPE = Dictionary::EVENT_TYPE['shared_collab'];
	/** @var Event  */
	private Event $event;
	/** @var int|null  */
	private ?int $hostId;
	/** @var int|null  */
	private ?int $ownerId;
	/** @var Sharing\Link\CrmDealLink|Sharing\Link\UserLink|Sharing\Link\GroupLink|null $link */
	private ?Sharing\Link\Link $link;

	/**
	 * @param Event $event
	 * @param int|null $hostId
	 * @param int|null $ownerId
	 * @param Sharing\Link\Link|null $link
	 */
	public function __construct(Event $event, ?int $hostId = null, ?int $ownerId = null, ?Sharing\Link\Link $link = null)
	{
		$this->event = $event;
		$this->hostId = $hostId;
		$this->ownerId = $ownerId;
		$this->link = $link;
	}

	/**
	 * @param Event $event
	 * @return $this
	 */
	public function setEvent(Event $event): self
	{
		$this->event = $event;

		return $this;
	}

	/**
	 * @param bool $sendInvitations
	 * @param string $externalUserName
	 * @return Result
	 * @throws ArgumentException
	 */
	public function createEvent(bool $sendInvitations = true, string $externalUserName = ''): Result
	{
		$result = new Result();

		if (!$this->doesEventHasCorrectTime())
		{
			$result->addError(new Error('Incorrect time has given'));

			return $result;
		}

		if (!$this->doesEventSatisfyRule())
		{
			$result->addError(new Error('Event time does not satisfy owner rule'));

			return $result;
		}

		$members = $this->link->getMembers();
		$users = array_map(static fn ($member) => $member->getId(), $members);
		if ($this->link->getObjectType() === Sharing\Link\Helper::GROUP_SHARING_TYPE)
		{
			$users[] = $this->link->getHostId();
		}
		else
		{
			$users[] = $this->link->getOwnerId();
		}

		if (!$this->checkUserAccessibility($users))
		{
			$result->addError(new Error(Loc::getMessage('EC_SHARINGAJAX_USER_BUSY')));

			return $result;
		}

		$eventId = (new Mappers\Event())->create($this->event, [
			'sendInvitations' => $sendInvitations,
		])?->getId();

		$this->event->setId($eventId);

		if (!$eventId)
		{
			$result->addError(new Error(Loc::getMessage('EC_SHARINGAJAX_EVENT_SAVE_ERROR')));

			return $result;
		}

		$eventLinkParams = [
			'eventId' => $eventId,
			'ownerId' => $this->ownerId,
			'hostId' => $this->hostId,
			'parentLinkHash' => $this->link->getHash(),
			'expiryDate' => Helper::createSharingLinkExpireDate(
				DateTime::createFromTimestamp($this->event->getEnd()->getTimestamp()),
				Sharing\Link\Helper::EVENT_SHARING_TYPE
			),
			'externalUserName' => $externalUserName,
		];

		$eventLink = (new Sharing\Link\Factory())->createEventLink($eventLinkParams);

		$result->setData([
			'eventLink' => $eventLink,
			'event' => $this->event,
		]);

		return $result;
	}

	/**
	 * @return Result
	 * @throws \Exception
	 */
	public function deleteEvent(): Result
	{
		$result = new Result();

		(new Mappers\Event())->delete($this->event);
		$this->notifyEventDeleted();

		return $result;
	}

	/**
	 * @return $this
	 * @throws \Exception
	 */
	public function deactivateEventLink(Sharing\Link\EventLink $eventLink): self
	{
		$eventLink
			->setCanceledTimestamp(time())
			->setActive(false)
		;

		(new Sharing\Link\EventLinkMapper())->update($eventLink);

		return $this;
	}

	/**
	 * @param string $userContact
	 * @return bool
	 */
	public static function validateContactData(string $userContact): bool
	{
		return self::isEmailCorrect($userContact)
			|| self::isPhoneNumberCorrect($userContact)
			;
	}

	/**
	 * @param string $userName
	 * @return bool
	 */
	public static function validateContactName(string $userName): bool
	{
		return self::isUserNameCorrect($userName);
	}

	private static function isUserNameCorrect(string $userName): bool
	{
		return $userName !== '';
	}

	public static function isEmailCorrect(string $userContact): bool
	{
		return check_email($userContact);
	}

	public static function isPhoneNumberCorrect(string $userContact): bool
	{
		return Helper::isPhoneFeatureEnabled()
			&& PhoneNumber\Parser::getInstance()->parse($userContact)->isValid()
			;
	}

	/**
	 * @param $data
	 * @param $userId
	 * @return Event
	 */
	public static function prepareEventForSave($data, $userId, Sharing\Link\Joint\JointLink $link): Event
	{
		$meeting = [
			'HOST_NAME' => \CCalendar::GetUserName($userId),
			'NOTIFY' => true,
			'REINVITE' => false,
			'ALLOW_INVITE' => true,
			'MEETING_CREATOR' => $userId,
			'HIDE_GUESTS' => false,
		];

		$eventData = [
			'NAME' => (string)($data['eventName'] ?? ''),
			'DATE_FROM' => (string)($data['dateFrom'] ?? ''),
			'DATE_TO' => (string)($data['dateTo'] ?? ''),
			'TZ_FROM' => (string)($data['timezone'] ?? ''),
			'TZ_TO' => (string)($data['timezone'] ?? ''),
			'SKIP_TIME' => 'N',
			'ACCESSIBILITY' => 'busy',
			'IMPORTANCE' => 'normal',
			'MEETING_HOST' => $userId,
			'IS_MEETING' => true,
			'MEETING' => $meeting,
			'DESCRIPTION' => (string)($data['description'] ?? ''),
		];

		$eventData = array_merge($eventData, self::prepareTypeDependedEventFields($link, (int)$userId));

		return (new EventBuilderFromArray($eventData))->build();
	}

	public static function getEventDataFromRequest($request): array
	{
		return [
			'ownerId' => (int)($request['ownerId'] ?? 0),
			'dateFrom' => (string)($request['dateFrom'] ?? ''),
			'dateTo' => (string)($request['dateTo'] ?? ''),
			'timezone' => (string)($request['timezone'] ?? ''),
			'description' => (string)($request['description'] ?? ''),
		];
	}

	public static function getSharingEventNameByUserId(int $userId): string
	{
		$user = CUser::GetByID($userId)->Fetch();
		$userName = ($user['NAME'] ?? '') . ' ' . ($user['LAST_NAME'] ?? '');

		return self::getSharingEventNameByUserName($userName);
	}

	public static function getSharingEventNameByUserName(?string $userName): string
	{
		if (!empty($userName))
		{
			$result = Loc::getMessage('CALENDAR_SHARING_EVENT_MANAGER_EVENT_NAME', [
				'#GUEST_NAME#' => trim($userName),
			]);
		}
		else
		{
			$result = Loc::getMessage('CALENDAR_SHARING_EVENT_MANAGER_EVENT_NAME_WITHOUT_GUEST');
		}

		return $result;
	}

	public static function getSharingEventNameByDealId(int $dealId): string
	{
		$deal = DealHandler::getDeal($dealId);
		if (!$deal)
		{
			return '';
		}

		return Loc::getMessage('CALENDAR_SHARING_EVENT_MANAGER_EVENT_NAME_DEAL', [
			'#DEAL_NAME#' => trim($deal->getTitle()),
		]);
	}

	/**
	 * @param $request
	 * @return array
	 */
	public static function getCrmEventDataFromRequest($request): array
	{
		return [
			'ownerId' =>(int)($request['ownerId'] ?? 0),
			'dateFrom' => (string)($request['dateFrom'] ?? ''),
			'dateTo' => (string)($request['dateTo'] ?? ''),
			'timezone' => (string)($request['timezone'] ?? ''),
			'description' => (string)($request['description'] ?? ''),
			'eventType' => Dictionary::EVENT_TYPE['shared_crm'],
		];
	}

	/**
	 * @return string[]
	 */
	public static function getSharingEventTypes(): array
	{
		return [
			self::SHARED_EVENT_CRM_TYPE,
			self::SHARED_EVENT_TYPE,
			self::SHARED_EVENT_COLLAB_TYPE,
		];
	}

	/**
	 * @param array $fields
	 * @return void
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	public static function onSharingEventEdit(array $fields): void
	{
		$eventId = $fields['ID'];
		$eventLink = (new Sharing\Link\Factory())->getEventLinkByEventId($eventId);
		if ($eventLink instanceof Sharing\Link\EventLink)
		{
			self::updateEventSharingLink($eventLink, $fields);
		}
	}

	/**
	 * @param int $eventId
	 * @return void
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	public static function setCanceledTimeOnSharedLink(int $eventId): void
	{
		$eventLink = (new Sharing\Link\Factory())->getEventLinkByEventId($eventId);
		if ($eventLink instanceof Sharing\Link\EventLink)
		{
			$eventLink->setCanceledTimestamp(time());
			(new Sharing\Link\EventLinkMapper())->update($eventLink);
		}
	}

	/**
	 * @param int $userId
	 * @param string $currentMeetingStatus
	 * @param array $userEventBeforeChange
	 * @param bool $isAutoAccept
	 *
	 * @return void
	 *
	 * @throws ArgumentException
	 * @throws LoaderException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	public static function onSharingEventMeetingStatusChange(
		int $userId,
		string $currentMeetingStatus,
		array $userEventBeforeChange,
		bool $isAutoAccept = false
	)
	{
		/** @var Sharing\Link\EventLink $eventLink*/
		$eventLink = (new Sharing\Link\Factory())->getEventLinkByEventId((int)$userEventBeforeChange['PARENT_ID']);

		if (!$eventLink)
		{
			return;
		}

		$ownerId = $eventLink->getOwnerId();
		//if not the link owner's event has changed, send notification to link owner
		if ($ownerId !== $userId && !$isAutoAccept)
		{
			self::onSharingEventGuestStatusChange($currentMeetingStatus, $userEventBeforeChange, $eventLink, $userId);
		}
		else if ($userEventBeforeChange['EVENT_TYPE'] === Dictionary::EVENT_TYPE['shared'])
		{
			self::onSharingCommonEventMeetingStatusChange($eventLink, $userId);
		}
		else if ($userEventBeforeChange['EVENT_TYPE'] === Dictionary::EVENT_TYPE['shared_crm'])
		{
			self::onSharingCrmEventStatusChange($currentMeetingStatus, $userEventBeforeChange, $userId, $ownerId);
		}
	}

	private static function onSharingEventGuestStatusChange(
		string $currentMeetingStatus,
		array $event,
		Sharing\Link\EventLink $eventLink,
		int $userId
	): void
	{
		\CCalendarNotify::Send([
			'mode' => $currentMeetingStatus === "Y" ? 'accept' : 'decline',
			'name' => $event['NAME'],
			'from' => $event["DATE_FROM"],
			'to' => $event["DATE_TO"],
			'location' => \CCalendar::GetTextLocation($userEvent["LOCATION"] ?? null),
			'guestId' => $userId,
			'eventId' => $event['PARENT_ID'],
			'userId' => $eventLink->getOwnerId(),
			'fields' => $event,
		]);
	}

	/**
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	private static function onSharingCommonEventMeetingStatusChange(
		Sharing\Link\EventLink $eventLink,
		?int $initiatorId = null,
	): void
	{
		/** @var Event $event */
		$event = (new Mappers\Event())->getById($eventLink->getEventId());

		$host = CUser::GetByID($eventLink->getHostId())->Fetch();
		$email = $host['PERSONAL_MAILBOX'] ?? null;
		$phone = $host['PERSONAL_PHONE'] ?? null;
		$userContact = !empty($email) ? $email : $phone;

		$notificationService = null;
		if ($userContact && self::isEmailCorrect($userContact))
		{
			$notificationService =
				(new Sharing\Notification\Mail())
					->setEventLink($eventLink)
					->setEvent($event)
					->setInitiatorId($initiatorId)
			;
		}

		$notificationService?->notifyAboutMeetingStatus($userContact);
	}

	/**
	 * @throws ArgumentException
	 * @throws LoaderException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	private static function onSharingCrmEventStatusChange(
		string $currentMeetingStatus,
		array $userEventBeforeChange,
		int $userId,
		int $ownerId
	): void
	{
		if (!Loader::includeModule('crm'))
		{
			return;
		}

		$previousMeetingStatus = $userEventBeforeChange['MEETING_STATUS'] ?? null;

		if (
			$currentMeetingStatus === Dictionary::MEETING_STATUS['Yes']
			&& $previousMeetingStatus === Dictionary::MEETING_STATUS['Question']
			&& $userId === $ownerId
		)
		{
			self::onSharingCrmEventConfirmed(
				(int)$userEventBeforeChange['PARENT_ID'],
				$userEventBeforeChange['DATE_FROM'] ?? null,
				$userEventBeforeChange['TZ_FROM'] ?? null,
			);
		}

		if (
			$currentMeetingStatus === Dictionary::MEETING_STATUS['No']
			&& (
				$previousMeetingStatus === Dictionary::MEETING_STATUS['Question']
				|| $previousMeetingStatus === Dictionary::MEETING_STATUS['Yes']
			)
		)
		{
			self::onSharingCrmEventDeclined((int)$userEventBeforeChange['PARENT_ID'], $userId);
		}
	}

	/**
	 * @param int $eventId
	 * @param string|null $dateFrom
	 * @param string|null $timezone
	 * @return void
	 * @throws LoaderException
	 */
	private static function onSharingCrmEventConfirmed(int $eventId, ?string $dateFrom, ?string $timezone): void
	{
		$crmDealLink = self::getCrmDealLink($eventId);

		$activity = \CCrmActivity::GetByCalendarEventId($eventId, false);

		if ($crmDealLink && $activity)
		{
			(new Sharing\Crm\NotifyManager($crmDealLink, Sharing\Crm\NotifyManager::NOTIFY_TYPE_EVENT_CONFIRMED))
				->sendSharedCrmActionsEvent(
					Util::getDateTimestamp($dateFrom, $timezone),
					$activity['ID'],
					\CCrmOwnerType::Activity,
				)
			;
		}
	}

	/**
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	private static function onSharingCrmEventDeclined(int $eventId, ?int $initiatorId = null): void
	{
		$sharingFactory = new Sharing\Link\Factory();

		/** @var Sharing\Link\EventLink $eventLink */
		$eventLink = $sharingFactory->getEventLinkByEventId($eventId);

		/** @var Sharing\Link\CrmDealLink $crmDealLink */
		$crmDealLink = $sharingFactory->getLinkByHash($eventLink->getParentLinkHash());

		/** @var Event $event */
		$event = (new Mappers\Event())->getById($eventId);

		$completeActivityStatus = Sharing\Crm\ActivityManager::STATUS_CANCELED_BY_MANAGER;

		$userId = \CCalendar::GetUserId();
		if ($userId === 0 || $userId === $event->getEventHost()->getId())
		{
			$completeActivityStatus = Sharing\Crm\ActivityManager::STATUS_CANCELED_BY_CLIENT;
		}

		(new Sharing\Crm\ActivityManager($eventId))
			->completeSharedCrmActivity($completeActivityStatus)
		;
		self::setCanceledTimeOnSharedLink($eventId);
		if ($crmDealLink->getContactId() > 0)
		{
			$notificationService =
				Crm\Integration\Calendar\Notification\Manager::getSenderInstance($crmDealLink)
					->setCrmDealLink($crmDealLink)
					->setEventLink($eventLink)
					->setEvent($event)
			;

			if (method_exists($notificationService, 'setInitiatorId'))
			{
				$notificationService->setInitiatorId($initiatorId);
			}

			$notificationService->sendCrmSharingCancelled();
		}
		else
		{
			$email = CUser::GetByID($eventLink->getHostId())->Fetch()['PERSONAL_MAILBOX'] ?? null;
			if (!is_string($email))
			{
				return;
			}

			$eventLink->setCanceledTimestamp(time());

			(new Sharing\Notification\Mail())
				->setEventLink($eventLink)
				->setEvent($event)
				->setInitiatorId($initiatorId)
				->notifyAboutMeetingCancelled($email)
			;
		}

		self::reSaveEventWithoutAttendeesExceptHostAndSharingLinkOwner($eventLink);
	}

	/**
	 * @param int $eventId
	 * @param string $eventType
	 * @param int|null $initiatorId
	 * @return void
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	public static function onSharingEventDeleted(int $eventId, string $eventType, ?int $initiatorId = null): void
	{
		$linkFactory = (new Sharing\Link\Factory());

		/**@var Sharing\Link\EventLink $eventLink */
		$eventLink = $linkFactory->getEventLinkByEventId($eventId);

		if ($eventLink)
		{
			self::setDeclinedStatusOnLinkOwnerEvent($eventLink);

			if ($eventType === Dictionary::EVENT_TYPE['shared'])
			{
				self::onSharingCommonEventDeclined($eventLink, $initiatorId);
			}
			else if ($eventType === Dictionary::EVENT_TYPE['shared_crm'])
			{
				self::onSharingCrmEventDeclined($eventId, $initiatorId);
			}

		}
	}

	/**
	 * @param Link\EventLink $eventLink
	 * @param int|null $initiatorId
	 * @return void
	 * @throws ArgumentException
	 * @throws ObjectPropertyException
	 * @throws SystemException
	 */
	public static function onSharingCommonEventDeclined(
		Sharing\Link\EventLink $eventLink,
		?int $initiatorId = null,
	): void
	{
		self::setCanceledTimeOnSharedLink($eventLink->getEventId());
		/** @var Event $event */
		$event = (new Mappers\Event())->getById($eventLink->getEventId());

		$host = CUser::GetByID($eventLink->getHostId())->Fetch();
		$email = $host['PERSONAL_MAILBOX'] ?? null;
		$phone = $host['PERSONAL_PHONE'] ?? null;
		$userContact = !empty($email) ? $email : $phone;

		$notificationService = null;
		if ($userContact && self::isEmailCorrect($userContact))
		{
			$notificationService =
				(new Sharing\Notification\Mail())
					->setEventLink($eventLink)
					->setEvent($event)
					->setInitiatorId($initiatorId)
			;
		}

		$notificationService?->notifyAboutMeetingCancelled($userContact);
	}

	public static function setDeclinedStatusOnLinkOwnerEvent(Sharing\Link\EventLink $eventLink)
	{
		$userId = \CCalendar::GetUserId();
		if ($userId !== 0 && $userId !== $eventLink->getHostId())
		{
			$ownerId = $eventLink->getOwnerId();
			$event = EventTable::query()
				->setSelect(['ID'])
				->where('PARENT_ID', $eventLink->getEventId())
				->whereIn('EVENT_TYPE', self::getSharingEventTypes())
				->where('OWNER_ID', $ownerId)
				->exec()
				->fetch()
			;
			if ($event['ID'] ?? false)
			{
				EventTable::update((int)$event['ID'], ['MEETING_STATUS' => Dictionary::MEETING_STATUS['No']]);
			}
		}
	}

	/**
	 * @param Link\EventLink $eventLink
	 * @param array $fields
	 * @return void
	 */
	private static function updateEventSharingLink(Sharing\Link\EventLink $eventLink, array $fields): void
	{
		if (!empty($fields['DATE_TO']))
		{
			$expireDate = Helper::createSharingLinkExpireDate(
				DateTime::createFromText($fields['DATE_TO']),
				Sharing\Link\Helper::EVENT_SHARING_TYPE
			);
			$eventLink->setDateExpire($expireDate);
		}

		(new Sharing\Link\EventLinkMapper())->update($eventLink);
	}

	/**
	 * @param int $eventId
	 * @return Link\CrmDealLink|null
	 */
	private static function getCrmDealLink(int $eventId): ?Link\CrmDealLink
	{
		$sharingLinkFactory = new Sharing\Link\Factory();
		/** @var Sharing\Link\EventLink $eventLink */
		$eventLink = $sharingLinkFactory->getEventLinkByEventId($eventId);
		if ($eventLink instanceof Sharing\Link\EventLink)
		{
			/** @var Sharing\Link\CrmDealLink $crmDealLink */
			$crmDealLink = $sharingLinkFactory->getLinkByHash($eventLink->getParentLinkHash());
			if ($crmDealLink instanceof Sharing\Link\CrmDealLink)
			{
				return $crmDealLink;
			}
		}

		return null;
	}

	private function doesEventHasCorrectTime(): bool
	{
		$start = new DateTime($this->event->getStart()->toString());
		$end = new DateTime($this->event->getEnd()->toString());

		$offset = $this->getOffset();
		$fromTs = Util::getDateTimestampUtc($start, $this->event->getStartTimeZone());
		$toTs = Util::getDateTimestampUtc($end, $this->event->getEndTimeZone());

		if ($fromTs < time())
		{
			return false;
		}

		$ownerDate = new \DateTime('now', new \DateTimeZone('UTC'));

		$holidays = $this->getYearHolidays();
		$intersectedHolidays = array_filter($holidays, static fn($holiday) => in_array($holiday, [
			$ownerDate->setTimestamp($fromTs + $offset)->format('j.m'),
			$ownerDate->setTimestamp($toTs + $offset)->format('j.m'),
		], true));

		if (!empty($intersectedHolidays))
		{
			return false;
		}

		return true;
	}

	private function getYearHolidays(): array
	{
		return explode(',', \COption::GetOptionString('calendar', 'year_holidays', Loc::getMessage('EC_YEAR_HOLIDAYS_DEFAULT')));
	}

	private function doesEventSatisfyRule(): bool
	{
		$start = new DateTime($this->event->getStart()->toString(), null, new \DateTimeZone('UTC'));
		$end = new DateTime($this->event->getEnd()->toString(), null, new \DateTimeZone('UTC'));

		$rule = $this->link->getSharingRule();
		$eventDurationMinutes = ($end->getTimestamp() - $start->getTimestamp()) / 60;
		if ($eventDurationMinutes !== $rule->getSlotSize())
		{
			return false;
		}

		$availableTime = [];
		foreach ($rule->getRanges() as $range)
		{
			foreach ($range->getWeekdays() as $weekday)
			{
				$availableTime[$weekday] ??= [];
				$availableTime[$weekday][] = [
					'from' => $range->getFrom(),
					'to' => $range->getTo(),
				];

				[$intersected, $notIntersected] = $this->separate(fn($interval) => Util::doIntervalsIntersect(
					$interval['from'],
					$interval['to'],
					$range->getFrom(),
					$range->getTo(),
				), $availableTime[$weekday]);

				if (!empty($intersected))
				{
					$from = min(array_column($intersected, 'from'));
					$to = max(array_column($intersected, 'to'));

					$availableTime[$weekday] = [...$notIntersected, [
						'from' => $from,
						'to' => $to,
					]];
				}
			}
		}


		$eventOffset = Util::getTimezoneOffsetUTC($this->event->getStartTimeZone()?->getTimeZone()->getName());
		$userOffset = $this->getOffset();

		// Calculated as time of event in owner timezone
		$fromTs = $start->getTimestamp() - ($eventOffset - $userOffset);
		$toTs = $end->getTimestamp() - ($eventOffset - $userOffset);

		//Minutes and range are in owner time so we can check them
		$minutesFrom = ($fromTs % 86400) / 60;
		$minutesTo = ($toTs % 86400) / 60;
		$weekday = (int)gmdate('N', $fromTs) % 7;

		foreach ($availableTime[$weekday] as $range)
		{
			if ($minutesFrom >= $range['from'] && $minutesTo <= $range['to'])
			{
				return true;
			}
		}

		return false;
	}

	private function separate($take, $array): array
	{
		return array_reduce($array, fn($s, $e) => $take($e) ? [[...$s[0], $e], $s[1]] : [$s[0], [...$s[1], $e]], [[], []]);
	}

	/**
	 * @return bool
	 */
	private function checkUserAccessibility(array $userIds): bool
	{
		$start = new DateTime($this->event->getStart()->toString());
		$end = new DateTime($this->event->getEnd()->toString());
		$fromTs = Util::getDateTimestampUtc($start, $this->event->getStartTimeZone());
		$toTs = Util::getDateTimestampUtc($end, $this->event->getEndTimeZone());

		return (new SharingAccessibilityManager([
			'userIds' => $userIds,
			'timestampFrom' => $fromTs,
			'timestampTo' => $toTs,
		]))->checkUsersAccessibility();
	}

	/**
	 * @param $userId
	 * @return mixed
	 */
	private static function getSectionId($userId)
	{
		$result = \CCalendarSect::GetList([
			'arFilter' => [
				'OWNER_ID' => $userId,
				'CAL_TYPE' => 'user',
				'ACTIVE' => 'Y',
			],
		]);

		if (!$result)
		{
			$createdSection = \CCalendarSect::CreateDefault([
				'type' => 'user',
				'ownerId' => $userId,
			]);
			$result[] = $createdSection;
		}

		return $result[0]['ID'];
	}

	/**
	 * @return false|null
	 */
	private function notifyEventDeleted()
	{
		return \CCalendarNotify::Send([
			'mode' => 'cancel_sharing',
			'userId' => $this->hostId,
			'guestId' => $this->ownerId,
			'eventId' => $this->event->getId(),
			'from' => $this->event->getStart()->toString(),
			'to' => $this->event->getEnd()->toString(),
			'name' => $this->event->getName(),
			'isSharing' => true,
		]);
	}

	public static function reSaveEventWithoutAttendeesExceptHostAndSharingLinkOwner(Sharing\Link\EventLink $eventLink): void
	{
		$event = (new Mappers\Event)->getById($eventLink->getEventId());
		if ($event)
		{
			$event = \CCalendarEvent::GetList([
				'arFilter' => [
					'ID' => $event->getId(),
				],
				'fetchAttendees' => true,
				'checkPermissions' => false,
				'parseRecursion' => false,
				'setDefaultLimit' => false,
				'limit' => null,
			]);

			$event = $event[0] ?? null;
			if ($event)
			{
				$event['ATTENDEES'] = [$eventLink->getOwnerId(), $eventLink->getHostId()];
				$event['ATTENDEES_CODES'] = ['U' . $eventLink->getOwnerId(), 'U' . $eventLink->getHostId()];
				\CCalendar::SaveEvent([
					'arFields' => $event,
					'userId' => $eventLink->getOwnerId(),
					'checkPermission' => false,
					'sendInvitations' => true,
				]);
			}
		}
	}

	private static function prepareTypeDependedEventFields(
		Sharing\Link\Link $link,
		int $userId
	): array
	{
		return match ($link->getObjectType())
		{
			Sharing\Link\Helper::GROUP_SHARING_TYPE => self::prepareGroupSharingEventFields($link, $userId),
			default => self::prepareUserSharingEventFields($link, $userId),
		};
	}

	private static function prepareGroupSharingEventFields(Sharing\Link\Link $link, int $userId): array
	{
		$groupId = $link->getOwnerId();
		$section = \CCalendarSect::GetList([
			'arFilter' => [
				'OWNER_ID' => $groupId,
				'CAL_TYPE' => Dictionary::CALENDAR_TYPE['group'],
				'ACTIVE' => 'Y',
			],
			'checkPermissions' => false,
			'getPermissions' => false,
		]);

		$attendeesCodes = ['U' . $userId];
		$members = $link->getMembers();

		if (!$members)
		{
			$attendeesCodes[] = 'U' . $link->getHostId();
		}
		else
		{
			foreach ($members as $member)
			{
				$attendeesCodes[] = 'U' . $member->getId();
			}
		}

		return [
			'OWNER_ID' => $link->getHostId(),
			'SECTIONS' => [$section[0]['ID']],
			'ATTENDEES_CODES' => $attendeesCodes,
			'EVENT_TYPE' => Dictionary::EVENT_TYPE['shared_collab'],
		];
	}

	private static function prepareUserSharingEventFields(Sharing\Link\Link $link, int $userId): array
	{
		$sectionId = self::getSectionId($userId);

		$ownerId = $link->getOwnerId();
		$attendeesCodes = ['U' . $userId, 'U' . $ownerId];
		$members = $link->getMembers();

		foreach ($members as $member)
		{
			$attendeesCodes[] = 'U' . $member->getId();
		}

		return [
			'OWNER_ID' => $userId,
			'SECTIONS' => [$sectionId],
			'ATTENDEES_CODES' => $attendeesCodes,
			'EVENT_TYPE' => $link instanceof Sharing\Link\CrmDealLink
				? Dictionary::EVENT_TYPE['shared_crm']
				: Dictionary::EVENT_TYPE['shared']
			,
		];
	}

	private function getOffset(): int
	{
		return Util::getTimezoneOffsetUTC(\CCalendar::GetUserTimezoneName($this->ownerId));
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit