', '|', '#'], '_', $name); $name = preg_replace('/\s+/', '_', $name); return trim($name); } // Найдём projectid по связи документа → проекта $docId = $recordId; $projectId = null; try { $sqlProject = "SELECT r.crmid AS projectid FROM vtiger_senotesrel r INNER JOIN vtiger_crmentity e ON e.crmid = r.crmid WHERE r.notesid = ? AND e.setype = 'Project' ORDER BY r.crmid DESC LIMIT 1"; $resProject = $adb->pquery($sqlProject, [$docId]); if ($resProject && $adb->num_rows($resProject) > 0) { $projectRow = $adb->fetchByAssoc($resProject); $projectId = (string)$projectRow['projectid']; } } catch (Exception $e) { error_log('Nextcloud Editor: DB error while resolving project by document: ' . $e->getMessage()); } // Получаем название проекта из БД (если нашли projectId) $projectName = null; if ($projectId) { $sql = "SELECT projectname FROM vtiger_project WHERE projectid = ?"; $result = $adb->pquery($sql, [$projectId]); if ($result && $adb->num_rows($result) > 0) { $row = $adb->fetchByAssoc($result); $projectName = sanitizeFolderName($row['projectname']); } } // Формируем пути к файлу в Nextcloud // НОВЫЙ формат: crm/crm2/CRM_Active_Files/Documents/{ProjectName}_{ProjectID}/{fileName} // СТАРЫЙ формат: crm/crm2/CRM_Active_Files/Documents/{DocumentID}/{fileName} // Вспомогательная функция: кодирование пути по сегментам (WebDAV) $encodePath = function(array $segments) { return implode('/', array_map('rawurlencode', $segments)); }; // Список путей для логирования (читаемые) и для запроса (url-encoded) $humanPaths = []; $requestPaths = []; if ($projectName && $projectId) { $humanPaths[] = "crm/crm2/CRM_Active_Files/Documents/{$projectName}_{$projectId}/{$fileName}"; $requestPaths[] = $encodePath(['crm','crm2','CRM_Active_Files','Documents',"{$projectName}_{$projectId}",$fileName]); } // Резерв - старый формат (папка по documentId) $humanPaths[] = "crm/crm2/CRM_Active_Files/Documents/{$docId}/{$fileName}"; $requestPaths[] = $encodePath(['crm','crm2','CRM_Active_Files','Documents',(string)$docId,$fileName]); $fileId = null; $usedPath = null; // Пробуем найти файл по всем возможным путям for ($i = 0; $i < count($requestPaths); $i++) { $tryPath = $requestPaths[$i]; $logPath = $humanPaths[$i]; $propfindUrl = $nextcloudUrl . '/remote.php/dav/files/' . $username . '/' . $tryPath; // error_log("Nextcloud Editor: PROPFIND -> {$propfindUrl} (читаемый путь: {$logPath})"); // XML запрос для получения fileid $xmlRequest = ' '; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $propfindUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERPWD, $username . ':' . $password); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PROPFIND'); curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlRequest); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Depth: 0', 'Content-Type: application/xml' ]); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($response === false) { error_log("Nextcloud Editor: Ошибка cURL: " . $curlError); continue; // Пробуем следующий путь } if ($httpCode === 207 && $response) { // 207 = Multi-Status для PROPFIND // Простой regex для извлечения fileid if (preg_match('/(\d+)<\/oc:fileid>/', $response, $matches)) { $fileId = $matches[1]; $usedPath = $tryPath; error_log("Nextcloud Editor: ✅ fileId получен: {$fileId} (путь: {$usedPath})"); break; // Нашли файл, выходим из цикла } } // error_log("Nextcloud Editor: Файл не найден по пути: {$logPath} (HTTP {$httpCode})"); } if (!$fileId) { // Простая ошибка без отладки $errorMsg = "❌ Ошибка: Не удалось получить fileId для файла {$fileName}"; error_log("Nextcloud Editor ERROR: " . $errorMsg); die($errorMsg); } // Формируем URL для Nextcloud // РАБОЧИЙ ФОРМАТ - редирект на файл с автооткрытием редактора! $redirectUrl = $nextcloudUrl . '/apps/files/files/' . $fileId . '?dir=/&editing=true&openfile=true'; // Логирование error_log("Nextcloud Editor: Redirect to $redirectUrl for file $fileName (ID: $fileId)"); // Делаем редирект header('Location: ' . $redirectUrl); exit; ?>