26 private const API_BASE_URL = Google\Helper::GOOGLE_SERVER_PATH_V3;
30 private $currentMethod =
'';
51 if (!Loader::includeModule(
'socialservices'))
53 throw new SystemException(
"Can not include module \"SocialServices\"! " . __METHOD__);
56 $this->client =
new Web\HttpClient();
57 if (RequestLogger::isEnabled())
59 $this->requestLogger =
new RequestLogger((
int)$userId, self::SERVICE_NAME);
62 if (CSocServGoogleProxyOAuth::isProxyAuth())
64 $oAuth =
new CSocServGoogleProxyOAuth($userId);
68 $oAuth =
new CSocServGoogleOAuth($userId);
71 $oAuth->getEntityOAuth()->addScope(
73 'https://www.googleapis.com/auth/calendar',
74 'https://www.googleapis.com/auth/calendar.readonly',
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(
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(
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(
145 self::API_BASE_URL .
'/channels/stop',
146 Web\Json::encode([
'id' => $channelId,
'resourceId' => $resourceId], JSON_UNESCAPED_SLASHES)
158 private function doRequest($type, $url, $requestParams =
'')
160 $this->errors = $response = [];
167 $this->client->query($type, $url, ($requestParams ?: null));
170 if ($this->client->getStatus() === 200)
172 $contentType = $this->client->getHeaders()->getContentType();
174 if ($contentType ===
'multipart/mixed')
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([
209 'requestParams' => $requestParams,
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__;
245 $requestBody = Web\Json::encode($patchData, JSON_UNESCAPED_SLASHES);
246 return $this->doRequest(Web\
HttpClient::HTTP_PUT, self::API_BASE_URL .
'/calendars/' . $calendarId .
'/events/' . $eventId, $requestBody);
259 $this->currentMethod = __METHOD__;
260 $requestBody = Web\Json::encode($eventData, JSON_UNESCAPED_SLASHES);
261 return $this->doRequest(Web\
HttpClient::HTTP_PUT, self::API_BASE_URL .
'/calendars/' . $calendarId .
'/events/' . $eventId, $requestBody);
273 $this->currentMethod = __METHOD__;
274 $requestBody = Web\Json::encode($eventData, JSON_UNESCAPED_SLASHES);
275 return $this->doRequest(Web\
HttpClient::HTTP_POST, self::API_BASE_URL .
'/calendars/' . $calendarId .
'/events/', $requestBody);
287 $this->currentMethod = __METHOD__;
288 $requestBody = Web\Json::encode($eventData, JSON_UNESCAPED_SLASHES);
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));
314 $this->currentMethod = __METHOD__;
325 public function getEvents($calendarId, $requestParams = array())
327 $this->currentMethod = __METHOD__;
328 $requestParams = array_filter($requestParams);
329 $url = self::API_BASE_URL .
'/calendars/' . urlencode($calendarId) .
'/events';
330 $url .= empty($requestParams) ?
'' :
'?' . preg_replace(
'/(%3D)/',
'=', http_build_query($requestParams));
341 return $this->errors;
352 return array_filter($this->errors,
function($error) use ($code)
354 return $error[
'code'] == $code;
366 if (!is_array($this->errors))
371 $errorsByCode = array_filter($this->errors,
function($error) use ($code)
373 return $error[
'code'] == $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));
412 $this->currentMethod = __METHOD__;
413 $requestBody = Web\Json::encode($calendarData, JSON_UNESCAPED_SLASHES);
414 return $this->doRequest(Web\
HttpClient::HTTP_POST, self::API_BASE_URL .
'/calendars/', $requestBody);
419 $url =
"https://www.googleapis.com/batch/calendar/v3/";
432 if (is_array($postData))
434 $boundary =
'BXC'.md5(rand().time());
435 $this->client->setHeader(
'Content-type',
'multipart/mixed; boundary='.$boundary);
439 foreach ($postData as $key => $value)
441 $data .=
'--'.$boundary.
"\r\n";
443 if (is_array($value))
445 $contentId =
'<item'.$key.
':'.$key.
'>';
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();
488 $response = str_replace(
"--$boundary--",
"--$boundary", $response);
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]);
509 $event = Web\Json::decode($partEvent[2]);
511 catch(Exception $exception)
516 $event[
'etag'] = $data[
'etag'];
517 $events[$id] = $event;
521 AddMessage2Log(
'Event sync error. ID: ' . $this->getId($partEvent[0]));
529 private function getMetaInfo($headers): array
532 foreach (explode(
"\n", $headers) as $k => $header)
536 if(preg_match(
'#HTTP\S+ (\d+)#', $header, $find))
538 $data[
'status'] = (int)$find[1];
541 elseif(mb_strpos($header,
':') !==
false)
543 [$headerName, $headerValue] = explode(
':', $header, 2);
544 if(mb_strtolower($headerName) ===
'etag')
546 $data[
'etag'] = trim($headerValue);
558 private function getId ($headers): ?int
561 foreach (explode(
"\n", $headers) as $k => $header)
563 if(mb_strpos($header,
':') !==
false)
565 [$headerName, $headerValue] = explode(
':', $header, 2);
566 if(mb_strtolower($headerName) ===
'content-id')
568 $part = explode(
':', $headerValue);
569 $id = rtrim($part[1],
">");
584 $this->currentMethod = __METHOD__;
591 private function getDomain(): string
593 if (CCalendar::isBitrix24())
595 return 'https://bitrix24.com';
598 if (defined(
'BX24_HOST_NAME') && BX24_HOST_NAME)
600 return "https://" . (string)BX24_HOST_NAME;
605 return "https://" . (string)$server[
'HTTP_HOST'];
616 $this->currentMethod = __METHOD__;
617 $requestBody = Web\Json::encode($calendarData, JSON_UNESCAPED_SLASHES);
619 return $this->doRequest(Web\
HttpClient::HTTP_PUT, self::API_BASE_URL .
'/calendars/' . $calendarId, $requestBody);
630 $this->currentMethod = __METHOD__;
632 $url = self::API_BASE_URL .
'/users/me/calendarList/' . $calendarId;
633 $url .=
'?' . preg_replace(
'/(%3D)/',
'=', http_build_query([
'colorRgbFormat' =>
"True"]));
635 $requestBody = Web\Json::encode($calendarData, JSON_UNESCAPED_SLASHES);
644 private function prepareResponseForDebug($response): string
646 if (!$response || !is_array($response))
653 foreach ($response as $key => $value)
655 if (is_string($value))
657 $result .=
"{$key}:{$value}; ";
659 elseif (is_array($value))
661 $result .=
"{$key}:";
662 foreach ($value as $valueKey => $valueValue)
664 $result .=
"{$valueKey}:{$valueValue}, ";
676 private function prepareErrorForDebug(): string
678 if (!$this->errors || !is_array($this->errors))
684 foreach ($this->errors as $error)
686 $result .= $error[
'code'] .
" " . $error[
'message'] .
"; ";