2namespace Bitrix\Calendar\Sync;
4use Bitrix\Calendar\Sync\Util\RequestLogger;
5use Bitrix\Main\Application;
6use Bitrix\Main\ArgumentException;
7use Bitrix\Main\ArgumentNullException;
8use Bitrix\Main\ArgumentOutOfRangeException;
9use Bitrix\Main\LoaderException;
10use Bitrix\Main\SystemException;
11use Bitrix\Main\Loader;
14use CSocServGoogleOAuth;
15use CSocServGoogleProxyOAuth;
30 private $currentMethod =
'';
51 if (!Loader::includeModule(
'socialservices'))
53 throw new SystemException(
"Can not include module \"SocialServices\"! " . __METHOD__);
57 if (RequestLogger::isEnabled())
62 if (CSocServGoogleProxyOAuth::isProxyAuth())
64 $oAuth =
new CSocServGoogleProxyOAuth(
$userId);
68 $oAuth =
new CSocServGoogleOAuth(
$userId);
71 $oAuth->getEntityOAuth()->addScope([
72 'https://www.googleapis.com/auth/calendar',
73 'https://www.googleapis.com/auth/calendar.readonly',
75 $oAuth->getEntityOAuth()->removeScope(
'https://www.googleapis.com/auth/drive');
77 $oAuth->getEntityOAuth()->setUser(
$userId);
78 if ($oAuth->getEntityOAuth()->GetAccessToken())
80 $this->client->setHeader(
'Authorization',
'Bearer ' . $oAuth->getEntityOAuth()->getToken());
81 $this->client->setHeader(
'Content-Type',
'application/json');
82 $this->client->setHeader(
'Referer', $this->getDomain());
87 $this->errors[] =
array(
"code" =>
"NO_ACCESS_TOKEN",
"message" =>
"No access token found");
101 $this->currentMethod = __METHOD__;
103 return $this->doRequest(
104 Web\HttpClient::HTTP_POST,
105 self::API_BASE_URL.
'/users/me/calendarList/watch',
106 Web\Json::encode($channelInfo, JSON_UNESCAPED_SLASHES)
121 $this->currentMethod = __METHOD__;
123 return $this->doRequest(
124 Web\HttpClient::HTTP_POST,
125 self::API_BASE_URL .
'/calendars/' . urlencode($calendarId) .
'/events/watch',
126 Web\Json::encode($channelInfo, JSON_UNESCAPED_SLASHES)
141 $this->currentMethod = __METHOD__;
143 return $this->doRequest(
144 Web\HttpClient::HTTP_POST,
145 self::API_BASE_URL .
'/channels/stop',
162 if (!in_array(
$type, [
Web\HttpClient::HTTP_PATCH,
Web\HttpClient::HTTP_PUT,
Web\HttpClient::HTTP_DELETE,
Web\HttpClient::HTTP_GET,
Web\HttpClient::HTTP_POST],
true))
170 if ($this->client->getStatus() === 200)
172 $contentType = $this->client->getHeaders()->getContentType();
182 $response = Web\Json::decode($this->client->getResult());
184 catch (ArgumentException $exception)
194 $error = Web\Json::decode($this->client->getResult());
195 $this->errors[] = [
"code" =>
"CONNECTION",
"message" =>
"[" .
$error[
'error'][
'code'] .
"] " .
$error[
'error'][
'message']];
197 catch (ArgumentException $exception)
199 foreach($this->client->getError() as
$code =>
$error)
201 $this->errors[] = [
"code" =>
$code,
"message" =>
$error];
206 if ($this->requestLogger)
208 $this->requestLogger->write([
212 'statusCode' => $this->client->getStatus(),
213 'response' => $this->prepareResponseForDebug(
$response),
214 'error' => $this->prepareErrorForDebug(),
230 $this->currentMethod = __METHOD__;
231 return $this->doRequest(
Web\HttpClient::HTTP_DELETE, self::API_BASE_URL .
'/calendars/' . $calendarId .
'/events/' . $eventId);
242 public function patchEvent($patchData, $calendarId, $eventId)
244 $this->currentMethod = __METHOD__;
246 return $this->doRequest(
Web\HttpClient::HTTP_PUT, self::API_BASE_URL .
'/calendars/' . $calendarId .
'/events/' . $eventId, $requestBody);
259 $this->currentMethod = __METHOD__;
261 return $this->doRequest(
Web\HttpClient::HTTP_PUT, self::API_BASE_URL .
'/calendars/' . $calendarId .
'/events/' . $eventId, $requestBody);
273 $this->currentMethod = __METHOD__;
275 return $this->doRequest(
Web\HttpClient::HTTP_POST, self::API_BASE_URL .
'/calendars/' . $calendarId .
'/events/', $requestBody);
287 $this->currentMethod = __METHOD__;
289 return $this->doRequest(
Web\HttpClient::HTTP_POST, self::API_BASE_URL .
'/calendars/' . $calendarId .
'/events/import', $requestBody);
299 $this->currentMethod = __METHOD__;
301 $url = self::API_BASE_URL .
'/users/me/calendarList';
302 $url .= empty($requestParameters) ?
'' :
'?' . preg_replace(
'/(%3D)/',
'=', http_build_query($requestParameters));
304 return $this->doRequest(
Web\HttpClient::HTTP_GET,
$url);
314 $this->currentMethod = __METHOD__;
315 return $this->doRequest(
Web\HttpClient::HTTP_GET, self::API_BASE_URL .
'/colors');
327 $this->currentMethod = __METHOD__;
329 $url = self::API_BASE_URL .
'/calendars/' . urlencode($calendarId) .
'/events';
331 return $this->doRequest(
Web\HttpClient::HTTP_GET,
$url);
352 return array_filter($this->errors,
function(
$error) use (
$code)
366 if (!is_array($this->errors))
371 $errorsByCode = array_filter($this->errors,
function(
$error) use (
$code)
376 if (!empty($errorsByCode))
378 return end($errorsByCode);
394 $this->currentMethod = __METHOD__;
396 $requestParameters = [
'originalStart' => $originalStart];
397 $requestParameters = array_filter($requestParameters);
398 $url = self::API_BASE_URL .
'/calendars/' . urlencode($calendarId) .
'/events/' . urlencode($eventId) .
'/instances/';
399 $url .= empty($requestParameters) ?
'' :
'?' . preg_replace(
'/(%3D)/',
'=', http_build_query($requestParameters));
401 return $this->doRequest(
Web\HttpClient::HTTP_GET,
$url);
412 $this->currentMethod = __METHOD__;
414 return $this->doRequest(
Web\HttpClient::HTTP_POST, self::API_BASE_URL .
'/calendars/', $requestBody);
419 $url =
"https://www.googleapis.com/batch/calendar/v3/";
421 return $this->doRequest(
Web\HttpClient::HTTP_POST,
$url, $requestBody);
434 $boundary =
'BXC'.md5(rand().time());
435 $this->client->setHeader(
'Content-type',
'multipart/mixed; boundary='.$boundary);
441 $data .=
'--'.$boundary.
"\r\n";
443 if (is_array($value))
447 if (is_array($value))
449 $data .=
'Content-Type: application/http'.
"\r\n";
450 $data .=
'Content-ID: '.$contentId.
"\r\n\r\n";
452 if (!empty($value[
'gEventId']))
454 $data .=
$params[
'method'].
' /calendar/v3/calendars/'.$calendarId.
'/events/'.$value[
'gEventId'].
"\r\n";
458 $data .=
'POST /calendar/v3/calendars/'.$calendarId.
'/events'.
"\r\n";
461 $data .=
'Content-type: application/json'.
"\r\n";
463 $data .=
'Content-Length: '.mb_strlen($value[
'partBody']).
"\r\n\r\n";
464 $data .= $value[
'partBody'];
470 $data .=
'--'.$boundary.
"--\r\n";
486 $boundary = $this->client->getHeaders()->getBoundary();
489 $parts = explode(
"--$boundary\r\n",
$response);
491 foreach ($parts as
$key => $part)
496 $partEvent = explode(
"\r\n\r\n", $part);
497 $data = $this->getMetaInfo($partEvent[1]);
499 if (
$data[
'status'] === 200)
501 $id = $this->getId($partEvent[0]);
525 private function getMetaInfo($headers):
array
528 foreach (explode(
"\n", $headers) as
$k => $header)
532 if(preg_match(
'#HTTP\S+ (\d+)#', $header, $find))
534 $data[
'status'] = (int)$find[1];
537 elseif(mb_strpos($header,
':') !==
false)
539 [$headerName, $headerValue] = explode(
':', $header, 2);
540 if(mb_strtolower($headerName) ===
'etag')
542 $data[
'etag'] = trim($headerValue);
554 private function getId ($headers): ?int
557 foreach (explode(
"\n", $headers) as
$k => $header)
559 if(mb_strpos($header,
':') !==
false)
561 [$headerName, $headerValue] = explode(
':', $header, 2);
562 if(mb_strtolower($headerName) ===
'content-id')
564 $part = explode(
':', $headerValue);
565 $id = rtrim($part[1],
">");
580 $this->currentMethod = __METHOD__;
581 return $this->doRequest(
Web\HttpClient::HTTP_DELETE, self::API_BASE_URL .
'/calendars/' . $calendarId .
'');
587 private function getDomain(): string
589 if (CCalendar::isBitrix24())
591 return 'https://bitrix24.com';
594 if (defined(
'BX24_HOST_NAME') && BX24_HOST_NAME)
596 return "https://" . (string)BX24_HOST_NAME;
599 $server = Application::getInstance()->getContext()->getServer();
601 return "https://" . (string)$server[
'HTTP_HOST'];
612 $this->currentMethod = __METHOD__;
615 return $this->doRequest(
Web\HttpClient::HTTP_PUT, self::API_BASE_URL .
'/calendars/' . $calendarId, $requestBody);
626 $this->currentMethod = __METHOD__;
628 $url = self::API_BASE_URL .
'/users/me/calendarList/' . $calendarId;
629 $url .=
'?' . preg_replace(
'/(%3D)/',
'=', http_build_query([
'colorRgbFormat' =>
"True"]));
633 return $this->doRequest(
Web\HttpClient::HTTP_PUT,
$url, $requestBody);
640 private function prepareResponseForDebug(
$response): string
651 if (is_string($value))
653 $result .=
"{$key}:{$value}; ";
658 foreach ($value as $valueKey => $valueValue)
660 $result .=
"{$valueKey}:{$valueValue}, ";
672 private function prepareErrorForDebug(): string
674 if (!$this->errors || !is_array($this->errors))
680 foreach ($this->errors as
$error)
if(empty( $fields)) foreach($fields as $field) $channelId
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
const GOOGLE_SERVER_PATH_V3
openCalendarListChannel($channelInfo)
sendBatchEvents($body, $calendarId, $params)
getEvents($calendarId, $requestParams=array())
stopChannel($channelId, $resourceId)
deleteCalendar(string $calendarId)
updateCalendar(string $calendarId, $calendarData)
prepareMultipartMixed($postData, $calendarId, $params)
deleteEvent($eventId, $calendarId)
importEvent($eventData, $calendarId)
updateCalendarList(string $calendarId, $calendarData)
getCalendarList(array $requestParameters=null)
insertCalendar($calendarData)
updateEvent($eventData, $calendarId, $eventId)
insertEvent($eventData, $calendarId)
patchEvent($patchData, $calendarId, $eventId)
getInstanceRecurringEvent($calendarId, $eventId, $originalStart)
multipartDecode($response)
openEventsWatchChannel($calendarId, $channelInfo)
static encode($data, $options=null)
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
if(empty($signedUserToken)) $key
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']