static function DoDataSync($paramUserId, &$lastError): ?bool
{
if (DAV_EXCH_DEBUG)
{
CDav::WriteToLog("Starting EXCHANGE sync...", "SYNCE");
}
$exchangeScheme = COption::GetOptionString("dav", "exchange_scheme", "http");
$exchangeServer = COption::GetOptionString("dav", "exchange_server", "");
$exchangePort = COption::GetOptionString("dav", "exchange_port", "80");
$exchangeUsername = COption::GetOptionString("dav", "exchange_username", "");
$exchangePassword = COption::GetOptionString("dav", "exchange_password", "");
if (empty($exchangeServer)/* || (COption::GetOptionString("dav", "agent_calendar", "N") != "Y")*/)
{
CAgent::RemoveAgent("CDavExchangeCalendar::DataSync();", "dav");
COption::SetOptionString("dav", "agent_calendar", "N");
return null;
}
static $arWeekDayMap = ["sunday" => 6, "monday" => 0, "tuesday" => 1, "wednesday" => 2, "thursday" => 3, "friday" => 4, "saturday" => 5];
$exchange = new CDavExchangeCalendar(
$exchangeScheme,
$exchangeServer,
$exchangePort,
$exchangeUsername,
$exchangePassword
);
if (GW_DEBUG)
{
$exchange->Debug();
}
$exchangeMailbox = COption::GetOptionString("dav", "exchange_mailbox", "");
$exchangeUseLogin = COption::GetOptionString("dav", "exchange_use_login", "Y");
self::InitUserEntity();
$maxNumber = 15;
$index = 0;
$bShouldClearCache = null;
$paramUserId = (int)$paramUserId;
$arUserFilter = ["ACTIVE" => "Y", "!UF_DEPARTMENT" => false];
if ($paramUserId > 0)
{
$arUserFilter["ID_EQUAL_EXACT"] = $paramUserId;
}
if ($exchangeUseLogin === "N")
{
$arUserFilter["!UF_BXDAVEX_MAILBOX"] = false;
}
$dbUserList = CUser::GetList("UF_BXDAVEX_CALSYNC",
"asc",
$arUserFilter,
[
"SELECT" => ["UF_BXDAVEX_MAILBOX", "UF_BXDAVEX_CALSYNC"],
"FIELDS" => ['ID', 'LOGIN'],
"NAV_PARAMS" => ["nTopCount" => $maxNumber]
]
);
$usersToSync = [];
$handledUsers = [];
while ($arUser = $dbUserList->Fetch())
{
$index++;
if ($index > $maxNumber)
{
break;
}
if (DAV_EXCH_DEBUG)
{
CDav::WriteToLog("Processing user [" . $arUser["ID"] . "] " . $arUser["LOGIN"], "SYNCE");
}
$GLOBALS["USER_FIELD_MANAGER"]->Update("USER", $arUser["ID"], array("UF_BXDAVEX_CALSYNC" => ConvertTimeStamp(time(), "FULL")));
$mailbox = (($exchangeUseLogin === "Y") ? $arUser["LOGIN"].$exchangeMailbox : trim($arUser["UF_BXDAVEX_MAILBOX"]));
if (empty($mailbox))
{
$lastError = GetMessage("DAV_EC_EMPTY_MAILBOX");
continue;
}
$arCalendarsList = $exchange->GetCalendarsList(["mailbox" => $mailbox]);
$arErrorsTmp = $exchange->GetErrors();
if (!empty($arErrorsTmp))
{
$txt = '';
foreach ($arErrorsTmp as $v)
{
if (!empty($txt))
{
$txt .= ", ";
}
$txt .= "[".$v[0]."] ".$v[1];
}
if (DAV_EXCH_DEBUG)
{
CDav::WriteToLog("ERROR: " . $txt, "SYNCE");
}
$lastError = $txt;
continue;
}
if (!is_array($arCalendarsList))
{
$lastError = "Incorrect Data from Exchange Server";
continue;
}
$bShouldClearCache = false;
$arUserCalendars = [
[
"XML_ID" => "calendar_".$arUser["ID"],
"NAME" => GetMessage("DAV_EC_CALENDAR"),
"MODIFICATION_LABEL" => "",
]
];
foreach ($arCalendarsList as $value)
{
$arUserCalendars[] = [
"XML_ID" => $value["XML_ID"],
"NAME" => $value["NAME"],
"MODIFICATION_LABEL" => $value["MODIFICATION_LABEL"],
];
}
$tmpNumCals = count($arUserCalendars);
$arUserCalendars = CCalendarSync::SyncCalendarSections("exchange", $arUserCalendars, "user", $arUser["ID"]);
$tmpNumItems = 0;
foreach ($arUserCalendars as $userCalendar)
{
$userCalendarXmlId = $userCalendar["XML_ID"];
$userCalendarXmlId = (($userCalendarXmlId === "calendar_".$arUser["ID"]) ? "calendar" : $userCalendarXmlId);
$arCalendarItemsList = $exchange->GetList(
["mailbox" => $mailbox, "CalendarId" => $userCalendarXmlId],
["ItemShape" => "IdOnly"]
);
if(!empty($arCalendarItemsList))
{
$arUserCalendarItems = [];
foreach ($arCalendarItemsList as $value)
{
$arUserCalendarItems[$value["XML_ID"]] = $value["MODIFICATION_LABEL"];
}
$arModifiedUserCalendarItems = CCalendar::SyncCalendarItems(
"exchange",
$userCalendar["CALENDAR_ID"],
$arUserCalendarItems
);
$tmpNumItems += count($arModifiedUserCalendarItems);
if (is_array($arModifiedUserCalendarItems))
{
$eventIds = [];
$eventsFromExchange = [];
foreach ($arModifiedUserCalendarItems as $value)
{
$eventIds[] = ['id' => $value['XML_ID']];
}
$modifiedItems = $exchange->GetById($eventIds);
if (is_array($modifiedItems) && !empty($modifiedItems))
{
foreach ($modifiedItems as $item)
{
$eventsFromExchange[$item['XML_ID']] = $item;
}
}
foreach ($arModifiedUserCalendarItems as $value)
{
if (
array_key_exists($value["XML_ID"], $eventsFromExchange)
&& $eventsFromExchange[$value["XML_ID"]]
&& is_array($eventsFromExchange[$value["XML_ID"]])
)
{
$modifiedItem = $eventsFromExchange[$value["XML_ID"]];
$modifyEventFields = [
"ID" => $value["ID"],
"NAME" => $modifiedItem["NAME"],
"DESCRIPTION" => $modifiedItem["DESCRIPTION"] ?? null,
"XML_ID" => $modifiedItem["XML_ID"],
"PROPERTY_LOCATION" => $modifiedItem["PROPERTY_LOCATION"] ?? null,
"DATE_FROM" => $modifiedItem["ACTIVE_FROM"],
"DATE_TO" => $modifiedItem["ACTIVE_TO"],
"SKIP_TIME" => $modifiedItem["SKIP_TIME"] ?? null,
"PROPERTY_IMPORTANCE" => $modifiedItem["PROPERTY_IMPORTANCE"] ?? null,
"PROPERTY_REMIND_SETTINGS" => $modifiedItem["PROPERTY_REMIND_SETTINGS"] ?? null,
"PROPERTY_PERIOD_TYPE" => "NONE",
"PROPERTY_BXDAVEX_LABEL" => $modifiedItem["MODIFICATION_LABEL"] ?? null,
"PRIVATE_EVENT" => mb_strtolower($modifiedItem["PROPERTY_SENSITIVITY"]) === 'private',
'TZ_FROM' => $modifiedItem['TIMEZONE'] ?? null,
'TZ_TO' => $modifiedItem['TIMEZONE'] ?? null,
];
if ($modifiedItem["PROPERTY_FREEBUSY"])
{
$modifiedItem["PROPERTY_FREEBUSY"] = mb_strtolower($modifiedItem["PROPERTY_FREEBUSY"]);
if ($modifiedItem["PROPERTY_FREEBUSY"] === "oof")
{
$modifyEventFields["PROPERTY_ACCESSIBILITY"] = "absent";
}
else if ($modifiedItem["PROPERTY_FREEBUSY"] === "free")
{
$modifyEventFields["PROPERTY_ACCESSIBILITY"] = "free";
}
else if ($modifiedItem["PROPERTY_FREEBUSY"] === "tentative")
{
$modifyEventFields["PROPERTY_ACCESSIBILITY"] = "quest";
}
else
{
$modifyEventFields["PROPERTY_ACCESSIBILITY"] = "busy";
}
}
if ($modifiedItem["IS_RECURRING"])
{
if ($modifiedItem["RECURRING_TYPE"] === "MONTHLY_ABSOLUTE"
|| $modifiedItem["RECURRING_TYPE"] === "MONTHLY_RELATIVE"
|| $modifiedItem["RECURRING_TYPE"] === "MONTHLY"
)
{
$modifyEventFields["PROPERTY_PERIOD_TYPE"] = "MONTHLY";
}
elseif ($modifiedItem["RECURRING_TYPE"] === "YEARLY_ABSOLUTE"
|| $modifiedItem["RECURRING_TYPE"] === "YEARLY_RELATIVE"
|| $modifiedItem["RECURRING_TYPE"] === "YEARLY"
)
{
$modifyEventFields["PROPERTY_PERIOD_TYPE"] = "YEARLY";
}
elseif ($modifiedItem["RECURRING_TYPE"] === "WEEKLY")
{
$modifyEventFields["PROPERTY_PERIOD_TYPE"] = "WEEKLY";
}
elseif ($modifiedItem["RECURRING_TYPE"] === "DAILY")
{
$modifyEventFields["PROPERTY_PERIOD_TYPE"] = "DAILY";
}
if (isset($modifiedItem["RECURRING_INTERVAL"]))
{
$modifyEventFields["PROPERTY_PERIOD_COUNT"] = $modifiedItem["RECURRING_INTERVAL"];
}
if (
($modifyEventFields["PROPERTY_PERIOD_TYPE"] === "WEEKLY")
&& isset($modifiedItem["RECURRING_DAYSOFWEEK"])
)
{
$ar = preg_split("/[;,s]/i", $modifiedItem["RECURRING_DAYSOFWEEK"]);
$ar1 = [];
foreach ($ar as $v)
{
$ar1[] = $arWeekDayMap[mb_strtolower($v)];
}
$modifyEventFields["PROPERTY_PERIOD_ADDITIONAL"] = implode(",", $ar1);
}
$modifyEventFields["PROPERTY_EVENT_LENGTH"] = MakeTimeStamp($modifyEventFields["DATE_TO"]) - MakeTimeStamp($modifyEventFields["DATE_FROM"]);
if ($modifyEventFields["PROPERTY_EVENT_LENGTH"] <= 0)
{
$modifyEventFields["PROPERTY_EVENT_LENGTH"] = 86400;
}
if (isset($modifiedItem["RECURRING_NUMBEROFOCCURRENCES"]) && $modifiedItem["RECURRING_NUMBEROFOCCURRENCES"] > 0)
{
$modifyEventFields["PROPERTY_RRULE_COUNT"] = (int)$modifiedItem["RECURRING_NUMBEROFOCCURRENCES"];
}
elseif (isset($modifiedItem["RECURRING_ENDDATE"]))
{
$modifyEventFields["PROPERTY_PERIOD_UNTIL"] = $modifiedItem["RECURRING_ENDDATE"];
}
else
{
$modifyEventFields["PROPERTY_PERIOD_UNTIL"] = ConvertTimeStamp(mktime(0, 0, 0, 12, 31, 2025), "FULL");
}
}
if (
isset($modifiedItem["ATTENDEES_EMAIL_LIST"])
&& !empty($modifiedItem["ATTENDEES_EMAIL_LIST"])
&& class_exists('CCalendarSync')
&& method_exists('CCalendarSync', 'isExchangeMeetingEnabled')
&& CCalendarSync::isExchangeMeetingEnabled()
)
{
$organizer = self::GetUsersByEmailList(array($modifiedItem["ORGANIZER_EMAIL"]));
$entityId = $arUser["ID"];
// Following code executes only for events from organizer
if (!empty($organizer) && $organizer[0])
{
if ($organizer[0] === $entityId)
{
$attendeesMap = self::GetUsersEmailMap($modifiedItem["ATTENDEES_EMAIL_LIST"]);
$modifyEventFields['IS_MEETING'] = true;
$modifyEventFields['MEETING_HOST'] = $entityId;
$modifyEventFields['MEETING'] = ['HOST_NAME' => CCalendar::GetUserName($entityId)];
$modifyEventFields['ATTENDEES_CODES'] = [];
$modifyEventFields['ATTENDEES_RESPONSE'] = [];
foreach($modifiedItem["ATTENDEES_EMAIL_LIST"] as $email)
{
$email = mb_strtolower($email);
if ($entityId == $attendeesMap[$email])
{
continue;
}
if(isset($attendeesMap[$email]) && $attendeesMap[$email])
{
$modifyEventFields['ATTENDEES_CODES'][] = 'U'.$attendeesMap[$email];
if (!empty($modifiedItem['ATTENDEES_RESPONSE'][$email]))
{
$modifyEventFields['ATTENDEES_RESPONSE'][$attendeesMap[$email]] = self::ConvertExchangeResponse($modifiedItem['ATTENDEES_RESPONSE'][$email]);
}
}
else
{
$modifyEventFields['ATTENDEES_CODES'][] = $email;
}
}
$modifyEventFields['ATTENDEES_CODES'] = array_unique($modifyEventFields['ATTENDEES_CODES']);
CCalendarSync::ModifyEvent($userCalendar["CALENDAR_ID"], $modifyEventFields);
}
else
{
$usersToSync[] = (int)$organizer[0];
}
}
}
// For not meetings
else
{
CCalendarSync::ModifyEvent($userCalendar["CALENDAR_ID"], $modifyEventFields);
}
$bShouldClearCache = true;
}
}
}
}
}
if (DAV_EXCH_DEBUG)
{
CDav::WriteToLog(
"Sync " . (int)$tmpNumCals . " calendars, " . (int)$tmpNumItems . " items",
"SYNCE"
);
}
$notify = new BitrixMainEvent(
'dav', 'OnExchandeCalendarDataSync',
[
'userId' => $arUser["ID"],
'shouldClearCache' => $bShouldClearCache,
'lastError' => $lastError
]
);
$notify->send();
$handledUsers[] = (int)$arUser["ID"];
}
if (!empty($usersToSync))
{
$usersToSync = array_unique($usersToSync);
$usersToSync = array_diff($usersToSync, $handledUsers);
// Here we set UF_BXDAVEX_CALSYNC to value one day before now to triger
// sync for these users as soon as possible
foreach($usersToSync as $userId)
{
$GLOBALS["USER_FIELD_MANAGER"]->Update("USER", $userId, ["UF_BXDAVEX_CALSYNC" => ConvertTimeStamp(time() - 86400, "FULL")]
);
}
}
if (DAV_EXCH_DEBUG)
{
CDav::WriteToLog("EXCHANGE sync finished", "SYNCE");
}
return $bShouldClearCache;
}