diff --git a/crm_extensions/file_storage/api/open_file.php b/crm_extensions/file_storage/api/open_file.php
index ee0bfd39..cd071e1d 100644
--- a/crm_extensions/file_storage/api/open_file.php
+++ b/crm_extensions/file_storage/api/open_file.php
@@ -9,7 +9,15 @@ $recordId = isset($_GET['recordId']) ? $_GET['recordId'] : '';
// Если fileName содержит полный URL, извлекаем только имя файла
if (strpos($fileName, 'http') === 0) {
- $fileName = basename($fileName);
+ $fileName = urldecode($fileName);
+ // ИСПРАВЛЕНИЕ: используем правильное извлечение имени файла
+ $lastSlash = strrpos($fileName, '/');
+ if ($lastSlash !== false) {
+ $fileName = substr($fileName, $lastSlash + 1);
+ } else {
+ $fileName = basename($fileName);
+ }
+ $fileName = trim($fileName);
error_log("Nextcloud Editor: Извлечено имя файла из URL: {$fileName}");
}
@@ -18,59 +26,128 @@ $nextcloudUrl = 'https://office.clientright.ru:8443';
$username = 'admin';
$password = 'office';
-// Путь к файлу в Nextcloud (относительно папки пользователя admin)
-// Предполагаем, что файлы хранятся по пути: crm/crm2/CRM_Active_Files/Documents/{recordId}/{fileName}
-$nextcloudFilePath = "crm/crm2/CRM_Active_Files/Documents/{$recordId}/" . urlencode($fileName);
+// Подключаемся к БД чтобы получить название проекта
+chdir('/var/www/fastuser/data/www/crm.clientright.ru');
+require_once 'include/utils/utils.php';
+require_once 'include/database/PearDatabase.php';
+
+global $adb;
+
+// Функция для санитизации названия папки
+function sanitizeFolderName($name) {
+ $name = str_replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|', '#'], '_', $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;
-// Попытка получить fileId через WebDAV PROPFIND
-error_log("Nextcloud Editor: Попытка получить fileId для файла: {$nextcloudFilePath} через WebDAV");
-
-// XML запрос для получения fileid
-$xmlRequest = '
-
-
-
-
-';
-
-$ch = curl_init();
-curl_setopt($ch, CURLOPT_URL, $nextcloudUrl . '/remote.php/dav/files/' . $username . '/' . $nextcloudFilePath);
-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 при запросе WebDAV: " . $curlError);
-} else {
- error_log("Nextcloud Editor: WebDAV Response (HTTP {$httpCode}): " . substr($response, 0, 500));
+// Пробуем найти файл по всем возможным путям
+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];
- error_log("Nextcloud Editor: fileId получен через WebDAV regex: " . $fileId);
- } else {
- error_log("Nextcloud Editor: fileid не найден в XML ответе");
+ $usedPath = $tryPath;
+ error_log("Nextcloud Editor: ✅ fileId получен: {$fileId} (путь: {$usedPath})");
+ break; // Нашли файл, выходим из цикла
}
- } else {
- error_log("Nextcloud Editor: WebDAV запрос неуспешен. HTTP Code: {$httpCode}");
}
+
+ // error_log("Nextcloud Editor: Файл не найден по пути: {$logPath} (HTTP {$httpCode})");
}
if (!$fileId) {
- die('❌ Ошибка: Не удалось получить fileId для файла ' . $fileName);
+ // Простая ошибка без отладки
+ $errorMsg = "❌ Ошибка: Не удалось получить fileId для файла {$fileName}";
+ error_log("Nextcloud Editor ERROR: " . $errorMsg);
+ die($errorMsg);
}
// Формируем URL для Nextcloud
diff --git a/crm_extensions/file_storage/migrate_project_files.php b/crm_extensions/file_storage/migrate_project_files.php
new file mode 100644
index 00000000..af28cac3
--- /dev/null
+++ b/crm_extensions/file_storage/migrate_project_files.php
@@ -0,0 +1,356 @@
+ 'latest',
+ 'region' => 'ru-1',
+ 'endpoint' => 'https://s3.twcstorage.ru',
+ 'use_path_style_endpoint' => true,
+ 'credentials' => [
+ 'key' => '2OMAK5ZNM900TAXM16J7',
+ 'secret' => 'f4ADllb5VZBAt2HdsyB8WcwVEU7U74MwFCa1DARG',
+ ],
+]);
+
+$bucket = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
+
+// Лог файл
+$logFile = __DIR__ . '/logs/migration_' . date('Y-m-d_H-i-s') . '.log';
+if (!is_dir(__DIR__ . '/logs')) {
+ mkdir(__DIR__ . '/logs', 0755, true);
+}
+
+function writeLog($message, $toScreen = true) {
+ global $logFile;
+ $timestamp = date('Y-m-d H:i:s');
+ $logMessage = "[$timestamp] $message\n";
+ file_put_contents($logFile, $logMessage, FILE_APPEND);
+ if ($toScreen) {
+ echo $message . "\n";
+ }
+}
+
+function sanitizeFileName($name) {
+ // Декодируем HTML entities (например, " → ")
+ $name = html_entity_decode($name, ENT_QUOTES, 'UTF-8');
+ // Убираем проблемные символы (включая кавычки и пробелы)
+ $name = str_replace(["/", "\\", ":", "*", "?", "\"", "<", ">", "|"], '_', $name);
+ // Заменяем все пробелы на подчёркивания
+ $name = preg_replace('/\s+/', '_', $name);
+ return trim($name);
+}
+
+function extractExtension($fileName) {
+ $parts = explode('.', $fileName);
+ return count($parts) > 1 ? array_pop($parts) : '';
+}
+
+function migrateProject($projectId, $dryRun = false) {
+ global $adb, $s3, $bucket;
+
+ writeLog("🔍 === МИГРАЦИЯ ПРОЕКТА $projectId ===");
+
+ if ($dryRun) {
+ writeLog("⚠️ РЕЖИМ DRY-RUN - изменения НЕ будут применены");
+ }
+
+ // Получаем все документы проекта
+ $sql = "SELECT n.* FROM vtiger_notes n
+ INNER JOIN vtiger_senotesrel r ON r.notesid = n.notesid
+ WHERE r.crmid = ? AND n.filelocationtype = 'E'
+ ORDER BY n.notesid";
+ $result = $adb->pquery($sql, [$projectId]);
+
+ $count = $adb->num_rows($result);
+ writeLog("📋 Найдено документов: $count");
+
+ if ($count === 0) {
+ writeLog("⚠️ Нет документов для миграции");
+ return;
+ }
+
+ // Получаем имя проекта для папки
+ $projectQuery = $adb->pquery("SELECT projectname FROM vtiger_project WHERE projectid = ?", [$projectId]);
+ if ($adb->num_rows($projectQuery) > 0) {
+ $projectName = $adb->query_result($projectQuery, 0, 'projectname');
+ $sanitizedProjectName = sanitizeFileName($projectName);
+ $newFolderPath = "crm2/CRM_Active_Files/Documents/{$sanitizedProjectName}_{$projectId}";
+ } else {
+ $newFolderPath = "crm2/CRM_Active_Files/Documents/project_{$projectId}";
+ }
+ writeLog("📁 Новая папка: $newFolderPath");
+
+ $stats = [
+ 'total' => $count,
+ 'success' => 0,
+ 'skipped' => 0,
+ 'errors' => 0,
+ ];
+
+ $usedNames = []; // Для отслеживания дубликатов
+
+ for ($i = 0; $i < $count; $i++) {
+ $doc = $adb->fetchByAssoc($result);
+ $docId = $doc['notesid'];
+ $title = sanitizeFileName($doc['title']);
+ $oldFileName = $doc['filename'];
+
+ writeLog("\n📄 Документ $docId: {$doc['title']}");
+ writeLog(" Старый путь: $oldFileName");
+
+ // Извлекаем расширение из старого имени файла
+ $extension = extractExtension(basename($oldFileName));
+
+ // Формируем новое имя файла
+ $baseNewName = $title ? "{$title}_{$docId}" : "document_{$docId}";
+ $newFileName = $baseNewName . ($extension ? ".$extension" : '');
+
+ // Проверяем дубликаты
+ $counter = 1;
+ $finalNewName = $newFileName;
+ while (isset($usedNames[$finalNewName])) {
+ $finalNewName = $baseNewName . "_{$counter}" . ($extension ? ".$extension" : '');
+ $counter++;
+ }
+ $usedNames[$finalNewName] = true;
+
+ $newS3Path = "$newFolderPath/$finalNewName";
+
+ writeLog(" Новый путь: $newS3Path");
+
+ // Извлекаем старый S3 путь
+ $oldS3Path = null;
+ if (strpos($oldFileName, 'https://s3.twcstorage.ru/') === 0) {
+ // Полный URL - декодируем
+ $oldS3Path = str_replace("https://s3.twcstorage.ru/$bucket/", '', $oldFileName);
+ $oldS3Path = urldecode($oldS3Path); // Декодируем URL-кодированные символы
+ } elseif (strpos($oldFileName, 'crm2/') === 0) {
+ // Уже путь
+ $oldS3Path = urldecode($oldFileName); // Декодируем на всякий случай
+ }
+
+ if (!$oldS3Path) {
+ writeLog(" ❌ Не удалось определить старый путь S3");
+ $stats['errors']++;
+ continue;
+ }
+
+ writeLog(" Старый S3: $oldS3Path");
+
+ if ($dryRun) {
+ writeLog(" [DRY-RUN] Будет скопировано: $oldS3Path → $newS3Path");
+ $stats['success']++;
+ continue;
+ }
+
+ // РЕАЛЬНАЯ МИГРАЦИЯ
+ try {
+ // Проверяем что старый файл существует
+ try {
+ $headObject = $s3->headObject([
+ 'Bucket' => $bucket,
+ 'Key' => $oldS3Path,
+ ]);
+ $oldSize = $headObject['ContentLength'];
+ writeLog(" ✓ Старый файл существует, размер: " . number_format($oldSize / 1024, 2) . " KB");
+ } catch (Exception $e) {
+ writeLog(" ❌ Старый файл не найден в S3: " . $e->getMessage());
+ $stats['errors']++;
+ continue;
+ }
+
+ // Копируем файл в новое место
+ writeLog(" 📋 Копирую файл...");
+ $s3->copyObject([
+ 'Bucket' => $bucket,
+ 'CopySource' => "$bucket/$oldS3Path",
+ 'Key' => $newS3Path,
+ ]);
+
+ // Проверяем что копия успешна
+ $headNewObject = $s3->headObject([
+ 'Bucket' => $bucket,
+ 'Key' => $newS3Path,
+ ]);
+ $newSize = $headNewObject['ContentLength'];
+
+ if ($newSize !== $oldSize) {
+ throw new Exception("Размер не совпадает! Старый: $oldSize, Новый: $newSize");
+ }
+
+ writeLog(" ✅ Файл успешно скопирован, размер совпадает: " . number_format($newSize / 1024, 2) . " KB");
+
+ // Обновляем путь в базе данных
+ $newUrl = "https://s3.twcstorage.ru/$bucket/$newS3Path";
+
+ $updateSql = "UPDATE vtiger_notes SET filename = ? WHERE notesid = ?";
+ $adb->pquery($updateSql, [$newUrl, $docId]);
+
+ writeLog(" ✅ База данных обновлена");
+ writeLog(" ✅ УСПЕХ! Документ $docId мигрирован");
+
+ $stats['success']++;
+
+ } catch (Exception $e) {
+ writeLog(" ❌ ОШИБКА при миграции: " . $e->getMessage());
+ $stats['errors']++;
+
+ // Пытаемся удалить частично скопированный файл
+ try {
+ $s3->deleteObject([
+ 'Bucket' => $bucket,
+ 'Key' => $newS3Path,
+ ]);
+ writeLog(" 🗑️ Частичная копия удалена");
+ } catch (Exception $cleanupError) {
+ writeLog(" ⚠️ Не удалось удалить частичную копию");
+ }
+ }
+ }
+
+ // Итоговая статистика
+ writeLog("\n📊 === СТАТИСТИКА МИГРАЦИИ ===");
+ writeLog("Всего документов: {$stats['total']}");
+ writeLog("Успешно: {$stats['success']}");
+ writeLog("Ошибок: {$stats['errors']}");
+ writeLog("Пропущено: {$stats['skipped']}");
+
+ return $stats;
+}
+
+// === ГЛАВНАЯ ЛОГИКА ===
+
+// Если запрошена статистика - показываем и выходим
+if ($showStats) {
+ echo "📊 === СТАТИСТИКА ДОКУМЕНТОВ В CRM ===\n";
+ echo "═══════════════════════════════════════\n";
+
+ // Общая статистика
+ $totalDocs = $adb->query("SELECT COUNT(*) as cnt FROM vtiger_notes WHERE filestatus = 1");
+ $totalDocsCount = $adb->query_result($totalDocs, 0, 'cnt');
+
+ $totalProjects = $adb->query("SELECT COUNT(DISTINCT projectid) as cnt FROM vtiger_senotesrel WHERE projectid IS NOT NULL AND projectid != ''");
+ $totalProjectsCount = $adb->query_result($totalProjects, 0, 'cnt');
+
+ $docsWithProjects = $adb->query("
+ SELECT COUNT(DISTINCT n.notesid) as cnt
+ FROM vtiger_notes n
+ INNER JOIN vtiger_senotesrel sr ON n.notesid = sr.notesid
+ WHERE n.filestatus = 1 AND sr.projectid IS NOT NULL AND sr.projectid != ''
+ ");
+ $docsWithProjectsCount = $adb->query_result($docsWithProjects, 0, 'cnt');
+
+ $docsWithoutProjects = $totalDocsCount - $docsWithProjectsCount;
+
+ echo "📄 Всего активных документов: $totalDocsCount\n";
+ echo "📁 Всего проектов с документами: $totalProjectsCount\n";
+ echo "✅ Документов привязанных к проектам: $docsWithProjectsCount\n";
+ echo "⚠️ Документов БЕЗ проекта: $docsWithoutProjects\n\n";
+
+ // Топ-10 проектов
+ echo "🏆 ТОП-10 ПРОЕКТОВ ПО КОЛИЧЕСТВУ ДОКУМЕНТОВ:\n";
+ echo "═══════════════════════════════════════════════\n";
+
+ $topProjects = $adb->query("
+ SELECT
+ p.projectid,
+ p.projectname,
+ COUNT(n.notesid) as doc_count
+ FROM vtiger_project p
+ INNER JOIN vtiger_senotesrel sr ON p.projectid = sr.projectid
+ INNER JOIN vtiger_notes n ON sr.notesid = n.notesid
+ WHERE n.filestatus = 1
+ GROUP BY p.projectid, p.projectname
+ ORDER BY doc_count DESC
+ LIMIT 10
+ ");
+
+ while ($row = $adb->fetch_array($topProjects)) {
+ $projectId = str_pad($row['projectid'], 6, ' ', STR_PAD_LEFT);
+ $projectName = mb_substr($row['projectname'], 0, 50);
+ $docCount = str_pad($row['doc_count'], 3, ' ', STR_PAD_LEFT);
+ echo " $projectId | $projectName | $docCount файлов\n";
+ }
+
+ exit(0);
+}
+
+writeLog("🚀 === СТАРТ МИГРАЦИИ ФАЙЛОВ ===");
+writeLog("Время: " . date('Y-m-d H:i:s'));
+writeLog("Лог файл: $logFile");
+
+if ($dryRun) {
+ writeLog("\n⚠️⚠️⚠️ РЕЖИМ DRY-RUN - НИЧЕГО НЕ БУДЕТ ИЗМЕНЕНО ⚠️⚠️⚠️\n");
+}
+
+// Создаём бэкап базы данных ПЕРЕД миграцией
+if (!$dryRun) {
+ writeLog("\n💾 === СОЗДАНИЕ РЕЗЕРВНОЙ КОПИИ БД ===");
+ $backupFile = "backup_before_migration_" . date('Y-m-d_H-i-s') . ".sql";
+ $backupCmd = "mysqldump -u ci20465_72new -p'EcY979Rn' ci20465_72new vtiger_notes vtiger_senotesrel vtiger_crmentity > $backupFile";
+ exec($backupCmd, $output, $returnCode);
+
+ if ($returnCode === 0) {
+ writeLog("✅ Резервная копия создана: $backupFile");
+ } else {
+ writeLog("❌ ОШИБКА создания резервной копии!");
+ writeLog("🛑 МИГРАЦИЯ ОТМЕНЕНА ДЛЯ БЕЗОПАСНОСТИ!");
+ exit(1);
+ }
+}
+
+// Выполняем миграцию
+if ($projectId) {
+ // Один проект
+ writeLog("\n🎯 Миграция проекта: $projectId");
+ migrateProject($projectId, $dryRun);
+} elseif ($batchSize > 0) {
+ // Пакет проектов
+ writeLog("\n📦 Миграция пакета проектов: $batchSize");
+ // TODO: реализовать позже
+} elseif ($migrateAll) {
+ // Все проекты
+ writeLog("\n🌍 Миграция ВСЕХ проектов");
+ // TODO: реализовать позже
+} else {
+ writeLog("\n❌ Не указан режим миграции!");
+ writeLog("Использование:");
+ writeLog(" --dry-run --project=ID Тестовый прогон одного проекта");
+ writeLog(" --project=ID Миграция одного проекта");
+ writeLog(" --batch=100 Миграция пакета проектов");
+ writeLog(" --all Миграция всех проектов");
+ exit(1);
+}
+
+writeLog("\n✅ === МИГРАЦИЯ ЗАВЕРШЕНА ===");
+writeLog("Лог файл: $logFile");
diff --git a/crm_extensions/nextcloud_editor/js/nextcloud-editor.js b/crm_extensions/nextcloud_editor/js/nextcloud-editor.js
index fc9dcaeb..41542efa 100644
--- a/crm_extensions/nextcloud_editor/js/nextcloud-editor.js
+++ b/crm_extensions/nextcloud_editor/js/nextcloud-editor.js
@@ -3,32 +3,46 @@
* JavaScript для интеграции редактора документов Nextcloud
*/
+/**
+ * Открытие папки проекта в Nextcloud
+ */
+function openProjectFolder(projectId, projectName) {
+ // Нормализуем имя проекта (убираем множественные пробелы, как в sanitizeFileName)
+ if (projectName) {
+ projectName = projectName.replace(/\s+/g, ' ').trim();
+ }
+
+ // Формируем URL для папки проекта в Nextcloud
+ const folderName = projectName ? `${projectName}_${projectId}` : `project_${projectId}`;
+ const encodedFolderName = encodeURIComponent(folderName);
+ const nextcloudUrl = 'https://office.clientright.ru:8443';
+
+ // URL для папки проекта в Nextcloud External Storage
+ const folderUrl = `${nextcloudUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${encodedFolderName}`;
+
+ // Открываем папку в новом окне
+ window.open(folderUrl, 'nextcloud_folder', 'width=1200,height=800,scrollbars=yes,resizable=yes');
+}
+
+/**
+ * Обёртка для вызова из шаблонов (с подстановкой Smarty переменных)
+ */
+function openProjectFolderInNextcloud() {
+ // Эта функция будет вызываться из шаблона, где переменные уже подставлены
+ // См. DetailViewHeaderTitle.tpl - там прямой вызов с параметрами
+ console.warn('⚠️ openProjectFolderInNextcloud() called without parameters - use openProjectFolder(projectId, projectName) instead');
+}
+
/**
* Открытие редактора Nextcloud для документа
*/
function openNextcloudEditor(recordId, fileName) {
- console.log('🚀 NEXTCLOUD EDITOR: Function called!', recordId, fileName);
-
- // Сначала тестируем debug API
- console.log('🔍 Testing debug API...');
- $.ajax({
- url: '/crm_extensions/file_storage/api/debug_api.php',
- method: 'GET',
- dataType: 'json',
- success: function(debugResponse) {
- console.log('✅ Debug API works:', debugResponse);
-
- // Теперь тестируем простой API
- testSimpleAPI(recordId, fileName);
- },
- error: function(xhr, status, error) {
- console.error('❌ Debug API failed:', error);
- console.error('Response:', xhr.responseText);
-
- // Все равно пытаемся вызвать основной API
- callMainAPI(recordId, fileName);
- }
- });
+ // ПРОСТОЕ РЕШЕНИЕ - используем промежуточную страницу для редиректа!
+ const cacheVersion = Date.now(); // Принудительное обновление кеша
+ const redirectUrl = `/crm_extensions/file_storage/api/open_file.php?recordId=${recordId}&fileName=${encodeURIComponent(fileName)}&v=${cacheVersion}`;
+
+ // Открываем редактор в новом окне через промежуточную страницу
+ window.open(redirectUrl, 'nextcloud_editor', 'width=1200,height=800,scrollbars=yes,resizable=yes');
}
function testSimpleAPI(recordId, fileName) {
@@ -228,7 +242,7 @@ function openEditor(editUrl, fileData) {
message: 'Редактор открыт! Файл: ' + fileData.file_name
});
} else {
- alert('✅ Редактор открыт! Файл: ' + fileData.file_name);
+ // alert('✅ Редактор открыт! Файл: ' + fileData.file_name);
}
// Показываем информацию о файле
@@ -477,4 +491,4 @@ $(document).ready(function() {
`)
.appendTo('head');
}
-});
\ No newline at end of file
+});// Version: 1761125337
diff --git a/layouts/v7/lib/nextcloud-editor.js b/layouts/v7/lib/nextcloud-editor.js
index 1a612b14..957c8a7c 100644
--- a/layouts/v7/lib/nextcloud-editor.js
+++ b/layouts/v7/lib/nextcloud-editor.js
@@ -3,6 +3,34 @@
* JavaScript для интеграции редактора документов Nextcloud
*/
+/**
+ * Открытие папки проекта в Nextcloud
+ */
+function openProjectFolder(projectId, projectName) {
+ console.log('📁 Opening project folder in Nextcloud:', projectId, projectName);
+
+ // Нормализуем имя проекта (убираем пробелы и кавычки)
+ if (projectName) {
+ // Убираем кавычки (заменяем на подчёркивание)
+ projectName = projectName.replace(/"/g, '_');
+ // Заменяем ВСЕ пробелы на подчёркивания
+ projectName = projectName.replace(/\s+/g, '_');
+ }
+
+ // Формируем URL для папки проекта в Nextcloud
+ const folderName = projectName ? `${projectName}_${projectId}` : `project_${projectId}`;
+ const encodedFolderName = encodeURIComponent(folderName);
+ const nextcloudUrl = 'https://office.clientright.ru:8443';
+
+ // URL для папки проекта в Nextcloud External Storage
+ const folderUrl = `${nextcloudUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${encodedFolderName}`;
+
+ console.log('🔗 Folder URL:', folderUrl);
+
+ // Открываем папку в новом окне
+ window.open(folderUrl, 'nextcloud_folder', 'width=1200,height=800,scrollbars=yes,resizable=yes');
+}
+
/**
* Открытие редактора Nextcloud для документа
*/
@@ -19,7 +47,7 @@ function openNextcloudEditor(recordId, fileName) {
if (win) {
console.log('✅ Editor opened successfully');
- alert('✅ Редактор открыт! Файл: ' + fileName);
+ // alert('✅ Редактор открыт! Файл: ' + fileName);
} else {
console.log('❌ Failed to open editor window - popup blocked');
alert('❌ Не удалось открыть редактор. Проверьте блокировку всплывающих окон.');
@@ -190,7 +218,7 @@ function openEditor(editUrl, fileData) {
message: 'Редактор открыт! Файл: ' + fileData.file_name
});
} else {
- alert('✅ Редактор открыт! Файл: ' + fileData.file_name);
+ // alert('✅ Редактор открыт! Файл: ' + fileData.file_name);
}
// Показываем информацию о файле
diff --git a/layouts/v7/modules/Project/DetailViewHeaderTitle.tpl b/layouts/v7/modules/Project/DetailViewHeaderTitle.tpl
index 36e94c58..ee424445 100644
--- a/layouts/v7/modules/Project/DetailViewHeaderTitle.tpl
+++ b/layouts/v7/modules/Project/DetailViewHeaderTitle.tpl
@@ -1,56 +1,66 @@
-{**}
-{strip}
-
-
-
+{**}
+{strip}
+
+
+
{/strip}
\ No newline at end of file