43use Bitrix\Tasks\Internals\Task\Status;
44use Bitrix\Tasks\Provider\TaskList;
45use Bitrix\Tasks\Provider\TaskQuery;
77 $meetingSections = [],
80 $arTimezoneOffsets = [],
82 $isArchivedGroup =
false,
83 $userNameTemplate =
"#NAME# #LAST_NAME#",
88 $userTimezoneList = [],
98 $allowReserveMeeting =
true,
99 $SectionsControlsDOMId =
'sidebar',
102 $cachePath =
"calendar/",
103 $cacheTime = 2592000,
106 $pathesForSite =
false,
108 $arUserDepartment = [],
111 $arExchEnabledCache = [],
112 $silentErrorMode =
false,
114 $bCurUserSocNetAdmin,
116 $pathesList =
array(
'path_to_user',
'path_to_user_calendar',
'path_to_group',
'path_to_group_calendar',
'path_to_vr',
'path_to_rm'),
117 $pathesListEx =
null,
118 $isGoogleApiEnabled =
null,
119 $isOffice365ApiEnabled =
null,
122 $userLanguageId = [],
138 self::$arTypes = CCalendarType::GetList();
139 self::$bIntranet = self::IsIntranetEnabled();
140 self::$bSocNet = self::IsSocNet();
141 self::$userId = (isset(
$params[
'userId']) &&
$params[
'userId'] > 0) ? (
int)
$params[
'userId'] : self::GetCurUserId(
true);
142 self::$bOwner = self::$type ===
'user' || self::$type ===
'group';
143 self::$settings = self::GetSettings();
145 self::$pathesForSite = self::GetPathes(self::$siteId);
146 self::$pathToUser = self::$pathesForSite[
'path_to_user'];
147 self::$bSuperpose =
$params[
'allowSuperpose'] !=
false && self::$bSocNet;
148 self::$bAnonym = !
$USER || !
$USER->IsAuthorized();
149 self::$userNameTemplate = self::$settings[
'user_name_template'];
152 self::$id = $this->
GetId();
154 if (isset(
$params[
'SectionControlsDOMId']))
156 self::$SectionsControlsDOMId =
$params[
'SectionControlsDOMId'];
159 if (self::$bOwner && isset(
$params[
'ownerId']) &&
$params[
'ownerId'] > 0)
161 self::$ownerId = (int)
$params[
'ownerId'];
164 self::$showTasks = (self::$type ===
'user' || self::$type ===
'group')
166 && (
$params[
'showTasks'] ??
'') !==
false
169 && self::$userSettings[
'showTasks'] !==
'N'
172 if (self::$showTasks)
174 self::$viewTaskPath =
$params[
'viewTaskPath'];
175 self::$editTaskPath =
$params[
'editTaskPath'];
178 self::GetPermissions(
array(
179 'type' => self::$type,
180 'bOwner' => self::$bOwner,
181 'userId' => self::$userId,
182 'ownerId' => self::$ownerId,
186 if (isset(
$params[
'cachePath']))
188 self::$cachePath =
$params[
'cachePath'];
190 if (isset(
$params[
'cacheTime']))
192 self::$cacheTime =
$params[
'cacheTime'];
194 self::$bCache = self::$cacheTime > 0;
197 $page = preg_replace(
200 "/EVENT_DATE=.*?\&/i",
201 "/CHOOSE_MR=.*?\&/i",
203 "/bx_event_calendar_request=.*?\&/i",
204 "/clear_cache=.*?\&/i",
205 "/bitrix_include_areas=.*?\&/i",
206 "/bitrix_show_mode=.*?\&/i",
207 "/back_url_admin=.*?\&/i",
209 "/IFRAME_TYPE=.*?\&/i",
213 $page = preg_replace(
220 self::$actionUrl =
$page;
222 self::$path = empty(self::$ownerId)
223 ? self::GetServerPath().$page
224 : self::GetPath(self::$type, self::$ownerId,
true);
228 "bx_event_calendar_request",
230 "bitrix_include_areas",
239 $RMiblockId = self::$settings[
'rm_iblock_id'];
240 self::$allowReserveMeeting =
$params[
"allowResMeeting"] && $RMiblockId > 0;
242 if(self::$allowReserveMeeting && !
$USER->IsAdmin() && (CIBlock::GetPermission($RMiblockId) <
"R"))
244 self::$allowReserveMeeting =
false;
253 foreach(self::$arTypes as $type)
255 if(self::$type === $type[
'XML_ID'])
263 $APPLICATION->ThrowException(
'[EC_WRONG_TYPE] '.Loc::getMessage(
'EC_WRONG_TYPE'),
'calendar_wrong_type');
269 $startupEvent =
false;
271 if (isset($_GET[
'EVENT_ID']))
276 $startupEvent = self::GetStartUpEvent($eventId);
279 $startupEvent[
'EDIT'] =
true;
281 if ($startupEvent[
'DT_FROM'] ??
false)
283 $ts = self::Timestamp($startupEvent[
'DT_FROM']);
284 $init_month = date(
'm', $ts);
285 $init_year = date(
'Y', $ts);
292 $eventId = (int)$_GET[
'EVENT_ID'];
293 $isSharing = (bool)($_GET[
'IS_SHARING'] ??
null) ===
true;
294 $startupEvent = self::GetStartUpEvent($eventId, $isSharing);
297 $eventFromTs = self::Timestamp($startupEvent[
'DATE_FROM']);
298 $currentDateTs = self::Timestamp($_GET[
'EVENT_DATE'] ??
null);
300 if ($currentDateTs > $eventFromTs)
302 $startupEvent[
'~CURRENT_DATE'] = self::Date($currentDateTs,
false);
303 $init_month = date(
'm', $currentDateTs);
304 $init_year = date(
'Y', $currentDateTs);
308 $init_month = date(
'm', $eventFromTs);
309 $init_year = date(
'Y', $eventFromTs);
315 if (!$init_month && !$init_year && (
$params[
"initDate"] ??
false) <>
'' && mb_strpos(
$params[
"initDate"],
'.') !==
false)
317 $ts = self::Timestamp(
$params[
"initDate"]);
318 $init_month = date(
'm', $ts);
319 $init_year = date(
'Y', $ts);
324 $init_month = date(
"m");
328 $init_year = date(
"Y");
331 $id = $this->
GetId();
334 if (isset(self::$settings[
'week_holidays']))
336 $days =
array(
'MO' => 0,
'TU' => 1,
'WE' => 2,
'TH' => 3,
'FR' => 4,
'SA' => 5,
'SU' => 6);
337 foreach(self::$settings[
'week_holidays'] as $day)
339 $weekHolidays[] = $days[$day];
344 $weekHolidays =
array(5, 6);
348 if (isset(self::$settings[
'year_holidays']))
350 foreach(explode(
',', self::$settings[
'year_holidays']) as $date)
353 $ardate = explode(
'.', $date);
354 if (
count($ardate) == 2 && $ardate[0] && $ardate[1])
356 $yearHolidays[] = (int)$ardate[0] .
'.' . ((
int)$ardate[1] - 1);
362 if (isset(self::$settings[
'year_workdays']))
364 foreach(explode(
',', self::$settings[
'year_workdays']) as $date)
367 $ardate = explode(
'.', $date);
368 if (
count($ardate) === 2 && $ardate[0] && $ardate[1])
370 $yearWorkdays[] = (int)$ardate[0] .
'.' . ((
int)$ardate[1] - 1);
375 $isPersonalCalendarContext = self::IsPersonal(self::$type, self::$ownerId, self::$userId);
376 $bExchange = self::IsExchangeEnabled() && self::$type ===
'user';
377 $bExchangeConnected = $bExchange && CDavExchangeCalendar::IsExchangeEnabledForUser(self::$ownerId);
378 $bWebservice = self::IsWebserviceEnabled();
379 $bExtranet = self::IsExtranetEnabled();
383 $userTimezoneOffsetUTC = self::GetCurrentOffsetUTC(self::$userId);
384 $userTimezoneName = self::GetUserTimezoneName(self::$userId,
false);
385 $userTimezoneDefault =
'';
389 if (!$userTimezoneName)
391 $userTimezoneDefault = self::GetGoodTimezoneForOffset($userTimezoneOffsetUTC);
396 'type' => self::$type,
397 'userId' => self::$userId,
398 'userName' => self::GetUserName(self::$userId),
399 'ownerId' => self::$ownerId,
400 'ownerName' => self::GetOwnerName(self::$type, self::$ownerId),
402 'id' => self::$userId,
403 'name' => self::GetUserName(self::$userId),
404 'url' => self::GetUserUrl(self::$userId),
405 'avatar' => self::GetUserAvatarSrc(self::$userId),
406 'smallAvatar' => self::GetUserAvatarSrc(self::$userId,
array(
'AVATAR_SIZE' => 18)),
408 'perm' => $arType[
'PERM'],
409 'locationAccess' => Rooms\Util::getLocationAccess(self::$userId),
410 'permEx' => self::$perm,
411 'showTasks' => self::$showTasks,
412 'sectionControlsDOMId' => self::$SectionsControlsDOMId,
413 'week_holidays' => $weekHolidays,
414 'year_holidays' => $yearHolidays,
415 'year_workdays' => $yearWorkdays,
416 'init_month' => $init_month,
417 'init_year' => $init_year,
418 'pathToUser' => self::$pathToUser,
419 'path' => self::$path,
420 'actionUrl' => self::$actionUrl,
421 'settings' => self::$settings,
422 'userSettings' => self::$userSettings,
423 'bAnonym' => self::$bAnonym,
424 'bIntranet' => self::$bIntranet,
425 'bWebservice' => $bWebservice,
426 'bExtranet' => $bExtranet,
427 'bSocNet' => self::$bSocNet,
428 'bExchange' => $bExchangeConnected,
429 'startupEvent' => $startupEvent,
430 'workTime' => [self::$settings[
'work_time_start'], self::$settings[
'work_time_end']],
431 'userWorkTime' => [self::$settings[
'work_time_start'], self::$settings[
'work_time_end']],
433 'RMiblockId' => self::$settings[
'rm_iblock_id'],
434 'pathToMR' => self::$pathesForSite[
'path_to_rm'],
436 'allowResMeeting' => self::$allowReserveMeeting,
437 'bAMPM' => self::$bAMPM,
438 'WDControllerCID' =>
'UFWD' . $id,
439 'userTimezoneOffsetUTC' => $userTimezoneOffsetUTC,
440 'userTimezoneName' => $userTimezoneName,
441 'userTimezoneDefault' => $userTimezoneDefault,
443 'locationFeatureEnabled' => !$isCollabUser && Bitrix24Manager::isFeatureEnabled(FeatureDictionary::CALENDAR_LOCATION),
444 'plannerFeatureEnabled' => Bitrix24Manager::isPlannerFeatureEnabled(),
445 'eventWithEmailGuestEnabled' => Bitrix24Manager::isFeatureEnabled(FeatureDictionary::CALENDAR_EVENTS_WITH_EMAIL_GUESTS),
446 'sharingFeatureLimitEnable' => Bitrix24Manager::isFeatureEnabled(FeatureDictionary::CALENDAR_SHARING),
447 'projectFeatureEnabled' =>
false,
450 'showAfterSyncAccent' => isset($_GET[
'googleAuthSuccess']) && $_GET[
'googleAuthSuccess'] ===
'y',
451 'isExtranetUser' => $isExtranetUser,
452 'isGoogleApplicationRefused' => COption::GetOptionString(
'calendar',
'isGoogleApplicationRefused',
'N'),
453 'showGoogleApplicationRefused' => CUserOptions::getOption(
'calendar',
'showGoogleApplicationRefused',
'Y'),
454 'useAirDesign' => defined(
'AIR_SITE_TEMPLATE'),
455 'isBitrix24Template' => SITE_TEMPLATE_ID ===
'bitrix24',
462 $JSConfig[
'projectFeatureEnabled'] =
468 if (self::$type ===
'user' && (
int)self::$userId !== (
int)self::$ownerId)
470 $JSConfig[
'ownerUser'] =
array(
471 'id' => self::$ownerId,
472 'name' => self::GetUserName(self::$ownerId),
473 'url' => self::GetUserUrl(self::$ownerId),
474 'avatar' => self::GetUserAvatarSrc(self::$ownerId),
475 'smallAvatar' => self::GetUserAvatarSrc(self::$ownerId,
array(
'AVATAR_SIZE' => 18)),
479 $culture = \Bitrix\Main\Context::getCurrent()?->getCulture();
481 $JSConfig[
'dayOfWeekMonthFormat'] =
$culture?->getDayOfWeekMonthFormat();
482 $JSConfig[
'dayMonthFormat'] =
$culture?->getDayMonthFormat();
483 $JSConfig[
'longDateFormat'] =
$culture?->getLongDateFormat();
485 $placementParams =
false;
491 'serviceUrl' =>
'/bitrix/components/bitrix/app.layout/lazyload.ajax.php?&site='.SITE_ID.
'&'.
bitrix_sessid_get(),
494 $JSConfig[
'placementParams'] = $placementParams;
497 $isCollabCalendar = $isGroupCalendar && Collabs::getInstance()->getCollabIfExists(self::$ownerId);
500 (self::$type === Dictionary::CALENDAR_TYPE[
'user'] && self::$userId === self::$ownerId)
501 || self::$type === Dictionary::CALENDAR_TYPE[
'group']
504 $groupIds = self::$type === Dictionary::CALENDAR_TYPE[
'group'] ? [self::$ownerId] : [];
505 $JSConfig[
'counters'] = CountersManager::getValues((
int)self::$userId, $groupIds);
514 self::$type ===
'company_calendar'
515 || self::$type ===
'calendar_company'
516 || self::$type ===
'company'
517 || self::$type ===
'group'
528 $typeModel = TypeModel::createFromXmlId(self::$type);
530 if (
$accessController->check(ActionDictionary::ACTION_TYPE_ACCESS, $typeModel, []))
532 $JSConfig[
'TYPE_ACCESS'] = $arType[
'ACCESS'];
535 if ($isPersonalCalendarContext)
538 'userId' => self::$userId,
539 'type' => self::$type,
543 $JSConfig[
'caldav_link_all'] = self::GetServerPath();
545 $JSConfig[
'isSetSyncGoogleSettings'] = self::isGoogleApiEnabled();
546 $JSConfig[
'isSetSyncOffice365Settings'] = self::isOffice365ApiEnabled();
547 $JSConfig[
'isIphoneConnected'] = self::isIphoneConnected();
548 $JSConfig[
'isMacConnected'] = self::isMacConnected();
549 $JSConfig[
'isIcloudConnected'] = ($JSConfig[
'syncInfo'][
'icloud'] ??
false)
550 ? $JSConfig[
'syncInfo'][
'icloud'][
'connected']
553 $JSConfig[
'isGoogleConnected'] = ($JSConfig[
'syncInfo'][
'google'] ??
false)
554 ? $JSConfig[
'syncInfo'][
'google'][
'connected']
560 $JSConfig[
'caldav_link_all'] = self::GetServerPath();
562 $JSConfig[
'syncInfo'] =
false;
563 $JSConfig[
'isIphoneConnected'] =
false;
564 $JSConfig[
'isMacConnected'] =
false;
565 $JSConfig[
'isIcloudConnected'] =
false;
566 $JSConfig[
'isGoogleConnected'] =
false;
569 self::$userMeetingSection = self::GetCurUserMeetingSection();
571 $defaultHiddenSections = [];
575 [$sectionList, $collabSectionList, $followedSectionList, $roomsList] = self::getSectionsInfo($isCollabUser);
578 foreach ($sectionList as $section)
580 if (!in_array((
int)$section[
'ID'], $sectionIdList,
true))
582 $sections[] = $section;
583 $sectionIdList[] = (int)$section[
'ID'];
587 ($section[
'CAL_TYPE'] !== self::$type || self::$ownerId !== (
int)$section[
'OWNER_ID'])
588 && !($isCollabUser && $section[
'IS_COLLAB'])
591 $defaultHiddenSections[] = (int)$section[
'ID'];
598 'type' => self::$type,
599 'ownerId' => self::$ownerId,
600 'isPersonalCalendarContext' => $isPersonalCalendarContext,
601 'defaultHiddenSections' => $defaultHiddenSections,
605 $readOnly = !self::$perm[
'edit'] && !self::$perm[
'section_edit'];
607 if (self::$type ===
'user' && self::$ownerId !== (
int)self::$userId)
617 $bCreateDefault = self::hasToCreateDefaultCalendar($sections);
619 $groupOrUser = self::$type ===
'user' || self::$type ===
'group';
622 $noEditAccessedCalendars =
true;
625 foreach ($sections as
$i => $section)
627 $canEdit =
$accessController->check(ActionDictionary::ACTION_TYPE_EDIT, $typeModel, []);
631 && $section[
'CAL_TYPE'] === self::$type
632 && (
int)$section[
'OWNER_ID'] === (
int)self::$ownerId
635 if ($noEditAccessedCalendars && $section[
'PERM'][
'edit'])
637 $noEditAccessedCalendars =
false;
643 && ($section[
'PERM'][
'edit'] || $section[
'PERM'][
'edit_section'])
644 && !self::$isArchivedGroup
651 if (in_array($section[
'ID'], $followedSectionList))
653 $sections[
$i][
'SUPERPOSED'] =
true;
656 $type = $sections[
$i][
'CAL_TYPE'];
657 if ($type ===
'user')
659 $path = CComponentEngine::MakePathFromTemplate(
660 self::$pathesForSite[
'path_to_user_calendar'],
661 [
"user_id" => $sections[
$i][
'OWNER_ID']]
664 elseif ($type ===
'group')
666 $groupId = $sections[
$i][
'OWNER_ID'];
667 $path = CComponentEngine::MakePathFromTemplate(
668 self::$pathesForSite[
'path_to_group_calendar'],
669 [
'group_id' => $groupId]
671 if ($sections[
$i][
'IS_COLLAB'])
673 $trackingCollabs[] = $groupId;
677 $trackingGroups[] = $groupId;
682 $path = self::$pathesForSite[
'path_to_type_'.$type];
684 $sections[
$i][
'LINK'] = $path;
687 if (!empty($collabSectionList))
689 $noEditAccessedCalendars = self::checkCollabSectionAccess($collabSectionList);
692 if ($groupOrUser && $noEditAccessedCalendars && !$bCreateDefault)
697 self::$readOnly = $readOnly;
702 if (self::$showTasks)
704 $JSConfig[
'viewTaskPath'] = self::$viewTaskPath;
705 $JSConfig[
'editTaskPath'] = self::$editTaskPath;
711 $fullSectionsList = $groupOrUser
712 ? self::GetSectionList([
'checkPermissions' =>
false,
'getPermissions' =>
false])
717 !empty($fullSectionsList)
718 && self::GetOwnerId() !== self::GetUserId()
725 $defCalendar = CCalendarSect::CreateDefault([
726 'type' => self::GetType(),
727 'ownerId' => self::GetOwnerId(),
729 $sections[] = $defCalendar;
730 self::$userMeetingSection = $defCalendar[
'ID'];
733 else if (!CCalendarSect::containsLocalSection($sectionList, self::$type))
735 $sectionsWithoutPermissions = CCalendarSect::GetList([
737 'CAL_TYPE' => self::$type,
738 'OWNER_ID' => self::$ownerId,
740 'EXTERNAL_TYPE' => CCalendarSect::EXTERNAL_TYPE_LOCAL,
743 'checkPermissions' =>
false,
744 'getPermissions' =>
false,
747 if (empty($sectionsWithoutPermissions))
749 $defCalendar = CCalendarSect::CreateDefault([
750 'type' => self::GetType(),
751 'ownerId' => self::GetOwnerId(),
753 $sections[] = $defCalendar;
754 self::$userMeetingSection = $defCalendar[
'ID'];
758 if (self::$type ===
'location')
760 $userSections = array_filter($sectionList,
static function ($section) {
761 return $section[
'CAL_TYPE'] ===
'user' && (int)$section[
'OWNER_ID'] === self::$userId;
763 if (empty($userSections))
765 $defUserCalendar = CCalendarSect::CreateDefault([
767 'ownerId' => self::$userId,
769 if ($defUserCalendar)
771 $sections[] = $defUserCalendar;
772 self::$userMeetingSection = $defUserCalendar[
'ID'];
777 if ($typeAccessController->check(ActionDictionary::ACTION_TYPE_EDIT, TypeModel::createFromXmlId(self::$type)))
779 $JSConfig[
'new_section_access'] = CCalendarSect::GetDefaultAccess(self::$type, self::$ownerId);
782 $colors = [
'#86B100',
'#0092CC',
'#00AFC7',
'#E89B06',
'#00B38C',
'#DE2B24',
'#BD7AC9',
'#838FA0',
'#C3612C',
'#E97090'];
784 $JSConfig[
'hiddenSections'] = $hiddenSections;
785 $JSConfig[
'readOnly'] = $readOnly;
786 $JSConfig[
'hideSettingsHintLocation'] = CUserOptions::GetOption(
'calendar',
'hideSettingsHintLocation');
788 $JSConfig[
'accessNames'] = self::GetAccessNames();
789 $JSConfig[
'sectionAccessTasks'] = self::$type ===
'location'
790 ? self::GetAccessTasks(
'calendar_section',
'location')
791 : self::GetAccessTasks()
793 $JSConfig[
'typeAccessTasks'] = self::$type ===
'location'
794 ? self::GetAccessTasks(
'calendar_type',
'location')
795 : self::GetAccessTasks(
'calendar_type')
798 $JSConfig[
'bSuperpose'] = self::$bSuperpose;
800 $sharing = match (self::$type)
809 $JSConfig[
'sharingOptions'] = $isGroupCalendar ? null : $sharing->getOptions();
810 $JSConfig[
'isCollabUser'] = $isCollabUser;
811 $JSConfig[
'isCollabCalendar'] = $isCollabCalendar;
812 $JSConfig[
'isCollabFeatureEnabled'] = CollabFeature::isAvailable();
815 $meetSectionId = (int)$userSettings[
'meetSection'];
817 $meetSection = CCalendarSect::GetById($meetSectionId);
818 $hasRightsForMeetSection =
false;
819 if (is_array($meetSection))
822 $sectionModel = SectionModel::createFromArray($meetSection);
823 $action = ActionDictionary::ACTION_SECTION_EDIT;
824 $hasRightsForMeetSection = $sectionAccessController->check(
$action, $sectionModel);
826 if ($meetSectionId && $hasRightsForMeetSection)
828 $JSConfig[
'meetSectionId'] = $meetSectionId;
831 $selectedUserCodes =
array(
'U'.self::$userId);
832 if (self::$type ===
'user')
834 $selectedUserCodes[] =
'U'.self::$ownerId;
837 $additionalParams =
array(
838 'socnetDestination' => self::GetSocNetDestination(
false, $selectedUserCodes),
839 'locationList' => $roomsList,
840 'timezoneList' => self::GetTimezoneList(),
841 'defaultColorsList' => $colors,
842 'formSettings' =>
array(
848 if (self::$type ===
'location')
853 'sections' => $sections,
854 'rooms' => $roomsList,
855 'categories' => $categoryList,
865 'sections' => $sections,
882 foreach(self::$arTypes as $type)
884 if(self::$type === $type[
'XML_ID'])
892 $APPLICATION->ThrowException(
'[EC_WRONG_TYPE] '.Loc::getMessage(
'EC_WRONG_TYPE'),
'calendar_wrong_type');
899 $APPLICATION->ThrowException(Loc::getMessage(
"EC_ACCESS_DENIED"));
910 $typeModel = TypeModel::createFromXmlId(self::$type);
913 if (!
$accessController->check(ActionDictionary::ACTION_TYPE_VIEW, $typeModel, []))
921 if (self::$type === Dictionary::CALENDAR_TYPE[
'user'] && $isExternalUser && !$isCollaber)
926 if (self::$type === Dictionary::CALENDAR_TYPE[
'user'] && $isCollaber && self::$userId !== self::$ownerId)
950 $groupId = self::$ownerId;
952 $featurePerms = CSocNetFeaturesPerms::CurrentUserCanPerformOperation(
959 $canViewGroup = is_array($featurePerms) && isset($featurePerms[$groupId]) && $featurePerms[$groupId];
963 $featurePerms = CSocNetFeaturesPerms::CurrentUserCanPerformOperation(
969 $canViewGroup = is_array($featurePerms) && isset($featurePerms[$groupId]) && $featurePerms[$groupId];
972 return $canViewGroup;
977 return str_starts_with($id, self::EDIT_PREFIX);
982 return (
int)mb_substr($id, strlen(self::EDIT_PREFIX));
987 if (class_exists(
'CUserOptions') && $userId)
989 $idList = array_unique(array_map(
'intval', $idList));
990 CUserOptions::SetOption(
"calendar",
"superpose_displayed", serialize($idList));
992 PushCommand::ChangeSectionSubscription,
1000 if (self::IsExchangeEnabled(self::GetCurUserId()) && self::$type ===
'user')
1002 $oSect = CCalendarSect::GetById($id);
1004 if ($oSect && $oSect[
'IS_EXCHANGE'] && $oSect[
'DAV_EXCH_CAL'])
1006 $exchRes = CDavExchangeCalendar::DoDeleteCalendar($oSect[
'OWNER_ID'], $oSect[
'DAV_EXCH_CAL']);
1007 if ($exchRes !==
true)
1009 return self::CollectExchangeErrors($exchRes);
1014 return CCalendarSect::Delete($id);
1019 if (empty($arErrors) || !is_array($arErrors))
1021 return '[EC_NO_EXCH] ' . Loc::getMessage(
'EC_NO_EXCHANGE_SERVER');
1025 foreach ($arErrors as
$error)
1048 $markDeleted =
$params[
'markDeleted'] ??
true;
1049 $originalFrom =
$params[
'originalFrom'] ??
null;
1055 $checkPermissions =
$params[
'checkPermissions'] ??
true;
1056 if (!isset(self::$userId))
1058 self::$userId = self::GetCurUserId();
1062 $res = CCalendarEvent::GetList([
1063 'arFilter' => [
"ID" => $id],
1064 'parseRecursion' =>
false,
1065 'setDefaultLimit' =>
false,
1066 'fetchAttendees' =>
true,
1067 'checkPermissions' => $checkPermissions,
1072 if (!isset(self::$type))
1074 self::$type =
$event[
'CAL_TYPE'];
1077 if (!isset(self::$ownerId))
1079 self::$ownerId =
$event[
'OWNER_ID'];
1083 $eventModel = \CCalendarEvent::getEventModelForPermissionCheck((
int)
$event[
'ID'],
$event, self::$userId);
1084 if ($checkPermissions && !
$accessController->check(ActionDictionary::ACTION_EVENT_DELETE, $eventModel))
1086 return Loc::getMessage(
'EC_ACCESS_DENIED');
1089 CCalendarSect::UpdateModificationLabel(
$event[
'SECT_ID']);
1091 if ($doExternalSync !==
false &&
$event[
'SECT_ID'])
1093 $bGoogleApi = self::isGoogleApiEnabled() &&
$event[
'CAL_TYPE'] ===
'user';
1094 $bCalDav = self::IsCalDAVEnabled() &&
$event[
'CAL_TYPE'] ===
'user';
1095 $bExchangeEnabled = self::IsExchangeEnabled() &&
$event[
'CAL_TYPE'] ===
'user';
1097 if ($bExchangeEnabled || $bCalDav || $bGoogleApi)
1100 'bCalDav' => $bCalDav,
1101 'bExchangeEnabled' => $bExchangeEnabled,
1102 'sectionId' =>
$event[
'SECT_ID'],
1105 if (
$res !==
true && self::$silentErrorMode)
1107 self::ThrowError(
$res);
1112 $sendNotification =
$params[
'sendNotification'] ?? ((
$params[
'recursionMode'] ??
null) !==
'all');
1113 $userId = !empty(
$params[
'userId']) ? (int)
$params[
'userId'] : self::$userId;
1115 $res = CCalendarEvent::Delete([
1118 'bMarkDeleted' => $markDeleted,
1119 'originalFrom' => $originalFrom,
1120 'userId' => $userId,
1121 'sendNotification' => $sendNotification,
1122 'requestUid' =>
$params[
'requestUid'] ??
null,
1125 if ((
$params[
'recursionMode'] ??
null) !==
'this' && !empty(
$event[
'RECURRENCE_ID']))
1127 self::DeleteEvent(
$event[
'RECURRENCE_ID'], $doExternalSync, [
1128 'sendNotification' => $sendNotification,
1129 'originalFrom' => $originalFrom,
1133 if (CCalendarEvent::CheckRecurcion(
$event))
1135 $events = CCalendarEvent::GetEventsByRecId($id);
1137 foreach($events as $ev)
1139 self::DeleteEvent($ev[
'ID'], $doExternalSync, [
1140 'sendNotification' => $sendNotification,
1141 'originalFrom' => $originalFrom,
1146 if (isset(
$params[
'recursionMode']) &&
$params[
'recursionMode'] ===
'all' && !empty(
$event[
'ATTENDEE_LIST']))
1148 foreach(
$event[
'ATTENDEE_LIST'] as $attendee)
1150 if ($attendee[
'status'] !==
'N')
1153 CCalendarNotify::Send([
1154 "mode" =>
'cancel_all',
1155 "name" =>
$event[
'NAME'],
1156 "from" =>
$event[
'DATE_FROM'],
1157 "guestId" => $attendee[
"id"],
1158 "eventId" =>
$event[
'PARENT_ID'],
1159 "userId" => $userId ??
$event[
'MEETING_HOST'],
1172 public static function SetOffset($userId =
false, $value = 0)
1174 if ($userId ===
false)
1176 self::$offset = $value;
1180 self::$arTimezoneOffsets[$userId] = $value;
1186 if (empty($arErrors) || !is_array($arErrors))
1188 return '[EC_NO_EXCH] ' . Loc::getMessage(
'EC_NO_CAL_DAV');
1192 foreach ($arErrors as
$error)
1202 $userId = (int)$userId;
1204 $cacheId =
'calendar_path_settings_'.$userId;
1205 $obCache =
new CPHPCache;
1207 if($obCache->InitCache(3600 * 6, $cacheId,
'/calendar/'.$cacheId))
1209 $calendarUrl = $obCache->GetVars();
1213 $obCache->StartDataCache();
1217 if ($bExtranet && self::IsExtranetUser($userId))
1219 $siteId = CExtranet::GetExtranetSiteID();
1223 $siteId = $bExtranet && !self::IsExtranetUser($userId)
1224 ? CSite::GetDefSite()
1225 : self::GetSiteId();
1228 self::$siteId == $siteId
1229 && isset(self::$pathesForSite)
1230 && is_array(self::$pathesForSite)
1233 self::$pathes[$siteId] = self::$pathesForSite;
1237 if (!isset(self::$pathes[$siteId]) || !is_array(self::$pathes[$siteId]))
1239 self::$pathes[$siteId] = self::GetPathes($siteId);
1242 $calendarUrl = self::$pathes[$siteId][
'path_to_user_calendar'] ??
'';
1243 $calendarUrl = str_replace(
array(
'#user_id#',
'#USER_ID#'), $userId, $calendarUrl);
1244 $calendarUrl = self::GetServerPath().$calendarUrl;
1246 $obCache->EndDataCache($calendarUrl);
1249 return $calendarUrl;
1259 $departments = self::GetUserDepartment($userId);
1261 return empty($departments);
1266 if (!isset(self::$arUserDepartment[$userId]))
1268 $rsUser = CUser::GetByID($userId);
1269 if($arUser = $rsUser->Fetch())
1271 self::SetUserDepartment($userId, $arUser[
"UF_DEPARTMENT"]);
1275 return self::$arUserDepartment[$userId];
1280 if (!is_array($dep))
1284 self::$arUserDepartment[$userId] = $dep;
1289 $userId = self::GetCurUserId();
1290 if ($module ===
"calendar" && $userId)
1292 $arTag = explode(
"|", $tag);
1293 $eventId = (int)$arTag[2];
1294 if ($arTag[0] ===
"CALENDAR" && $arTag[1] ===
"INVITE" && $eventId && $userId)
1296 CCalendarEvent::SetMeetingStatus([
1297 'userId' => $userId,
1298 'eventId' => $eventId,
1299 'status' => $value ===
'Y' ?
'Y' :
'N',
1300 'personalNotification' =>
true,
1303 return $value ===
'Y' ? Loc::getMessage(
'EC_PROP_CONFIRMED_TEXT_Y') : Loc::getMessage(
'EC_PROP_CONFIRMED_TEXT_N');
1308 public static function SetSettings($settings = [], $clearOptions =
false)
1318 'user_name_template',
1323 'denied_superpose_types',
1331 $optionNames = array_merge($optionNames,
$arPathes);
1332 if (isset($settings[
'rm_iblock_ids']) && !$settings[
'rm_for_sites'])
1334 foreach($settings[
'rm_iblock_ids'] as
$site => $value)
1336 COption::SetOptionString(
"calendar",
'rm_iblock_id', $value,
false,
$site);
1340 foreach($optionNames as $opt)
1344 COption::RemoveOption(
"calendar", $opt);
1346 else if (isset($settings[$opt]))
1348 if ($opt ===
'rm_iblock_id' && !$settings[
'rm_for_sites'])
1353 if ($opt ===
'sync_by_push')
1355 if (self::isOffice365ApiEnabled() || self::isGoogleApiEnabled())
1357 \CAgent::RemoveAgent(
"\\Bitrix\\Calendar\\Sync\\Managers\\DataExchangeManager::importAgent();",
'calendar');
1358 \CAgent::RemoveAgent(
"\\Bitrix\\Calendar\\Sync\\Managers\\PushWatchingManager::renewWatchChannels();",
'calendar');
1360 if ($settings[$opt])
1363 \CAgent::RemoveAgent(
1364 "\\Bitrix\\Calendar\\Sync\\GoogleApiPush::clearPushChannels();",
1369 "\\Bitrix\\Calendar\\Sync\\Managers\\PushWatchingManager::renewWatchChannels();",
1379 \CAgent::RemoveAgent(
"\\Bitrix\\Calendar\\Sync\\GoogleApiPush::processPush();",
"calendar");
1380 \CAgent::RemoveAgent(
"\\Bitrix\\Calendar\\Sync\\GoogleApiPush::renewWatchChannels();",
"calendar");
1381 $DB->Query(
"DELETE FROM b_agent WHERE NAME LIKE '%GoogleApiPush::checkPushChannel%'");
1384 "\\Bitrix\\Calendar\\Sync\\Managers\\DataExchangeManager::importAgent();",
1393 if ($opt ===
'pathes' && is_array($settings[$opt]))
1395 $sitesPathes = $settings[$opt];
1398 $arAffectedSites = [];
1399 foreach($sitesPathes as $s => $pathes)
1404 if ($pathes[$path] != $settings[$path])
1406 $ar[$path] = $pathes[$path] ?? $settings[$path];
1411 if ($affect && !in_array($s, $arAffectedSites))
1413 $arAffectedSites[] = $s;
1414 COption::SetOptionString(
"calendar",
'pathes_'.$s, serialize(
$ar));
1418 COption::RemoveOption(
"calendar",
'pathes_'.$s);
1421 COption::SetOptionString(
"calendar",
'pathes_sites', serialize($arAffectedSites));
1425 else if ($opt ===
'denied_superpose_types' && is_array($settings[$opt]))
1427 $settings[$opt] = serialize($settings[$opt]);
1430 else if ($opt ===
'week_holidays' && is_array($settings[$opt]))
1432 $settings[$opt] = implode(
1434 array_intersect(array_unique($settings[$opt]), [
'SU',
'MO',
'TU',
'WE',
'TH',
'FR',
'SA'])
1438 COption::SetOptionString(
"calendar", $opt, $settings[$opt]);
1445 return \Bitrix\Main\ModuleManager::isModuleInstalled(
'bitrix24');
1448 public static function ReminderAgent($eventId = 0, $userId = 0, $viewPath =
'', $calendarType =
'', $ownerId = 0, $index = 0)
1455 return self::CALENDAR_MAX_TIMESTAMP;
1470 $type = mb_strtolower($type);
1471 $key = $type.
'_'.$ownerId;
1473 if (isset(self::$ownerNames[
$key]))
1475 return self::$ownerNames[
$key];
1479 if($type ===
'user')
1481 $ownerName = self::GetUserName($ownerId);
1483 elseif($type ===
'group')
1491 if ($arGroup = CSocNetGroup::GetByID($ownerId))
1493 $ownerName = $arGroup[
"~NAME"];
1499 $arTypes = CCalendarType::GetList(
array(
"arFilter" =>
array(
"XML_ID" => $type)));
1500 $ownerName = $arTypes[0][
'NAME'];
1502 self::$ownerNames[
$key] = $ownerName;
1503 $ownerName = is_string($ownerName) ? trim($ownerName) :
'';
1518 $offset = $oTz->getOffset(
new DateTime($dateTimestamp ?
"@$dateTimestamp" :
"now", $oTz));
1535 $currentTime =
new DateTime(
'now', $timezone);
1537 $transitions = $timezone->getTransitions(
1538 $currentTime->getTimestamp(),
1539 $currentTime->getTimestamp() + 365 * self::DAY_LENGTH
1542 foreach ($transitions as $transition)
1544 if ($transition[
'isdst'] ===
true)
1559 if (!isset(
$params[
'arUserIds']))
1570 if (!isset(
$params[
'checkPermissions']))
1572 $params[
'checkPermissions'] =
true;
1575 $res = CCalendarEvent::GetAccessibilityForUsers([
1579 'curEventId' =>
$params[
'curEventId'] ??
null,
1580 'checkPermissions' =>
$params[
'checkPermissions'],
1584 if (isset(
$params[
'getFromHR']) && self::IsIntranetEnabled())
1586 $resHR = CIntranetUtils::GetAbsenceData(
1588 'DATE_START' =>
$params[
'from'],
1589 'DATE_FINISH' =>
$params[
'to'],
1592 'SELECT' =>
array(
'ID',
'DATE_ACTIVE_FROM',
'DATE_ACTIVE_TO'),
1594 BX_INTRANET_ABSENCE_HR
1597 foreach($resHR as $userId => $forUser)
1599 if (!isset(
$res[$userId]) || !is_array(
$res[$userId]))
1604 foreach($forUser as
$event)
1609 'DT_FROM' =>
$event[
'DATE_ACTIVE_FROM'],
1610 'DT_TO' =>
$event[
'DATE_ACTIVE_TO'],
1611 'ACCESSIBILITY' =>
'absent',
1612 'IMPORTANCE' =>
'normal',
1613 "FROM" => self::Timestamp(
$event[
'DATE_ACTIVE_FROM']),
1614 "TO" => self::Timestamp(
$event[
'DATE_ACTIVE_TO']),
1626 $isFromRest = (
$params[
'fromRest'] ??
false) ===
true;
1631 $curUserId = self::GetCurUserId();
1635 $curUserId = (int)
$params[
'userId'];
1639 if (!
$accessController->check(ActionDictionary::ACTION_TYPE_VIEW, TypeModel::createFromXmlId($type)))
1641 return 'access_denied';
1649 class_exists(
'CSocNetFeatures')
1655 return 'inactive_feature';
1658 $maxAmount = isset(
$params[
'maxAmount']) && (int)
$params[
'maxAmount'] > 0
1669 'CAL_TYPE' => $type,
1670 'FROM_LIMIT' =>
$params[
'fromLimit'],
1671 'TO_LIMIT' =>
$params[
'toLimit'],
1673 'ACTIVE_SECTION' =>
'Y',
1714 $eventsList = CCalendarEvent::GetList([
1717 'parseRecursion' =>
true,
1718 'fetchAttendees' => $isFromRest,
1719 'userId' => $curUserId,
1720 'fetchMeetings' => $type ===
'user',
1721 'preciseLimits' =>
true,
1722 'skipDeclined' =>
true,
1723 'getUserfields' => $isFromRest,
1726 $pathToCalendar = self::GetPathForCalendarEx($curUserId);
1728 if (self::Date(time(),
false) ===
$params[
'fromLimit'])
1730 $limitTime = time();
1734 $limitTime = self::Timestamp(
$params[
'fromLimit']);
1737 $limitTime -= (int)date(
"Z", $limitTime);
1740 foreach ($eventsList as
$event)
1742 if (
$event[
'IS_MEETING'] &&
$event[
"MEETING_STATUS"] ===
'N')
1747 if ($type ===
'user' && !
$event[
'IS_MEETING'] &&
$event[
'CAL_TYPE'] !==
'user')
1752 $fromTs = self::Timestamp(
$event[
'DATE_FROM']);
1753 $toTs = $fromTs +
$event[
'DT_LENGTH'];
1755 $toTsUtc = $toTs -
$event[
'TZ_OFFSET_FROM'];
1757 if ($toTsUtc >= $limitTime)
1759 if (
$event[
'DT_SKIP_TIME'] !==
"Y")
1761 $fromTs -=
$event[
'~USER_OFFSET_FROM'];
1762 $toTs -=
$event[
'~USER_OFFSET_TO'];
1764 $event[
'DATE_FROM'] = self::Date($fromTs,
$event[
'DT_SKIP_TIME'] !==
'Y');
1765 $event[
'DATE_TO'] = self::Date($toTs,
$event[
'DT_SKIP_TIME'] !==
'Y');
1767 $event[
'DT_FROM_TS'] = $fromTs;
1768 $event[
'DT_TO_TS'] = $toTs;
1771 'EVENT_ID' =>
$event[
'ID'],
1772 'EVENT_DATE' => self::Date($fromTs,
false),
1777 $event[
'~FROM_TO_HTML'] = self::GetFromToHtml(
1780 $event[
'DT_SKIP_TIME'] ===
'Y',
1789 usort($entryList,
static function(
$a, $b){
1790 if (
$a[
'DT_FROM_TS'] === $b[
'DT_FROM_TS'])
1794 return $a[
'DT_FROM_TS'] < $b[
'DT_FROM_TS'] ? -1 : 1;
1796 array_splice($entryList, $maxAmount);
1803 return Rooms\Util::getTextLocation($loc);
1808 return Rooms\Util::parseLocation(
$location);
1815 [$sectionId, $entityType,
$entityId] = $calendarId;
1816 $entityType = mb_strtolower($entityType);
1820 SectionModel::createFromId((
int)$sectionId)
1821 ->setType($entityType)
1825 ActionDictionary::ACTION_SECTION_EDIT => [],
1826 ActionDictionary::ACTION_SECTION_EVENT_VIEW_FULL => [],
1827 ActionDictionary::ACTION_SECTION_EVENT_VIEW_TIME => [],
1828 ActionDictionary::ACTION_SECTION_EVENT_VIEW_TITLE => [],
1833 'bAccess' =>
$result[ActionDictionary::ACTION_SECTION_EVENT_VIEW_TIME],
1834 'bReadOnly' => !
$result[ActionDictionary::ACTION_SECTION_EDIT],
1837 if (
$res[
'bReadOnly'])
1839 if (
$result[ActionDictionary::ACTION_SECTION_EVENT_VIEW_TIME])
1841 $res[
'privateStatus'] =
'time';
1843 if (
$result[ActionDictionary::ACTION_SECTION_EVENT_VIEW_TITLE])
1845 $res[
'privateStatus'] =
'title';
1854 return self::DAY_LENGTH;
1859 return Rooms\Util::unParseTextLocation($loc);
1865 $html = str_replace(chr(13),
"\n", trim($html, chr(13)));
1866 $html = preg_replace(
"/(\s|\S)*<a\s*name=\"bm_begin\"><\/a>/isu",
"", $html);
1867 $html = preg_replace(
"/<br>(\n|\r)+/isu",
"<br>", $html);
1868 return self::ParseHTMLToBB($html);
1873 $id =
AddEventHandler(
"main",
"TextParserBeforeTags", Array(
"CCalendar",
"_ParseHack"));
1876 $TextParser->allow =
array(
"HTML" =>
"N",
"BIU" =>
"Y",
"IMG" =>
"Y",
"QUOTE" =>
"Y",
"CODE" =>
"Y",
"FONT" =>
"N",
"LIST" =>
"Y",
"SMILES" =>
"Y",
"NL2BR" =>
"Y",
"VIDEO" =>
"Y",
"TABLE" =>
"Y",
"CUT_ANCHOR" =>
"Y",
"ALIGN" =>
"Y");
1877 $html = $TextParser->convertText($html);
1881 $html = preg_replace(
"/<br\s*\/*>/isu",
"\n", $html);
1883 $html = preg_replace(
"/<\/(p|div)>/isu",
"\n", $html);
1885 $html = preg_replace(
"/ /isu",
"", $html);
1887 $html = preg_replace(
1888 "#<img[^>]+src\\s*=[\\s'\"]*((cid):[.\\-_:a-z0-9@]+)*[\\s'\"]*[^>]*>#isu",
1889 "[img]\\1[/img]", $html
1892 $html = preg_replace(
"/<([^>]*?)>/isu",
"", $html);
1894 $html = preg_replace(
"/\n[\s\n]+\n/",
"\n" , $html);
1907 $arDays = [
'SU',
'MO',
'TU',
'WE',
'TH',
'FR',
'SA'];
1911 $arDays = [
'MO',
'TU',
'WE',
'TH',
'FR',
'SA',
'SU'];
1918 $weekdays = [
'SU',
'MO',
'TU',
'WE',
'TH',
'FR',
'SA'];
1919 $weekdays = array_combine($weekdays, array_keys(array_values($weekdays)));
1920 return $weekdays[$weekday] ?? 0;
1927 if (is_array(
$res) && isset(
$res[
'originalDavXmlId']))
1932 if (is_array(
$res) && isset(
$res[
'id']))
1946 if (self::$type && !isset(
$arFields[
'CAL_TYPE']))
1954 if (self::$bOwner && !isset(
$arFields[
'OWNER_ID']))
1974 $params[
'editParentEvents'] =
$params[
'editParentEvents'] ??
false;
1976 $params[
'originalDavXmlId'] =
$params[
'originalDavXmlId'] ??
null;
1980 $params[
'sendInvitesToDeclined'] =
$params[
'sendInvitesToDeclined'] ??
false;
1981 $params[
'autoDetectSection'] =
$params[
'autoDetectSection'] ??
false;
1982 $userId =
$params[
'userId'] ?? self::getCurUserId();
1988 $sectionId = (int)
$arFields[
'SECTION_ID'];
1998 $bPersonal = self::IsPersonal(
$arFields[
'CAL_TYPE'] ??
null,
$arFields[
'OWNER_ID'] ??
null, $userId);
1999 $checkPermission = !isset(
$params[
'checkPermission']) ||
$params[
'checkPermission'] !==
false;
2000 $silentErrorModePrev = self::$silentErrorMode;
2001 self::SetSilentErrorMode();
2019 $curEvent = self::getCurrentEventForSaving((
int)
$arFields[
'ID'], $userId, $checkPermission);
2023 $curEvent[
'EVENT_TYPE'] ??
'',
2043 if (!isset(
$arFields[
'DATE_FROM']) && isset($curEvent[
'DATE_FROM']))
2045 $arFields[
'DATE_FROM'] = $curEvent[
'DATE_FROM'];
2047 if (!isset(
$arFields[
'DATE_TO']) && isset($curEvent[
'DATE_TO']))
2049 $arFields[
'DATE_TO'] = $curEvent[
'DATE_TO'];
2052 $canChangeDateRecurrenceEvent = isset(
$params[
'recursionEditMode'])
2053 && in_array(
$params[
'recursionEditMode'], [
'all',
''],
true)
2054 && ((
$arFields[
'DATE_FROM'] ??
null) !== ($curEvent[
'DATE_FROM'] ??
null))
2055 && (
$arFields[
'RRULE'][
'FREQ'] ??
null) !==
'NONE'
2058 if ($canChangeDateRecurrenceEvent)
2060 $arFields[
'DATE_FROM'] = self::GetOriginalDate(
2062 $curEvent[
'DATE_FROM'],
2065 $arFields[
'DATE_TO'] = self::GetOriginalDate(
2067 $curEvent[
'DATE_TO'],
2072 $bPersonal = $bPersonal && self::IsPersonal($curEvent[
'CAL_TYPE'], $curEvent[
'OWNER_ID'], $userId);
2074 $arFields[
'CAL_TYPE'] = $curEvent[
'CAL_TYPE'];
2075 $arFields[
'OWNER_ID'] = $curEvent[
'OWNER_ID'];
2076 $arFields[
'CREATED_BY'] = $curEvent[
'CREATED_BY'];
2077 $arFields[
'ACTIVE'] = $curEvent[
'ACTIVE'] ??
null;
2079 $eventModel = CCalendarEvent::getEventModelForPermissionCheck((
int)($curEvent[
'ID'] ?? 0), $curEvent, $userId);
2081 $accessCheckResult =
$accessController->check(ActionDictionary::ACTION_EVENT_EDIT, $eventModel);
2082 $bChangeMeeting = !$checkPermission || $accessCheckResult;
2084 if (!$bChangeMeeting)
2086 return Loc::getMessage(
'EC_ACCESS_DENIED');
2095 $arFields[
'DESCRIPTION'] = $curEvent[
'DESCRIPTION'];
2097 if (!isset(
$arFields[
'COLOR']) && $curEvent[
'COLOR'])
2099 $arFields[
'COLOR'] = $curEvent[
'COLOR'];
2101 if (!isset(
$arFields[
'TEXT_COLOR']) && !empty($curEvent[
'TEXT_COLOR']))
2103 $arFields[
'TEXT_COLOR'] = $curEvent[
'TEXT_COLOR'];
2107 $arFields[
'SECTIONS'] = [$curEvent[
'SECT_ID']];
2112 $arFields[
'IS_MEETING'] = $curEvent[
'IS_MEETING'];
2116 $arFields[
'MEETING_HOST'] = $curEvent[
'MEETING_HOST'];
2118 if (!isset(
$arFields[
'MEETING_STATUS']))
2120 $arFields[
'MEETING_STATUS'] = $curEvent[
'MEETING_STATUS'];
2122 if (!isset(
$arFields[
'ACTIVE']) && isset($curEvent[
'ACTIVE']))
2124 $arFields[
'ACTIVE'] = $curEvent[
'ACTIVE'];
2128 $arFields[
'PRIVATE_EVENT'] = $curEvent[
'PRIVATE_EVENT'];
2132 $arFields[
'ACCESSIBILITY'] = $curEvent[
'ACCESSIBILITY'];
2136 $arFields[
'IMPORTANCE'] = $curEvent[
'IMPORTANCE'];
2140 $arFields[
'SKIP_TIME'] = $curEvent[
'DT_SKIP_TIME'] ===
'Y';
2144 $arFields[
'TZ_FROM'] = $curEvent[
'TZ_FROM'];
2148 $arFields[
'TZ_TO'] = $curEvent[
'TZ_TO'];
2152 $arFields[
'RELATIONS'] = $curEvent[
'RELATIONS'];
2156 $arFields[
'MEETING'] = $curEvent[
'MEETING'];
2158 if (!isset(
$arFields[
'SYNC_STATUS']) && $curEvent[
'SYNC_STATUS'])
2160 $arFields[
'SYNC_STATUS'] = $curEvent[
'SYNC_STATUS'];
2162 if (!isset(
$arFields[
'EVENT_TYPE']) && $curEvent[
'EVENT_TYPE'])
2164 $arFields[
'EVENT_TYPE'] = $curEvent[
'EVENT_TYPE'];
2166 $arFields[
'MEETING'][
'LANGUAGE_ID'] = self::getUserLanguageId((
int)$userId);
2171 && !empty($curEvent[
'ATTENDEE_LIST'])
2172 && is_array($curEvent[
'ATTENDEE_LIST'])
2176 foreach ($curEvent[
'ATTENDEE_LIST'] as $attendee)
2178 $arFields[
'ATTENDEES'][] = $attendee[
'id'];
2183 $arFields[
'ATTENDEES_CODES'] = $curEvent[
'ATTENDEES_CODES'];
2186 if (!isset(
$arFields[
'LOCATION']) && $curEvent[
'LOCATION'] !==
"")
2189 'OLD' => $curEvent[
'LOCATION'],
2190 'NEW' => $curEvent[
'LOCATION'],
2194 $parsedLoc = Bitrix\Calendar\Rooms\Util::parseLocation($curEvent[
'LOCATION']);
2195 if ($parsedLoc[
'room_event_id'])
2197 $arFields[
'LOCATION'][
'NEW'] =
'calendar_' . $parsedLoc[
'room_id'];
2203 $arFields[
'SECTION_ID'] = $curEvent[
'SECT_ID'];
2204 $arFields[
'SECTIONS'] = [$curEvent[
'SECT_ID']];
2209 (!empty(
$params[
'bSilentAccessMeeting'])
2210 || (isset(
$params[
'fromWebservice']) &&
$params[
'fromWebservice'] ===
true)
2212 && !empty($curEvent[
'IS_MEETING'])
2213 && ($curEvent[
'PARENT_ID'] !== $curEvent[
'ID'])
2222 if (!isset(
$arFields[
"RRULE"]) && $curEvent[
"RRULE"] !==
'' && (
$params[
'fromWebservice'] ??
null) !==
true)
2224 $arFields[
"RRULE"] = CCalendarEvent::ParseRRULE($curEvent[
"RRULE"]);
2228 ((
$params[
'fromWebservice'] ??
null) ===
true)
2230 && CCalendarEvent::CheckRecurcion($curEvent)
2233 $arFields[
"RRULE"] = CCalendarEvent::ParseRRULE($curEvent[
'RRULE']);
2238 $arFields[
'EXDATE'] = $curEvent[
'EXDATE'];
2242 isset(
$arFields[
'EXDATE'], $curEvent[
'EXDATE'])
2244 && $curEvent[
'EXDATE']
2248 $arFields[
'EXDATE'] = self::mergeExcludedDates($curEvent[
'EXDATE'],
$arFields[
'EXDATE']);
2253 $params[
'currentEvent'] = $curEvent;
2259 elseif ($checkPermission && $sectionId > 0 && !$bPersonal)
2261 $section = CCalendarSect::GetList([
'arFilter' => [
'ID' => $sectionId],
2262 'checkPermissions' =>
false,
2263 'getPermissions' =>
false,
2268 $arFields[
'CAL_TYPE'] = $section[
'CAL_TYPE'];
2272 return self::ThrowError(Loc::getMessage(
'EC_ACCESS_DENIED'));
2277 $userCollabIds = UserCollabs::getInstance()->getIds($userId);
2278 if (!in_array((
int)$section[
'OWNER_ID'], $userCollabIds,
true))
2280 return self::ThrowError(Loc::getMessage(
'EC_ACCESS_DENIED'));
2286 EventModel::createNew()
2287 ->setOwnerId((
int)
$arFields[
'OWNER_ID'])
2288 ->setSectionId((
int)$sectionId)
2289 ->setSectionType(
$arFields[
'CAL_TYPE']);
2291 if (!
$accessController->check(ActionDictionary::ACTION_EVENT_ADD, $newEventModel))
2293 return self::ThrowError(Loc::getMessage(
'EC_ACCESS_DENIED'));
2298 if (
$params[
'autoDetectSection'] && $sectionId <= 0)
2303 $sectionId = self::GetMeetingSection(
$arFields[
'OWNER_ID'],
true);
2306 $res = CCalendarSect::GetList(
2317 if (!
$res || !
$res[0] || CCalendarSect::CheckGoogleVirtualSection(
$res[0][
'GAPI_CALENDAR_ID']))
2340 $sectRes = CCalendarSect::GetSectionForOwner(
2345 if ($sectRes[
'sectionId'] > 0)
2347 $sectionId = $sectRes[
'sectionId'];
2349 if ($sectRes[
'autoCreated'])
2351 $params[
'bAffectToDav'] =
false;
2381 $arFields[
'TZ_TO'] = $tzFrom->getName();
2384 $arFields[
'TZ_FROM'] = $tzFrom->getName();
2387 if ($bNew && !
$params[
'editInstance'] && !(
$arFields[
'DAV_XML_ID'] ??
null))
2389 $arFields[
'DAV_XML_ID'] = UidGenerator::createInstance()
2409 if (!isset(
$arFields[
'VERSION']) || (
$arFields[
'VERSION'] <= ($curEvent[
'VERSION'] ??
null)))
2411 $arFields[
'VERSION'] = ($curEvent[
'VERSION'] ??
null)
2412 ? $curEvent[
'VERSION'] + 1
2417 if (
$params[
'autoDetectSection'] && $sectionId <= 0 &&
$arFields[
'OWNER_ID'] > 0)
2419 $res = CCalendarSect::GetList(
2425 'checkPermissions' =>
false,
2430 $sectionId =
$res[0][
'ID'];
2434 $defCalendar = CCalendarSect::CreateDefault(
array(
2438 $sectionId = $defCalendar[
'ID'];
2439 self::SetCurUserMeetingSection($defCalendar[
'ID']);
2441 $params[
'bAffectToDav'] =
false;
2453 $bExchange = self::IsExchangeEnabled() &&
$arFields[
'CAL_TYPE'] ===
'user';
2454 $bCalDav = self::IsCalDAVEnabled() &&
$arFields[
'CAL_TYPE'] ===
'user';
2457 ((
$params[
'editNextEvents'] ??
null) ===
false && (
$params[
'recursionEditMode'] ??
null) ===
'next')
2458 || (in_array(
$params[
'recursionEditMode'] ??
null, [
'this',
'skip'])
2459 && (
$params[
'editInstance'] ??
null) ===
false)
2464 if ((
$params[
'editParentEvents'] ??
null) ===
true)
2472 (
$params[
'bAffectToDav'] ??
null) !==
false
2473 && ($bExchange || $bCalDav)
2475 && !(isset(
$params[
'dontSyncParent']) &&
$params[
'dontSyncParent'])
2476 && (
$params[
'overSaving'] ??
false) !==
true
2482 'bCalDav' => $bCalDav,
2483 'bExchange' => $bExchange,
2484 'sectionId' => $sectionId,
2485 'modeSync' =>
$params[
'modeSync'],
2486 'editInstance' =>
$params[
'editInstance'],
2487 'originalDavXmlId' =>
$params[
'originalDavXmlId'],
2488 'instanceTz' =>
$params[
'instanceTz'],
2489 'editParentEvents' =>
$params[
'editParentEvents'],
2490 'editNextEvents' =>
$params[
'editNextEvents'],
2491 'syncCaldav' =>
$params[
'syncCaldav'],
2492 'parentDateFrom' => (
$params[
'parentDateFrom'] ??
null),
2493 'parentDateTo' => (
$params[
'parentDateTo'] ??
null),
2498 if (
$res !==
true && self::$silentErrorMode ===
true)
2500 self::ThrowError(
$res);
2509 isset($curEvent[
'EVENT_TYPE'])
2513 $params[
'isSharingEvent'] = $isSharingEvent;
2515 if (!empty(
$arFields[
'ID']) && $isSharingEvent)
2522 && in_array((
$params[
'recursionEditMode'] ??
null), [
'this',
'next'],
true)
2523 && CCalendarEvent::CheckRecurcion($curEvent)
2527 if (
$params[
'recursionEditMode'] ===
'this')
2530 $excludeDates = CCalendarEvent::GetExDate($curEvent[
'EXDATE']);
2531 $excludeDate = self::Date(
2532 self::Timestamp(
$params[
'currentEventDateFrom'] ??
$arFields[
'DATE_FROM']),
2535 $excludeDates[] = $excludeDate;
2538 'recursionEditMode' =>
'skip',
2539 'silentErrorMode' =>
$params[
'silentErrorMode'],
2540 'sendInvitesToDeclined' =>
$params[
'sendInvitesToDeclined'],
2541 'sendInvitations' =>
false,
2542 'sendEditNotification' =>
false,
2543 'userId' => $userId,
2544 'requestUid' =>
$params[
'requestUid'] ??
null,
2545 'checkPermission' => $checkPermission,
2548 $arFieldsCurrent = [
2549 'ID' => $curEvent[
"ID"],
2550 'EXDATE' => CCalendarEvent::SetExDate($excludeDates),
2554 !empty(
$params[
'arFields'][
'SECTIONS'][0])
2555 && (
int)$curEvent[
'SECTION_ID'] !== (
int)
$params[
'arFields'][
'SECTIONS'][0]
2558 $arFieldsCurrent[
'SECTIONS'] =
$params[
'arFields'][
'SECTIONS'];
2559 $arFieldsCurrent[
'CAL_TYPE'] =
$params[
'arFields'][
'CAL_TYPE'];
2560 $arFieldsCurrent[
'OWNER_ID'] = $userId;
2563 $saveEventData[
'arFields'] = $arFieldsCurrent;
2566 $id = self::SaveEvent($saveEventData);
2571 if (!($newParams[
'arFields'][
'MEETING'][
'REINVITE'] ??
null))
2573 $newParams[
'saveAttendeesStatus'] =
true;
2576 $newParams[
'arFields'][
'RECURRENCE_ID'] = $curEvent[
'RECURRENCE_ID'] ?: $newParams[
'arFields'][
'ID'];
2577 $newParams[
'arFields'][
'ORIGINAL_RECURSION_ID'] = (int)($curEvent[
'ORIGINAL_RECURSION_ID'] ?? $newParams[
'arFields'][
'ID']);
2580 $newParams[
'arFields'][
'ID'],
2581 $newParams[
'arFields'][
'DAV_XML_ID'],
2582 $newParams[
'arFields'][
'G_EVENT_ID'],
2583 $newParams[
'arFields'][
'SYNC_STATUS'],
2584 $newParams[
'arFields'][
'CAL_DAV_LABEL'],
2585 $newParams[
'arFields'][
'RRULE'],
2586 $newParams[
'arFields'][
'EXDATE'],
2587 $newParams[
'recursionEditMode'],
2590 $newParams[
'arFields'][
'REMIND'] =
$params[
'currentEvent'][
'REMIND'];
2592 $fromTs = self::Timestamp($newParams[
'currentEventDateFrom']);
2593 $currentFromTs = self::Timestamp($newParams[
'arFields'][
'DATE_FROM']);
2594 $length = self::Timestamp($newParams[
'arFields'][
'DATE_TO']) - self::Timestamp($newParams[
'arFields'][
'DATE_FROM']);
2596 if (!isset($newParams[
'arFields'][
'DATE_FROM'], $newParams[
'arFields'][
'DATE_TO']))
2598 $length = $curEvent[
'DT_LENGTH'];
2599 $currentFromTs = self::Timestamp($curEvent[
'DATE_FROM']);
2602 $instanceDate = !isset($newParams[
'arFields'][
'DATE_FROM'])
2603 || self::Date(self::Timestamp($curEvent[
'DATE_FROM']),
false) === self::Date($currentFromTs,
false);
2605 if ($newParams[
'arFields'][
'SKIP_TIME'])
2609 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($fromTs,
false);
2610 $newParams[
'arFields'][
'DATE_TO'] = self::Date($fromTs + $length - self::GetDayLen(),
false);
2614 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($currentFromTs,
false);
2615 $newParams[
'arFields'][
'DATE_TO'] = self::Date($currentFromTs + $length - self::GetDayLen(),
false);
2620 $newFromTs = self::DateWithNewTime($currentFromTs, $fromTs);
2621 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($newFromTs);
2622 $newParams[
'arFields'][
'DATE_TO'] = self::Date($newFromTs + $length);
2625 $eventMod = $curEvent;
2626 if (!isset($eventMod[
'~DATE_FROM']))
2628 $eventMod[
'~DATE_FROM'] = $eventMod[
'DATE_FROM'];
2631 $eventMod[
'DATE_FROM'] = $newParams[
'currentEventDateFrom'];
2632 $commentXmlId = CCalendarEvent::GetEventCommentXmlId($eventMod);
2633 $newParams[
'arFields'][
'RELATIONS'] =
array(
'COMMENT_XML_ID' => $commentXmlId);
2634 $newParams[
'editInstance'] =
true;
2635 $newParams[
'sendEditNotification'] =
true;
2638 $newParams[
'arFields'][
'ORIGINAL_DATE_FROM'] = self::GetOriginalDate(
2639 $params[
'currentEvent'][
'DATE_FROM'],
2640 $eventMod[
'DATE_FROM'],
2641 $newParams[
'arFields'][
'TZ_FROM']
2643 $newParams[
'originalDavXmlId'] =
$params[
'currentEvent'][
'G_EVENT_ID'];
2644 $newParams[
'instanceTz'] =
$params[
'currentEvent'][
'TZ_FROM'];
2645 $newParams[
'parentDateFrom'] =
$params[
'currentEvent'][
'DATE_FROM'];
2646 $newParams[
'parentDateTo'] =
$params[
'currentEvent'][
'DATE_TO'];
2647 $newParams[
'requestUid'] =
$params[
'requestUid'] ??
null;
2648 $newParams[
'sendInvitesToDeclined'] =
$params[
'sendInvitesToDeclined'] ??
null;
2651 $eventMod[
'DATE_FROM'] = $newParams[
'arFields'][
'ORIGINAL_DATE_FROM'];
2652 $eventMod[
'DATE_TO'] = self::Date(
2653 self::Timestamp($eventMod[
'DATE_FROM'],
false) + $curEvent[
'DT_LENGTH']
2655 unset($eventMod[
'RRULE']);
2657 $instanceChanges = CCalendarEvent::CheckEntryChanges($newParams[
'arFields'], $eventMod);
2658 if (!empty($instanceChanges))
2660 $newParams[
'instanceChanges'] = $instanceChanges;
2663 $result[
'recEventId'] = self::SaveEvent($newParams);
2666 $newParams[
'arFields'][
'ORIGINAL_RECURSION_ID'] ?? 0,
2672 $currentDateTimestamp = self::Timestamp(
$params[
'currentEventDateFrom'] ??
null);
2676 $recId = $curEvent[
'RECURRENCE_ID'] ?: $newParams[
'arFields'][
'ID'];
2678 $newParams[
'arFields'][
'RELATIONS'] ??= [];
2679 $newParams[
'arFields'][
'RELATIONS'] = [
2680 'ORIGINAL_RECURSION_ID' => $curEvent[
'RELATIONS'][
'ORIGINAL_RECURSION_ID'] ?? $recId,
2682 $newParams[
'arFields'][
'ORIGINAL_RECURSION_ID'] = (int)($curEvent[
'ORIGINAL_RECURSION_ID'] ?? $recId);
2684 if (empty($newParams[
'arFields'][
'MEETING'][
'REINVITE']))
2686 $newParams[
'saveAttendeesStatus'] =
true;
2689 $currentFromTs = self::Timestamp($newParams[
'arFields'][
'DATE_FROM'] ??
null);
2690 $length = self::Timestamp($newParams[
'arFields'][
'DATE_TO']) - self::Timestamp($newParams[
'arFields'][
'DATE_FROM']);
2692 if (!isset($newParams[
'arFields'][
'DATE_FROM'], $newParams[
'arFields'][
'DATE_TO']))
2694 $length = $curEvent[
'DT_LENGTH'];
2695 $currentFromTs = self::Timestamp($curEvent[
'DATE_FROM']);
2699 [$isRecurrentLocationChanged, $newParams] = self::checkRecurrenceLocationChanges($newParams, $curEvent);
2701 $isRecurrentNameChanged = !empty($newParams[
'arFields'][
'NAME']) && ($curEvent[
'NAME'] !== $newParams[
'arFields'][
'NAME']);
2703 $isRecurrentTimeChanged = (self::Timestamp($curEvent[
'DATE_FROM'],
false) % self::DAY_LENGTH) !== ($currentFromTs % self::DAY_LENGTH);
2705 $isRecurrentAttendeesChanged = self::checkRecurrenceAttendeesChanges($newParams, $curEvent);
2707 $instanceDate = !isset($newParams[
'arFields'][
'DATE_FROM'])
2708 || self::Date(self::Timestamp($curEvent[
'DATE_FROM']),
false) === self::Date($currentFromTs,
false)
2711 if ($newParams[
'arFields'][
'SKIP_TIME'])
2715 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($currentDateTimestamp,
false);
2716 $newParams[
'arFields'][
'DATE_TO'] = self::Date($currentDateTimestamp + $length,
false);
2720 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($currentFromTs,
false);
2721 $newParams[
'arFields'][
'DATE_TO'] = self::Date($currentFromTs + $length,
false);
2726 $newFromTs = self::DateWithNewTime($currentFromTs, $currentDateTimestamp);
2727 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($newFromTs);
2728 $newParams[
'arFields'][
'DATE_TO'] = self::Date($newFromTs + $length);
2731 if (isset($curEvent[
'EXDATE']) && $curEvent[
'EXDATE'] !==
'')
2733 $newParams[
'arFields'][
'EXDATE'] = $curEvent[
'EXDATE'];
2736 if (!isset($newParams[
'arFields'][
'REMIND']) && !empty($curEvent[
'REMIND']))
2738 $newParams[
'arFields'][
'REMIND'] = $curEvent[
'REMIND'];
2741 if (isset($newParams[
'arFields'][
'RRULE'][
'COUNT']) && $newParams[
'arFields'][
'RRULE'][
'COUNT'] > 0)
2744 'rrule' => $newParams[
'arFields'][
'RRULE'],
2745 'dateFrom' => $curEvent[
'DATE_FROM'],
2746 'dateTo' => $newParams[
'arFields'][
'DATE_FROM'],
2747 'timeZone' => $curEvent[
'TZ_FROM'],
2750 $newParams[
'arFields'][
'RRULE'][
'COUNT'] = self::CountNumberFollowEvents($countParams);
2751 unset($newParams[
'arFields'][
'RRULE'][
'UNTIL'], $newParams[
'arFields'][
'RRULE'][
'~UNTIL']);
2755 isset($newParams[
'arFields'][
'RRULE'][
'FREQ'], $curEvent[
'RRULE'][
'FREQ'])
2756 && $newParams[
'arFields'][
'RRULE'][
'FREQ'] ===
'WEEKLY'
2757 && $curEvent[
'RRULE'][
'FREQ'] ===
'WEEKLY'
2758 && $newParams[
'arFields'][
'RRULE'][
'BYDAY'] === $curEvent[
'RRULE'][
'BYDAY']
2762 $currentFromDate =
new Type\Date($newParams[
'arFields'][
'DATE_FROM']);
2763 $currentDateWeekday = self::WeekDayByInd($currentDate->format(
'N'));
2764 $currentFromDateWeekday = self::WeekDayByInd($currentFromDate->format(
'N'));
2766 if (isset($newParams[
'arFields'][
'RRULE'][
'BYDAY'][$currentDateWeekday]))
2768 unset($newParams[
'arFields'][
'RRULE'][
'BYDAY'][$currentDateWeekday]);
2771 $newParams[
'arFields'][
'RRULE'][
'BYDAY'][$currentFromDateWeekday] = $currentFromDateWeekday;
2775 if (self::Date(self::Timestamp($curEvent[
'DATE_FROM']),
false) === self::Date($currentDateTimestamp,
false))
2777 $newParams[
'recursionEditMode'] =
'skip';
2782 $arFieldsCurrent = [
2783 "ID" => $curEvent[
"ID"],
2784 "RRULE" => CCalendarEvent::ParseRRULE($curEvent[
'RRULE']),
2786 $arFieldsCurrent[
'RRULE'][
'UNTIL'] = self::Date($currentDateTimestamp - self::GetDayLen(),
false);
2787 unset($arFieldsCurrent[
'RRULE'][
'~UNTIL'], $arFieldsCurrent[
'RRULE'][
'COUNT']);
2790 !empty(
$params[
'arFields'][
'SECTIONS'][0])
2791 && (
int)$curEvent[
'SECTION_ID'] !== (
int)
$params[
'arFields'][
'SECTIONS'][0]
2794 $arFieldsCurrent[
'SECTIONS'] =
$params[
'arFields'][
'SECTIONS'];
2798 $id = self::SaveEvent([
2799 'arFields' => $arFieldsCurrent,
2800 'silentErrorMode' =>
$params[
'silentErrorMode'] ??
null,
2801 'recursionEditMode' =>
'skip',
2802 'sendInvitations' =>
false,
2803 'sendEditNotification' =>
false,
2804 'sendInvitesToDeclined' =>
$params[
'sendInvitesToDeclined'] ??
null,
2805 'userId' => $userId,
2806 'editNextEvents' =>
true,
2807 'editParentEvents' =>
true,
2808 'checkPermission' => $checkPermission,
2809 'requestUid' =>
$params[
'requestUid'] ??
null,
2810 'checkLocationOccupancyFields' => $newParams[
'arFields'],
2811 'checkLocationOccupancy' =>
$params[
'checkLocationOccupancy'] ??
false,
2815 $newParams[
'arFields'][
'ID'],
2816 $newParams[
'arFields'][
'DAV_XML_ID'],
2817 $newParams[
'arFields'][
'G_EVENT_ID'],
2818 $newParams[
'recursionEditMode']
2822 if (empty($newParams[
'arFields'][
'DAV_XML_ID']))
2824 $newParams[
'arFields'][
'DAV_XML_ID'] = UidGenerator::createInstance()
2828 $newParams[
'arFields'][
'ORIGINAL_DATE_FROM'] ??
null,
2829 $newParams[
'arFields'][
'SKIP_TIME'] ??
null,
2830 $newParams[
'arFields'][
'TZ_FROM'] ??
null
2833 ->setUserId((
int)($newParams[
'arFields'][
'OWNER_ID'] ??
null))
2838 $newParams[
'sendInvitesToDeclined'] =
$params[
'sendInvitesToDeclined'];
2839 $newParams[
'editNextEvents'] =
true;
2840 $newParams[
'previousRecurrentId'] = $recId;
2842 $result = self::SaveEvent($newParams);
2853 $newParams[
'arFields'][
'ORIGINAL_RECURSION_ID'] ?? 0,
2858 $recRelatedEvents = CCalendarEvent::GetEventsByRecId($recId,
false);
2860 foreach($recRelatedEvents as $recRelatedEvent)
2862 if ($recRelatedEvent[
'ID'] ===
$result[
'id'])
2867 if ((
int)$recRelatedEvent[
'ID'] !== (
int)$recRelatedEvent[
'PARENT_ID'])
2872 $evFromTs = self::Timestamp($recRelatedEvent[
'DATE_FROM'],
false);
2873 $instanceLength = $recRelatedEvent[
'DT_LENGTH'] ?? $length;
2875 if ($evFromTs > $currentDateTimestamp)
2877 $newParams[
'arFields'][
'ID'] = $recRelatedEvent[
'ID'];
2878 $newParams[
'arFields'][
'RRULE'] = CCalendarEvent::ParseRRULE($recRelatedEvent[
'RRULE']);
2886 if ($newParams[
'arFields'][
'SKIP_TIME'])
2888 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($evFromTs,
false);
2889 $newParams[
'arFields'][
'DATE_TO'] = self::Date(self::Timestamp($recRelatedEvent[
'DATE_TO']),
false);
2891 else if ($isRecurrentTimeChanged)
2893 $newFromTs = self::DateWithNewTime($currentFromTs, $evFromTs);
2894 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($newFromTs);
2895 $newParams[
'arFields'][
'DATE_TO'] = self::Date($newFromTs + $length);
2899 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($evFromTs);
2900 $newParams[
'arFields'][
'DATE_TO'] = self::Date($evFromTs + $instanceLength);
2908 $newRecurrentLocation =
null;
2909 if (!$isRecurrentLocationChanged)
2911 $newRecurrentLocation = $newParams[
'arFields'][
'LOCATION'] ??
'';
2913 if (!empty($recRelatedEvent[
'LOCATION']))
2915 $parsedRelatedLocation = Rooms\Util::parseLocation($recRelatedEvent[
'LOCATION']);
2916 if (!empty($parsedRelatedLocation[
'room_event_id']))
2918 $recRelatedEvent[
'LOCATION'] =
'calendar_' . $parsedRelatedLocation[
'room_id'];
2922 $newParams[
'arFields'][
'LOCATION'] = $recRelatedEvent[
'LOCATION'] ??
'';
2928 if (!$isRecurrentNameChanged)
2930 $newParams[
'arFields'][
'NAME'] = $recRelatedEvent[
'NAME'] ?? $newParams[
'arFields'][
'NAME'];
2937 if (!$isRecurrentAttendeesChanged)
2939 $newParams[
'arFields'][
'ATTENDEES_CODES'] = $recRelatedEvent[
'ATTENDEES_CODES']
2940 ?? $newParams[
'arFields'][
'ATTENDEES_CODES']
2947 $newParams[
'arFields'][
'RECURRENCE_ID'] =
$result[
'id'];
2948 $newParams[
'originalDavXmlId'] =
$result[
'originalDavXmlId'];
2950 $parentDateTime = self::Date($currentFromTs);
2951 $newParams[
'arFields'][
'ORIGINAL_DATE_FROM'] = self::GetOriginalDate(
2953 $recRelatedEvent[
'ORIGINAL_DATE_FROM'] ?? $newParams[
'currentEventDateFrom'],
2957 $newParams[
'instanceTz'] =
$result[
'instanceTz'];
2958 $newParams[
'editInstance'] =
true;
2960 unset($newParams[
'arFields'][
'EXDATE']);
2962 if (isset($newParams[
'arFields'][
'RELATIONS'][
'ORIGINAL_RECURSION_ID']))
2964 unset($newParams[
'arFields'][
'RELATIONS']);
2967 self::SaveEvent($newParams);
2969 if ($newRecurrentLocation !==
null)
2971 $newParams[
'arFields'][
'LOCATION'] = $newRecurrentLocation;
2980 if ((
$params[
'recursionEditMode'] ??
null) !==
'all')
2982 $params[
'recursionEditMode'] =
'skip';
2986 $params[
'recursionEditMode'] =
'';
2989 $id = CCalendarEvent::Edit(
$params);
2994 if(!empty($UFs) && is_array($UFs))
2996 CCalendarEvent::UpdateUserFields($id, $UFs,
false);
2998 if (!empty(
$arFields[
'IS_MEETING']) && !empty($UFs[
'UF_WEBDAV_CAL_EVENT']))
3000 $UF =
$GLOBALS[
'USER_FIELD_MANAGER']->GetUserFields(
"CALENDAR_EVENT", $id, LANGUAGE_ID);
3001 self::UpdateUFRights(
3002 $UFs[
'UF_WEBDAV_CAL_EVENT'],
3004 $UF[
'UF_WEBDAV_CAL_EVENT'] ??
null
3008 CCalendarEvent::updateSearchIndex((
int)$id, [
3009 'updateAllByParent' =>
true,
3014 if (
$params[
'editNextEvents'] ===
true &&
$params[
'editParentEvents'] ===
false)
3026 && !
$params[
'recursionEditMode']
3027 && !(
$params[
'arFields'][
'RECURRENCE_ID'] ??
null)
3028 && CCalendarEvent::CheckRecurcion($curEvent)
3032 $recId = $curEvent[
'RECURRENCE_ID'] ?: $curEvent[
'ID'];
3046 $currentFromTs = self::Timestamp(
$params[
'arFields'][
'DATE_FROM'],
false);
3047 $length = self::Timestamp(
$params[
'arFields'][
'DATE_TO'],
false) - self::Timestamp(
$params[
'arFields'][
'DATE_FROM'],
false);
3049 if (!isset(
$params[
'arFields'][
'DATE_FROM'],
$params[
'arFields'][
'DATE_TO']))
3051 $length = $curEvent[
'DT_LENGTH'];
3052 $currentFromTs = self::Timestamp($curEvent[
'DATE_FROM']);
3056 [$isRecurrentLocationChanged,
$params] = self::checkRecurrenceLocationChanges(
$params, $curEvent);
3058 $isRecurrentNameChanged = !empty(
$params[
'arFields'][
'NAME']) && ($curEvent[
'NAME'] !==
$params[
'arFields'][
'NAME']);
3060 $isRecurrentTimeChanged = (self::Timestamp($curEvent[
'DATE_FROM'],
false) % self::DAY_LENGTH) !== ($currentFromTs % self::DAY_LENGTH);
3062 $isRecurrentAttendeesChanged = self::checkRecurrenceAttendeesChanges(
$params, $curEvent);
3067 $instances = CCalendarEvent::GetEventsByRecId($recId,
false);
3071 $events = array_merge($events, $instances);
3075 foreach($events as $ev)
3077 if ($ev[
'ID'] === $curEvent[
'ID'])
3082 if ($ev[
'PARENT_ID'] !== $ev[
'ID'])
3089 $newParams[
'arFields'][
'ID'] = $ev[
'ID'];
3090 $newParams[
'arFields'][
'RECURRENCE_ID'] = $ev[
'RECURRENCE_ID'];
3091 $newParams[
'arFields'][
'DAV_XML_ID'] = $ev[
'DAV_XML_ID'];
3092 $newParams[
'arFields'][
'G_EVENT_ID'] = $ev[
'G_EVENT_ID'];
3093 $newParams[
'arFields'][
'ORIGINAL_DATE_FROM'] = self::GetOriginalDate(
$arFields[
'DATE_FROM'], $ev[
'ORIGINAL_DATE_FROM'],
$arFields[
'TZ_FROM']);
3094 $newParams[
'arFields'][
'CAL_DAV_LABEL'] = $ev[
'CAL_DAV_LABEL'];
3095 $newParams[
'arFields'][
'RRULE'] = CCalendarEvent::ParseRRULE($ev[
'RRULE']);
3096 $newParams[
'recursionEditMode'] =
'skip';
3097 $newParams[
'currentEvent'] = $ev;
3099 $eventFromTs = self::Timestamp($ev[
'DATE_FROM']);
3100 $instanceLength = $ev[
'DT_LENGTH'] ?? $length;
3108 if ($newParams[
'arFields'][
'SKIP_TIME'])
3110 $newParams[
'arFields'][
'DATE_FROM'] = $ev[
'DATE_FROM'];
3111 $newParams[
'arFields'][
'DATE_TO'] = self::Date($eventFromTs + $length,
false);
3113 else if ($isRecurrentTimeChanged)
3115 $newFromTs = self::DateWithNewTime($currentFromTs, $eventFromTs);
3116 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($newFromTs);
3117 $newParams[
'arFields'][
'DATE_TO'] = self::Date($newFromTs + $length);
3121 $newParams[
'arFields'][
'DATE_FROM'] = self::Date($eventFromTs);
3122 $newParams[
'arFields'][
'DATE_TO'] = self::Date($eventFromTs + $instanceLength);
3130 if (!$isRecurrentLocationChanged)
3132 if (!empty($ev[
'LOCATION']))
3134 $parsedRelatedLocation = Rooms\Util::parseLocation($ev[
'LOCATION']);
3135 if (!empty($parsedRelatedLocation[
'room_event_id']))
3137 $ev[
'LOCATION'] =
'calendar_' . $parsedRelatedLocation[
'room_id'];
3141 $newParams[
'arFields'][
'LOCATION'] = $ev[
'LOCATION'] ??
'';
3147 if (!$isRecurrentNameChanged)
3149 $newParams[
'arFields'][
'NAME'] = $ev[
'NAME'] ?? $newParams[
'arFields'][
'NAME'];
3156 if (!$isRecurrentAttendeesChanged)
3158 $newParams[
'arFields'][
'ATTENDEES_CODES'] = $ev[
'ATTENDEES_CODES']
3159 ?? $newParams[
'arFields'][
'ATTENDEES_CODES']
3163 if (isset($ev[
'EXDATE']) && $ev[
'EXDATE'])
3165 $newParams[
'arFields'][
'EXDATE'] = $ev[
'EXDATE'];
3168 if (isset($newParams[
'arFields'][
'RELATIONS'][
'ORIGINAL_RECURSION_ID']))
3170 unset($newParams[
'arFields'][
'RELATIONS']);
3173 self::SaveEvent($newParams);
3183 if ((
$params[
'overSaving'] ??
false) !==
true)
3185 foreach(
GetModuleEvents(
"calendar",
"OnAfterCalendarEventEdit",
true) as $arEvent)
3192 self::SetSilentErrorMode($silentErrorModePrev);
3199 private static function CountNumberFollowEvents(
$params)
3201 $curCount = self::CountPastEvents(
$params);
3215 if (isset(self::$userLanguageId[$userId]))
3217 return self::$userLanguageId[$userId];
3221 ->where(
'ID', $userId)
3222 ->setSelect([
'NOTIFICATION_LANGUAGE_ID'])
3227 self::$userLanguageId[$userId] =
$user[
'NOTIFICATION_LANGUAGE_ID'] ?? LANGUAGE_ID;
3229 return self::$userLanguageId[$userId];
3236 $dateFromTz = !empty(
$params[
'timeZone']) ? new \DateTimeZone(
$params[
'timeZone']) : new \DateTimeZone(
"UTC");
3237 $dateToTz = !empty(
$params[
'timeZone']) ? new \DateTimeZone(
$params[
'timeZone']) : new \DateTimeZone(
"UTC");
3241 $parentInfoDate = getdate($dateFrom->getTimestamp());
3242 $dateTo->setTime($parentInfoDate[
'hours'], $parentInfoDate[
'minutes']);
3244 $diff = $dateFrom->getDiff($dateTo);
3246 if (
$params[
'rrule'][
'FREQ'] ===
'DAILY')
3248 $diff = (int)$diff->format(
'%a');
3249 $curCount = $diff / (int)
$params[
'rrule'][
'INTERVAL'];
3252 if (
$params[
'rrule'][
'FREQ'] ===
'WEEKLY')
3254 $diff = (int)$diff->format(
'%a');
3256 for (
$i = 0;
$i < $diff;
$i++)
3258 $timestamp = $dateFrom->getTimestamp();
3259 $date = getdate($timestamp);
3260 $weekday = mb_strtoupper(mb_substr($date[
'weekday'], 0, 2));
3262 if (in_array($weekday,
$params[
'rrule'][
'BYDAY'],
true))
3267 $dateFrom = $dateFrom->add(
'+1 day');
3271 if (
$params[
'rrule'][
'FREQ'] ===
'MONTHLY')
3273 $diff = (int)$diff->format(
'%m');
3274 $curCount = $diff / (int)
$params[
'rrule'][
'INTERVAL'];
3277 if (
$params[
'rrule'][
'FREQ'] ===
'YEARLY')
3279 $diff = (int)$diff->format(
'%y');
3280 $curCount = $diff / (int)
$params[
'rrule'][
'INTERVAL'];
3288 if (self::$silentErrorMode)
3290 self::$errors[] =
$str;
3295 echo
'<!-- BX_EVENT_CALENDAR_ACTION_ERROR:'.$str.
'-->';
3301 return self::$errors;
3304 private static ?
array $tasksForUpdateUFRights =
null;
3306 private static function getTasksForUpdateUFRights():
array
3308 if (!Loader::includeModule(
'webdav'))
3313 if (self::$tasksForUpdateUFRights ===
null)
3315 self::$tasksForUpdateUFRights = CWebDavIblock::GetTasks() ?? [];
3318 return self::$tasksForUpdateUFRights;
3324 $arTasks = self::getTasksForUpdateUFRights();
3365 if (mb_strpos($value,
'SG') === 0)
3374 $arViewRights = $arEditRights = [];
3375 $curUserID =
'U'.$USER->GetID();
3378 if ($curUserID ==
$right)
3388 $dbWDFile = $ibe->GetList([],
array(
'ID' =>
$arFiles,
'SHOW_NEW' =>
'Y'),
false,
false,
array(
'ID',
'NAME',
'SECTION_ID',
'IBLOCK_ID',
'WF_NEW'));
3392 while ($arWDFile = $dbWDFile->Fetch())
3394 $id = $arWDFile[
'ID'];
3396 if ($arWDFile[
'WF_NEW'] ===
'Y')
3398 $ibe->Update($id, [
'BP_PUBLISHED' =>
'Y']);
3401 if (CIBlock::GetArrayByID($arWDFile[
'IBLOCK_ID'],
"RIGHTS_MODE") ===
"E")
3403 $ibRights = CWebDavIblock::_get_ib_rights_object(
'ELEMENT', $id, $arWDFile[
'IBLOCK_ID']);
3404 $ibRights->SetRights(CWebDavTools::appendRights($ibRights, $arViewRights,
$arTasks));
3405 if(empty($iblockIds[$arWDFile[
'IBLOCK_ID']]))
3406 $iblockIds[$arWDFile[
'IBLOCK_ID']] = $arWDFile[
'IBLOCK_ID'];
3421 public static function TempUser($TmpUser =
false, $create =
true,
$ID =
false)
3424 if ($create && $TmpUser ===
false && (!
$USER || !is_object(
$USER)))
3445 $type =
$params[
'arFields'][
'CAL_TYPE'] ?? self::$type;
3448 if ((
$params[
'bAffectToDav'] ??
null) !==
false && $type ===
'user' && self::IsExchangeEnabled(self::$ownerId))
3451 $ownerId =
$params[
'arFields'][
'OWNER_ID'] ?? self::$ownerId;
3453 if (
$params[
'arFields'][
'IS_EXCHANGE'])
3455 $exchRes = CDavExchangeCalendar::DoAddCalendar($ownerId,
$params[
'arFields']);
3458 if ($exchRes !==
true)
3460 if (!is_array($exchRes) || !isset($exchRes[
'XML_ID']))
3462 return self::ThrowError(self::CollectExchangeErrors($exchRes));
3466 $params[
'arFields'][
'DAV_EXCH_CAL'] = $exchRes[
'XML_ID'];
3467 $params[
'arFields'][
'DAV_EXCH_MOD'] = $exchRes[
'MODIFICATION_LABEL'];
3472 $id = (int)CCalendarSect::Edit(
$params);
3473 self::ClearCache([
'section_list',
'event_list']);
3501 $cache =
new CPHPCache;
3506 $cache->CleanDir(self::CachePath() . $path);
3514 return self::$cachePath;
3522 self::$silentErrorMode =
true;
3524 [$sectionId, $entityType,
$entityId] = $calendarId;
3525 $entityType = mb_strtolower($entityType);
3529 $xmlIdField =
'DAV_EXCH_LABEL';
3533 $xmlIdField =
'CAL_DAV_LABEL';
3540 $eventsList = CCalendarEvent::GetList([
3542 ...CCalendarEvent::$defaultSelectEvent,
3549 'CAL_TYPE' => $entityType,
3551 'SECTION' => $sectionId,
3553 'getUserfields' =>
false,
3554 'parseRecursion' =>
false,
3555 'fetchAttendees' =>
false,
3556 'fetchMeetings' =>
false,
3557 'userId' => $entityType ===
'user' ?
$entityId : 0,
3560 foreach ($eventsList as
$event)
3562 $eventXmlId =
$event[
'DAV_XML_ID'];
3563 if (
$event[
'RECURRENCE_ID'] && $instanceChangeKey = self::FindSyncInstance(
$event))
3565 $arCalendarItems[$eventXmlId] = $instanceChangeKey;
3568 if (isset($arCalendarItems[$eventXmlId]))
3570 if (
$event[$xmlIdField] !== $arCalendarItems[$eventXmlId])
3573 'XML_ID' => $eventXmlId,
3578 unset($arCalendarItems[$eventXmlId]);
3584 self::DeleteCalendarEvent(
$event[
"ID"], self::$userId);
3589 self::DeleteCalendarEvent(
$event[
"ID"], self::$userId);
3593 foreach ($arCalendarItems as
$key => $value)
3601 self::$silentErrorMode =
false;
3606 private static function FindSyncInstance(
$event)
3608 $exchangeScheme = COption::GetOptionString(
'dav',
'exchange_scheme',
'http');
3609 $exchangeServer = COption::GetOptionString(
'dav',
'exchange_server',
'');
3610 $exchangePort = COption::GetOptionString(
'dav',
'exchange_port',
'80');
3611 $exchangeUsername = COption::GetOptionString(
'dav',
'exchange_username',
'');
3612 $exchangePassword = COption::GetOptionString(
'dav',
'exchange_password',
'');
3614 if (empty($exchangeServer))
3619 $exchange =
new CDavExchangeCalendar($exchangeScheme, $exchangeServer, $exchangePort, $exchangeUsername, $exchangePassword);
3622 'dateTo' =>
$event[
'DATE_TO'],
3623 'parentDateTo' =>
$event[
'DATE_TO'],
3624 'dateFrom' =>
$event[
'DATE_FROM'],
3625 'parentDateFrom' =>
$event[
'DATE_FROM'],
3626 'parentTz' =>
$event[
'TZ_FROM'],
3627 'changekey' =>
$event[
'DAV_EXCH_LABEL'],
3630 [ , $changeKey] = $exchange->FindInstance(
$params);
3637 return CCalendarEvent::Delete(
array(
3639 'userId' => $userId,
3640 'bMarkDeleted' =>
true,
3645 public static function Color($color =
'', $defaultColor =
true)
3647 if ((
string)$color !==
'')
3649 $color = ltrim(trim(preg_replace(
'/\W/',
'', $color)),
"#");
3650 if (mb_strlen($color) > 6)
3652 $color = mb_substr($color, 0, 6);
3654 elseif(mb_strlen($color) < 6)
3659 $color =
'#'.$color;
3662 $DEFAULT_COLOR =
'#9dcf00';
3665 if ($defaultColor ===
true)
3667 $color = $DEFAULT_COLOR;
3671 $color = $defaultColor;
3730 $res = $h.
':'.$m.
' '.$ampm;
3734 $res = (($h < 10) ?
'0' :
'').$h.
':'.$m;
3744 self::$userId = self::GetCurUserId();
3746 return self::$userId;
3759 if ($avatar_src ===
false)
3761 $avatar_src = (isset(
$params[
'fillAvatar']) &&
$params[
'fillAvatar'] ===
false ?
'' :
'/bitrix/images/1.gif');
3774 if (!empty(
$user[
"PERSONAL_PHOTO"]))
3776 if (empty(
$params[
'AVATAR_SIZE']))
3780 $arFileTmp = CFile::ResizeImageGet(
3781 $user[
"PERSONAL_PHOTO"],
3788 $avatar_src = $arFileTmp[
'src'];
3792 $avatar_src =
false;
3799 if ($pathToUser ==
'')
3801 if (self::$pathToUser ==
'')
3803 if (empty(self::$pathesForSite))
3805 self::$pathesForSite = self::GetPathes(
SITE_ID);
3807 self::$pathToUser = self::$pathesForSite[
'path_to_user'];
3809 $pathToUser = self::$pathToUser;
3812 return CUtil::JSEscape(CComponentEngine::MakePathFromTemplate($pathToUser,
array(
"user_id" => $userId,
"USER_ID" => $userId)));
3817 $arTasks = self::GetAccessTasks($binging);
3821 if ($task[
'name'] ==
$name)
3834 if (isset(self::$arAccessTask[$binging]) && is_array(self::$arAccessTask[$binging]))
3836 return self::$arAccessTask[$binging];
3839 $bIntranet = self::IsIntranetEnabled();
3841 $res =
CTask::GetList(Array(
'ID' =>
'asc'), Array(
'MODULE_ID' =>
'calendar',
'BINDING' => $binging));
3844 if($type ===
'location')
3847 mb_strtolower(
$arRes[
'NAME']) ===
'calendar_view_time'
3848 || mb_strtolower(
$arRes[
'NAME']) ===
'calendar_view_title'
3857 mb_strtolower(
$arRes[
'NAME']) ===
'calendar_edit'
3858 || mb_strtolower(
$arRes[
'NAME']) ===
'calendar_view'
3859 || mb_strtolower(
$arRes[
'NAME']) ===
'calendar_type_edit'
3860 || mb_strtolower(
$arRes[
'NAME']) ===
'calendar_type_view')
3862 $name = Loc::getMessage(
'TASK_NAME_LOCATION_'.mb_strtoupper(
$arRes[
'NAME']));
3866 $name = Loc::getMessage(
'TASK_NAME_'.mb_strtoupper(
$arRes[
'NAME']));
3875 mb_strtolower(
$arRes[
'NAME']) ===
'calendar_view_time'
3876 || mb_strtolower(
$arRes[
'NAME']) ===
'calendar_view_title'
3886 $name = Loc::getMessage(
'TASK_NAME_' . mb_strtoupper(
$arRes[
'NAME']));
3896 'name' =>
$arRes[
'NAME'],
3909 if (!array_key_exists(
$code, self::$accessNames))
3911 self::$accessNames[
$code] =
null;
3918 return self::$outerUrl;
3925 return Loc::getMessage(
'EC_CAL_OPERATION_CANNOT_BE_PERFORMED');
3929 'ENTITY_TYPE' =>
'user',
3938 \CDavConnection::ParseFields(
$arFields);
3940 $davConnection = \CDavConnection::getList(
3943 'SERVER_HOST' =>
$arFields[
'SERVER_HOST'],
3944 'SERVER_PATH' =>
$arFields[
'SERVER_PATH'],
3951 if (
$con = $davConnection->fetch())
3971 $parsedUrl = parse_url(
$url);
3972 $arServer = array_merge($arServer, $parsedUrl);
3976 mb_strpos(mb_strtolower(
$_SERVER[
'SERVER_NAME']), mb_strtolower($arServer[
'host'])) !==
false
3977 || mb_strpos(mb_strtolower(
$_SERVER[
'HTTP_HOST']), mb_strtolower($arServer[
'host'])) !==
false
3985 return \CDavGroupdavClientCalendar::DoCheckCalDAVServer($arServer[
"scheme"], $arServer[
"host"], $arServer[
"port"], $username,
$password, $arServer[
"path"]);
3991 public static function RemoveConnection(
array $params = [])
3993 if (Loader::includeModule(
'dav'))
3995 $sections = self::getSectionsByConnectionId(
$params[
'id']);
4001 if (is_array($sections))
4003 foreach ($sections as $section)
4005 if (
$params[
'del_calendars'] )
4007 CCalendarSect::Delete($section[
'ID'],
false);
4011 self::markSectionLikeDelete($section[
'ID']);
4016 \CDavConnection::Delete(
$params[
'id']);
4021 $googleHelper = ServiceLocator::getInstance()->get(
'calendar.service.google.helper');
4022 $caldavHelper = ServiceLocator::getInstance()->get(
'calendar.service.caldav.helper');
4023 $connectionType = $caldavHelper->isYandex(
$connection[
'SERVER_HOST'])
4024 ? Bitrix\Calendar\Sync\Caldav\Helper::YANDEX_TYPE
4025 : Bitrix\Calendar\Sync\Caldav\Helper::CALDAV_TYPE
4027 $connectionName = $googleHelper->isGoogleConnection(
$connection[
'ACCOUNT_TYPE'])
4032 PushCommand::DeleteSyncConnection,
4036 $connectionName => [
4037 'type' => $connectionType,
4040 'requestUid' => Util::getRequestUid(),
4051 $res = CCalendarType::GetList(
array(
'arFilter' =>
array(
'EXTERNAL_ID' => $externalId)));
4054 return $res[0][
'XML_ID'];
4062 self::$userMeetingSection = $userMeetingSection;
4067 if (
$time !==
false)
4069 self::$cacheTime =
$time;
4071 return self::$cacheTime;
4078 $text = preg_replace(
"/<br\s*\/*>/isu",
"",
$text);
4079 $text = preg_replace(
"/<(\w+)[^>]*>(.+?)<\/\\1[^>]*>/isu",
"\\2",
$text);
4080 $text = preg_replace(
"/<*\/li>/isu",
"",
$text);
4084 $TextParser->allow = [];
4090 if (!isset(self::$bCurUserSocNetAdmin))
4094 && CSocNetUser::IsCurrentUserModuleAdmin()
4098 return self::$bCurUserSocNetAdmin;
4103 if (!self::$CALENDAR_MAX_DATE)
4106 $date->setDate(2038, 1, 1);
4107 self::$CALENDAR_MAX_DATE = self::Date($date->getTimestamp(),
false);
4109 return self::$CALENDAR_MAX_DATE;
4114 if (!self::$CALENDAR_MIN_DATE)
4117 $date->setDate(1970, 1, 1);
4118 self::$CALENDAR_MIN_DATE = self::Date($date->getTimestamp(),
false);
4120 return self::$CALENDAR_MIN_DATE;
4129 $users = \CSocNetLogDestination::getDestinationUsers($codes, $fetchUsers);
4133 foreach ($users as
$i =>
$user)
4135 $users[
$i][
'FORMATTED_NAME'] = self::GetUserName(
$user);
4136 $users[
$i][
'ID'] = (int)
$user[
'ID'];
4141 foreach ($users as &
$user)
4143 if(is_numeric(
$user))
4157 && ($cnt % 100) < 20
4164 $suffix = $cnt % 10;
4167 return Loc::getMessage(
"EC_ATTENDEE_".$suffix, Array(
"#NUM#" => $cnt));
4174 && ($cnt % 100) < 20
4181 $suffix = $cnt % 10;
4184 return Loc::getMessage(
"EC_ATTENDEE_MORE_".$suffix, Array(
"#NUM#" => $cnt));
4196 if ((
int)$fromTs != $fromTs)
4198 $fromTs = self::Timestamp($fromTs);
4200 if ((
int)$toTs != $toTs)
4202 $toTs = self::Timestamp($toTs);
4204 if ($toTs < $fromTs)
4210 $formatShort = self::DFormat(
false);
4211 $formatFull = self::DFormat(
true);
4212 $formatTime = str_replace($formatShort,
'', $formatFull);
4213 $formatTime = $formatTime == $formatFull ?
"H:i" : str_replace(
':s',
'', $formatTime);
4216 $formatFull = str_replace(
':s',
'', $formatFull);
4220 if ((
int)$dtLength === self::DAY_LENGTH || !$dtLength)
4225 "tomorrow" =>
"tomorrow",
4227 "yesterday" =>
"yesterday",
4228 "-" => $formatShort,
4230 ], $fromTs, time() + CTimeZone::GetOffset(), $languageId);
4234 $html .= Loc::getMessage(
'EC_VIEW_FULL_DAY',
null, $languageId);
4239 "tomorrow" =>
"tomorrow",
4241 "yesterday" =>
"yesterday",
4242 "-" => $formatShort,
4244 ], $fromTs, time() + CTimeZone::GetOffset(), $languageId);
4247 "tomorrow" =>
"tomorrow",
4249 "yesterday" =>
"yesterday",
4250 "-" => $formatShort,
4252 ], $toTs - self::DAY_LENGTH, time() + CTimeZone::GetOffset(), $languageId);
4254 $html = Loc::getMessage(
4255 'EC_VIEW_DATE_FROM_TO',
4256 [
'#DATE_FROM#' => $from,
'#DATE_TO#' => $to],
4264 if(date(
'dmY', $fromTs) == date(
'dmY', $toTs))
4269 "tomorrow" =>
"tomorrow",
4271 "yesterday" =>
"yesterday",
4272 "-" => $formatShort,
4274 ], $fromTs, time() + CTimeZone::GetOffset(), $languageId);
4278 $html .= Loc::getMessage(
4279 'EC_VIEW_TIME_FROM_TO_TIME',
4281 '#TIME_FROM#' =>
FormatDate($formatTime, $fromTs,
false, $languageId),
4282 '#TIME_TO#' =>
FormatDate($formatTime, $toTs,
false, $languageId),
4289 $html = Loc::getMessage(
4290 'EC_VIEW_DATE_FROM_TO',
4292 '#DATE_FROM#' =>
FormatDate($formatFull, $fromTs, time() + CTimeZone::GetOffset(), $languageId),
4293 '#DATE_TO#' =>
FormatDate($formatFull, $toTs, time() + CTimeZone::GetOffset(), $languageId),
4312 if (!is_array($selected))
4317 if (method_exists(
'CSocNetLogDestination',
'GetDestinationSort'))
4319 $DESTINATION =
array(
4324 CSocNetLogDestination::fillLastDestination($DESTINATION[
'DEST_SORT'], $DESTINATION[
'LAST']);
4328 $DESTINATION =
array(
4330 'SONETGROUPS' => CSocNetLogDestination::GetLastSocnetGroup(),
4331 'DEPARTMENT' => CSocNetLogDestination::GetLastDepartment(),
4332 'USERS' => CSocNetLogDestination::GetLastUser(),
4339 $user_id = self::GetCurUserId();
4342 $cacheTtl = defined(
"BX_COMP_MANAGED_CACHE") ? 3153600 : 3600*4;
4343 $cacheId =
'calendar_dest_'.$user_id;
4344 $cacheDir =
'/calendar/socnet_destination/'.SITE_ID.
'/'.$user_id;
4346 $obCache =
new CPHPCache;
4347 if($obCache->InitCache($cacheTtl, $cacheId, $cacheDir))
4349 $DESTINATION[
'SONETGROUPS'] = $obCache->GetVars();
4353 $obCache->StartDataCache();
4354 $DESTINATION[
'SONETGROUPS'] = CSocNetLogDestination::GetSocnetGroup(Array(
'features' =>
array(
"calendar",
array(
"view"))));
4356 if(defined(
"BX_COMP_MANAGED_CACHE"))
4359 foreach($DESTINATION[
'SONETGROUPS'] as
$val)
4367 $obCache->EndDataCache($DESTINATION[
'SONETGROUPS']);
4370 $destinationUserList = [];
4371 $DESTINATION[
'SELECTED'] = [];
4373 if (!empty($userList))
4375 foreach ($userList as $userId)
4377 $DESTINATION[
'SELECTED'][
'U'.$userId] =
"users";
4378 $DESTINATION[
'LAST'][
'USERS'][
'U'.$userId] =
'U'.$userId;
4382 foreach ($selected as $ind =>
$code)
4384 if (str_starts_with(
$code,
'DR'))
4386 $DESTINATION[
'SELECTED'][
$code] =
"department";
4390 $DESTINATION[
'SELECTED'][
$code] =
"groups";
4394 $DESTINATION[
'SELECTED'][
$code] =
"sonetgroups";
4398 $DESTINATION[
'SELECTED'][
$code] =
"users";
4399 $destinationUserList[] = (int)str_replace(
'U',
'',
$code);
4404 $arStructure = CSocNetLogDestination::GetStucture();
4405 $DESTINATION[
'DEPARTMENT'] = $arStructure[
'department'];
4406 $DESTINATION[
'DEPARTMENT_RELATION'] = $arStructure[
'department_relation'];
4407 $DESTINATION[
'DEPARTMENT_RELATION_HEAD'] = $arStructure[
'department_relation_head'];
4411 $DESTINATION[
'EXTRANET_USER'] =
'Y';
4412 $DESTINATION[
'USERS'] = CSocNetLogDestination::GetExtranetUser();
4413 $DESTINATION[
'USERS'] = array_merge($DESTINATION[
'USERS'], CSocNetLogDestination::GetUsers([
'id' => [$user_id]]));
4417 if (is_array($DESTINATION[
'LAST'][
'USERS']))
4419 foreach ($DESTINATION[
'LAST'][
'USERS'] as $value)
4421 $destinationUserList[] = (int)str_replace(
'U',
'', $value);
4425 if (!empty($userList))
4427 $destinationUserList = array_unique(array_merge($destinationUserList, $userList));
4430 $DESTINATION[
'EXTRANET_USER'] =
'N';
4431 $DESTINATION[
'USERS'] = CSocNetLogDestination::GetUsers(Array(
'id' => $destinationUserList));
4435 foreach ($DESTINATION[
'USERS'] as
$key => $entry)
4437 if ($entry[
'isExtranet'] ===
'N')
4439 $users[
$key] = $entry;
4442 $DESTINATION[
'USERS'] = $users;
4444 return $DESTINATION;
4454 CUserOptions::SetOption(
"calendar",
"timezone".self::GetCurrentOffsetUTC(
$user[
'ID']), $tzName,
false,
$user[
'ID']);
4459 $groupId = (int)$groupId;
4462 $event = EventTable::query()
4463 ->setSelect([
'ID',
'OWNER_ID',
'CAL_TYPE',
'DELETED'])
4464 ->where(
'OWNER_ID', $groupId)
4465 ->where(
'CAL_TYPE', Dictionary::CALENDAR_TYPE[
'group'])
4466 ->where(
'DELETED',
'N')
4475 Loc::getMessage(
'EC_DECLINE_GROUP_DELETING_WITH_EVENTS'),
4476 'CALENDAR_NOT_EMPTY'
4482 $res = CCalendarSect::GetList(
4484 'arFilter' =>
array(
4485 'CAL_TYPE' =>
'group',
4486 'OWNER_ID' => $groupId,
4488 'checkPermissions' =>
false,
4492 foreach(
$res as $sect)
4494 CCalendarSect::Delete($sect[
'ID'],
false);
4509 $calendarId =
$event->getParameter(
'id');
4510 $userAgent = mb_strtolower(
$event->getParameter(
'agent'));
4512 [$sectionId, $entityType,
$entityId] = $calendarId;
4515 'android' =>
'android',
4516 'iphone' =>
'iphone',
4519 'mac_os_x' =>
'mac',
4520 'mac+os+x' =>
'mac',
4522 'dataaccess' =>
'iphone',
4529 if (mb_strpos($userAgent,
$pattern) !==
false)
4536 if ($entityType ===
'user' && $agent)
4551 $syncTypes =
array(
'iphone',
'android',
'mac',
'exchange',
'outlook');
4552 if (in_array($syncType, $syncTypes))
4554 if (!CUserOptions::GetOption(
'calendar',
'last_sync_'.$syncType,
false, $userId))
4556 AddEventToStatFile(
'calendar',
'sync_connection_connected', $syncType,
'',
'client_connection');
4559 CUserOptions::SetOption(
"calendar",
"last_sync_".$syncType, self::Date(time()),
false, $userId);
4562 PushCommand::RefreshSyncStatus,
4567 'type' => $syncType,
4568 'connected' =>
true,
4584 $syncTypes = [
'outlook'];
4585 if (in_array($syncType, $syncTypes,
true))
4587 if (!CUserOptions::GetOption(
'calendar',
'last_sync_'.$syncType,
false, $userId))
4589 AddEventToStatFile(
'calendar',
'sync_connection_connected', $syncType,
'',
'client_connection');
4592 $options = CUserOptions::GetOption(
"calendar",
"last_sync_".$syncType,
false, $userId);
4599 $options[$sectionId] = self::Date(time());
4600 CUserOptions::SetOption(
"calendar",
"last_sync_".$syncType,
$options,
false, $userId);
4603 PushCommand::RefreshSyncStatus,
4608 'type' => $syncType,
4609 'connected' =>
true,
4620 self::SaveSyncDate(
$event->getParameter(
'userId'),
'exchange');
4635 $users =
array(self::GetCurUserId());
4637 elseif(!is_array($users))
4639 $users =
array($users);
4643 foreach($users as
$user)
4647 $ids[] = (int)
$user;
4655 'event_ids' => $eventIds,
4656 'group_ids' => $groupIds,
4661 private static function GetInstance()
4663 if (!isset(self::$instance))
4666 self::$instance =
new $c;
4668 return self::$instance;
4674 if (!isset(self::$bIntranet))
4678 return self::$bIntranet;
4683 if (!isset(self::$bSocNet))
4687 self::$bSocNet =
false;
4695 return self::$bSocNet;
4702 if (!isset(self::$curUserId)
4703 || !is_numeric(self::$curUserId)
4709 ? (
int)
$USER->GetId()
4714 return self::$curUserId;
4724 isset(self::$settings)
4725 && !empty(self::$settings)
4726 && (
$params[
'request'] ??
'') ===
false
4729 return self::$settings;
4732 $pathes_for_sites = COption::GetOptionString(
'calendar',
'pathes_for_sites',
true);
4733 if ((
$params[
'forseGetSitePathes'] ??
false) || !$pathes_for_sites)
4735 $pathes = self::GetPathes(
$params[
'site'] ??
false);
4742 if (!isset(
$params[
'getDefaultForEmpty']) ||
$params[
'getDefaultForEmpty'] !==
false)
4744 $params[
'getDefaultForEmpty'] =
true;
4748 $resMeetingCommonForSites = COption::GetOptionString(
'calendar',
'rm_for_sites',
true);
4749 $siteIdForResMeet = !$resMeetingCommonForSites && $siteId ? $siteId :
false;
4752 'work_time_start' => COption::GetOptionString(
'calendar',
'work_time_start', 9),
4753 'work_time_end' => COption::GetOptionString(
'calendar',
'work_time_end', 19),
4754 'year_holidays' => COption::GetOptionString(
'calendar',
'year_holidays', Loc::getMessage(
'EC_YEAR_HOLIDAYS_DEFAULT')),
4755 'year_workdays' => COption::GetOptionString(
'calendar',
'year_workdays', Loc::getMessage(
'EC_YEAR_WORKDAYS_DEFAULT')),
4756 'week_holidays' => array_filter(
4757 explode(
'|', COption::GetOptionString(
'calendar',
'week_holidays',
'SA|SU'))
4759 'week_start' => COption::GetOptionString(
'calendar',
'week_start',
'MO'),
4760 'user_name_template' => self::GetUserNameTemplate(
$params[
'getDefaultForEmpty']),
4761 'sync_by_push' => COption::GetOptionString(
'calendar',
'sync_by_push',
false),
4762 'user_show_login' => COption::GetOptionString(
'calendar',
'user_show_login',
true),
4763 'path_to_user' => COption::GetOptionString(
'calendar',
'path_to_user',
"/company/personal/user/#user_id#/"),
4764 'path_to_user_calendar' => COption::GetOptionString(
'calendar',
'path_to_user_calendar',
"/company/personal/user/#user_id#/calendar/"),
4765 'path_to_group' => COption::GetOptionString(
'calendar',
'path_to_group',
"/workgroups/group/#group_id#/"),
4766 'path_to_group_calendar' => COption::GetOptionString(
'calendar',
'path_to_group_calendar',
"/workgroups/group/#group_id#/calendar/"),
4767 'path_to_vr' => COption::GetOptionString(
'calendar',
'path_to_vr',
""),
4768 'path_to_rm' => COption::GetOptionString(
'calendar',
'path_to_rm',
""),
4769 'rm_iblock_type' => COption::GetOptionString(
'calendar',
'rm_iblock_type',
""),
4770 'rm_iblock_id' => COption::GetOptionString(
'calendar',
'rm_iblock_id',
"", $siteIdForResMeet, !!$siteIdForResMeet),
4771 'dep_manager_sub' => COption::GetOptionString(
'calendar',
'dep_manager_sub',
true),
4772 'denied_superpose_types' => unserialize(COption::GetOptionString(
'calendar',
'denied_superpose_types', serialize([])), [
'allowed_classes' =>
false]),
4773 'pathes_for_sites' => $pathes_for_sites,
4774 'pathes' => $pathes,
4775 'forum_id' => COption::GetOptionString(
'calendar',
'forum_id',
""),
4776 'rm_for_sites' => COption::GetOptionString(
'calendar',
'rm_for_sites',
true),
4782 if (!isset(self::$settings[$pathName]))
4784 self::$settings[$pathName] = COption::GetOptionString(
'calendar', $pathName,
"");
4788 if(self::$settings[
'work_time_start'] > 23)
4790 self::$settings[
'work_time_start'] = 23;
4792 if (self::$settings[
'work_time_end'] <= self::$settings[
'work_time_start'])
4794 self::$settings[
'work_time_end'] = self::$settings[
'work_time_start'] + 1;
4796 if (self::$settings[
'work_time_end'] > 23.30)
4798 self::$settings[
'work_time_end'] = 23.30;
4801 if (empty(self::$settings[
'forum_id']))
4803 self::$settings[
'forum_id'] = COption::GetOptionString(
"tasks",
"task_forum_id",
"");
4807 if (
$ar = $db->GetNext())
4809 self::$settings[
'forum_id'] =
$ar[
"ID"];
4812 COption::SetOptionString(
"calendar",
"forum_id", self::$settings[
'forum_id']);
4815 return self::$settings;
4821 $pathes_for_sites = COption::GetOptionString(
'calendar',
'pathes_for_sites',
true);
4822 if ($forSite ===
null)
4824 $arAffectedSites = COption::GetOptionString(
'calendar',
'pathes_sites',
false);
4828 $arAffectedSites = unserialize($arAffectedSites, [
'allowed_classes' =>
false]);
4831 elseif (is_array($forSite))
4833 $arAffectedSites = $forSite;
4837 $arAffectedSites = [$forSite];
4840 if(is_array($arAffectedSites) && !empty($arAffectedSites))
4842 foreach($arAffectedSites as $s)
4844 $ar = COption::GetOptionString(
"calendar",
'pathes_'.$s,
false);
4847 $ar = unserialize(
$ar, [
'allowed_classes' =>
false]);
4856 if ($forSite !==
false)
4859 if (isset($pathes[$forSite]) && is_array($pathes[$forSite]))
4868 if (empty(
$val) || $pathes_for_sites)
4872 $SET = self::GetSettings();
4885 if (!self::$pathesListEx)
4887 self::$pathesListEx = self::$pathesList;
4888 $arTypes = CCalendarType::GetList(
array(
'checkPermissions' =>
false));
4889 foreach ($arTypes as $type)
4891 if ($type[
'XML_ID'] !==
'user' && $type[
'XML_ID'] !==
'group')
4893 self::$pathesList[] =
'path_to_type_'. $type[
'XML_ID'];
4897 return self::$pathesList;
4902 $user_name_template = COption::GetOptionString(
'calendar',
'user_name_template',
'');
4903 if ($fromSite && empty($user_name_template))
4905 $user_name_template = CSite::GetNameFormat(
false);
4907 return $user_name_template;
4923 $type = $Params[
'type'] ?? self::$type;
4924 $ownerId = (int)($Params[
'ownerId'] ?? self::$ownerId);
4925 $userId = (int)($Params[
'userId'] ?? self::$userId);
4930 $typeModel = TypeModel::createFromXmlId($type);
4932 ActionDictionary::ACTION_TYPE_VIEW => [],
4933 ActionDictionary::ACTION_TYPE_EDIT => [],
4938 if ($type ===
'user' && $ownerId !== $userId)
4941 $bEditSection =
false;
4945 $bView =
$result[ActionDictionary::ACTION_TYPE_VIEW];
4947 $bEditSection =
$result[ActionDictionary::ACTION_TYPE_EDIT];
4950 if (($type ===
'group') && !
$USER->CanDoOperation(
'edit_php'))
4952 $keyOwner =
'SG' . $ownerId.
'_A';
4953 $keyMod =
'SG' . $ownerId.
'_E';
4954 $keyMember =
'SG' . $ownerId.
'_K';
4960 $group = CSocNetGroup::getByID($ownerId);
4962 !empty($group[
'CLOSED']) && $group[
'CLOSED'] ===
'Y' &&
4966 self::$isArchivedGroup =
true;
4970 if (in_array($keyOwner, $codes,
true))
4973 $bEditSection =
true;
4975 elseif(!self::$isArchivedGroup && in_array($keyMod, $codes,
true))
4978 $bEditSection =
true;
4980 elseif(!self::$isArchivedGroup && in_array($keyMember, $codes,
true))
4983 $bEditSection =
false;
4988 $bEditSection =
false;
4992 if (($Params[
'setProperties'] ??
'') !==
false)
4994 self::$perm[
'view'] = $bView;
4995 self::$perm[
'edit'] =
$bEdit;
4996 self::$perm[
'section_edit'] = $bEditSection;
5002 'section_edit' => $bEditSection,
5014 public static function getEntryUrl(
string $calType,
int $ownerId,
int $entryId,
string $dateFrom): string
5024 $dateFormatted = $dateFrom;
5028 'EVENT_ID' => $entryId,
5029 'EVENT_DATE' => urlencode($dateFormatted),
5032 return $uri->getUri();
5035 public static function GetPath($type =
'', $ownerId =
'', $hard =
false)
5046 return self::$siteId;
5051 if (!isset(self::$serverPath))
5053 self::$serverPath = (CMain::IsHTTPS() ?
"https://" :
"http://").self::GetServerName();
5056 return self::$serverPath;
5062 if (defined(
"SITE_SERVER_NAME") && SITE_SERVER_NAME <>
'')
5064 $server_name = SITE_SERVER_NAME;
5068 $server_name = COption::GetOptionString(
"main",
"server_name",
"");
5072 $server_name =
$_SERVER[
'HTTP_HOST'];
5074 $server_name = rtrim($server_name,
'/');
5075 if (!preg_match(
'/^[a-z0-9\.\-]+$/i', $server_name))
5078 $host = $converter->Encode($server_name);
5079 $server_name =
$host;
5082 return $server_name;
5091 $res = [self::getDeletedSharedEvent($eventId)];
5095 $res = CCalendarEvent::GetList(
5097 'arFilter' =>
array(
5098 "PARENT_ID" => $eventId,
5099 "OWNER_ID" => self::$userId,
5103 'parseRecursion' =>
false,
5104 'fetchAttendees' =>
true,
5105 'fetchMeetings' =>
true,
5106 'checkPermissions' =>
true,
5107 'setDefaultLimit' =>
false,
5114 $res = CCalendarEvent::GetList(
5116 'arFilter' =>
array(
5120 'parseRecursion' =>
false,
5121 'userId' => self::$userId,
5122 'fetchAttendees' =>
false,
5123 'fetchMeetings' =>
true,
5131 $event[
'MEETING_STATUS'] ===
'Y'
5132 ||
$event[
'MEETING_STATUS'] ===
'N'
5133 ||
$event[
'MEETING_STATUS'] ===
'Q'
5136 $_GET[
'CONFIRM'] ??=
null;
5139 && (
int)self::$userId === (
int)self::$ownerId
5140 && self::$type ===
'user'
5141 && ($_GET[
'CONFIRM'] ===
'Y' || $_GET[
'CONFIRM'] ===
'N')
5144 CCalendarEvent::SetMeetingStatus(
array(
5145 'userId' => self::$userId,
5146 'eventId' =>
$event[
'ID'],
5147 'status' => $_GET[
'CONFIRM'] ===
'Y' ?
'Y' :
'N',
5148 'personalNotification' =>
true,
5155 $event[
'RRULE'] = CCalendarEvent::ParseRRULE(
$event[
'RRULE']);
5158 $event[
'~userIndex'] = CCalendarEvent::getUserIndex();
5163 CCalendarNotify::ClearNotifications($eventId);
5169 public static function getDeletedSharedEvent(
int $entryId): ?
array
5180 ->where(
'OWNER_ID', $eventLink->getOwnerId())
5181 ->where(Query::filter()
5185 [
'MEETING_STATUS', Core\Event\Tools\Dictionary::MEETING_STATUS[
'No']],
5188 ->where(Query::filter()
5192 [
'PARENT_ID', $entryId],
5195 ->whereIn(
'EVENT_TYPE', SharingEventManager::getSharingEventTypes())
5202 $canceledUserId = (int)
$event[
"MEETING_HOST"];
5203 if (
$event[
'MEETING_STATUS'] === Core\Event\Tools\Dictionary::MEETING_STATUS[
'No'])
5205 $canceledUserId = (int)
$event[
"OWNER_ID"];
5206 $event[
'canceledByManager'] =
true;
5208 $host = Sharing\Helper::getOwnerInfo($canceledUserId);
5211 $event[
'timestampFromUTC'] = Sharing\Helper::getEventTimestampUTC(
$event[
'DATE_FROM'],
$event[
'TZ_FROM']);
5212 $event[
'timestampToUTC'] = Sharing\Helper::getEventTimestampUTC(
$event[
'DATE_TO'],
$event[
'TZ_TO']);
5213 $event[
'canceledUserId'] = $canceledUserId;
5219 public static function Timestamp($date, $bRound =
true, $bTime =
true)
5221 $timestamp =
MakeTimeStamp($date, self::TSFormat($bTime ?
"FULL" :
"SHORT"));
5224 $timestamp = self::RoundTimestamp($timestamp);
5232 $dateTime = self::createDateTimeObjectFromString($date,
'UTC');
5234 return (
int)$dateTime->format(
'U');
5242 $hours = (int)($parsedDateTime[
'HH'] ?? $parsedDateTime[
'H'] ?? 0);
5243 if (isset($parsedDateTime[
'TT']) || isset($parsedDateTime[
'T']))
5245 $amPm = $parsedDateTime[
'TT'] ?? $parsedDateTime[
'T'];
5246 if (strcasecmp(
'pm', $amPm) === 0)
5259 $dateTime = (new \DateTime(
'now', $timezone ?
new \
DateTimeZone($timezone) :
null))
5260 ->setDate($parsedDateTime[
'YYYY'], $parsedDateTime[
'MM'], $parsedDateTime[
'DD'])
5261 ->setTime(
$hours, $parsedDateTime[
'MI'] ?? 0);
5265 $dateTime = new \DateTime($date, $timezone ?
new \
DateTimeZone($timezone) :
null );
5275 return CSite::GetDateFormat($format);
5280 return round($ts / 60) * 60;
5283 public static function IsPersonal($type =
false, $ownerId =
false, $userId =
false)
5287 $type = self::$type;
5291 $ownerId = self::$ownerId;
5295 $userId = self::$userId;
5298 return $type ===
'user' && $ownerId == $userId;
5303 if (isset(self::$arExchEnabledCache[$userId]))
5305 return self::$arExchEnabledCache[$userId];
5308 if (!
IsModuleInstalled(
'dav') || COption::GetOptionString(
"dav",
"agent_calendar") !==
"Y")
5316 elseif ($userId ===
false)
5318 $res = CDavExchangeCalendar::IsExchangeEnabled();
5322 $res = CDavExchangeCalendar::IsExchangeEnabled() && CDavExchangeCalendar::IsExchangeEnabledForUser($userId);
5325 self::$arExchEnabledCache[$userId] =
$res;
5332 if (!isset(self::$isGoogleApiEnabled))
5341 if (self::$isGoogleApiEnabled && !self::IsBitrix24())
5343 self::$isGoogleApiEnabled =
5345 CSocServGoogleOAuth::GetOption(
'google_appid') !==
''
5346 && CSocServGoogleOAuth::GetOption(
'google_appsecret') !==
''
5348 || CSocServGoogleOAuth::GetOption(
'google_sync_proxy') ===
'Y'
5353 return self::$isGoogleApiEnabled;
5358 if (!
IsModuleInstalled(
'dav') || COption::GetOptionString(
"dav",
"agent_calendar_caldav") !==
"Y")
5370 return $info[
'connected'];
5377 return $info[
'connected'];
5382 if (!isset(self::$bWebservice))
5386 return self::$bWebservice;
5391 if (!isset(self::$bExtranet))
5395 return self::$bExtranet;
5400 if (!$userId && self::$userId)
5402 $userId = self::$userId;
5405 return (
int)(date(
"Z") + self::GetOffset($userId));
5412 if (!isset(self::$arTimezoneOffsets[$userId]))
5414 $offset = CTimeZone::GetOffset($userId,
true);
5415 self::$arTimezoneOffsets[$userId] = $offset;
5419 $offset = self::$arTimezoneOffsets[$userId];
5422 else if (!isset(self::$offset))
5424 $offset = CTimeZone::GetOffset(
null,
true);
5425 self::$offset = $offset;
5429 $offset = self::$offset;
5437 if (isset(self::$userTimezoneList[
$user]) && !is_array(
$user) && (
int)
$user > 0)
5439 return self::$userTimezoneList[
$user];
5442 if (is_array(
$user) && (
int)
$user[
'ID'] > 0 && isset(self::$userTimezoneList[
$user[
'ID']]))
5444 return self::$userTimezoneList[
$user[
'ID']];
5452 if (\CTimezone::OptionEnabled() &&
$user && is_array(
$user))
5454 $offset = isset(
$user[
'TIME_ZONE_OFFSET'])
5455 ? (int)(date(
'Z') +
$user[
'TIME_ZONE_OFFSET'])
5456 : self::GetCurrentOffsetUTC(
$user[
'ID']);
5458 $tzName = CUserOptions::GetOption(
5460 "timezone" . $offset,
5465 if ($tzName ===
'undefined' || $tzName ===
'false')
5469 if (!$tzName && (
$user[
'AUTO_TIME_ZONE'] ??
'') !==
'Y' &&
$user[
'TIME_ZONE'])
5471 $tzName =
$user[
'TIME_ZONE'];
5476 $offset = date(
'Z');
5477 $tzName = date_default_timezone_get();
5489 if (!$tzName && $getDefault)
5491 $tzName = self::GetGoodTimezoneForOffset($offset);
5496 self::$userTimezoneList[
$user[
'ID']] = $tzName;
5502 public static function GetUser($userId, $bPhoto =
false)
5504 $userId = (int)$userId;
5506 return self::$userList[$userId] ?? current(self::GetUserList([$userId]));
5513 $userIdToRequest = self::GetNotRequestedUserIdList($userIdList);
5515 if (!empty($userIdToRequest))
5528 'NOTIFICATION_LANGUAGE_ID',
5530 ->whereIn(
'ID', $userIdToRequest)
5535 foreach ($userIdToRequest as $userId)
5537 self::$userList[$userId] =
false;
5540 foreach ($userList as
$user)
5549 foreach ($userIdList as $userId)
5551 $result[] = self::$userList[$userId];
5557 private static function GetNotRequestedUserIdList(
array $userIdList):
array
5561 foreach ($userIdList as
$userId)
5564 if (!isset(self::$userList[
$userId]))
5575 $timezones = self::GetTimezoneList();
5579 foreach($timezones as $tz)
5581 if ($tz[
'offset'] == $offset)
5584 if (LANGUAGE_ID ==
'ru')
5586 if (preg_match(
'/(kaliningrad|moscow|samara|yekaterinburg|novosibirsk|krasnoyarsk|irkutsk|yakutsk|vladivostok)/i', $tz[
'timezone_id']))
5593 elseif (mb_strpos($tz[
'timezone_id'],
'Europe') !==
false)
5601 if (!
$result && !empty($goodTz))
5603 $result = $goodTz[0][
'timezone_id'];
5608 $result = date_default_timezone_get();
5616 if (empty(self::$timezones))
5618 self::$timezones = [];
5619 $aExcept = [
"Etc/",
"GMT",
"UCT",
"HST",
"PST",
"MST",
"CST",
"EST",
"CET",
"MET",
"WET",
"EET",
"PRC",
"ROC",
"ROK",
"W-SU"];
5620 foreach(DateTimeZone::listIdentifiers() as $tz)
5622 foreach($aExcept as $ex)
5624 if(str_starts_with($tz, $ex))
5632 self::$timezones[$tz] = [
5633 'timezone_id' => $tz,
5634 'offset' => $oTz->getOffset(
new DateTime(
"now", $oTz)),
5641 uasort(self::$timezones,
static function(
$a, $b){
5642 if(
$a[
'offset'] === $b[
'offset'])
5644 return strcmp(
$a[
'timezone_id'], $b[
'timezone_id']);
5646 return $a[
'offset'] < $b[
'offset'] ? -1 : 1;
5649 foreach(self::$timezones as
$k =>
$z)
5651 $offset =
$z[
'offset'];
5652 $hours = floor(abs($offset) / 3600);
5654 if (
$z[
'timezone_id'] ===
'UTC')
5656 self::$timezones[
$k][
'title'] =
$z[
'timezone_id'];
5660 self::$timezones[
$k][
'title'] =
5663 ?
' ' . ($offset < 0 ?
'-' :
'+')
5665 .
':' . sprintf(
"%02d", abs($offset)/60 -
$hours * 60)
5667 ) .
') ' .
$z[
'timezone_id'];
5671 return self::$timezones;
5685 return CUser::FormatName(self::$userNameTemplate,
$user,
true,
false);
5692 return $USER->IsAdmin();
5697 if (!isset(self::$weekStart))
5699 $days = [
'1' =>
'MO',
'2' =>
'TU',
'3' =>
'WE',
'4' =>
'TH',
'5' =>
'FR',
'6' =>
'SA',
'0' =>
'SU'];
5700 $cultureWeekStart = \Bitrix\Main\Context::getCurrent()?->getCulture()?->getWeekStart();
5701 self::$weekStart = $days[$cultureWeekStart];
5703 if (!in_array(self::$weekStart, $days))
5705 self::$weekStart =
'MO';
5709 return self::$weekStart;
5712 public static function Date($timestamp, $bTime =
true, $bRound =
true, $bCutSeconds =
false)
5716 $timestamp = self::RoundTimestamp($timestamp);
5719 $format = self::DFormat($bTime);
5720 if ($bTime && $bCutSeconds)
5722 $format = str_replace(
':s',
'', $format);
5730 return CDatabase::DateFormatToPHP(CSite::GetDateFormat($bTime ?
"FULL" :
"SHORT",
SITE_ID));
5735 return mktime(date(
"H", $timestampTime), date(
"i", $timestampTime), 0, date(
"m", $timestampDate), date(
"d", $timestampDate), date(
"Y", $timestampDate));
5740 if (!isset(self::$userMeetingSection) || !self::$userMeetingSection)
5742 self::$userMeetingSection = self::GetMeetingSection(self::$userId, $bCreate);
5745 return self::$userMeetingSection;
5750 if (!$userId || is_array($userId))
5755 if (isset(self::$meetingSections[$userId]))
5757 return self::$meetingSections[$userId];
5761 $meetingSectionId =
false;
5767 $meetingSectionId = (int)$set[
'meetSection'];
5769 $result = self::getSectionForUser($meetingSectionId, $userId, $autoCreate);
5779 $set[
'meetSection'] =
$result;
5783 self::$meetingSections[$userId] =
$result;
5790 if (!$userId || is_array($userId))
5795 if (isset(self::$crmSections[$userId]))
5797 return self::$crmSections[$userId];
5801 $crmSectionId =
false;
5807 $crmSectionId = (int)$set[
'crmSection'];
5809 $result = self::getSectionForUser($crmSectionId, $userId, $autoCreate);
5818 self::$crmSections[$userId] =
$result;
5823 private static function getSectionForUser(
$result,
$userId, $autoCreate =
false)
5829 $sectionQueryResult = \Bitrix\Calendar\Internals\SectionTable::query()
5830 ->setSelect([
'ID',
'CAL_TYPE',
'OWNER_ID',
'ACTIVE'])
5832 ->where(
'CAL_TYPE', Dictionary::CALENDAR_TYPE[
'user'])
5833 ->where(
'OWNER_ID', (
int)
$userId)
5834 ->where(
'ACTIVE',
'Y')
5838 $section = !empty($sectionQueryResult) ? (int)$sectionQueryResult[
'ID'] :
false;
5848 $sectionQueryResult = \Bitrix\Calendar\Internals\SectionTable::query()
5849 ->setSelect([
'ID',
'CAL_TYPE',
'OWNER_ID',
'ACTIVE'])
5850 ->where(
'CAL_TYPE', Dictionary::CALENDAR_TYPE[
'user'])
5851 ->where(
'OWNER_ID', (
int)
$userId)
5852 ->where(
'ACTIVE',
'Y')
5857 if (!empty($sectionQueryResult) && $sectionQueryResult[
'ID'])
5859 $result = (int)$sectionQueryResult[
'ID'];
5865 'type' => Dictionary::CALENDAR_TYPE[
'user'],
5868 if ($defCalendar && $defCalendar[
'ID'] > 0)
5870 $result = (int)$defCalendar[
'ID'];
5880 $type =
$params[
'CAL_TYPE'] ?? self::$type;
5883 'CAL_TYPE' => $type,
5886 if (isset(
$params[
'OWNER_ID']))
5890 elseif ($type ===
'user' || $type ===
'group')
5892 $arFilter[
'OWNER_ID'] = self::GetOwnerId();
5900 if (!empty(
$params[
'ADDITIONAL_IDS']))
5905 $sectionList = CCalendarSect::GetList([
5907 'checkPermissions' => (
$params[
'checkPermissions'] ??
null),
5908 'getPermissions' => (
$params[
'getPermissions'] ??
null),
5913 || (is_array($type) && in_array(
Core\
Event\
Tools\Dictionary::CALENDAR_TYPE[
'user'], $type,
true))
5916 $sectionIdList = [];
5917 foreach ($sectionList as $section)
5919 $sectionIdList[] = (int)$section[
'ID'];
5922 $sectionLinkList = !empty($sectionIdList) ? CCalendarSect::getSectionConnectionList($sectionIdList) : [];
5924 if (!empty($sectionLinkList))
5926 foreach ($sectionList as
$i => $section)
5928 $sectionList[
$i][
'connectionLinks'] = [];
5929 foreach ($sectionLinkList as $sectionLink)
5931 if ($sectionLink->getSectionId() === (
int)$section[
'ID'])
5933 $sectionList[
$i][
'connectionLinks'][] = [
5934 'id' => $sectionLink->getConnectionId(),
5935 'active' => $sectionLink->getActive() ?
'Y' :
'N',
5936 'isPrimary' => $sectionLink->getIsPrimary() ?
'Y' :
'N',
5944 if ((
$params[
'getImages'] ??
null))
5946 $sectionList = self::fetchIconsForSectionList($sectionList);
5949 return $sectionList;
5954 $SECTION_IMG_SIZE = 28;
5957 $userIndexList = [];
5958 $groupListIndex = [];
5960 foreach ($sectionList as $section)
5962 $ownerId = (int)$section[
'OWNER_ID'];
5964 $section[
'CAL_TYPE'] ===
'user'
5965 && !in_array($ownerId, $userIdList)
5968 $userIdList[] = $ownerId;
5970 elseif ($section[
'CAL_TYPE'] ===
'group'
5971 && !in_array($ownerId, $groupIdList))
5973 $groupIdList[] = $ownerId;
5977 if (!empty($userIdList))
5979 $userIndexList = \CCalendarEvent::getUsersDetails($userIdList);
5984 $res = Bitrix\Socialnetwork\WorkgroupTable::getList([
5987 '@ID' => $groupIdList,
5989 'select' => [
'ID',
'IMAGE_ID'],
5991 while ($workgroupFields =
$res->fetch())
5993 if (!empty($workgroupFields[
"IMAGE_ID"]))
5995 $arFileTmp = CFile::ResizeImageGet(
5996 $workgroupFields[
"IMAGE_ID"],
5997 [
'width' => $SECTION_IMG_SIZE,
'height' => $SECTION_IMG_SIZE],
6003 $workgroupFields[
'IMAGE'] = $arFileTmp[
'src'];
6005 $groupListIndex[$workgroupFields[
'ID']] = $workgroupFields;
6009 foreach ($sectionList as
$k => $section)
6011 $ownerId = (int)$section[
'OWNER_ID'];
6012 if ($section[
'CAL_TYPE'] ===
'user'
6013 && isset($userIndexList[$ownerId])
6014 && !empty($userIndexList[$ownerId][
'AVATAR'])
6015 && $userIndexList[$ownerId][
'AVATAR'] !==
'/bitrix/images/1.gif'
6018 $sectionList[
$k][
'IMAGE'] = $userIndexList[$ownerId][
'AVATAR'];
6021 $section[
'CAL_TYPE'] ===
'group'
6022 && isset($groupListIndex[$ownerId])
6023 && !empty($groupListIndex[$ownerId][
'IMAGE'])
6026 $sectionList[
$k][
'IMAGE'] = $groupListIndex[$ownerId][
'IMAGE'];
6029 $pathesForSite = self::getPathes(
SITE_ID);
6030 if ($section[
'CAL_TYPE'] ===
'user')
6032 $sectionList[
$k][
'LINK'] = str_replace(
6033 [
'#user_id#',
'#USER_ID#'],
6034 $section[
'OWNER_ID'],
6035 $pathesForSite[
'path_to_user_calendar']
6038 else if($section[
'CAL_TYPE'] ===
'group')
6040 $sectionList[
$k][
'LINK'] = str_replace(
6041 [
'#group_id#',
'#GROUP_ID#'],
6042 $section[
'OWNER_ID'],
6043 $pathesForSite[
'path_to_user_calendar']
6048 $path = $pathesForSite[
'path_to_type_'.$section[
'CAL_TYPE']];
6049 $sectionList[
$k][
'LINK'] = $path;
6052 return $sectionList;
6057 return self::$ownerId;
6068 $type =
$params[
'type'] ?? self::$type;
6069 $ownerId = isset(
$params[
'ownerId']) ? (int)
$params[
'ownerId'] : self::$ownerId;
6070 $userId = isset(
$params[
'userId']) ? (int)
$params[
'userId'] : self::$userId;
6072 if (empty(
$params[
'section']))
6078 if (isset(
$params[
'fromLimit']))
6082 if (isset(
$params[
'toLimit']))
6089 if ($type ===
'user')
6091 $fetchMeetings = in_array(self::GetMeetingSection($ownerId),
$params[
'section'],
true);
6095 $fetchMeetings = in_array(self::GetCurUserMeetingSection(),
$params[
'section'],
true);
6102 $res = CCalendarEvent::GetList([
6104 'parseRecursion' =>
true,
6105 'fetchAttendees' =>
true,
6106 'userId' => $userId,
6107 'fetchMeetings' => $fetchMeetings,
6108 'setDefaultLimit' =>
false,
6109 'limit' =>
$params[
'limit'] ??
null,
6110 'getUserfields' =>
true,
6116 if (in_array((
int)
$event[
'SECT_ID'],
$params[
'section'],
true))
6118 unset(
$event[
'~DESCRIPTION']);
6140 'CHECK_PERMISSIONS' =>
'Y',
6143 if ($userSettings[
'showCompletedTasks'] ===
'N')
6145 $filter[
'!STATUS'][] = Status::COMPLETED;
6156 $tzEnabled = CTimeZone::Enabled();
6159 CTimeZone::Disable();
6173 'STATUS_CHANGED_DATE',
6179 ->setOrder([
'START_DATE_PLAN' =>
'ASC'])
6182 $tasks = (
new TaskList())->getList(
$query);
6184 $offset = self::GetOffset();
6185 foreach ($tasks as $task)
6190 $skipFromOffset =
false;
6191 $skipToOffset =
false;
6193 if (isset($task[
"START_DATE_PLAN"]) && $task[
"START_DATE_PLAN"])
6195 $dtFrom = self::CutZeroTime($task[
"START_DATE_PLAN"]);
6198 if (isset($task[
"END_DATE_PLAN"]) && $task[
"END_DATE_PLAN"])
6200 $dtTo = self::CutZeroTime($task[
"END_DATE_PLAN"]);
6203 if (!isset($dtFrom) && isset($task[
"DATE_START"]))
6205 $dtFrom = self::CutZeroTime($task[
"DATE_START"]);
6208 if (!isset($dtTo) && isset($task[
"CLOSED_DATE"]))
6210 $dtTo = self::CutZeroTime($task[
"CLOSED_DATE"]);
6214 !isset($dtTo) && isset($task[
"STATUS_CHANGED_DATE"])
6216 (
int)$task[
"REAL_STATUS"],
6217 [Status::SUPPOSEDLY_COMPLETED, Status::COMPLETED, Status::DEFERRED, Status::DECLINED],
6222 $dtTo = self::CutZeroTime($task[
"STATUS_CHANGED_DATE"]);
6227 $ts = self::Timestamp($dtTo);
6228 if (date(
"H:i", $ts) ===
'00:00')
6230 $dtTo = self::Date($ts - 24 * 60 * 60);
6233 elseif (isset($task[
"DEADLINE"]))
6235 $dtTo = self::CutZeroTime($task[
"DEADLINE"]);
6236 $ts = self::Timestamp($dtTo);
6237 if (date(
"H:i", $ts) ===
'00:00')
6239 $dtTo = self::Date($ts - 24 * 60 * 60);
6242 if (!isset($dtFrom))
6244 $skipFromOffset =
true;
6245 $dtFrom = self::Date(time(),
false);
6251 $dtTo = self::Date(time(),
false);
6254 if (!isset($dtFrom))
6259 $dtFromTS = self::Timestamp($dtFrom);
6260 $dtToTS = self::Timestamp($dtTo);
6262 if ($dtToTS < $dtFromTS)
6264 $dtToTS = $dtFromTS;
6265 $dtTo = self::Date($dtToTS,
true);
6268 $skipTime = date(
"H:i", $dtFromTS) ===
'00:00' && date(
"H:i", $dtToTS) ===
'00:00';
6269 if (!$skipTime && $offset != 0)
6271 if (!$skipFromOffset)
6273 $dtFromTS += $offset;
6274 $dtFrom = self::Date($dtFromTS,
true);
6280 $dtTo = self::Date($dtToTS,
true);
6285 "ID" => $task[
"ID"],
6287 "NAME" => $task[
"TITLE"],
6288 "DATE_FROM" => $dtFrom,
6290 "DT_SKIP_TIME" => $skipTime ?
'Y' :
'N',
6291 "CAN_EDIT" => CTasks::CanCurrentUserEdit($task),
6297 CTimeZone::Enable();
6305 if (preg_match(
'/.*\s\d\d:\d\d:\d\d/i', $date))
6307 $date = trim($date);
6308 if (mb_substr($date, -9) ===
' 00:00:00')
6310 return mb_substr($date, 0, -9);
6312 if (mb_substr($date, -3) ===
':00')
6314 return mb_substr($date, 0, -3);
6328 foreach (self::$accessNames as
$code =>
$name)
6339 $names =
$access->GetNames($codes);
6345 self::$accessNames[
'UA'] = Loc::getMessage(
'EC_ENTITY_SELECTOR_ALL_EMPLOYEES');
6348 return self::$accessNames;
6353 self::$silentErrorMode = $silentErrorMode;
6358 return self::$id ?:
'EC'.rand();
6371 public static function GetOriginalDate(
6378 CTimeZone::Disable();
6380 $parentTimestamp = Util::getDateObject($parentDateTime,
false, $timeZone)->getTimestamp();
6381 $baseTimeZone = date_default_timezone_get();
6384 date_default_timezone_set($timeZone);
6386 $parentInfoDate = getdate($parentTimestamp);
6388 $instanceDateTime = Util::getDateObject($instanceDateTime,
false, $timeZone);
6389 $eventDate = $instanceDateTime->setTime($parentInfoDate[
'hours'], $parentInfoDate[
'minutes'])->format($format);
6392 date_default_timezone_set($baseTimeZone);
6401 return self::GetSectionList([
6402 'CAL_TYPE' =>
'user',
6403 'OWNER_ID' => $userId,
6412 self::$ownerId = $userId;
6417 if (!isset(self::$isOffice365ApiEnabled))
6423 if (self::$isOffice365ApiEnabled && !self::IsBitrix24())
6425 self::$isOffice365ApiEnabled = CSocServGoogleOAuth::GetOption(
'office365_appid') !==
''
6426 && CSocServGoogleOAuth::GetOption(
'office365_appid') !==
''
6431 return self::$isOffice365ApiEnabled;
6454 $mapperFactory = ServiceLocator::getInstance()->get(
'calendar.service.mappers.factory');
6456 $event = $mapperFactory->getEvent()->resetCacheById($id)->getById($id);
6464 && !empty($curEvent[
'SECT_ID'])
6466 && (
int)$curEvent[
'SECT_ID'] !==
$event->getSection()->getId()
6469 return self::changeCalendarSync(
$event, $curEvent,
$params);
6472 $factories = FactoriesCollection::createBySection(
6476 if ($factories->count() === 0)
6481 $syncManager =
new Synchronization($factories);
6491 $connection = (
new Bitrix\Calendar\Core\Mappers\Connection())->getMap([
6492 '=ACCOUNT_TYPE' =>
$params[
'originalFrom'],
6493 '=ENTITY_TYPE' =>
$event->getCalendarType(),
6494 '=ENTITY_ID' =>
$event->getOwner()->getId(),
6499 $syncManager->upEventVersion(
6507 $pushManager =
new Sync\Managers\PushManager();
6511 foreach ($factories as $factory)
6513 $pushManager->unLockConnection($factory->getConnection());
6514 $pushManager->lockConnection($factory->getConnection(), 30);
6516 if ((
$params[
'recursionEditMode'] ??
null) ===
'skip')
6518 if (
$event->isInstance())
6524 if (!empty(
$params[
'modeSync']))
6526 $recurrenceSyncMode = (
$params[
'editInstance'] << 2)
6527 | (
$params[
'editNextEvents'] << 1)
6528 | (
$params[
'editEntryUntil'] << 1)
6531 switch ($recurrenceSyncMode)
6533 case Sync\Dictionary::RECURRENCE_SYNC_MODE[
'exception']:
6534 if (
$event->getMeetingStatus() !==
'N')
6542 case Sync\Dictionary::RECURRENCE_SYNC_MODE[
'deleteInstance']:
6543 $context->add(
'diff',
'EXDATE', $curEvent[
'EXDATE']);
6546 case Sync\Dictionary::RECURRENCE_SYNC_MODE[
'exceptionNewSeries']:
6547 $syncManager->deleteInstanceEventConnection(
$event);
6548 if (
$event->getMeetingStatus() !==
'N')
6554 if (
$event->getMeetingStatus() !==
'N')
6564 elseif (empty($curEvent))
6566 if (
$event->isInstance())
6568 $attendeeMasterEvent = $mapperFactory->getEvent()->getMap([
6569 '=PARENT_ID' =>
$event->getRecurrenceId(),
6570 '=OWNER_ID' =>
$event->getOwner()->getId(),
6571 '=CAL_TYPE' =>
'user',
6574 if ($attendeeMasterEvent)
6576 $result = $syncManager->reCreateRecurrence($attendeeMasterEvent,
$context);
6581 new Main\
Error(
"Master event not found", 404)
6585 else if (
$event->getMeetingStatus() !==
'N')
6588 !empty(
$params[
'editNextEvents'])
6589 && !empty(
$params[
'previousRecurrentId'])
6590 &&
$event->getExcludedDateCollection()?->count()
6593 self::removeIncorrectRecurrentExDates(
$event->getExcludedDateCollection(),
$params[
'previousRecurrentId']);
6618 private static function removeIncorrectRecurrentExDates(
6619 Core\Event\Properties\ExcludedDatesCollection $exDatesCollection,
6623 $exDatesToRemove = [];
6627 foreach ($recEvents as $recEvent)
6629 if ((
int)$recEvent[
'ID'] !== (
int)$recEvent[
'PARENT_ID'])
6634 $exDatesToRemove[] =
new Date(
6635 Util::getDateObject(
6636 $recEvent[
'ORIGINAL_DATE_FROM'] ?? $recEvent[
'DATE_FROM'],
6637 ($recEvent[
'DT_SKIP_TIME'] ??
null) ===
'Y',
6638 $recEvent[
'TZ_FROM'],
6644 foreach ($exDatesToRemove as $exDate)
6646 $exDatesCollection->removeDateFromCollection($exDate);
6654 $mapperFactory = ServiceLocator::getInstance()->get(
'calendar.service.mappers.factory');
6655 $pushManager =
new Sync\Managers\PushManager();
6657 $oldSection = $mapperFactory->getSection()->getById($currentEvent[
'SECTION_ID']);
6659 $oldFactories = FactoriesCollection::createBySection($oldSection);
6660 if ($oldFactories->count() > 0)
6662 $syncManager =
new Synchronization($oldFactories);
6664 foreach ($oldFactories as $factory)
6666 $pushManager->unLockConnection($factory->getConnection());
6667 $pushManager->lockConnection($factory->getConnection());
6670 $eventId = (int)$currentEvent[
'ID'];
6671 if ($currentEvent[
'RECURRENCE_ID'] && $currentEvent[
'ORIGINAL_DATE_FROM'])
6673 $masterEvent = \Bitrix\Calendar\Internals\EventTable::query()
6675 ->where(
'DELETED',
'N')
6676 ->where(
'PARENT_ID', $currentEvent[
'RECURRENCE_ID'])
6677 ->where(
'OWNER_ID', $currentEvent[
'OWNER_ID'])
6681 if (!empty($masterEvent[
'ID']))
6683 $eventId = (int)$masterEvent[
'ID'];
6688 $eventToDelete = $mapperFactory->getEvent()->getById($eventId);
6692 $clonedEvent = (
new Core\Builders\EventCloner(
$event))->build();
6693 $clonedEvent->setSection($oldSection);
6694 $syncManager->deleteEvent($clonedEvent,
$context);
6698 $newFactories = FactoriesCollection::createBySection(
$event->getSection());
6699 if ($newFactories->count() > 0)
6701 foreach ($newFactories as $factory)
6703 $pushManager->unLockConnection($factory->getConnection());
6704 $pushManager->lockConnection($factory->getConnection());
6707 $syncManager =
new Synchronization($newFactories);
6710 if (
$event->isInstance())
6712 $masterEvent = $mapperFactory->getEvent()->getMap([
6713 '=PARENT_ID' =>
$event->getRecurrenceId(),
6714 '=OWNER_ID' =>
$event->getOwner()->getId(),
6719 else if (
$event->getRecurringRule())
6736 private static function getSectionsByConnectionId($connectionId)
6738 return \CCalendarSect::GetList([
6740 'CAL_TYPE' =>
'user',
6741 'OWNER_ID' => self::$ownerId,
6742 'CAL_DAV_CON' => $connectionId,
6750 private static function markSectionLikeDelete(
int $sectionId): void
6755 "CAL_DAV_CON" =>
'',
6756 'CAL_DAV_CAL' =>
'',
6757 'CAL_DAV_MOD' =>
'',
6762 private static function mergeExcludedDates($currentExDates, $newExDates)
6764 if (is_string($currentExDates))
6766 $currentExDates = explode(
';', $currentExDates);
6768 if (is_string($newExDates))
6770 $newExDates = explode(
';', $newExDates);
6773 return implode(
';', array_unique(array_merge($currentExDates, $newExDates)));
6783 $isRecurrentLocationChanged =
false;
6784 if (!empty(
$params[
'arFields'][
'LOCATION']) && is_string(
$params[
'arFields'][
'LOCATION']))
6786 $parsedLocation = Rooms\Util::parseLocation(
$params[
'arFields'][
'LOCATION']);
6787 if (!empty($parsedLocation[
'room_event_id']))
6789 $params[
'arFields'][
'LOCATION'] =
'calendar_' . $parsedLocation[
'room_id'];
6792 $isRecurrentLocationChanged =
true;
6794 if (!empty($curEvent[
'LOCATION']) && is_string($curEvent[
'LOCATION']))
6796 $currentLocation = $curEvent[
'LOCATION'];
6797 $parsedCurrentLocation = Rooms\Util::parseLocation($curEvent[
'LOCATION']);
6798 if (!empty($parsedCurrentLocation[
'room_event_id']))
6800 $currentLocation =
'calendar_' . $parsedCurrentLocation[
'room_id'];
6803 $isRecurrentLocationChanged =
$params[
'arFields'][
'LOCATION'] !== $currentLocation;
6807 return [$isRecurrentLocationChanged,
$params];
6815 private static function checkRecurrenceAttendeesChanges(
array $params,
array $curEvent): bool
6817 $isRecurrentAttendeesChanged =
false;
6820 !empty(
$params[
'arFields'][
'ATTENDEES_CODES'])
6821 && is_array(
$params[
'arFields'][
'ATTENDEES_CODES'])
6822 && !empty($curEvent[
'ATTENDEES_CODES'])
6823 && is_array($curEvent[
'ATTENDEES_CODES'])
6826 $firstDiff = array_diff(
$params[
'arFields'][
'ATTENDEES_CODES'], $curEvent[
'ATTENDEES_CODES']);
6827 $secondDiff = array_diff($curEvent[
'ATTENDEES_CODES'],
$params[
'arFields'][
'ATTENDEES_CODES']);
6829 $isRecurrentAttendeesChanged = !empty($firstDiff) || !empty($secondDiff);
6832 return $isRecurrentAttendeesChanged;
6835 private static function checkCollabSectionAccess(
array $collabSections): bool
6837 $noEditAccessedCalendars =
true;
6839 foreach ($collabSections as $section)
6841 if ($noEditAccessedCalendars && $section[
'PERM'][
'edit'])
6843 $noEditAccessedCalendars =
false;
6849 return $noEditAccessedCalendars;
6854 $result = CCalendarEvent::GetList(
6860 'parseRecursion' =>
false,
6861 'fetchAttendees' =>
true,
6862 'fetchMeetings' =>
false,
6863 'userId' => $userId,
6864 'checkPermissions' => $checkPermission,
6865 'loadOriginalRecursion' =>
true,
6882 $typeModel = TypeModel::createFromXmlId(self::$type);
6885 ->check(ActionDictionary::ACTION_TYPE_ACCESS, $typeModel, [])
6891 $permission = self::GetPermissions([
6892 'type' => self::$type,
6893 'ownerId' => self::$ownerId,
6894 'userId' => self::$userId,
6897 $readOnly = !$permission[
'edit'] && !$permission[
'section_edit'];
6899 if (self::$type === Dictionary::CALENDAR_TYPE[
'user'] && self::$userId !== self::$ownerId)
6909 $groupOrUser = self::$type === Dictionary::CALENDAR_TYPE[
'user']
6910 || self::$type === Dictionary::CALENDAR_TYPE[
'group']
6912 $noEditAccessedCalendars = $groupOrUser;
6914 if (self::hasToCreateDefaultCalendar($sections))
6916 $sections[] = \CCalendarSect::CreateDefault([
6917 'type' => self::$type,
6918 'ownerId' => self::$ownerId,
6922 foreach ($sections as $section)
6926 && $section[
'CAL_TYPE'] === self::$type
6927 && (
int)$section[
'OWNER_ID'] === (
int)self::$ownerId
6930 if ($noEditAccessedCalendars && $section[
'PERM'][
'edit'])
6932 $noEditAccessedCalendars =
false;
6935 if ($readOnly && ($section[
'PERM'][
'edit'] || $section[
'PERM'][
'edit_section']))
6942 if (!empty($collabSectionList))
6944 $noEditAccessedCalendars = self::checkCollabSectionAccess($collabSectionList);
6947 if ($groupOrUser && $noEditAccessedCalendars)
6964 $collabSectionList = [];
6966 if (self::$type === Dictionary::CALENDAR_TYPE[
'location'])
6968 $sectionList = $roomsList ?? [];
6972 $owners = [self::$ownerId];
6973 $types = [self::$type];
6980 $sectionList = self::getSectionList([
6981 'CAL_TYPE' => $types,
6982 'OWNER_ID' => $owners,
6984 'ADDITIONAL_IDS' => $followedSectionList,
6985 'checkPermissions' =>
true,
6986 'getPermissions' =>
true,
6987 'getImages' =>
true,
6990 if (self::$type === Dictionary::CALENDAR_TYPE[
'user'] && $isCollabUser)
6992 $userCollabIds = UserCollabs::getInstance()->getIds(self::$userId);
6994 $collabSectionList = self::getSectionList([
6995 'CAL_TYPE' => Dictionary::CALENDAR_TYPE[
'group'],
6996 'OWNER_ID' => $userCollabIds,
6998 'checkPermissions' =>
true,
6999 'getPermissions' =>
true,
7004 $sectionList = array_merge(
7006 self::getSectionListAvailableForUser(self::$userId),
7010 return [$sectionList, $collabSectionList, $followedSectionList, $roomsList];
7015 $createDefault = !self::$bAnonym;
7017 if (self::$type === Dictionary::CALENDAR_TYPE[
'user'])
7019 $createDefault = self::$userId === (int)self::$ownerId;
7022 foreach ($sections as $section)
7026 && $section[
'CAL_TYPE'] === self::$type
7027 && (
int)$section[
'OWNER_ID'] === (
int)self::$ownerId
7034 return $createDefault;
while($arIBlock=$dbIBlock->Fetch()) $SET
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
static addEvent(string $type, array $data)
static getMeetingRoomList(array $params=[])
static payAttentionToNewSharingFeature()
static getSharingEventTypes()
static onSharingEventEdit(array $fields)
static getFilterId($type, $ownerId, $userId)
static getFormSettings($formType, $userId=false, ?string $entryType=null)
static getFollowedSectionIdList($userId=false)
static getSectionCustomization($userId=false)
static set($settings=[], $userId=false)
static getHiddenSections($userId=false, $options=[])
static getTrackingUsers($userId=false, $params=[])
static getUserSelectorContext()
static addPullEvent(PushCommand $command, int $userId, array $params=[])
static prepareTimezone(?string $tz=null)
static getPathToCalendar(?int $ownerId, ?string $type)
static getUserAccessCodes(int $userId)
static getDateObject(string $date=null, ?bool $fullDay=true, ?string $tz='UTC')
static isCollabUser(int $userId)
static get($moduleId, $name, $default="", $siteId=false)
static set($moduleId, $name, $value="", $siteId="")
static includeModule($moduleName)
static loadLanguageFile($file, $language=null, $normalize=true)
static convertFormatToPhp($format)
static getHandlersList($placement, $skipInstallCheck=false, int $userId=null)
static canTurnOnTrial(string $featureName)
static isFeatureEnabled(string $featureName, int $groupId=0)
static GetListEx($arOrder=Array("SORT"=>"ASC"), $arFilter=Array(), $bCount=false, $iNum=0, $arAddParams=array())
static GetList($arOrder=['MODULE_ID'=> 'asc', 'LETTER'=> 'asc'], $arFilter=[])
static IsFeatureEnabled($_1488512778)
static GetEventsByRecId($recurrenceId, $checkPermissions=true, $userId=null)
static GetNearestEventsList($params=[])
static SetOffset($userId=false, $value=0)
static CollectCalDAVErros($arErrors=[])
static getSectionsInfo($isCollabUser)
static FormatTime($h=0, $m=0)
static IsExchangeEnabled($userId=false)
const CALENDAR_MAX_TIMESTAMP
static Color($color='', $defaultColor=true)
static GetOffset($userId=false)
static GetPathes($forSite=null)
static SetUserDepartment($userId=0, $dep=[])
static UnParseTextLocation($loc='')
static GetAccessibilityForUsers($params)
static DeleteCalendarEvent($eventId, $userId, $oEvent=false)
static IsPersonal($type=false, $ownerId=false, $userId=false)
static UpdateCounter($users=false, array $eventIds=[], array $groupIds=[])
static GetMoreAttendeesMessage($cnt=0)
static SaveSyncDate($userId, $syncType)
static OnExchangeCalendarSync(\Bitrix\Main\Event $event)
static getEntryUrl(string $calType, int $ownerId, int $entryId, string $dateFrom)
static GetEventList($params, &$arAttendees)
static GetSectionList($params=[])
static GetTypeByExternalId($externalId=false)
static SetUserSettings($settings=[], $userId=false)
static isIphoneConnected()
static isDaylightSavingTimezone(string $timezoneId)
static SaveEventEx($params=[])
static GetFromToHtml( $fromTs=false, $toTs=false, $skipTime=false, $dtLength=0, $forRrule=false, $languageId=null)
static SaveUserTimezoneName($user, $tzName='')
static OnSocNetGroupDelete($groupId)
static CacheTime($time=false)
const CALENDAR_CHAT_ENTITY_TYPE
static GetAccessTasksByName($binging='calendar_section', $name='calendar_denied')
static CollectExchangeErrors($arErrors=[])
static IsIntranetEnabled()
static GetUserList(array $userIdList)
static PushAccessNames($arCodes=[])
static createDateTimeObjectFromString(string $date, ?string $timezone=null)
static GetDestinationUsers($codes, $fetchUsers=false)
static CutZeroTime($date)
static fetchIconsForSectionList($sectionList)
static TimestampUTC(string $date)
static GetTextLocation($loc='')
static DateWithNewTime($timestampTime, $timestampDate)
static GetOwnerName($type='', $ownerId='')
static GetUserPermissionsForCalendar($calendarId, $userId)
static GetStartUpEvent($eventId=false, $isSharing=false)
getEditEventId(string $id)
static OnDavCalendarSync(\Bitrix\Main\Event $event)
static TSFormat($format="FULL")
static UpdateUFRights($files, $rights, $ufEntity=[])
static GetPathForCalendarEx($userId=0)
static getTaskList(TaskQueryParameter $parameter)
static IsExtranetEnabled()
static GetUserNameTemplate($fromSite=true)
static GetUser($userId, $bPhoto=false)
static GetUserAvatarSrc($user=[], $params=[])
static SetMaxPlannerUsers(int $maxPlannerUsers)
static SyncCalendarItems($connectionType, $calendarId, $arCalendarItems)
static GetAbsentEvents($params)
static GetCurUserMeetingSection($bCreate=false)
static DeleteSection($id)
static SetCurUserMeetingSection($userMeetingSection)
static ClearExchangeHtml($html="")
static IndByWeekDay(string $weekday)
static ParseLocation($location='')
static GetCurUserId($refresh=false)
static GetSettings($params=[])
doOpenEventInEditMode(string $id)
static HandleImCallback($module, $tag, $value, $arNotify)
static AddConnection($connection, $type='caldav')
static GetMaxPlannerUsers()
static SaveMultipleSyncDate($userId, $syncType, $sectionId)
static RoundTimestamp($ts)
static ReminderAgent($eventId=0, $userId=0, $viewPath='', $calendarType='', $ownerId=0, $index=0)
const INTEGRATION_GOOGLE_API
static CheckCalDavUrl($url, $username, $password)
static getUserLanguageId(?int $userId)
static _ParseHack(&$text, &$TextParser)
static GetCrmSection($userId, $autoCreate=false)
static GetAttendeesMessage($cnt=0)
static SaveEvent($params=[])
static GetUserTimezoneName($user, $getDefault=true)
static getSectionListAvailableForUser($userId, $additionalSectionIdList=[], $params=[])
static IsExtranetUser($userId=0)
static GetGoodTimezoneForOffset($offset)
static GetPermissions($Params=[])
static isReadOnly(array $sections, array $collabSectionList=[])
static Date($timestamp, $bTime=true, $bRound=true, $bCutSeconds=false)
static GetMeetingSection($userId, $autoCreate=false)
static GetSocNetDestination($user_id=false, $selected=[], $userList=[])
static GetPath($type='', $ownerId='', $hard=false)
static SetSettings($settings=[], $clearOptions=false)
static Timestamp($date, $bRound=true, $bTime=true)
static CountPastEvents($params)
static GetUserName($user)
static GetUserUrl($userId=0, $pathToUser="")
static GetCurrentOffsetUTC($userId=false)
static DFormat($bTime=true)
static GetAccessTasks($binging='calendar_section', $type='')
static SetDisplayedSuperposed($userId=false, $idList=[])
static SetSilentErrorMode($silentErrorMode=true)
static ClearCache($arPath=[])
static isOffice365ApiEnabled()
static TempUser($TmpUser=false, $create=true, $ID=false)
static SaveSection($params)
static GetTimezoneOffset($timezoneId, $dateTimestamp=false)
static setOwnerId($userId)
static DeleteEvent($id, $doExternalSync=true, $params=[])
static ParseHTMLToBB($html="")
static GetUserDepartment($userId=0)
static getCurrentEventForSaving(int $eventId, $userId, $checkPermission)
static GetUserAvatar($user=[], $params=[])
static isGoogleApiEnabled()
static IsWebserviceEnabled()
static GetUserSettings($userId=false)
static hasToCreateDefaultCalendar(array $sections)
static WeekDayByInd($i, $binv=true)
static ReminderAgent($eventId=0, $userId=0, $viewPath='', $calendarType='', $ownerId=0, $index=0)
const PLACEMENT_GRID_VIEW
static InitJS($config=array(), $data=array(), $additionalParams=array())
static CreateDefault($params=[])
static DoDeleteToDav($params, $event)
static GetSyncInfo($params=[])
static GetSyncInfoItem($userId, $syncType)
static DoSaveToDav(&$arFields, $params=[], $event=false)
static urlAddParams($url, $add_params, $options=[])
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
$_SERVER["DOCUMENT_ROOT"]
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
const BX_RESIZE_IMAGE_EXACT
if(file_exists($_SERVER['DOCUMENT_ROOT'] . "/urlrewrite.php")) $uri
if(!is_array($deviceNotifyCodes)) $access
if($NS['step']==6) if( $NS[ 'step']==7) if(COption::GetOptionInt('main', 'disk_space', 0) > 0) $info
CheckSerializedData($str, $max_depth=200)
IsAmPmMode($returnConst=false)
ExecuteModuleEventEx($arEvent, $arParams=[])
FormatDate($format="", $timestamp=false, $now=false, ?string $languageId=null)
htmlspecialcharsback($str)
IsModuleInstalled($module_id)
ParseDateTime($datetime, $format=false)
AddEventHandler($FROM_MODULE_ID, $MESSAGE_ID, $CALLBACK, $SORT=100, $FULL_PATH=false)
AddEventToStatFile($module, $action, $tag, $label, $action_type='', $user_id=null)
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
GetModuleEvents($MODULE_ID, $MESSAGE_ID, $bReturnArray=false)
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
MakeTimeStamp($datetime, $format=false)
bitrix_sessid_get($varname='sessid')
RemoveEventHandler($FROM_MODULE_ID, $MESSAGE_ID, $iEventHandlerKey)
$GLOBALS['____1690880296']
addError(string $class, string $message='')
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
if(empty($signedUserToken)) $key
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
if(!Loader::includeModule('sale')) $pattern