36 public const EXTERNAL_LINK =
'https://www.bitrix24.com/controller/google_calendar_push.php?target_host=';
41 private $syncTransport;
42 private $nextSyncToken =
'',
43 $defaultTimezone = self::DEFAULT_TIMEZONE,
45 $calendarList =
array(),
46 $defaultReminderData =
array(),
47 $calendarColors =
false,
49 $eventMapping =
array(
50 'DAV_XML_ID' =>
'iCalUID',
53 'CAL_DAV_LABEL' =>
'etag'
58 private $connectionId;
62 private $nextPageToken =
'';
74 $userId = \CCalendar::GetUserId();
76 $this->userId = $userId;
77 $this->connectionId = $connectionId;
109 $channel = $this->syncTransport->openCalendarListChannel($this->makeChannelParams(
$name, self::CONNECTION_CHANNEL_TYPE));
110 if (!$this->syncTransport->getErrors())
125 private function makeChannelParams($inputSecretWord,
$type)
127 if (defined(
'BX24_HOST_NAME') && BX24_HOST_NAME)
129 $externalUrl = self::EXTERNAL_LINK . BX24_HOST_NAME;
133 $request = Context::getCurrent()->getRequest();
134 if (defined(
'SITE_SERVER_NAME') && SITE_SERVER_NAME)
136 $host = SITE_SERVER_NAME;
140 $host = Option::get(
'main',
'server_name',
$request->getHttpHost());
143 $externalUrl =
'https://' .
$host .
'/bitrix/tools/calendar/push.php';
147 'id' =>
$type.
'_'.$this->userId.
'_'.md5($inputSecretWord. time()),
148 'type' =>
'web_hook',
149 'address' => $externalUrl,
150 'expiration' => (time() + self::CHANNEL_EXPIRATION) * 1000,
162 $channel = $this->syncTransport->openEventsWatchChannel(
164 $this->makeChannelParams($calendarId, self::SECTION_CHANNEL_TYPE)
167 if (!$this->syncTransport->getErrors())
201 return $this->syncTransport->getErrors();
204 private function setColors()
206 if ($this->calendarColors ===
false || $this->eventColors ===
false)
208 $cacheTime = 86400 * 7;
213 $cache = \Bitrix\Main\Data\Cache::createInstance();
214 $cacheId =
"google_colors";
215 $cachePath =
'googlecolors';
217 if ($cache->initCache($cacheTime, $cacheId, $cachePath))
219 $res = $cache->getVars();
220 $colorData =
$res[
"colorData"];
224 if (!$cacheTime || empty($colorData))
226 $colorData = $this->syncTransport->getColors();
227 if ($cacheTime && isset($cache, $cacheId, $cachePath))
229 $cache->startDataCache($cacheTime, $cacheId, $cachePath);
230 $cache->endDataCache(
array(
231 "colorData" => $colorData
236 $this->calendarColors =
array();
237 $this->eventColors =
array();
238 if (is_array($colorData) && !empty($colorData[
'calendar']) && !empty($colorData[
'event']))
240 foreach ($colorData[
'calendar'] as
$key => $color)
242 $this->calendarColors[
$key] = $color;
245 foreach ($colorData[
'event'] as
$key => $color)
247 $this->eventColors[
$key] = $color;
258 private function getCalendarColor($colorId,
$background =
true)
260 $calendarColors = is_array($this->calendarColors) ? $this->calendarColors :
array();
261 return $calendarColors[$colorId][(
$background ?
'background' :
'foreground')];
269 private function getEventColor($colorId,
$background =
true)
271 $eventColors = is_array($this->eventColors) ? $this->eventColors :
array();
272 return $eventColors[$colorId][(
$background ?
'background' :
'foreground')];
282 $connectionError = $this->syncTransport->getErrorByCode(
'CONNECTION');
283 if ($connectionError)
285 return $connectionError[
'message'];
302 if (empty($this->calendarList))
304 $response = $this->syncTransport->getCalendarList($this->getCalendarListParams($syncToken));
309 foreach(
$response[
'items'] as $calendarItem)
311 $calendarItem[
'backgroundColor'] = $this->getCalendarColor($calendarItem[
'colorId']);
312 $calendarItem[
'textColor'] = $this->getCalendarColor($calendarItem[
'colorId'],
true);
313 $this->calendarList[] = $calendarItem;
316 $this->nextSyncToken =
$response[
'nextSyncToken'];
320 return $this->calendarList;
328 return $this->nextSyncToken;
340 $this->nextSyncToken = $calendarData[
'SYNC_TOKEN'] ??
'';
341 $this->nextPageToken = $calendarData[
'PAGE_TOKEN'] ??
'';
343 if (!empty(
$response = $this->runSyncEvents($calendarData[
'GAPI_CALENDAR_ID'])))
345 return $this->processResponseReceivingEvents(
$response);
358 $calendar = $this->getCalendarById(
'primary');
359 return !empty($calendar) ? $calendar[
'id'] :
'';
368 private function getCalendarById($calendarId)
372 foreach ($this->calendarList as $calendar)
374 if (($calendar[
'id'] == $calendarId) || (isset($calendar[
'primary']) && $calendarId ==
'primary'))
391 return $this->syncTransport->deleteEvent($eventId, urlencode($calendarId));
403 $params[
'editInstance'] = $parameters[
'editInstance'] ??
false;
404 $params[
'originalDavXmlId'] = $parameters[
'originalDavXmlId'] ??
null;
405 $params[
'editParentEvents'] = $parameters[
'editParentEvents'] ??
false;
406 $params[
'editNextEvents'] = $parameters[
'editNextEvents'] ??
false;
407 $params[
'calendarId'] = $calendarId;
408 $params[
'instanceTz'] = $parameters[
'instanceTz'] ??
null;
409 $params[
'originalDateFrom'] = $eventData[
'ORIGINAL_DATE_FROM'] ??
null;
410 $params[
'gEventId'] = $eventData[
'G_EVENT_ID'] ?: str_replace(
'@google.com',
'', $eventData[
'DAV_XML_ID']);
411 $params[
'syncCaldav'] = $parameters[
'syncCaldav'] ??
false;
413 $newEvent = $this->prepareToSaveEvent($eventData,
$params);
415 $externalEvent = $this->sendToSaveEvent($newEvent,
$params);
420 'DAV_XML_ID' => $externalEvent[
'iCalUID'],
421 'CAL_DAV_LABEL' => $externalEvent[
'etag'],
422 'ORIGINAL_DATE_FROM' => $externalEvent[
'originalStartTime'] ? $eventData[
'ORIGINAL_DATE_FROM'] :
null,
423 'G_EVENT_ID' => $externalEvent[
'id'],
439 $responseFields = [];
442 foreach ($events as
$event)
444 $localEvent[
'gEventId'] =
$event[
'gEventId'];
445 $partBody = $this->prepareToSaveEvent(
$event);
446 $localEvent[
'partBody'] =
Web\Json::encode($partBody, JSON_UNESCAPED_SLASHES);
447 $prepareEvents[
$event[
'ID']] = $localEvent;
450 $externalEvents = $this->syncTransport->sendBatchEvents($prepareEvents, $gApiCalendarId,
$params);
454 foreach ($externalEvents as
$key => $externalEvent)
456 $responseFields[
$key][
'DAV_XML_ID'] = $externalEvent[
'iCalUID'];
457 $responseFields[
$key][
'CAL_DAV_LABEL'] = $externalEvent[
'etag'];
458 $responseFields[
$key][
'G_EVENT_ID'] = $externalEvent[
'id'];
459 $responseFields[
$key][
'ORIGINAL_DATE_FROM'] = $externalEvent[
'originalStartTime'] ? $events[
$key][
'ORIGINAL_DATE_FROM'] :
null;
463 return $responseFields;
468 if (Loader::includeModule(
'dav') && !empty($this->connectionId))
470 CDavConnection::Update($this->connectionId, [
471 "LAST_RESULT" => $lastResult,
472 "SYNCHRONIZED" => ConvertTimeStamp(time(),
"FULL"),
490 'TZ_FROM' => $this->defaultTimezone,
491 'TZ_TO' => $this->defaultTimezone
494 foreach ($this->eventMapping as $internalKey => $externalKey)
496 $returnData[$internalKey] = (isset(
$event[$externalKey]) ?
$event[$externalKey] :
'');
499 $returnData[
'iCalUID'] =
$event[
'iCalUID'];
500 $returnData[
'DAV_XML_ID'] =
$event[
'iCalUID'];
501 $returnData[
'G_EVENT_ID'] =
$event[
'id'];
503 if (!empty(
$event[
'description']))
509 if (empty(
$event[
'summary']))
511 $returnData[
'NAME'] =
GetMessage(
'EC_T_NEW_EVENT');
514 if (!empty(
$event[
'transparency']) &&
$event[
'transparency'] ==
'transparent')
516 $returnData[
'ACCESSIBILITY'] =
'free';
520 $returnData[
'ACCESSIBILITY'] =
'busy';
523 if (!empty(
$event[
'visibility']) &&
$event[
'visibility'] ===
'private')
525 $returnData[
'PRIVATE_EVENT'] =
true;
529 $returnData[
'PRIVATE_EVENT'] =
false;
534 $returnData[
'CAL_TYPE'] =
'user';
536 if (!empty(
$event[
'start'][
'dateTime']) && !empty(
$event[
'end'][
'dateTime']))
541 $eventStartDateTime =
new Type\DateTime(
$event[
'start'][
'dateTime'], self::DATE_TIME_FORMAT,
new \DateTimeZone($this->defaultTimezone));
542 $returnData[
'DATE_FROM'] = \CCalendar::Date(\CCalendar::Timestamp($eventStartDateTime->setTimeZone(
new \DateTimeZone($returnData[
'TZ_FROM']))->format(Type\Date::convertFormatToPhp(
FORMAT_DATETIME))));
544 $eventStartDateTime =
new Type\DateTime(
$event[
'end'][
'dateTime'], self::DATE_TIME_FORMAT,
new \DateTimeZone($this->defaultTimezone));
545 $returnData[
'DATE_TO'] = \CCalendar::Date(\CCalendar::Timestamp($eventStartDateTime->setTimeZone(
new \DateTimeZone($returnData[
'TZ_TO']))->format(Type\Date::convertFormatToPhp(
FORMAT_DATETIME))));
548 if (!empty(
$event[
'start'][
'date']))
550 $returnData[
'DATE_FROM'] = \CCalendar::Date(strtotime(
$event[
'start'][
'date']),
false);
553 if (!empty(
$event[
'end'][
'date']))
557 $dateStr = strtotime(
$event[
'end'][
'date']);
561 $dateStr = strtotime(
$event[
'end'][
'date']) - self::ONE_DAY;
564 $returnData[
'DATE_TO'] = \CCalendar::Date($dateStr,
false);
565 $returnData[
'DT_SKIP_TIME'] =
'Y';
569 $returnData[
'DT_SKIP_TIME'] =
'N';
572 $returnData[
'DATE_CREATE'] = \CCalendar::Date(strtotime(
$event[
'created']));
574 if (!empty(
$event[
'colorId']))
576 $returnData[
'COLOR'] = $this->getEventColor(
$event[
'colorId']);
577 $returnData[
'TEXT_COLOR'] = $this->getEventColor(
$event[
'colorId'],
false);
580 $returnData[
'DATE_CREATE'] = \CCalendar::Date(time());
581 $returnData[
'status'] =
$event[
'status'];
582 $returnData[
'hasMoved'] =
"N";
583 $returnData[
'isRecurring'] =
"N";
589 foreach (
$event[
'recurrence'] as $recurrence)
591 if (preg_match(
'/(RRULE)/', $recurrence))
593 $rRuleData = preg_replace(
'/(RRULE\:)/',
'', $recurrence);
594 $rRuleList = explode(
';', $rRuleData);
596 foreach ($rRuleList as $rRuleElement)
598 [$rRuleProp, $rRuleVal] = explode(
'=', $rRuleElement);
602 if (in_array($rRuleVal, [
'HOURLY',
'DAILY',
'WEEKLY',
'MONTHLY',
'YEARLY']))
604 $rRuleSet[
'FREQ'] = $rRuleVal;
608 $rRuleSet[
'COUNT'] = $rRuleVal;
611 $rRuleSet[
'INTERVAL'] = $rRuleVal;
614 $rRuleByDay =
array();
615 foreach(explode(
',', $rRuleVal) as $day)
618 if (preg_match(
'/((\-|\+)?\d+)?(MO|TU|WE|TH|FR|SA|SU)/', $day,
$matches))
628 if (!empty($rRuleByDay))
630 $rRuleSet[
'BYDAY'] = $rRuleByDay;
636 $rRuleValDateTime =
new Type\DateTime($rRuleVal,
'Ymd\THis\Z',
new \DateTimeZone(
'UTC'));
637 $rRuleValDateTime->setTimeZone(
new \DateTimeZone($returnData[
'TZ_TO']));
638 $untilDateTime = explode(
"T", $rRuleVal);
640 if ($untilDateTime[1] ===
"000000Z")
642 $rRuleValDateTime = $rRuleValDateTime->add(
"-1 day");
645 $rRuleSet[
'UNTIL'] = \CCalendar::Date(\CCalendar::Timestamp($rRuleValDateTime->format(Type\Date::convertFormatToPhp(
FORMAT_DATETIME))) - 60,
false,
false);
649 $rRuleSet[
'UNTIL'] = \CCalendar::Date(strtotime($rRuleVal),
false,
false);
655 $returnData[
"RRULE"] = \CCalendarEvent::CheckRRULE($rRuleSet);
657 elseif (preg_match(
'/(\d{4}-?\d{2}-?\d{2})(Z)?/', $recurrence, $date))
659 if (!empty($date[1]))
661 $exDates[] = \CCalendar::Date(strtotime($date[1]),
false);
667 if (!empty(
$event[
'recurringEventId']))
669 $returnData[
'isRecurring'] =
"Y";
670 if (
$event[
'status'] ===
'cancelled')
672 $exDates[] = date(Date::convertFormatToPhp(
FORMAT_DATE), strtotime(
673 !empty(
$event[
'originalStartTime'][
'dateTime'])
674 ?
$event[
'originalStartTime'][
'dateTime']
675 :
$event[
'originalStartTime'][
'date']
680 $returnData[
'hasMoved'] =
"Y";
681 $exDates[] = date(Date::convertFormatToPhp(
FORMAT_DATE), strtotime(!empty(
$event[
'originalStartTime'][
'dateTime']) ?
$event[
'originalStartTime'][
'dateTime'] :
$event[
'originalStartTime'][
'date']));
683 if (!empty(
$event[
'originalStartTime'][
'dateTime']))
686 $eventOriginalDateFrom =
new Type\DateTime(
$event[
'originalStartTime'][
'dateTime'], self::DATE_TIME_FORMAT,
new \DateTimeZone($this->defaultTimezone));
687 $returnData[
'ORIGINAL_DATE_FROM'] = \CCalendar::Date(\CCalendar::Timestamp($eventOriginalDateFrom->setTimeZone(
new \DateTimeZone($originalTimeZone))->format(Type\Date::convertFormatToPhp(
FORMAT_DATETIME))));
690 if (!empty(
$event[
'originalStartTime'][
'date']))
692 $returnData[
'ORIGINAL_DATE_FROM'] = \CCalendar::Date(strtotime(
$event[
'originalStartTime'][
'date']),
false);
696 $returnData[
'recurringEventId'] =
$event[
'recurringEventId'];
698 if (!empty($exDates))
700 $returnData[
'EXDATE'] = implode(
';', $exDates);
703 $returnData[
'REMIND'] = [];
704 if (!empty(
$event[
'reminders'][
'overrides']))
706 foreach (
$event[
'reminders'][
'overrides'] as $remindData)
708 $remindTimeout = $remindData[
'minutes'];
709 $returnData[
'REMIND'][] = [
711 'count' => $remindTimeout
715 if (!empty(
$event[
'reminders'][
'useDefault']) && !empty($this->defaultReminderData) &&
$event[
'reminders'][
'useDefault'] === 1)
717 foreach ($this->defaultReminderData as $remindData)
719 $remindTimeout = $remindData[
'minutes'];
720 $returnData[
'REMIND'][] = [
722 'count' => $remindTimeout
726 if (!empty(
$event[
'location']))
728 $returnData[
'LOCATION'] = Rooms\Util::unParseTextLocation(
$event[
'location']);
731 if (!empty(
$event[
'sequence']))
748 private function prepareToSaveEvent($eventData,
$params =
null):
array
751 $newEvent[
'summary'] = $eventData[
'NAME'];
753 if (!empty($eventData[
'ATTENDEES_CODES']) && is_string($eventData[
'ATTENDEES_CODES']))
755 $eventData[
'ATTENDEES_CODES'] = explode(
",", $eventData[
'ATTENDEES_CODES']);
758 if (is_string($eventData[
'MEETING']))
760 $eventData[
'MEETING'] = unserialize($eventData[
'MEETING'], [
'allowed_classes' =>
false]);
762 if (empty($eventData[
'MEETING'][
'LANGUAGE_ID']))
764 $eventData[
'MEETING'][
'LANGUAGE_ID'] = \CCalendar::getUserLanguageId((
int)$eventData[
'OWNER_ID']);
767 if (isset($eventData[
'ATTENDEES_CODES']) && is_countable($eventData[
'ATTENDEES_CODES']) &&
count($eventData[
'ATTENDEES_CODES']) > 1)
770 $newEvent[
'description'] = Loc::getMessage(
'ATTENDEES_EVENT',
null, $eventData[
'MEETING'][
'LANGUAGE_ID']).
': '
771 . stripcslashes(implode(
', ', $users))
773 . $eventData[
"DESCRIPTION"];
775 elseif (!empty($eventData[
'DESCRIPTION']) && is_string($eventData[
'DESCRIPTION']))
777 $newEvent[
'description'] = $eventData[
'DESCRIPTION'];
780 if (!empty($eventData[
'ACCESSIBILITY']) && $eventData[
'ACCESSIBILITY'] ===
'busy')
782 $newEvent[
'transparency'] =
'opaque';
786 $newEvent[
'transparency'] =
'transparent';
789 if (!empty($eventData[
'LOCATION'][
'NEW']) && is_string($eventData[
'LOCATION'][
'NEW']))
791 $newEvent[
'location'] = \CCalendar::GetTextLocation($eventData[
'LOCATION'][
'NEW']);
793 elseif (!empty($eventData[
'LOCATION']) && is_string($eventData[
'LOCATION']))
795 $newEvent[
'location'] = \CCalendar::GetTextLocation($eventData[
'LOCATION']);
798 if (!empty($eventData[
'REMIND']))
800 $newEvent[
'reminders'] = $this->prepareRemind($eventData);
803 if ($eventData[
'DT_SKIP_TIME'] ===
"Y")
811 if (!empty($eventData[
'G_EVENT_ID']))
813 $newEvent[
'start'][
'dateTime'] =
null;
814 $newEvent[
'end'][
'dateTime'] =
null;
815 $newEvent[
'start'][
'timeZone'] =
null;
816 $newEvent[
'end'][
'timeZone'] =
null;
821 $newEvent[
'start'][
'dateTime'] =
Util::getDateObject($eventData[
'DATE_FROM'],
false, $eventData[
'TZ_FROM'])
822 ->format(self::DATE_TIME_FORMAT);
825 $newEvent[
'end'][
'dateTime'] =
Util::getDateObject($eventData[
'DATE_TO'],
false, $eventData[
'TZ_TO'])
826 ->format(self::DATE_TIME_FORMAT);
829 if (!empty($eventData[
'G_EVENT_ID']))
831 $newEvent[
'start'][
'date'] =
null;
832 $newEvent[
'end'][
'date'] =
null;
837 !empty($eventData[
'RRULE'])
838 && is_array($eventData[
'RRULE'])
839 && isset($eventData[
'RRULE'][
'FREQ'])
840 && $eventData[
'RRULE'][
'FREQ'] !==
'NONE'
843 $newEvent[
'recurrence'] = $this->prepareRRule($eventData,
$params[
'editNextEvents']);
846 if (isset($eventData[
'ORIGINAL_DATE_FROM']))
848 $newEvent[
'originalStartTime'] =
Util::getDateObject($eventData[
'ORIGINAL_DATE_FROM'],
false, $eventData[
'TZ_FROM'])
849 ->format(self::DATE_TIME_FORMAT);
852 if (isset($eventData[
'G_EVENT_ID']) && isset($eventData[
'RECURRENCE_ID']))
854 $newEvent[
'recurringEventId'] = $eventData[
'G_EVENT_ID'];
857 if (
$params[
'syncCaldav'] || isset($eventData[
'DAV_XML_ID']))
859 $newEvent[
'iCalUID'] = $eventData[
'DAV_XML_ID'];
862 if (isset($eventData[
'G_EVENT_ID']))
864 $newEvent[
'id'] = $eventData[
'G_EVENT_ID'];
867 if (!empty($eventData[
'PRIVATE_EVENT']))
869 $newEvent[
'visibility'] =
"private";
873 $newEvent[
'visibility'] =
"public";
876 if (isset($eventData[
'VERSION']))
890 private function sendToSaveEvent($newEvent,
$params)
892 if (
$params[
'editInstance'] ===
true)
895 $originalStart = $eventOriginalStart->format(self::DATE_TIME_FORMAT);
897 $instance = $this->syncTransport->getInstanceRecurringEvent(
$params[
'calendarId'], $externalId, $originalStart);
899 $newEvent[
'originalStartTime'] = $originalStart;
900 $newEvent[
'recurringEventId'] =
$params[
'originalDavXmlId'];
904 return $this->syncTransport->updateEvent($newEvent, urlencode(
$params[
'calendarId']),
$instance[
'items'][0][
'id']);
909 return $this->syncTransport->updateEvent($newEvent, urldecode(
$params[
'calendarId']),
$params[
'gEventId']);
913 return $this->syncTransport->importEvent($newEvent, urlencode(
$params[
'calendarId']));
917 return $this->syncTransport->updateEvent($newEvent, urlencode(
$params[
'calendarId']),
$params[
'gEventId']);
921 return $this->syncTransport->insertEvent($newEvent, urlencode(
$params[
'calendarId']));
933 $externalData = $this->syncTransport->insertCalendar($this->prepareCalendar($calendar));
936 ? [
'GAPI_CALENDAR_ID' => $externalData[
'id']]
945 private function prepareCalendar($calendar):
array
947 $returnData[
'summary'] = Emoji::decode($calendar[
'NAME']);
948 if (isset($calendar[
'EXTERNAL_TYPE']) && $calendar[
'EXTERNAL_TYPE'] === \CCalendarSect::EXTERNAL_TYPE_LOCAL)
951 $returnData[
'summary'] = Loc::getMessage(
'EC_CALENDAR_BITRIX24_NAME') .
" " . $returnData[
'summary'];
969 preg_match(
'/(' . self::CONNECTION_CHANNEL_TYPE .
'|' . self::SECTION_CHANNEL_TYPE .
')_(\d+)_.+/',
$channelId,
$matches);
979 return !empty($this->nextPageToken);
985 private function hasExpiredSyncTokenError(): bool
987 return !empty($this->getExpiredSyncTokenError());
993 private function getExpiredSyncTokenError():
array
995 return array_filter($this->syncTransport->getErrors(), function (
$error) {
996 return preg_match(
"/^\[(410)\][a-z0-9 _]*/i",
$error[
'message']);
1008 return $this->getEventsList(
$response[
'items']);
1014 private function getRequestParamsWithSyncToken():
array
1017 'pageToken' => $this->nextPageToken,
1018 'syncToken' => $this->nextSyncToken,
1019 'showDeleted' =>
'true',
1026 private function getRequestParamsForFirstSync():
array
1029 'pageToken' => $this->nextPageToken,
1030 'showDeleted' =>
'true',
1031 'maxResults' => self::SYNC_EVENTS_LIMIT,
1032 'timeMin' => (
new Type\DateTime())->add(self::SYNC_EVENTS_DATE_INTERVAL)->format(self::DATE_TIME_FORMAT),
1040 private function runSyncEvents($gApiCalendarId)
1042 $response = !empty($this->nextSyncToken)
1043 ? $this->syncTransport->getEvents($gApiCalendarId, $this->getRequestParamsWithSyncToken())
1044 : $this->syncTransport->getEvents($gApiCalendarId, $this->getRequestParamsForFirstSync());
1046 if (!
$response && $this->hasExpiredSyncTokenError())
1048 return $this->syncTransport->getEvents($gApiCalendarId, $this->getRequestParamsForFirstSync());
1058 private function getEventsList(iterable $events =
null):
array
1064 foreach ($events as
$event)
1066 $preparedEvent = $this->prepareEvent(
$event);
1067 $eventsList[$preparedEvent[
'G_EVENT_ID']] = $preparedEvent;
1078 $this->nextPageToken =
$response[
'nextPageToken'] ??
'';
1079 $this->nextSyncToken =
$response[
'nextSyncToken'] ??
'';
1080 $this->defaultReminderData =
$response[
'defaultReminders'] ?? $this->defaultReminderData;
1089 private function prepareRemind($eventData):
array
1092 $reminders[
'useDefault'] =
false;
1093 $reminders[
'overrides'] = [];
1095 if (!is_iterable($eventData[
'REMIND']))
1100 foreach ($eventData[
'REMIND'] as $remindRule)
1102 $minutes = $remindRule[
'count'];
1103 if ($remindRule[
'type'] ===
'hour')
1105 $minutes = 60 * $remindRule[
'count'];
1107 elseif ($remindRule[
'type'] ===
'day')
1109 $minutes = 24 * 60 * $remindRule[
'count'];
1111 elseif ($remindRule[
'type'] ===
'daybefore')
1114 $eventData[
'DATE_FROM'],
1115 $eventData[
'DT_SKIP_TIME'] ===
'Y',
1116 $eventData[
'TZ_FROM']
1119 $remind = clone $dateFrom;
1120 if (method_exists($remind,
'setTime'))
1122 $remind->setTime(0, 0, 0);
1124 $remind->add(
"-{$remindRule['before']} days")->add(
"{$remindRule['time']} minutes");
1126 if ($dateFrom->getTimestamp() < $remind->getTimestamp())
1131 $minutes = $this->calculateMinutes($dateFrom, $remind);
1133 elseif ($remindRule[
'type'] ===
'date')
1136 $eventData[
'DATE_FROM'],
1137 $eventData[
'DT_SKIP_TIME'] ===
'Y',
1138 $eventData[
'TZ_FROM']
1141 $remindRule[
'value'],
1142 $eventData[
'DT_SKIP_TIME'] ===
'Y',
1143 $eventData[
'TZ_FROM']
1146 if ($dateFrom->getTimestamp() < $remind->getTimestamp())
1151 $minutes = $this->calculateMinutes($dateFrom, $remind);
1154 $reminders[
'overrides'][] = [
1155 'minutes' => $minutes,
1156 'method' =>
'popup',
1168 private function calculateMinutes(Type\Date $dateFrom, Type\Date $remind): int
1170 $diff = $dateFrom->getDiff($remind);
1171 $days = $diff->format(
'%d');
1172 $hours = $diff->format(
'%h');
1173 $minutes = $diff->format(
'%i');
1175 return ((
int)$days * 24 * 60) + ((int)
$hours * 60) + (
int)$minutes;
1184 private function prepareRRule(
$event, $editNextEvents):
array
1187 $parsedRule = \CCalendarEvent::ParseRRULE(
$event[
'RRULE']);
1189 $rRule .=
'FREQ=' .$parsedRule[
'FREQ'];
1190 $rRule .= !empty($parsedRule[
'INTERVAL']) ?
';INTERVAL=' . $parsedRule[
'INTERVAL'] :
'';
1191 if (!empty($parsedRule[
'BYDAY']))
1193 if (is_string($parsedRule[
'BYDAY']))
1195 $rRule .=
';BYDAY=' . $parsedRule[
'BYDAY'];
1197 elseif (is_array($parsedRule[
'BYDAY']))
1199 $rRule .=
';BYDAY=' . implode(
",", $parsedRule[
'BYDAY']);
1207 if (!empty($parsedRule[
'COUNT']))
1209 $rRule .=
';COUNT=' . $parsedRule[
'COUNT'];
1211 elseif (!empty($parsedRule[
'UNTIL']))
1214 if (
$event[
'DT_SKIP_TIME'] ===
"N" && $tsTo->getTimestamp() < (
new Type\Date(self::END_OF_DATE,
"d.m.Y"))->
getTimestamp())
1216 $tsTo->add(
'+1 day');
1218 $rRule .=
';UNTIL='.$tsTo->format(
'Ymd\THis\Z');
1223 if (!empty(
$event[
'EXDATE']) && $editNextEvents !==
true)
1225 $exDates = explode(
';',
$event[
'EXDATE']);
1226 foreach ($exDates as $exDate)
1228 if (
$event[
'DT_SKIP_TIME'] ===
"Y")
1230 $rule[] =
'EXDATE;VALUE=DATE:' . date(
'Ymd', strtotime($exDate));
1234 $rule[] =
'EXDATE;TZID=UTC:'
1235 . date(
"Ymd", strtotime($exDate))
1237 ->setTimeZone(
new \DateTimeZone(
'UTC'))->format(
'\\THis\\Z');
1251 $this->syncTransport->deleteCalendar($gApiCalendarId);
1257 private function getCalendarListParams(
string $syncToken =
null):
array
1259 if ($syncToken ===
null)
1265 'showDeleted' =>
'true',
1266 'showHidden' =>
'true',
1267 'syncToken' => $syncToken,
1278 $this->syncTransport->updateCalendar($gApiCalendarId, $this->prepareCalendar($calendarData));
1289 return $this->syncTransport->updateCalendarList($gApiCalendarId, $this->prepareCalendarList($section));
1297 private function prepareCalendarList(
array $calendar):
array
1301 if (isset($calendar[
'COLOR']))
1303 $parameters[
'backgroundColor'] = $calendar[
'COLOR'];
1304 $parameters[
'foregroundColor'] =
'#ffffff';
1307 $parameters[
'selected'] =
'true';
1317 return $this->nextPageToken;