36 private ?
int $ownerId;
48 $this->
event = $event;
49 $this->hostId = $hostId;
50 $this->ownerId = $ownerId;
60 $this->
event = $event;
71 public function createEvent(
bool $sendInvitations =
true,
string $externalUserName =
''):
Result
75 if (!$this->doesEventHasCorrectTime())
77 $result->addError(
new Error(
'Incorrect time has given'));
82 if (!$this->doesEventSatisfyRule())
84 $result->addError(
new Error(
'Event time does not satisfy owner rule'));
89 $members = $this->link->getMembers();
90 $users = array_map(
static fn ($member) => $member->getId(), $members);
91 if ($this->link->getObjectType() ===
Sharing\
Link\Helper::GROUP_SHARING_TYPE)
93 $users[] = $this->link->getHostId();
97 $users[] = $this->link->getOwnerId();
100 if (!$this->checkUserAccessibility($users))
102 $result->addError(
new Error(Loc::getMessage(
'EC_SHARINGAJAX_USER_BUSY')));
108 'sendInvitations' => $sendInvitations,
111 $this->
event->setId($eventId);
115 $result->addError(
new Error(Loc::getMessage(
'EC_SHARINGAJAX_EVENT_SAVE_ERROR')));
121 'eventId' => $eventId,
122 'ownerId' => $this->ownerId,
123 'hostId' => $this->hostId,
124 'parentLinkHash' => $this->link->getHash(),
125 'expiryDate' => Helper::createSharingLinkExpireDate(
126 DateTime::createFromTimestamp($this->event->getEnd()->getTimestamp()),
129 'externalUserName' => $externalUserName,
135 'eventLink' => $eventLink,
136 'event' => $this->event,
151 $this->notifyEventDeleted();
163 ->setCanceledTimestamp(time())
189 return self::isUserNameCorrect(
$userName);
192 private static function isUserNameCorrect(
string $userName): bool
204 return Helper::isPhoneFeatureEnabled()
217 'HOST_NAME' => \CCalendar::GetUserName(
$userId),
220 'ALLOW_INVITE' =>
true,
222 'HIDE_GUESTS' =>
false,
226 'NAME' => (string)(
$data[
'eventName'] ??
''),
227 'DATE_FROM' => (string)(
$data[
'dateFrom'] ??
''),
228 'DATE_TO' => (string)(
$data[
'dateTo'] ??
''),
229 'TZ_FROM' => (string)(
$data[
'timezone'] ??
''),
230 'TZ_TO' => (string)(
$data[
'timezone'] ??
''),
232 'ACCESSIBILITY' =>
'busy',
233 'IMPORTANCE' =>
'normal',
235 'IS_MEETING' =>
true,
236 'MEETING' => $meeting,
237 'DESCRIPTION' => (string)(
$data[
'description'] ??
''),
240 $eventData = array_merge($eventData, self::prepareTypeDependedEventFields($link, (
int)
$userId));
248 'ownerId' => (int)(
$request[
'ownerId'] ?? 0),
249 'dateFrom' => (string)(
$request[
'dateFrom'] ??
''),
250 'dateTo' => (string)(
$request[
'dateTo'] ??
''),
251 'timezone' => (string)(
$request[
'timezone'] ??
''),
252 'description' => (string)(
$request[
'description'] ??
''),
268 $result = Loc::getMessage(
'CALENDAR_SHARING_EVENT_MANAGER_EVENT_NAME', [
274 $result = Loc::getMessage(
'CALENDAR_SHARING_EVENT_MANAGER_EVENT_NAME_WITHOUT_GUEST');
282 $deal = DealHandler::getDeal($dealId);
288 return Loc::getMessage(
'CALENDAR_SHARING_EVENT_MANAGER_EVENT_NAME_DEAL', [
289 '#DEAL_NAME#' => trim($deal->getTitle()),
300 'ownerId' =>(int)(
$request[
'ownerId'] ?? 0),
301 'dateFrom' => (string)(
$request[
'dateFrom'] ??
''),
302 'dateTo' => (string)(
$request[
'dateTo'] ??
''),
303 'timezone' => (string)(
$request[
'timezone'] ??
''),
304 'description' => (string)(
$request[
'description'] ??
''),
305 'eventType' => Dictionary::EVENT_TYPE[
'shared_crm'],
315 self::SHARED_EVENT_CRM_TYPE,
316 self::SHARED_EVENT_TYPE,
317 self::SHARED_EVENT_COLLAB_TYPE,
334 self::updateEventSharingLink($eventLink,
$fields);
350 $eventLink->setCanceledTimestamp(time());
368 public static function onSharingEventMeetingStatusChange(
370 string $currentMeetingStatus,
371 array $userEventBeforeChange,
372 bool $isAutoAccept =
false
376 $eventLink = (
new Sharing\Link\Factory())->getEventLinkByEventId((
int)$userEventBeforeChange[
'PARENT_ID']);
383 $ownerId = $eventLink->getOwnerId();
385 if ($ownerId !==
$userId && !$isAutoAccept)
387 self::onSharingEventGuestStatusChange($currentMeetingStatus, $userEventBeforeChange, $eventLink,
$userId);
389 else if ($userEventBeforeChange[
'EVENT_TYPE'] === Dictionary::EVENT_TYPE[
'shared'])
391 self::onSharingCommonEventMeetingStatusChange($eventLink,
$userId);
393 else if ($userEventBeforeChange[
'EVENT_TYPE'] === Dictionary::EVENT_TYPE[
'shared_crm'])
395 self::onSharingCrmEventStatusChange($currentMeetingStatus, $userEventBeforeChange,
$userId, $ownerId);
399 private static function onSharingEventGuestStatusChange(
400 string $currentMeetingStatus,
402 Sharing\Link\EventLink $eventLink,
406 \CCalendarNotify::Send([
407 'mode' => $currentMeetingStatus ===
"Y" ?
'accept' :
'decline',
408 'name' => $event[
'NAME'],
409 'from' => $event[
"DATE_FROM"],
410 'to' => $event[
"DATE_TO"],
411 'location' => \CCalendar::GetTextLocation($userEvent[
"LOCATION"] ??
null),
413 'eventId' => $event[
'PARENT_ID'],
414 'userId' => $eventLink->getOwnerId(),
424 private static function onSharingCommonEventMeetingStatusChange(
425 Sharing\Link\EventLink $eventLink,
426 ?
int $initiatorId =
null,
430 $event = (
new Mappers\Event())->getById($eventLink->getEventId());
434 $phone =
$host[
'PERSONAL_PHONE'] ??
null;
437 $notificationService =
null;
438 if ($userContact && self::isEmailCorrect($userContact))
440 $notificationService =
441 (
new Sharing\Notification\Mail())
442 ->setEventLink($eventLink)
444 ->setInitiatorId($initiatorId)
448 $notificationService?->notifyAboutMeetingStatus($userContact);
457 private static function onSharingCrmEventStatusChange(
458 string $currentMeetingStatus,
459 array $userEventBeforeChange,
464 if (!Loader::includeModule(
'crm'))
469 $previousMeetingStatus = $userEventBeforeChange[
'MEETING_STATUS'] ??
null;
472 $currentMeetingStatus === Dictionary::MEETING_STATUS[
'Yes']
473 && $previousMeetingStatus === Dictionary::MEETING_STATUS[
'Question']
477 self::onSharingCrmEventConfirmed(
478 (
int)$userEventBeforeChange[
'PARENT_ID'],
479 $userEventBeforeChange[
'DATE_FROM'] ??
null,
480 $userEventBeforeChange[
'TZ_FROM'] ??
null,
485 $currentMeetingStatus === Dictionary::MEETING_STATUS[
'No']
487 $previousMeetingStatus === Dictionary::MEETING_STATUS[
'Question']
488 || $previousMeetingStatus === Dictionary::MEETING_STATUS[
'Yes']
492 self::onSharingCrmEventDeclined((
int)$userEventBeforeChange[
'PARENT_ID'],
$userId);
503 private static function onSharingCrmEventConfirmed(
int $eventId, ?
string $dateFrom, ?
string $timezone): void
505 $crmDealLink = self::getCrmDealLink($eventId);
507 $activity = \CCrmActivity::GetByCalendarEventId($eventId,
false);
511 (
new Sharing\Crm\NotifyManager($crmDealLink, Sharing\Crm\NotifyManager::NOTIFY_TYPE_EVENT_CONFIRMED))
512 ->sendSharedCrmActionsEvent(
515 \CCrmOwnerType::Activity,
526 private static function onSharingCrmEventDeclined(
int $eventId, ?
int $initiatorId =
null): void
528 $sharingFactory =
new Sharing\Link\Factory();
531 $eventLink = $sharingFactory->getEventLinkByEventId($eventId);
534 $crmDealLink = $sharingFactory->getLinkByHash($eventLink->getParentLinkHash());
537 $event = (
new Mappers\Event())->getById($eventId);
539 $completeActivityStatus = Sharing\Crm\ActivityManager::STATUS_CANCELED_BY_MANAGER;
541 $userId = \CCalendar::GetUserId();
544 $completeActivityStatus = Sharing\Crm\ActivityManager::STATUS_CANCELED_BY_CLIENT;
547 (
new Sharing\Crm\ActivityManager($eventId))
548 ->completeSharedCrmActivity($completeActivityStatus)
551 if ($crmDealLink->getContactId() > 0)
553 $notificationService =
554 Crm\Integration\Calendar\Notification\Manager::getSenderInstance($crmDealLink)
555 ->setCrmDealLink($crmDealLink)
556 ->setEventLink($eventLink)
560 if (method_exists($notificationService,
'setInitiatorId'))
562 $notificationService->setInitiatorId($initiatorId);
565 $notificationService->sendCrmSharingCancelled();
575 $eventLink->setCanceledTimestamp(time());
577 (
new Sharing\Notification\Mail())
578 ->setEventLink($eventLink)
580 ->setInitiatorId($initiatorId)
581 ->notifyAboutMeetingCancelled(
$email)
597 public static function onSharingEventDeleted(
int $eventId,
string $eventType, ?
int $initiatorId =
null): void
599 $linkFactory = (
new Sharing\Link\Factory());
602 $eventLink = $linkFactory->getEventLinkByEventId($eventId);
608 if ($eventType === Dictionary::EVENT_TYPE[
'shared'])
610 self::onSharingCommonEventDeclined($eventLink, $initiatorId);
612 else if ($eventType === Dictionary::EVENT_TYPE[
'shared_crm'])
614 self::onSharingCrmEventDeclined($eventId, $initiatorId);
628 public static function onSharingCommonEventDeclined(
629 Sharing\Link\EventLink $eventLink,
630 ?
int $initiatorId =
null,
635 $event = (
new Mappers\Event())->getById($eventLink->getEventId());
639 $phone =
$host[
'PERSONAL_PHONE'] ??
null;
642 $notificationService =
null;
643 if ($userContact && self::isEmailCorrect($userContact))
645 $notificationService =
646 (
new Sharing\Notification\Mail())
647 ->setEventLink($eventLink)
649 ->setInitiatorId($initiatorId)
653 $notificationService?->notifyAboutMeetingCancelled($userContact);
658 $userId = \CCalendar::GetUserId();
661 $ownerId = $eventLink->getOwnerId();
662 $event = EventTable::query()
664 ->where(
'PARENT_ID', $eventLink->getEventId())
665 ->whereIn(
'EVENT_TYPE', self::getSharingEventTypes())
666 ->where(
'OWNER_ID', $ownerId)
670 if ($event[
'ID'] ??
false)
672 EventTable::update((
int)$event[
'ID'], [
'MEETING_STATUS' => Dictionary::MEETING_STATUS[
'No']]);
684 if (!empty(
$fields[
'DATE_TO']))
686 $expireDate = Helper::createSharingLinkExpireDate(
687 DateTime::createFromText(
$fields[
'DATE_TO']),
690 $eventLink->setDateExpire($expireDate);
700 private static function getCrmDealLink(
int $eventId): ?Link\CrmDealLink
702 $sharingLinkFactory =
new Sharing\Link\Factory();
704 $eventLink = $sharingLinkFactory->getEventLinkByEventId($eventId);
705 if ($eventLink instanceof Sharing\Link\EventLink)
708 $crmDealLink = $sharingLinkFactory->getLinkByHash($eventLink->getParentLinkHash());
709 if ($crmDealLink instanceof Sharing\Link\CrmDealLink)
718 private function doesEventHasCorrectTime(): bool
720 $start =
new DateTime($this->event->getStart()->toString());
721 $end =
new DateTime($this->event->getEnd()->toString());
723 $offset = $this->getOffset();
727 if ($fromTs < time())
732 $ownerDate = new \DateTime(
'now',
new \DateTimeZone(
'UTC'));
734 $holidays = $this->getYearHolidays();
735 $intersectedHolidays = array_filter($holidays,
static fn($holiday) => in_array($holiday, [
736 $ownerDate->setTimestamp($fromTs + $offset)->format(
'j.m'),
737 $ownerDate->setTimestamp($toTs + $offset)->format(
'j.m'),
740 if (!empty($intersectedHolidays))
748 private function getYearHolidays():
array
750 return explode(
',', \COption::GetOptionString(
'calendar',
'year_holidays', Loc::getMessage(
'EC_YEAR_HOLIDAYS_DEFAULT')));
753 private function doesEventSatisfyRule(): bool
755 $start =
new DateTime($this->event->getStart()->toString(),
null,
new \DateTimeZone(
'UTC'));
756 $end =
new DateTime($this->event->getEnd()->toString(),
null,
new \DateTimeZone(
'UTC'));
758 $rule = $this->link->getSharingRule();
759 $eventDurationMinutes = ($end->getTimestamp() -
$start->getTimestamp()) / 60;
760 if ($eventDurationMinutes !== $rule->getSlotSize())
766 foreach ($rule->getRanges() as $range)
768 foreach ($range->getWeekdays() as $weekday)
770 $availableTime[$weekday] ??= [];
771 $availableTime[$weekday][] = [
772 'from' => $range->getFrom(),
773 'to' => $range->getTo(),
781 ), $availableTime[$weekday]);
783 if (!empty($intersected))
785 $from = min(array_column($intersected,
'from'));
786 $to = max(array_column($intersected,
'to'));
788 $availableTime[$weekday] = [...$notIntersected, [
798 $userOffset = $this->getOffset();
801 $fromTs =
$start->getTimestamp() - ($eventOffset - $userOffset);
802 $toTs = $end->getTimestamp() - ($eventOffset - $userOffset);
805 $minutesFrom = ($fromTs % 86400) / 60;
806 $minutesTo = ($toTs % 86400) / 60;
807 $weekday = (int)gmdate(
'N', $fromTs) % 7;
809 foreach ($availableTime[$weekday] as $range)
811 if ($minutesFrom >= $range[
'from'] && $minutesTo <= $range[
'to'])
820 private function separate($take, $array):
array
822 return array_reduce($array, fn($s, $e) => $take($e) ? [[...$s[0], $e], $s[1]] : [$s[0], [...$s[1], $e]], [[], []]);
828 private function checkUserAccessibility(
array $userIds): bool
830 $start =
new DateTime($this->event->getStart()->toString());
831 $end =
new DateTime($this->event->getEnd()->toString());
835 return (
new SharingAccessibilityManager([
836 'userIds' => $userIds,
837 'timestampFrom' => $fromTs,
838 'timestampTo' => $toTs,
839 ]))->checkUsersAccessibility();
846 private static function getSectionId(
$userId)
848 $result = \CCalendarSect::GetList([
851 'CAL_TYPE' =>
'user',
858 $createdSection = \CCalendarSect::CreateDefault([
871 private function notifyEventDeleted()
873 return \CCalendarNotify::Send([
874 'mode' =>
'cancel_sharing',
875 'userId' => $this->hostId,
876 'guestId' => $this->ownerId,
877 'eventId' => $this->event->getId(),
878 'from' => $this->event->getStart()->toString(),
879 'to' => $this->event->getEnd()->toString(),
880 'name' => $this->event->getName(),
887 $event = (
new Mappers\Event)->getById($eventLink->getEventId());
890 $event = \CCalendarEvent::GetList([
892 'ID' => $event->getId(),
894 'fetchAttendees' =>
true,
895 'checkPermissions' =>
false,
896 'parseRecursion' =>
false,
897 'setDefaultLimit' =>
false,
901 $event = $event[0] ??
null;
904 $event[
'ATTENDEES'] = [$eventLink->getOwnerId(), $eventLink->getHostId()];
905 $event[
'ATTENDEES_CODES'] = [
'U' . $eventLink->getOwnerId(),
'U' . $eventLink->getHostId()];
906 \CCalendar::SaveEvent([
907 'arFields' => $event,
908 'userId' => $eventLink->getOwnerId(),
909 'checkPermission' =>
false,
910 'sendInvitations' =>
true,
916 private static function prepareTypeDependedEventFields(
921 return match ($link->getObjectType())
924 default => self::prepareUserSharingEventFields($link,
$userId),
928 private static function prepareGroupSharingEventFields(Sharing\Link\Link $link,
int $userId):
array
930 $groupId = $link->getOwnerId();
931 $section = \CCalendarSect::GetList([
933 'OWNER_ID' => $groupId,
934 'CAL_TYPE' => Dictionary::CALENDAR_TYPE[
'group'],
937 'checkPermissions' =>
false,
938 'getPermissions' =>
false,
941 $attendeesCodes = [
'U' .
$userId];
942 $members = $link->getMembers();
946 $attendeesCodes[] =
'U' . $link->getHostId();
950 foreach ($members as $member)
952 $attendeesCodes[] =
'U' . $member->getId();
957 'OWNER_ID' => $link->getHostId(),
958 'SECTIONS' => [$section[0][
'ID']],
959 'ATTENDEES_CODES' => $attendeesCodes,
960 'EVENT_TYPE' => Dictionary::EVENT_TYPE[
'shared_collab'],
964 private static function prepareUserSharingEventFields(Sharing\Link\Link $link,
int $userId):
array
968 $ownerId = $link->getOwnerId();
969 $attendeesCodes = [
'U' .
$userId,
'U' . $ownerId];
970 $members = $link->getMembers();
972 foreach ($members as $member)
974 $attendeesCodes[] =
'U' . $member->getId();
980 'ATTENDEES_CODES' => $attendeesCodes,
981 'EVENT_TYPE' => $link instanceof Sharing\Link\CrmDealLink
982 ? Dictionary::EVENT_TYPE[
'shared_crm']
983 : Dictionary::EVENT_TYPE[
'shared']
988 private function getOffset(): int