368 lines
13 KiB
PHP
368 lines
13 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* Nextcloud WebDAV Client (Fixed)
|
|||
|
|
* Клиент для работы с Nextcloud через WebDAV API
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
class NextcloudClient {
|
|||
|
|
private $baseUrl;
|
|||
|
|
private $username;
|
|||
|
|
private $password;
|
|||
|
|
private $activeFolder;
|
|||
|
|
|
|||
|
|
public function __construct($config) {
|
|||
|
|
$this->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 => '<?xml version="1.0" encoding="UTF-8"?>
|
|||
|
|
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
|
|||
|
|
<d:prop>
|
|||
|
|
<oc:fileid/>
|
|||
|
|
</d:prop>
|
|||
|
|
</d:propfind>'
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
$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=/crm2/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';
|
|||
|
|
}
|
|||
|
|
}
|