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} -
-
- -
-
-

-
- {foreach item=NAME_FIELD from=$MODULE_MODEL->getNameFields()} - {assign var=FIELD_MODEL value=$MODULE_MODEL->getField($NAME_FIELD)} - {if $FIELD_MODEL->getPermissions()} - {$RECORD->get($NAME_FIELD)}  - {/if} - {/foreach} -
-

-
- {include file="DetailViewHeaderFieldsView.tpl"|vtemplate_path:$MODULE} - - {* - {assign var=RELATED_TO value=$RECORD->get('linktoaccountscontacts')} - {assign var=CONTACT value=$RECORD->get('contactid')} -
- {if !empty($RELATED_TO)} -
- - {$RECORD->getDisplayValue('linktoaccountscontacts')} - -
- {elseif !empty($CONTACT)} -
-
- - {$RECORD->getDisplayValue('contactid')} -
-
- {/if} -
- *} -
-
-
+{**} +{strip} +
+
+ +
+
+

+
+ {foreach item=NAME_FIELD from=$MODULE_MODEL->getNameFields()} + {assign var=FIELD_MODEL value=$MODULE_MODEL->getField($NAME_FIELD)} + {if $FIELD_MODEL->getPermissions()} + {$RECORD->get($NAME_FIELD)}  + {/if} + {/foreach} +
+

+
+ {include file="DetailViewHeaderFieldsView.tpl"|vtemplate_path:$MODULE} + + {* Кнопка открытия папки проекта в Nextcloud *} +
+ +
+ + {* Подключаем Nextcloud Editor JS *} + + + {* + {assign var=RELATED_TO value=$RECORD->get('linktoaccountscontacts')} + {assign var=CONTACT value=$RECORD->get('contactid')} +
+ {if !empty($RELATED_TO)} +
+ + {$RECORD->getDisplayValue('linktoaccountscontacts')} + +
+ {elseif !empty($CONTACT)} +
+
+ + {$RECORD->getDisplayValue('contactid')} +
+
+ {/if} +
+ *} +
+
+
{/strip} \ No newline at end of file