17 private const RENEW_LIMIT = 5;
18 private const CREATE_LIMIT = 2;
19 private const CLEAR_LIMIT = 6;
20 private const CHECK_LIMIT = 10;
35 $pushesDb = PushTable::getList([
42 'limit' => self::RENEW_LIMIT,
45 while ($row = $pushesDb->fetch())
48 if ($row[
'ENTITY_TYPE'] === self::TYPE_CONNECTION)
50 $connectionIds[] = (int)$row[
'ENTITY_ID'];
53 if ($row[
'ENTITY_TYPE'] === self::TYPE_SECTION)
55 $sectionIds[] = (int)$row[
'ENTITY_ID'];
59 if (!empty($pushRows))
65 if (!empty($sectionIds))
67 $sectionResult =
$DB->query(
"SELECT * FROM b_calendar_section WHERE ID IN (" . implode(
',', $sectionIds) .
")");
68 while($row = $sectionResult->fetch())
70 $sections[$row[
'ID']] = $row;
71 if (!empty($row[
'CAL_DAV_CON']) && !in_array((
int)$row[
'CAL_DAV_CON'], $connectionIds,
true))
73 $connectionIds[] = (int)$row[
'CAL_DAV_CON'];
78 if (!empty($connectionIds))
80 $connectionResult =
$DB->query(
"SELECT * FROM b_dav_connections WHERE ID IN (" . implode(
',', $connectionIds) .
")");
81 while($row = $connectionResult->fetch())
83 $connections[$row[
'ID']] = $row;
87 foreach ($pushRows as $row)
90 if ($row[
'ENTITY_TYPE'] === self::TYPE_CONNECTION && !empty($connections[$row[
'ENTITY_ID']]))
92 $connectionData = $connections[$row[
'ENTITY_ID']];
93 if (is_string($connectionData[
'LAST_RESULT']) && self::isAuthError($connectionData[
'LAST_RESULT']))
97 $googleApiConnection =
new GoogleApiSync($connectionData[
'ENTITY_ID'], $connectionData[
'ID']);
98 $googleApiConnection->stopChannel($row[
'CHANNEL_ID'], $row[
'RESOURCE_ID']);
100 $channelInfo = $googleApiConnection->startWatchCalendarList($connectionData[
'NAME']);
102 elseif ($row[
'ENTITY_TYPE'] === self::TYPE_SECTION && !empty($sections[$row[
'ENTITY_ID']]))
104 $section = $sections[$row[
'ENTITY_ID']];
107 (!empty($connectionData)
108 && is_string($connectionData[
'LAST_RESULT'])
109 && self::isAuthError($connectionData[
'LAST_RESULT'])
111 || self::isVirtualCalendar($section[
'GAPI_CALENDAR_ID'], $section[
'EXTERNAL_TYPE'])
117 $connectionData = $connections[$section[
'CAL_DAV_CON']];
118 $googleApiConnection =
new GoogleApiSync($section[
'OWNER_ID'], $section[
'CAL_DAV_CON']);
119 $googleApiConnection->stopChannel($row[
'CHANNEL_ID'], $row[
'RESOURCE_ID']);
121 $channelInfo = $googleApiConnection->startWatchEventsChannel($section[
'GAPI_CALENDAR_ID']);
130 && isset($channelInfo[
'id'], $channelInfo[
'resourceId'])
131 && isset($googleApiConnection)
134 $googleApiConnection->updateSuccessLastResultConnection();
136 'ENTITY_TYPE' => $row[
'ENTITY_TYPE'],
137 'ENTITY_ID' => $row[
'ENTITY_ID'],
138 'CHANNEL_ID' => $channelInfo[
'id'],
139 'RESOURCE_ID' => $channelInfo[
'resourceId'],
140 'EXPIRES' => $channelInfo[
'expiration'],
141 'NOT_PROCESSED' =>
'N'
147 if (
count($pushRows) < 4)
149 \CAgent::removeAgent(
"\\Bitrix\\Calendar\\Sync\\GoogleApiPush::renewWatchChannels();",
"calendar");
161 $sectionIds = self::getNotVirtualSectionIds($localSections);
163 $pushChannels = PushTable::getList([
165 '=ENTITY_TYPE' => self::TYPE_SECTION,
166 '=ENTITY_ID' => $sectionIds,
169 $inactiveSections = array_flip($sectionIds);
171 while($row = $pushChannels->fetch())
174 $tsExpires = strtotime($row[
'EXPIRES']);
176 if ($now > $tsExpires)
183 || self::isAuthError(self::getLastResultBySectionId((
int)$row[
'ENTITY_ID']))
186 unset($inactiveSections[$row[
'ENTITY_ID']]);
190 $googleApiConnection->stopChannel($row[
'CHANNEL_ID'], $row[
'RESOURCE_ID']);
192 unset($inactiveSections[$row[
'ENTITY_ID']]);
194 $localCalendarIndex = array_search($row[
'ENTITY_ID'], array_column($localSections,
'ID'));
195 if ($localCalendarIndex !==
false)
197 $channelInfo = $googleApiConnection->startWatchCalendarList($localSections[$localCalendarIndex][
'GAPI_CALENDAR_ID']);
203 'ENTITY_TYPE' => $row[
'ENTITY_TYPE'],
204 'ENTITY_ID' => $row[
'ENTITY_ID']
207 'CHANNEL_ID' => $channelInfo[
'id'],
208 'RESOURCE_ID' => $channelInfo[
'resourceId'],
209 'EXPIRES' => $channelInfo[
'expiration'],
210 'NOT_PROCESSED' =>
'N'
217 if (is_array($localSections) && is_array($inactiveSections) && $googleApiConnection instanceof
GoogleApiSync)
219 self::startChannelForInActiveSections($localSections, $inactiveSections, $googleApiConnection);
234 $pushOptionEnabled = \COption::getOptionString(
'calendar',
'sync_by_push',
false);
235 if (!$pushOptionEnabled && !\CCalendar::isBitrix24())
241 if(!Loader::includeModule(
'dav'))
246 $davConnections = \CDavConnection::getList(
249 'ACCOUNT_TYPE' =>
Google\Helper::GOOGLE_ACCOUNT_TYPE_API,
253 [
'nTopCount' => self::CREATE_LIMIT]
257 $pushConnectionIds = [];
258 while($row = $davConnections->fetch())
261 if (!self::isConnectionError($row[
'LAST_RESULT']))
264 $connections[] = $row;
265 $pushConnectionIds[] = $row[
'ID'];
269 if(!empty($connections))
274 '=ENTITY_TYPE' => self::TYPE_CONNECTION,
275 '=ENTITY_ID' => $pushConnectionIds,
283 $pushChannels[$row[
'ENTITY_ID']] = $row;
286 foreach($connections as $davConnection)
288 if(isset($pushChannels[$davConnection[
'ID']]))
293 $googleApiConnection =
new GoogleApiSync($davConnection[
'ENTITY_ID'], $davConnection[
'ID']);
294 $channelInfo = $googleApiConnection->startWatchCalendarList($connections[
'NAME']);
295 if($channelInfo && isset($channelInfo[
'id'], $channelInfo[
'resourceId']))
298 $googleApiConnection->updateSuccessLastResultConnection();
300 'ENTITY_TYPE' => self::TYPE_CONNECTION,
301 'ENTITY_ID' => $davConnection[
'ID'],
302 'CHANNEL_ID' => $channelInfo[
'id'],
303 'RESOURCE_ID' => $channelInfo[
'resourceId'],
304 'EXPIRES' => $channelInfo[
'expiration'],
305 'NOT_PROCESSED' =>
'N'
313 \CAgent::removeAgent(
"\\Bitrix\\Calendar\\Sync\\GoogleApiPush::createWatchChannels(".
$start.
");",
"calendar");
314 \CAgent::removeAgent(
"\\Bitrix\\Calendar\\Sync\\GoogleApiPush::createWatchChannels(0);",
"calendar");
331 if ($row[
'ENTITY_TYPE'] === self::TYPE_SECTION)
333 if (Loader::includeModule(
'dav'))
335 $connectionData = \CDavConnection::getById($row[
'ENTITY_ID']);
338 $ownerId = $connectionData[
'ENTITY_ID'];
342 if ($ownerId > 0 && isset($connectionData) && !self::isConnectionError($connectionData[
'LAST_RESULT']))
344 $googleApiConnection =
new GoogleApiSync($ownerId, $row[
'ENTITY_ID']);
345 $googleApiConnection->stopChannel($row[
'CHANNEL_ID'], $row[
'RESOURCE_ID']);
349 if ($row[
'ENTITY_TYPE'] === self::TYPE_SECTION)
351 $section = \CCalendarSect::getById($row[
'ENTITY_ID']);
354 $ownerId = $section[
'OWNER_ID'];
358 if (Loader::includeModule(
'dav') && !empty($section[
'CAL_DAV_CON']))
360 $connectionData = \CDavConnection::getById($section[
'CAL_DAV_CON']);
363 if ($ownerId > 0 && isset($connectionData) && !self::isConnectionError($connectionData[
'LAST_RESULT']))
365 $googleApiConnection =
new GoogleApiSync($ownerId, $section[
'CAL_DAV_CON']);
366 $googleApiConnection->stopChannel($row[
'CHANNEL_ID'], $row[
'RESOURCE_ID']);
380 return !empty($lastResult) && preg_match(
"/^\[(4\d\d)\][a-z0-9 _]*/i", $lastResult);
383 public static function isAuthError(
string $lastResult =
null): bool
385 return !empty($lastResult) && preg_match(
"/^\[(401)\][a-z0-9 _]*/i", $lastResult);
390 return !empty($lastResult) && preg_match(
"/^\[(410)\][a-z0-9 _]*/i", $lastResult);
401 "/^\[404\] Channel \'[a-z0-9 _]*\' not found for project \'[a-z0-9 _]*\'/i",
437 if ($entityType === self::TYPE_SECTION)
440 "SELECT s.*, c.LAST_RESULT as LAST_RESULT
441 FROM b_calendar_section s
442 LEFT JOIN b_dav_connections c
443 ON s.CAL_DAV_CON = c.ID
446 if ($section = $r->fetch())
448 if (self::isAuthError($section[
'LAST_RESULT']))
456 if (empty($tokens[
'nextSyncToken']))
461 \CCalendarSect::edit(
465 'ID' => $section[
'ID'],
466 'SYNC_TOKEN' => $tokens[
'nextSyncToken'],
467 'PAGE_TOKEN' => $tokens[
'nextPageToken'],
473 \CCalendar::clearCache();
476 elseif ($entityType === self::TYPE_CONNECTION)
478 $r =
$DB->query(
"SELECT * FROM b_dav_connections WHERE ID=" .
$entityId);
483 || !Loader::includeModule(
'dav')
490 \CCalendar::clearCache();
514 private static function checkPushConnectionChannel(
array $connectionIds,
array $connections): void
516 $existedConnectionChannels = [];
517 $pushConnectionChannelsDb = PushTable::getList(
520 '=ENTITY_TYPE' => self::TYPE_CONNECTION,
521 '=ENTITY_ID' => $connectionIds
526 while ($row = $pushConnectionChannelsDb->fetch())
529 if (!empty($connections[$row[
'ENTITY_ID']]))
531 $connectionData = $connections[$row[
'ENTITY_ID']];
532 if (!self::isConnectionError($connectionData[
'LAST_RESULT']))
534 $existedConnectionChannels[] = $row[
'ENTITY_ID'];
538 $googleApiConnection =
new GoogleApiSync($connectionData[
'ENTITY_ID'], $connectionData[
'ID']);
539 if ($googleApiConnection->stopChannel($row[
'CHANNEL_ID'], $row[
'RESOURCE_ID']))
542 $channelInfo = $googleApiConnection->startWatchCalendarList($connectionData[
'NAME']);
545 if (is_string($googleApiConnection->getTransportConnectionError()))
550 if ($channelInfo && isset($channelInfo[
'id'], $channelInfo[
'resourceId']))
552 $existedConnectionChannels[] = $row[
'ENTITY_ID'];
553 $googleApiConnection->updateSuccessLastResultConnection();
555 'ENTITY_TYPE' => $row[
'ENTITY_TYPE'],
556 'ENTITY_ID' => $row[
'ENTITY_ID'],
557 'CHANNEL_ID' => $channelInfo[
'id'],
558 'RESOURCE_ID' => $channelInfo[
'resourceId'],
559 'EXPIRES' => $channelInfo[
'expiration'],
560 'NOT_PROCESSED' =>
'N'
567 $missedChannelConnections = array_diff($connectionIds, $existedConnectionChannels);
568 if (!empty($missedChannelConnections))
570 foreach ($missedChannelConnections as $missedConnection)
572 if (self::isAuthError($missedConnection[
'LAST_RESULT']))
578 $connectionData = $connections[$missedConnection];
579 $googleApiConnection =
new GoogleApiSync($connectionData[
'ENTITY_ID'], $connectionData[
'ID']);
580 $channelInfo = $googleApiConnection->startWatchCalendarList($connectionData[
'NAME']);
581 if ($channelInfo && isset($channelInfo[
'id'], $channelInfo[
'resourceId']))
583 $googleApiConnection->updateSuccessLastResultConnection();
585 'ENTITY_TYPE' => self::TYPE_CONNECTION,
586 'ENTITY_ID' => $connectionData[
'ID'],
587 'CHANNEL_ID' => $channelInfo[
'id'],
588 'RESOURCE_ID' => $channelInfo[
'resourceId'],
589 'EXPIRES' => $channelInfo[
'expiration'],
590 'NOT_PROCESSED' =>
'N'
595 $error = $googleApiConnection->getTransportConnectionError();
598 $googleApiConnection->updateLastResultConnection(
$error);
611 private static function checkPushSectionChannel(
array $connectionIds,
array $connections): void
613 $existedSectionChannels = [];
617 $sectionsDb = Internals\SectionTable::getList(
620 'CAL_DAV_CON' => $connectionIds,
628 while ($section = $sectionsDb->fetch())
630 $sections[$section[
'ID']] = $section;
631 $sectionIds[] = $section[
'ID'];
634 if (!empty($sectionIds))
636 $pushSectionChannelsDb = PushTable::getList(
639 '=ENTITY_TYPE' => self::TYPE_SECTION,
640 '=ENTITY_ID' => $sectionIds
645 while ($row = $pushSectionChannelsDb->fetch())
648 if (!empty($sections[$row[
'ENTITY_ID']]))
650 $section = $sections[$row[
'ENTITY_ID']];
652 if (self::isVirtualCalendar($section[
'GAPI_CALENDAR_ID'], $section[
'EXTERNAL_TYPE']))
657 if (!self::isConnectionError($connections[$section[
'CAL_DAV_CON']][
'LAST_RESULT']))
659 $existedSectionChannels[] = $row[
'ENTITY_ID'];
663 $googleApiConnection =
new GoogleApiSync($section[
'OWNER_ID'], $section[
'CAL_DAV_CON']);
664 if ($googleApiConnection->stopChannel($row[
'CHANNEL_ID'], $row[
'RESOURCE_ID']))
667 $channelInfo = $googleApiConnection->startWatchEventsChannel($section[
'GAPI_CALENDAR_ID']);
669 if (is_string($googleApiConnection->getTransportConnectionError()))
674 if ($channelInfo && isset($channelInfo[
'id'], $channelInfo[
'resourceId']))
676 $existedSectionChannels[] = $row[
'ENTITY_ID'];
677 $googleApiConnection->updateSuccessLastResultConnection();
679 'ENTITY_TYPE' => $row[
'ENTITY_TYPE'],
680 'ENTITY_ID' => $row[
'ENTITY_ID'],
681 'CHANNEL_ID' => $channelInfo[
'id'],
682 'RESOURCE_ID' => $channelInfo[
'resourceId'],
683 'EXPIRES' => $channelInfo[
'expiration'],
684 'NOT_PROCESSED' =>
'N'
691 $missedChannelSections = array_diff($sectionIds, $existedSectionChannels);
692 if (!empty($missedChannelSections))
694 foreach ($missedChannelSections as $missedSection)
697 $connectionData = $connections[$sections[$missedSection][
'CAL_DAV_CON']];
698 $section = $sections[$missedSection];
700 self::isAuthError($connectionData[
'LAST_RESULT'])
701 || self::isVirtualCalendar($section[
'GAPI_CALENDAR_ID'], $section[
'EXTERNAL_TYPE'])
707 $googleApiConnection =
new GoogleApiSync($connectionData[
'ENTITY_ID'], $connectionData[
'ID']);
708 $channelInfo = $googleApiConnection->startWatchEventsChannel($section[
'GAPI_CALENDAR_ID']);
709 if ($channelInfo && isset($channelInfo[
'id'], $channelInfo[
'resourceId']))
711 $googleApiConnection->updateSuccessLastResultConnection();
713 'ENTITY_TYPE' => self::TYPE_SECTION,
714 'ENTITY_ID' => $missedSection,
715 'CHANNEL_ID' => $channelInfo[
'id'],
716 'RESOURCE_ID' => $channelInfo[
'resourceId'],
717 'EXPIRES' => $channelInfo[
'expiration'],
718 'NOT_PROCESSED' =>
'N'
721 PushTable::add($row);
725 $error = $googleApiConnection->getTransportConnectionError();
728 $googleApiConnection->updateLastResultConnection(
$error);
743 PushTable::delete([
'ENTITY_TYPE' => $row[
'ENTITY_TYPE'],
'ENTITY_ID' => $row[
'ENTITY_ID']]);
752 private static function isVirtualCalendar(?
string $gApiCalendarId, ?
string $externalType): bool
754 return preg_match(
'/(holiday.calendar.google.com)/', $gApiCalendarId)
755 || preg_match(
'/(group.v.calendar.google.com)/', $gApiCalendarId)
756 || preg_match(
'/(@virtual)/', $gApiCalendarId)
757 || preg_match(
'/(_readonly)/', $externalType)
758 || preg_match(
'/(_freebusy)/', $externalType);
768 private static function startChannelForInActiveSections(
769 array $localSections,
770 array $inactiveSections,
771 GoogleApiSync $googleApiConnection
774 foreach ($localSections as $section)
776 if (self::isVirtualCalendar($section[
'GAPI_CALENDAR_ID'], $section[
'EXTERNAL_TYPE']))
781 if (isset($inactiveSections[$section[
'ID']]))
783 if (($push = self::getPush(self::TYPE_SECTION, $section[
'ID'])) && self::isValid($push))
788 $channelInfo = $googleApiConnection->startWatchEventsChannel($section[
'GAPI_CALENDAR_ID']);
789 if ($channelInfo && isset($channelInfo[
'id'], $channelInfo[
'resourceId']))
793 "ENTITY_TYPE" => self::TYPE_SECTION,
794 'ENTITY_ID' => $section[
'ID']
797 $googleApiConnection->updateSuccessLastResultConnection();
799 'ENTITY_TYPE' => self::TYPE_SECTION,
800 'ENTITY_ID' => $section[
'ID'],
801 'CHANNEL_ID' => $channelInfo[
'id'],
802 'RESOURCE_ID' => $channelInfo[
'resourceId'],
803 'EXPIRES' => $channelInfo[
'expiration'],
804 'NOT_PROCESSED' =>
'N'
809 $error = $googleApiConnection->getTransportConnectionError();
812 $googleApiConnection->updateLastResultConnection(
$error);
824 private static function getLastResultBySectionId(
int $sectionId): ?string
828 $strSql =
"SELECT c.ID as CONNECTON_ID, c.LAST_RESULT, s.ID as SECTION_ID
829 FROM b_dav_connections c
830 INNER JOIN b_calendar_section s
832 .
" WHERE s.CAL_DAV_CON = c.ID";
834 $connectionDb =
$DB->Query($strSql);
848 private static function getNotVirtualSectionIds(
array $localSections):
array
851 foreach ($localSections as $section)
854 if (self::isVirtualCalendar($section[
'GAPI_CALENDAR_ID'], $section[
'EXTERNAL_TYPE']))
859 $sectionIds[] = (int)$section[
'ID'];
875 $pushResultDb = PushTable::getByPrimary([
876 'ENTITY_TYPE' => self::TYPE_CONNECTION,
880 return $pushResultDb->fetch();
894 && isset($push[
'NOT_PROCESSED'])
898 $strSql =
"UPDATE b_calendar_push"
899 .
" SET NOT_PROCESSED = '" . Google\Dictionary::PUSH_STATUS_PROCESS[
'block'] .
"'"
900 .
" WHERE ENTITY_TYPE = '" .
$type .
"' AND ENTITY_ID = " .
$entityId .
";";
917 $strSql =
"UPDATE b_calendar_push"
918 .
" SET NOT_PROCESSED = 'N'"
919 .
" WHERE ENTITY_TYPE = '" .
$type .
"' AND ENTITY_ID = " .
$entityId .
";";
941 && isset($push[
'NOT_PROCESSED'])
945 $strSql =
"UPDATE b_calendar_push"
946 .
" SET NOT_PROCESSED = '" . Google\Dictionary::PUSH_STATUS_PROCESS[
'unprocessed'] .
"'"
947 .
" WHERE ENTITY_TYPE = '" .
$type .
"' AND ENTITY_ID = " .
$entityId .
";";
962 $strSql =
"SELECT * FROM b_calendar_push"
963 .
" WHERE ENTITY_TYPE = '" .
$type .
"' AND ENTITY_ID = " .
$entityId .
";";
964 $pushDb =
$DB->Query($strSql);
966 if ($push = $pushDb->Fetch())
974 private static function isValid(?
array $push): bool
982 $tsExpires = strtotime($push[
'EXPIRES']);
984 return !($now > $tsExpires);
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)