baseUrl = rtrim($config['base_url'], '/'); $this->username = $config['username']; $this->password = $config['password']; $this->activeFolder = trim($config['active_folder'], '/'); } /** * Загрузка файла в Nextcloud */ public function uploadFile($localPath, $remotePath) { try { // Убираем activeFolder из remotePath если он уже там есть if (strpos($remotePath, $this->activeFolder) === 0) { $fullRemotePath = $remotePath; } else { $fullRemotePath = $this->activeFolder . '/' . ltrim($remotePath, '/'); } $url = $this->baseUrl . '/remote.php/dav/files/' . $this->username . '/' . $fullRemotePath; echo "Uploading to URL: $url\n"; // Создаём папку если не существует $this->ensureFolderExists(dirname($fullRemotePath)); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_INFILE => fopen($localPath, 'r'), CURLOPT_INFILESIZE => filesize($localPath), CURLOPT_USERPWD => $this->username . ':' . $this->password, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 120, CURLOPT_HTTPHEADER => [ 'Content-Type: ' . $this->getMimeType($localPath) ] ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($error) { throw new Exception('Curl error: ' . $error); } if ($httpCode < 200 || $httpCode >= 300) { throw new Exception('HTTP error: ' . $httpCode . ' - ' . $response); } return [ 'success' => true, 'remote_path' => $fullRemotePath, 'url' => $url ]; } catch (Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Создание папки (публичный метод) */ public function createFolder($folderPath) { try { $this->ensureFolderExists($folderPath); return [ 'success' => true, 'folder_path' => $folderPath ]; } catch (Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Создание папки (улучшенная версия) */ private function ensureFolderExists($folderPath) { if (empty($folderPath) || $folderPath === '.') { return; } // Разбиваем путь на части $parts = explode('/', trim($folderPath, '/')); $currentPath = ''; foreach ($parts as $part) { if (empty($part)) continue; $currentPath .= '/' . $part; $url = $this->baseUrl . '/remote.php/dav/files/' . $this->username . $currentPath; echo "Creating folder: $url\n"; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_CUSTOMREQUEST => 'MKCOL', CURLOPT_USERPWD => $this->username . ':' . $this->password, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30 ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); echo "Folder creation result: HTTP $httpCode\n"; // 201 = created, 405 = already exists, оба OK if ($httpCode !== 201 && $httpCode !== 405) { echo "Response: $response\n"; if ($error) { throw new Exception("Curl error creating folder '$currentPath': $error"); } else { throw new Exception("HTTP error creating folder '$currentPath': $httpCode - $response"); } } } } /** * Получение ID файла из Nextcloud */ public function getFileId($remotePath) { try { $fullRemotePath = $this->activeFolder . '/' . ltrim($remotePath, '/'); $url = $this->baseUrl . '/remote.php/dav/files/' . $this->username . '/' . $fullRemotePath; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_CUSTOMREQUEST => 'PROPFIND', CURLOPT_USERPWD => $this->username . ':' . $this->password, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => [ 'Content-Type: application/xml', 'Depth: 0' ], CURLOPT_POSTFIELDS => ' ' ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 207 && $response) { $xml = simplexml_load_string($response); if ($xml) { foreach ($xml->xpath('//oc:fileid') as $element) { return (string)$element; } } } return null; } catch (Exception $e) { error_log("Error getting file ID: " . $e->getMessage()); return null; } } /** * Создание прямой ссылки для редактирования файла */ public function createDirectEditLink($remotePath, $recordId, $fileName = null) { // Создаем ссылку на файловый менеджер Nextcloud с открытием файла if ($fileName === null) { $fileName = basename($remotePath); } // Убираем подчеркивание в начале, если есть if (substr($fileName, 0, 1) === '_') { $fileName = substr($fileName, 1); } $editUrl = $this->baseUrl . '/apps/files/?dir=/crm/CRM_Active_Files/Documents/' . $recordId . '&openfile=' . urlencode($fileName); return [ 'success' => true, 'edit_url' => $editUrl, 'direct_url' => $editUrl, 'file_id' => null ]; } /** * Создание ссылки для редактирования файла */ public function createEditLink($remotePath, $permissions = 3) { try { $fullRemotePath = $this->activeFolder . '/' . ltrim($remotePath, '/'); // Создаём публичную ссылку через OCS API $url = $this->baseUrl . '/ocs/v2.php/apps/files_sharing/api/v1/shares'; $postData = http_build_query([ 'path' => '/' . $fullRemotePath, 'shareType' => 3, // Public link 'permissions' => $permissions, // 1=read, 2=update, 3=read+update 'format' => 'json' ]); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData, CURLOPT_USERPWD => $this->username . ':' . $this->password, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => [ 'OCS-APIRequest: true', 'Content-Type: application/x-www-form-urlencoded' ] ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { throw new Exception('Failed to create share link: HTTP ' . $httpCode . ' - ' . $response); } $data = json_decode($response, true); if (!$data || $data['ocs']['meta']['statuscode'] !== 200) { $message = $data['ocs']['meta']['message'] ?? 'Unknown OCS error'; throw new Exception('OCS error: ' . $message); } $shareUrl = $data['ocs']['data']['url']; // Для редактирования добавляем параметр $editUrl = $shareUrl . '/edit'; return [ 'success' => true, 'share_url' => $shareUrl, 'edit_url' => $editUrl, 'share_id' => $data['ocs']['data']['id'] ]; } catch (Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Проверка существования файла */ public function fileExists($remotePath) { $fullRemotePath = $this->activeFolder . '/' . ltrim($remotePath, '/'); $url = $this->baseUrl . '/remote.php/dav/files/' . $this->username . '/' . $fullRemotePath; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_CUSTOMREQUEST => 'HEAD', CURLOPT_USERPWD => $this->username . ':' . $this->password, CURLOPT_NOBODY => true, CURLOPT_TIMEOUT => 10, CURLOPT_RETURNTRANSFER => true ]); curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $httpCode === 200; } /** * Скачивание файла из Nextcloud */ public function downloadToTemp($remotePath) { $fullRemotePath = $this->activeFolder . '/' . ltrim($remotePath, '/'); $url = $this->baseUrl . '/remote.php/dav/files/' . $this->username . '/' . $fullRemotePath; $tempFile = sys_get_temp_dir() . '/' . uniqid('nextcloud_download_') . '_' . basename($remotePath); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_USERPWD => $this->username . ':' . $this->password, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 120 ]); $result = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { throw new Exception('Download failed: HTTP ' . $httpCode); } file_put_contents($tempFile, $result); return $tempFile; } /** * Удаление файла */ public function deleteFile($remotePath) { $fullRemotePath = $this->activeFolder . '/' . ltrim($remotePath, '/'); $url = $this->baseUrl . '/remote.php/dav/files/' . $this->username . '/' . $fullRemotePath; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_USERPWD => $this->username . ':' . $this->password, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30 ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $httpCode === 204; // 204 = No Content (успешное удаление) } /** * Получение MIME типа файла */ private function getMimeType($filePath) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $filePath); finfo_close($finfo); return $mimeType ?: 'application/octet-stream'; } }