uawdijnntqw1x1x1
IP : 3.147.104.221
Hostname : axolotl
Kernel : Linux axolotl 4.9.0-13-amd64 #1 SMP Debian 4.9.228-1 (2020-07-05) x86_64
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,
OS : Linux
PATH:
/
var
/
www
/
axolotl
/
data
/
www
/
ekb.axolotls.ru
/
bitrix
/
modules
/
disk
/
lib
/
document
/
googlehandler.php
/
/
<?php namespace Bitrix\Disk\Document; use Bitrix\Disk\Document\Contract\FileCreatable; use Bitrix\Disk\Document\Upload\GoogleResumableUpload; use Bitrix\Disk\Internals\Error\Error; use Bitrix\Disk\ShowSession; use Bitrix\Disk\SpecificFolder; use Bitrix\Disk\TypeFile; use Bitrix\Main\IO; use Bitrix\Main\Loader; use Bitrix\Main\Localization\Loc; use Bitrix\Main\Web\HttpClient; use Bitrix\Main\Web\Json; Loc::loadMessages(__FILE__); class GoogleHandler extends DocumentHandler implements IViewer, FileCreatable { const API_URL_V2 = 'https://www.googleapis.com/drive/v2'; const API_URL_V3 = 'https://www.googleapis.com/drive/v3'; const API_URL_UPLOAD_V3 = 'https://www.googleapis.com/upload/drive/v3'; const PERMISSION_ROLE_OWNER = 'owner'; const PERMISSION_ROLE_WRITER = 'writer'; const PERMISSION_ROLE_COMMENTER = 'commenter'; const PERMISSION_ROLE_READER = 'reader'; const PERMISSION_TYPE_USER = 'user'; const PERMISSION_TYPE_GROUP = 'group'; const PERMISSION_TYPE_DOMAIN = 'domain'; const PERMISSION_TYPE_ANYONE = 'anyone'; const SPECIFIC_FOLDER_CODE = SpecificFolder::CODE_FOR_IMPORT_GDRIVE; const ERROR_NOT_INSTALLED_SOCSERV = 'DISK_GOOGLE_HANDLER_22002'; const ERROR_UNSUPPORTED_FILE_FORMAT = 'DISK_GOOGLE_HANDLER_22003'; const ERROR_HTTP_CREATE_BLANK = 'DISK_GOOGLE_HANDLER_22004'; const ERROR_BAD_JSON = 'DISK_GOOGLE_HANDLER_22005'; const ERROR_HTTP_DELETE_FILE = 'DISK_GOOGLE_HANDLER_22006'; const ERROR_HTTP_DOWNLOAD_FILE = 'DISK_GOOGLE_HANDLER_22007'; const ERROR_HTTP_GET_METADATA = 'DISK_GOOGLE_HANDLER_22008'; const ERROR_HTTP_GET_LOCATION_FOR_UPLOAD = 'DISK_GOOGLE_HANDLER_22009'; const ERROR_HTTP_INSERT_PERMISSION = 'DISK_GOOGLE_HANDLER_22010'; const ERROR_HTTP_RESUMABLE_UPLOAD = 'DISK_GOOGLE_HANDLER_22012'; const ERROR_COULD_NOT_VIEW_FILE = 'DISK_GOOGLE_HANDLER_22013'; const ERROR_COULD_NOT_FIND_ID = 'DISK_GOOGLE_HANDLER_22014'; const ERROR_HTTP_LIST_FOLDER = 'DISK_GOOGLE_HANDLER_22015'; const ERROR_EMBED_FILE_LINK = 'DISK_GOOGLE_HANDLER_22016'; const ERROR_HTTP_PATCH = 'DISK_GOOGLE_HANDLER_22017'; /** * @inheritdoc */ public static function getCode() { return 'gdrive'; } /** * @inheritdoc */ public static function getName() { return Loc::getMessage('DISK_GOOGLE_HANDLER_NAME'); } /** * Public name storage of documents. May show in user interface. * @throws \Bitrix\Main\NotImplementedException * @return string */ public static function getStorageName() { return Loc::getMessage('DISK_GOOGLE_HANDLER_NAME_STORAGE'); } /** * Execute this method for check potential possibility get access token. * @return bool * @throws \Bitrix\Main\LoaderException */ public function checkAccessibleTokenService() { if(!Loader::includeModule('socialservices')) { $this->errorCollection[] = new Error( Loc::getMessage('DISK_GOOGLE_HANDLER_ERROR_NOT_INSTALLED_SOCSERV'), self::ERROR_NOT_INSTALLED_SOCSERV ); return false; } $authManager = new \CSocServAuthManager(); $socNetServices = $authManager->getActiveAuthServices(array()); return !empty($socNetServices[\CSocServGoogleOAuth::ID]); } /** * Return link for authorize user in external service. * @param string $mode * @return string * @throws \Bitrix\Main\LoaderException */ public function getUrlForAuthorizeInTokenService($mode = 'modal') { if(!Loader::includeModule('socialservices')) { $this->errorCollection[] = new Error( Loc::getMessage('DISK_GOOGLE_HANDLER_ERROR_NOT_INSTALLED_SOCSERV'), self::ERROR_NOT_INSTALLED_SOCSERV ); return false; } $socGoogleOAuth = new \CSocServGoogleOAuth($this->userId); if($mode === 'opener') { return $socGoogleOAuth->getUrl( 'opener', $this->getScopes(), array('BACKURL' => '#external-auth-ok') ); } return $socGoogleOAuth->getUrl('modal', $this->getScopes()); } private function getScopes() { return array( 'https://www.googleapis.com/auth/drive' ); } /** * Request and store access token (self::accessToken) for self::userId * @return $this */ public function queryAccessToken() { if(!Loader::includeModule('socialservices')) { $this->errorCollection[] = new Error( Loc::getMessage('DISK_GOOGLE_HANDLER_ERROR_NOT_INSTALLED_SOCSERV'), self::ERROR_NOT_INSTALLED_SOCSERV ); return false; } $socGoogleOAuth = new \CSocServGoogleOAuth($this->userId); //this bug. SocServ fill entityOAuth in method getUrl..... $socGoogleOAuth->getUrl('modal', $this->getScopes()); $this->accessToken = $socGoogleOAuth->getStorageToken(); return $this; } /** * Create new blank file in cloud service. * It is not necessary set shared rights on file. * @param FileData $fileData * @return FileData|null */ public function createBlankFile(FileData $fileData) { if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'name', ))) { return null; } $accessToken = $this->getAccessToken(); $googleMimeType = static::getInternalMimeTypeByExtension(getFileExtension($fileData->getName())); $fileName = getFileNameWithoutExtension($fileData->getName()); $fileName = $this->convertToUtf8($fileName); if(!$googleMimeType) { $this->errorCollection[] = new Error( "Unsupported file format with name {$fileData->getName()}", self::ERROR_UNSUPPORTED_FILE_FORMAT ); return null; } $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Content-Type', 'application/json; charset=UTF-8'); $http->setHeader('Authorization', "Bearer {$accessToken}"); $postFields = "{\"name\":\"{$fileName}\",\"mimeType\":\"{$googleMimeType}\"}"; if($http->post(self::API_URL_V3 . '/files?fields=id,webViewLink', $postFields) === false) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_CREATE_BLANK ); return null; } if(!$this->checkHttpResponse($http)) { return null; } $fileMetadata = Json::decode($http->getResult()); if($fileMetadata === null) { $this->errorCollection[] = new Error( 'Could not decode response as json', self::ERROR_BAD_JSON ); return null; } if(empty($fileMetadata['id']) || empty($fileMetadata['webViewLink'])) { $this->errorCollection[] = new Error( 'Could not find id or webViewLink in response from Google.', self::ERROR_COULD_NOT_FIND_ID ); return null; } $fileData->setLinkInService($fileMetadata['webViewLink']); $fileData->setId($fileMetadata['id']); //last signed user must delete file from google drive $this->insertPermission($fileData); return $fileData; } /** * Create file in cloud service by upload from us server. * Necessary set shared rights on file for common work. * * @param FileData $fileData * @return FileData|null */ public function createFile(FileData $fileData) { $newFile = $this->createByResumableUpload($fileData, $lastStatus, $metadata); if(!$newFile) { //retry upload, but not convert content if($lastStatus == '500') { $fileData->setNeedConvert(false); $newFile = $this->createByResumableUpload($fileData, $lastStatus, $metadata); } } if(!$newFile) { return null; } //last signed user must delete file from google drive $this->insertPermission($newFile); return $newFile; } /** * @param FileData $fileData * @param string $lastStatus * @param array $fileMetadata * @return FileData|null */ protected function createByResumableUpload(FileData $fileData, &$lastStatus, &$fileMetadata) { $resumableUpload = new GoogleResumableUpload($this, $fileData); if (!$resumableUpload->upload()) { $this->errorCollection->add($resumableUpload->getErrors()); return null; } $lastStatus = $resumableUpload->getLastStatus(); $fileMetadata = $resumableUpload->getLastResponse(); return $resumableUpload->getFileData(); } private function insertPermission(FileData $fileData, $role = self::PERMISSION_ROLE_WRITER, $type = self::PERMISSION_TYPE_ANYONE) { if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'id', ))) { return null; } $accessToken = $this->getAccessToken(); $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Content-Type', 'application/json; charset=UTF-8'); $http->setHeader('Authorization', "Bearer {$accessToken}"); $postFields = Json::encode(array( 'role' => $role, 'type' => $type, )); if($http->post(self::API_URL_V3 . "/files/{$fileData->getId()}/permissions", $postFields) === false) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_INSERT_PERMISSION ); return false; } return $this->checkHttpResponse($http); } /** * Download file from cloud service by FileData::id, put contents in FileData::src * @param FileData $fileData * @return FileData|null */ public function downloadFile(FileData $fileData) { if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'id', 'mimeType', 'src', ))) { return null; } $accessToken = $this->getAccessToken(); $fileMetaData = $this->getFileMetadataInternal($fileData); if($fileMetaData === null) { $this->errorCollection[] = new Error( 'Could not decode response as json', self::ERROR_BAD_JSON ); return null; } $link = $this->getDownloadUrl($fileData, $fileMetaData); if(!$link) { $this->errorCollection[] = new Error( 'Could not get link for download', self::ERROR_BAD_JSON ); return null; } @set_time_limit(0); $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Authorization', "Bearer {$accessToken}"); if($http->download($link, $fileData->getSrc()) === false) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_DOWNLOAD_FILE ); return null; } if (!$this->checkHttpResponse($http)) { return null; } //$file['title'] = BaseComponent::convertFromUtf8($file['title']); $this->recoverExtensionInName($fileMetaData['name'], $fileData->getMimeType()); $fileData->setName($fileMetaData['name']); return $fileData; } /** * Repacks exported document from Google.Drive, which has wrong order files in archive to show preview * in Google.Viewer. In Google.Viewer document should have file '[Content_Types].xml' on first position in archive. * @param FileData $fileData * @return FileData * @throws IO\FileNotFoundException * @throws IO\InvalidPathException * @internal */ public function repackDocument(FileData $fileData) { if(!extension_loaded('zip')) { return null; } if(!TypeFile::isDocument($fileData->getName())) { return null; } $file = new IO\File($fileData->getSrc()); if(!$file->isExists() || $file->getSize() > 15*1024*1024) { return null; } unset($file); $ds = DIRECTORY_SEPARATOR; $targetDir = \CTempFile::getDirectoryName(2, 'disk_repack' . $ds . md5(uniqid('di', true))); checkDirPath($targetDir); $targetDir = IO\Path::normalize($targetDir) . $ds; $zipOrigin = new \ZipArchive(); if($zipOrigin->open($fileData->getSrc()) !== true) { return null; } if($zipOrigin->getNameIndex(0) === '[Content_Types].xml') { $zipOrigin->close(); return null; } if(!$zipOrigin->extractTo($targetDir)) { $zipOrigin->close(); return null; } $zipOrigin->close(); unset($zipOrigin); if(is_dir($targetDir) !== true) { return null; } $newName = md5(uniqid('di', true)); $newFilepath = $targetDir . '..' . $ds . $newName; $repackedZip = new \ZipArchive; if(!$repackedZip->open($newFilepath, \ZipArchive::CREATE)) { return null; } $source = realpath($targetDir); $repackedZip->addFile($source . $ds . '[Content_Types].xml', '[Content_Types].xml'); $files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($source, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST ); foreach($files as $file) { if($file->getBasename() === '[Content_Types].xml') { continue; } $file = str_replace('\\', '/', $file); $file = realpath($file); if(is_dir($file) === true) { $repackedZip->addEmptyDir(str_replace('\\', '/', str_replace($source . $ds, '', $file . $ds))); } elseif(is_file($file) === true) { $repackedZip->addFile($file, str_replace('\\', '/', str_replace($source . $ds, '', $file))); } } $repackedZip->close(); $newFileData = new FileData(); $newFileData->setSrc($newFilepath); return $newFileData; } protected function getDownloadUrl(FileData $fileData, $fileMetaData = array()) { if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'id', ))) { return null; } if(!$fileMetaData) { $fileMetaData = $this->getFileMetadataInternal($fileData); } if(!$fileMetaData) { return null; } if(!$this->isGoogleDocument($fileMetaData['mimeType'])) { return self::API_URL_V3 . "/files/{$fileData->getId()}?alt=media"; } $exportMimeByInternalMimeType = $this->getExportMimeByInternalMimeType($fileMetaData['mimeType']); $extensionByMimeType = TypeFile::getExtensionByMimeType($exportMimeByInternalMimeType); if (in_array($extensionByMimeType, array('docx'))) { return "https://docs.google.com/document/d/{$fileData->getId()}/export?format=docx"; } if (in_array($extensionByMimeType, array('pptx'))) { return "https://docs.google.com/presentation/d/{$fileData->getId()}/export/pptx"; } if (in_array($extensionByMimeType, array('xlsx'))) { return "https://docs.google.com/spreadsheets/d/{$fileData->getId()}/export?format=xlsx"; } return self::API_URL_V3 . "/files/{$fileData->getId()}/export?" . http_build_query(array('mimeType' => $this->getExportMimeByInternalMimeType($fileMetaData['mimeType']))); } /** * Download part of file from cloud service by FileData::id, put contents in FileData::src * @param FileData $fileData * @param $startRange * @param $chunkSize * @return FileData|null */ public function downloadPartFile(FileData $fileData, $startRange, $chunkSize) { if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'id', 'mimeType', 'src', ))) { return null; } $accessToken = $this->getAccessToken(); @set_time_limit(0); $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Authorization', "Bearer {$accessToken}"); $endRange = $startRange + $chunkSize - 1; $http->setHeader('Range', "bytes={$startRange}-{$endRange}"); if($http->download($this->getDownloadUrl($fileData), $fileData->getSrc()) === false) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_DOWNLOAD_FILE ); return null; } return $fileData; } /** * Delete file from cloud service by FileData::id * @param FileData $fileData * @return bool */ public function deleteFile(FileData $fileData) { $this->errorCollection->clear(); if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'id', ))) { return null; } $accessToken = $this->getAccessToken(); $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Authorization', "Bearer {$accessToken}"); if($http->query('DELETE', self::API_URL_V3 . '/files/' . $fileData->getId()) === false) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_DELETE_FILE ); return false; } return $this->checkHttpResponse($http); } /** * Shares file to edit for anyone by id. * * @param FileData $fileData * @internal * @return bool */ public function shareFileToEdit(FileData $fileData) { if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'id', ))) { return false; } return $this->insertPermission($fileData, self::PERMISSION_ROLE_WRITER, self::PERMISSION_TYPE_ANYONE); } /** * Get data for showing preview file. * Array must be contains keys: id, viewUrl, neededDelete, neededCheckView * @param FileData $fileData * @return array|null */ public function getDataForViewFile(FileData $fileData) { $newFile = $this->createByResumableUpload($fileData, $lastStatus, $metadata); if(!$newFile) { //retry upload, but not convert content if($lastStatus == '500') { $fileData->setNeedConvert(false); $newFile = $this->createByResumableUpload($fileData, $lastStatus, $metadata); } } if($newFile === null) { $this->errorCollection[] = new Error( Loc::getMessage('DISK_GOOGLE_HANDLER_ERROR_COULD_NOT_VIEW_FILE'), self::ERROR_COULD_NOT_VIEW_FILE ); return null; } $this->insertPermission($newFile, self::PERMISSION_ROLE_READER, self::PERMISSION_TYPE_ANYONE); $embedFileLink = $this->getEmbedFileLink($fileData)?: $metadata['webViewLink']; if(!empty($metadata['createdTime'])) { $this->patchFile($fileData, array('modifiedTime' => $metadata['createdTime'])); } $updatedMetadata = $this->getFileMetadata($fileData); $fileData->setMetaData($updatedMetadata); ShowSession::register($this, $fileData, $this->errorCollection); return array( 'id' => $newFile->getId(), 'viewUrl' => $embedFileLink, 'neededDelete' => true, 'neededCheckView' => false, ); } /** * Tells if file in cloud service was changed. For example, the method compares created date and modified date. * * @param array $currentMetadata Metadata (@see \Bitrix\Disk\Document::getFileMetadata()); * @param array $oldMetadata Old metadata. * @return bool */ public function wasChangedAfterCreation(array $currentMetadata, array $oldMetadata = array()) { //google changes etag (version or etag in api v2) every time and it doesn't matter content or permission. if (isset($currentMetadata['original']['createdTime'], $currentMetadata['original']['modifiedTime'])) { $createdTime = new \DateTime($currentMetadata['original']['createdTime']); $modifiedTime = new \DateTime($currentMetadata['original']['modifiedTime']); return $modifiedTime->getTimestamp() - $createdTime->getTimestamp() > 3; } return false; } /** * Lists folder contents * @param $path * @param $folderId * @return mixed */ public function listFolder($path, $folderId) { if($path === '/') { $folderId = 'root'; } $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Content-Type', 'application/json; charset=UTF-8'); $http->setHeader('Authorization', "Bearer {$this->getAccessToken()}"); if( $http->get( self::API_URL_V3 . "/files?q='{$folderId}'+in+parents+and+trashed=false&" . http_build_query(array( 'fields' => 'files/id,files/mimeType,files/webViewLink,files/size,files/name,files/version,files/modifiedTime' )) ) === false ) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_LIST_FOLDER ); return null; } if(!$this->checkHttpResponse($http)) { return null; } $items = Json::decode($http->getResult()); if($items === null) { $this->errorCollection[] = new Error( 'Could not decode response as json', self::ERROR_BAD_JSON ); return null; } if(!isset($items['files'])) { $this->errorCollection[] = new Error( 'Could not find items in response', self::ERROR_HTTP_LIST_FOLDER ); return null; } $reformatItems = array(); foreach($items['files'] as $item) { $isFolder = $item['mimeType'] === 'application/vnd.google-apps.folder'; $dateTime = new \DateTime($item['modifiedTime']); $reformatItems[$item['id']] = array( 'id' => $item['id'], 'name' => $item['name'], 'type' => $isFolder? 'folder' : 'file', 'size' => $isFolder? '' : \CFile::formatSize($item['size']), 'sizeInt' => $isFolder? '' : $item['size'], 'modifyBy' => '', 'modifyDate' => $dateTime->format('d.m.Y'), 'modifyDateInt' => $dateTime->getTimestamp(), 'provider' => static::getCode(), ); if(!$isFolder && empty($item['size'])) { //Google.Drive doesn't show size of google documents. We should export docs $reformatItems[$item['id']]['size'] = $reformatItems[$item['id']]['sizeInt'] = ''; } if(!$isFolder) { $reformatItems[$item['id']]['ext'] = getFileExtension($item['name']); } } unset($item); return $reformatItems; } public function checkHttpResponse(HttpClient $http) { $status = (int)$http->getStatus(); if($status === 401) { $this->errorCollection[] = new Error( 'Invalid credentials (401)', self::ERROR_CODE_INVALID_CREDENTIALS ); } elseif($status === 403) { $headers = $http->getHeaders(); $response = $http->getResult(); $errorMessage = 'Unknown error'; if($response && is_string($response)) { $jsonResponse = Json::decode($response); if(isset($jsonResponse['error']['message'])) { $errorMessage = $jsonResponse['error']['message']; } unset($jsonResponse, $response); } $headerAuthenticate = $headers->get('WWW-Authenticate'); if(is_string($headerAuthenticate) && strpos($headerAuthenticate, 'insufficient') !== false) { $this->errorCollection[] = new Error( 'Insufficient scope (403)', self::ERROR_CODE_INSUFFICIENT_SCOPE ); return false; } elseif(strpos($errorMessage, 'The authenticated user has not installed the app with client') !== false) { $this->errorCollection[] = new Error( 'The authenticated user has not installed the app (403)', self::ERROR_CODE_NOT_INSTALLED_APP ); } elseif(strpos($errorMessage, 'The authenticated user has not granted the app') !== false) { $this->errorCollection[] = new Error( 'The authenticated user has not granted the app (403)', self::ERROR_CODE_NOT_GRANTED_APP ); } elseif(strpos($errorMessage, 'Invalid accessLevel') !== false) { $this->errorCollection[] = new Error( 'Invalid accessLevel (403)', self::ERROR_CODE_INVALID_ACCESS_LEVEL ); } elseif(strpos($errorMessage, 'is not properly configured as a Google Drive app') !== false) { $this->errorCollection[] = new Error( 'The app does not exist or is not properly configured as a Google Drive app (403)', self::ERROR_CODE_APP_NOT_CONFIGURED ); } elseif(strpos($errorMessage, 'is blacklisted') !== false) { $this->errorCollection[] = new Error( 'The app is blacklisted as a Google Drive app. (403)', self::ERROR_CODE_APP_IN_BLACKLIST ); } elseif($errorMessage) { $this->errorCollection[] = new Error( $errorMessage, self::ERROR_CODE_UNKNOWN ); } } if($this->errorCollection->hasErrors()) { return false; } return parent::checkHttpResponse($http); } /** * Gets a file's metadata by ID * @param FileData $fileData * @return array|null */ private function getFileMetadataInternal(FileData $fileData) { if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'id', ))) { return null; } $accessToken = $this->getAccessToken(); $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Content-Type', 'application/json; charset=UTF-8'); $http->setHeader('Authorization', "Bearer {$accessToken}"); if( $http->get( self::API_URL_V3 . '/files/' . $fileData->getId() . '?' . http_build_query(array('fields' => 'id,mimeType,webViewLink,size,name,version,createdTime,modifiedTime')) ) === false) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_GET_METADATA ); return null; } if(!$this->checkHttpResponse($http)) { return null; } $file = Json::decode($http->getResult()); if($file === null) { $this->errorCollection[] = new Error( 'Could not decode response as json', self::ERROR_BAD_JSON ); return null; } return $file; } private function patchFile(FileData $fileData, array $fields) { if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'id', ))) { return null; } if(!$this->checkRequiredInputParams($fields, array( 'modifiedTime', ))) { return null; } $accessToken = $this->getAccessToken(); $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Content-Type', 'application/json; charset=UTF-8'); $http->setHeader('Authorization', "Bearer {$accessToken}"); $patchData = Json::encode(array( 'modifiedTime' => $fields['modifiedTime'], )); if( $http->query( 'PATCH', self::API_URL_V3 . '/files/' . $fileData->getId() . '?' . http_build_query(array('fields' => 'id,mimeType,webViewLink,size,name,version,createdTime,modifiedTime')), $patchData ) === false) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_PATCH ); return null; } if(!$this->checkHttpResponse($http)) { return null; } $file = Json::decode($http->getResult()); if($file === null) { $this->errorCollection[] = new Error( 'Could not decode response as json', self::ERROR_BAD_JSON ); return null; } return $file; } /** * Returns link which we can embed in iframe to view file. * Notice: the method uses api v2 to get embedLink field, because in v3 that field is deleted. * * @param FileData $fileData File data. * @return string|null */ private function getEmbedFileLink(FileData $fileData) { if(!$this->checkRequiredInputParams($fileData->toArray(), array( 'id', ))) { return null; } $accessToken = $this->getAccessToken(); $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Content-Type', 'application/json; charset=UTF-8'); $http->setHeader('Authorization', "Bearer {$accessToken}"); if( $http->get( self::API_URL_V2 . '/files/' . $fileData->getId() . '?' . http_build_query(array('fields' => 'embedLink')) ) === false) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_GET_METADATA ); return null; } if(!$this->checkHttpResponse($http)) { return null; } $file = Json::decode($http->getResult()); if($file === null) { $this->errorCollection[] = new Error( 'Could not decode response as json', self::ERROR_BAD_JSON ); return null; } if(empty($file['embedLink'])) { if($fileData->getMimeType() === 'application/pdf') { return "https://drive.google.com/file/d/{$fileData->getId()}/preview"; } $this->errorCollection[] = new Error( 'Could not find {embedLink} in response', self::ERROR_EMBED_FILE_LINK ); return null; } return $file['embedLink']; } private function getFileSizeInternal($downloadUrl) { $accessToken = $this->getAccessToken(); $http = new HttpClient(array( 'socketTimeout' => 10, 'streamTimeout' => 30, 'version' => HttpClient::HTTP_1_1, )); $http->setHeader('Authorization', "Bearer {$accessToken}"); if($http->query('HEAD', $downloadUrl) === false) { $errorString = implode('; ', array_keys($http->getError())); $this->errorCollection[] = new Error( $errorString, self::ERROR_HTTP_GET_METADATA ); return null; } if(!$this->checkHttpResponse($http)) { return null; } return $http->getHeaders()->get('Content-Length'); } /** * Gets a file's metadata by ID. * * @param FileData $fileData * @return array|null Describes file (id, title, size, mimeType) */ public function getFileMetadata(FileData $fileData) { $metaData = $this->getFileMetadataInternal($fileData); if(!$metaData) { return null; } if(empty($metaData['size'])) { $link = $this->getDownloadUrl($fileData, $metaData); if(!$link) { return null; } $metaData['size'] = $this->getFileSizeInternal($link); if(!$metaData['size']) { //todo Google is quite bad guy. He does not send Content-Length header when we try to export GoogleDocs. $metaData['size'] = 0; } } return $this->normalizeMetadata($metaData); } public static function getInternalMimeTypeByExtension($ext) { $ext = trim($ext, '.'); $googleMimeTypes = array( 'docx' => 'application/vnd.google-apps.document', 'xlsx' => 'application/vnd.google-apps.spreadsheet', 'pptx' => 'application/vnd.google-apps.presentation', 'doc' => 'application/vnd.google-apps.document', 'xls' => 'application/vnd.google-apps.spreadsheet', 'ppt' => 'application/vnd.google-apps.presentation', ); return isset($googleMimeTypes[$ext])? $googleMimeTypes[$ext] : null; } private function getExportMimeByInternalMimeType($internalMimeType) { if(!$this->isGoogleDocument($internalMimeType)) { return $internalMimeType; } $googleMimeTypes = array( 'application/vnd.google-apps.document' => 'docx', 'application/vnd.google-apps.spreadsheet' => 'xlsx', 'application/vnd.google-apps.presentation' => 'pptx', ); $filename = 'f'; if(isset($googleMimeTypes[$internalMimeType])) { $filename = $filename . '.' . $googleMimeTypes[$internalMimeType]; } return TypeFile::getMimeTypeByFilename($filename); } private function isGoogleDocument($mimeType) { return strpos($mimeType, 'application/vnd.google-apps.') !== false; } /** * Returns normalized metadata. * * @param array $metaData * @return array */ public function normalizeMetadata($metaData) { return array( 'id' => $metaData['id'], 'name' => $metaData['name'], 'size' => $metaData['size'], 'mimeType' => $metaData['mimeType'], 'etag' => $metaData['version'], 'original' => $metaData, ); } }
/var/www/axolotl/data/www/ekb.axolotls.ru/bitrix/modules/disk/lib/document/googlehandler.php