/** * Nextcloud Editor Integration JavaScript * JavaScript для интеграции редактора документов Nextcloud */ /** * Открытие папки проекта в Nextcloud */ function openProjectFolder(projectId, projectName) { // Нормализуем имя проекта как в FilePathManager::sanitizeFileName if (projectName) { // Убираем HTML entities projectName = projectName.replace(/"/g, '"').replace(/'/g, "'"); // Заменяем проблемные символы на подчеркивания (как в FilePathManager::sanitizeFileName) projectName = projectName.replace(/[/\\:*?"<>|№]/g, '_'); // Заменяем пробелы и запятые на подчеркивания projectName = projectName.replace(/[\s,]+/g, '_'); // Убираем множественные подчеркивания projectName = projectName.replace(/_+/g, '_'); // Убираем подчеркивания с концов projectName = projectName.replace(/^_+|_+$/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/Project/${encodedFolderName}`; console.log('🔗 Opening project folder:', { projectId, projectName, folderName, folderUrl }); // Открываем папку в новом окне 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 openContactFolder(contactId, firstName, lastName) { // Формируем полное имя контакта let contactName = ''; if (firstName) { contactName = firstName.trim(); } if (lastName) { contactName = contactName ? `${contactName}_${lastName.trim()}` : lastName.trim(); } // Нормализуем имя контакта как в FilePathManager::sanitizeFileName if (contactName) { // Убираем HTML entities contactName = contactName.replace(/"/g, '"').replace(/'/g, "'"); // Заменяем проблемные символы на подчеркивания contactName = contactName.replace(/[/\\:*?"<>|№]/g, '_'); // Заменяем пробелы и запятые на подчеркивания contactName = contactName.replace(/[\s,]+/g, '_'); // Убираем множественные подчеркивания contactName = contactName.replace(/_+/g, '_'); // Убираем подчеркивания с концов contactName = contactName.replace(/^_+|_+$/g, ''); } // Формируем URL для папки контакта в Nextcloud const folderName = contactName ? `${contactName}_${contactId}` : `contact_${contactId}`; 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/Contacts/${encodedFolderName}`; console.log('🔗 Opening contact folder:', { contactId, firstName, lastName, contactName, folderName, folderUrl }); // Открываем папку в новом окне window.open(folderUrl, 'nextcloud_folder', 'width=1200,height=800,scrollbars=yes,resizable=yes'); } /** * Открытие папки контрагента в Nextcloud */ function openAccountFolder(accountId, accountName) { // Нормализуем имя контрагента как в FilePathManager::sanitizeFileName if (accountName) { // Убираем HTML entities accountName = accountName.replace(/"/g, '"').replace(/'/g, "'"); // Заменяем проблемные символы на подчеркивания accountName = accountName.replace(/[/\\:*?"<>|№]/g, '_'); // Заменяем пробелы и запятые на подчеркивания accountName = accountName.replace(/[\s,]+/g, '_'); // Убираем множественные подчеркивания accountName = accountName.replace(/_+/g, '_'); // Убираем подчеркивания с концов accountName = accountName.replace(/^_+|_+$/g, ''); } // Формируем URL для папки контрагента в Nextcloud const folderName = accountName ? `${accountName}_${accountId}` : `account_${accountId}`; 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/Accounts/${encodedFolderName}`; console.log('🔗 Opening account folder:', { accountId, accountName, folderName, folderUrl }); // Открываем папку в новом окне window.open(folderUrl, 'nextcloud_folder', 'width=1200,height=800,scrollbars=yes,resizable=yes'); } /** * Универсальная функция открытия папки записи в Nextcloud * Работает для любых модулей (HelpDesk, Invoice, Leads, Act, ProjectTask, SPPayments и т.д.) */ function openRecordFolder(moduleName, recordId, recordName) { // Нормализуем имя записи как в FilePathManager::sanitizeFileName if (recordName) { // Убираем HTML entities recordName = recordName.replace(/"/g, '"').replace(/'/g, "'"); // Для HelpDesk и Invoice: убираем все кроме цифр, дефисов и подчеркиваний // Это превратит "ЗАЯВКА_762" → "762", "инв_18" → "18" (как в скрипте миграции) if (moduleName === 'HelpDesk' || moduleName === 'Invoice') { recordName = recordName.replace(/[^a-zA-Z0-9\-_]/g, '_'); } else { // Для других модулей: заменяем только проблемные символы recordName = recordName.replace(/[/\\:*?"<>|№]/g, '_'); } // Заменяем пробелы и запятые на подчеркивания recordName = recordName.replace(/[\s,]+/g, '_'); // Убираем множественные подчеркивания recordName = recordName.replace(/_+/g, '_'); // Убираем подчеркивания с концов recordName = recordName.replace(/^_+|_+$/g, ''); } // Формируем URL для папки записи в Nextcloud const folderName = recordName ? `${recordName}_${recordId}` : `${moduleName}_${recordId}`; 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/${moduleName}/${encodedFolderName}`; console.log('🔗 Opening record folder:', { moduleName, recordId, recordName, folderName, folderUrl }); // Открываем папку в новом окне window.open(folderUrl, 'nextcloud_folder', 'width=1200,height=800,scrollbars=yes,resizable=yes'); } /** * Открытие редактора Nextcloud для документа */ function openNextcloudEditor(recordId, fileName) { // ПРОСТОЕ РЕШЕНИЕ - используем промежуточную страницу для редиректа! const cacheVersion = Date.now(); // Принудительное обновление кеша const redirectUrl = `/crm_extensions/file_storage/api/open_file_v2.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) { console.log('🧪 Testing simple API...', recordId, fileName); $.ajax({ url: '/crm_extensions/file_storage/api/simple_test.php', method: 'GET', data: { recordId: recordId, fileName: fileName }, dataType: 'json', success: function(response) { console.log('✅ Simple API works:', response); if (response.success) { // Если простой API работает, пробуем открыть редактор напрямую console.log('🎯 Opening editor directly...'); // Создаем различные варианты URL прямо в JavaScript const urls = createEditUrls(response.data.edit_url, recordId, fileName, response.data.file_id); openEditor(urls.recommended, { ...response.data, urls: urls.all, recommended: 'correct_path' }); } else { console.error('❌ Simple API error:', response.error); showError('Ошибка простого API: ' + response.error); } }, error: function(xhr, status, error) { console.error('❌ Simple API failed:', error); console.error('❌ Response:', xhr.responseText); // Если простой API не работает, пробуем основной callMainAPI(recordId, fileName); } }); } function createEditUrls(baseEditUrl, recordId, fileName, fileId = 662) { console.log('🔗 Creating edit URLs in JavaScript...', { fileId, fileName }); // Извлекаем базовый URL из базовой ссылки const baseUrl = 'https://office.clientright.ru:8443'; const encodedFileName = encodeURIComponent(fileName); // Определяем структуру пути в зависимости от модуля let filePath; if (window.app && window.app.getModuleName && window.app.getModuleName() === 'Project') { // Для проектов используем новую структуру Project/название_ID/ const projectName = window.app.getRecordName ? window.app.getRecordName() : 'project'; // Нормализуем имя проекта как в FilePathManager::sanitizeFileName let sanitizedProjectName = projectName; if (sanitizedProjectName) { // Убираем HTML entities sanitizedProjectName = sanitizedProjectName.replace(/"/g, '"').replace(/'/g, "'"); // Заменяем проблемные символы на подчеркивания (как в FilePathManager::sanitizeFileName) sanitizedProjectName = sanitizedProjectName.replace(/[/\\:*?"<>|№]/g, '_'); // Заменяем пробелы и запятые на подчеркивания sanitizedProjectName = sanitizedProjectName.replace(/[\s,]+/g, '_'); // Убираем множественные подчеркивания sanitizedProjectName = sanitizedProjectName.replace(/_+/g, '_'); // Убираем подчеркивания с концов sanitizedProjectName = sanitizedProjectName.replace(/^_+|_+$/g, ''); } filePath = `/crm/crm2/CRM_Active_Files/Documents/Project/${sanitizedProjectName}_${recordId}/${encodedFileName}`; } else { // Для других модулей используем старую структуру filePath = `/crm/crm2/CRM_Active_Files/Documents/${recordId}/${encodedFileName}`; } // Токен для RichDocuments (из настроек Nextcloud) const richDocumentsToken = '1sanuq71b3n4fm1ldkbb'; const urls = { // ЛУЧШИЙ СПОСОБ! - редирект через нашу промежуточную страницу (БЕЗ CSRF!) 'redirect_to_nextcloud': fileId ? `/crm_extensions/file_storage/api/open_file.php?fileId=${fileId}&fileName=${encodedFileName}&recordId=${recordId}` : null, // РАБОЧИЙ СПОСОБ! - прямая ссылка на Nextcloud 'files_editing_auto': fileId ? `${baseUrl}/apps/files/files/${fileId}?dir=/&editing=true&openfile=true` : null, // Вариант без автоматического редактирования 'files_editing': fileId ? `${baseUrl}/apps/files/files/${fileId}?dir=/&editing=false&openfile=true` : null, // Collabora Editor 'collabora_editor': fileId ? `${baseUrl}/index.php/apps/richdocuments/index?fileId=${fileId}` : null, // OnlyOffice Editor 'onlyoffice_editor': fileId ? `${baseUrl}/apps/onlyoffice?fileId=${fileId}` : null, // Прямое открытие файла 'files_direct': fileId ? `${baseUrl}/apps/files/files/${fileId}` : `${baseUrl}/apps/files/?dir=/&openfile=${encodedFileName}`, // Файловый менеджер 'files_manager': `${baseUrl}/apps/files/?dir=/&openfile=${encodedFileName}` }; // Убираем null значения Object.keys(urls).forEach(key => { if (urls[key] === null) { delete urls[key]; } }); return { all: urls, // РЕДИРЕКТ через нашу страницу - лучший способ (обходит CSRF) recommended: urls.redirect_to_nextcloud || urls.files_editing_auto || urls.files_editing || urls.collabora_editor }; } function getEditUrls(recordId, fileName, simpleData) { console.log('🔗 Getting edit URLs...'); $.ajax({ url: '/crm_extensions/file_storage/api/get_edit_urls.php', method: 'GET', data: { recordId: recordId, fileName: fileName }, dataType: 'json', success: function(response) { console.log('✅ Edit URLs received:', response); if (response.success) { // Пробуем открыть рекомендуемый URL const recommendedUrl = response.data.urls[response.data.recommended]; console.log('🎯 Trying recommended URL:', recommendedUrl); openEditor(recommendedUrl, { ...simpleData, urls: response.data.urls, recommended: response.data.recommended }); } else { // Если не получилось, используем простой API openEditor(simpleData.edit_url, simpleData); } }, error: function(xhr, status, error) { console.error('❌ Failed to get edit URLs:', error); // Если не получилось, используем простой API openEditor(simpleData.edit_url, simpleData); } }); } function callMainAPI(recordId, fileName) { console.log('🎯 Calling main API...', recordId, fileName); // Показываем прогресс if (typeof app !== 'undefined' && app.helper && app.helper.showProgress) { app.helper.showProgress({ message: 'Подготовка файла к редактированию...' }); } // Вызываем API v2 для подготовки файла $.ajax({ url: '/crm_extensions/file_storage/api/prepare_edit_v2.php', method: 'GET', data: { recordId: recordId, fileName: fileName, module: window.app && window.app.getModuleName ? window.app.getModuleName() : 'Project' }, dataType: 'json', success: function(response) { console.log('📡 API Response:', response); // Скрываем прогресс if (typeof app !== 'undefined' && app.helper && app.helper.hideProgress) { app.helper.hideProgress(); } if (response.success) { console.log('✅ File prepared successfully'); // Открываем редактор openEditor(response.data.edit_url, response.data); } else { console.error('❌ API Error:', response.error); showError('Ошибка подготовки файла: ' + response.error); } }, error: function(xhr, status, error) { console.error('❌ AJAX Error:', error); console.error('❌ Status:', status); console.error('❌ Response:', xhr.responseText); console.error('❌ Status Code:', xhr.status); // Скрываем прогресс if (typeof app !== 'undefined' && app.helper && app.helper.hideProgress) { app.helper.hideProgress(); } // Показываем подробную ошибку let errorMessage = 'Ошибка подключения к серверу: ' + error; if (xhr.responseText) { errorMessage += '\n\nОтвет сервера:\n' + xhr.responseText; } showError(errorMessage); } }); } // Функция для открытия редактора function openEditor(editUrl, fileData) { console.log('🎯 Opening editor with URL:', editUrl); console.log('📋 All available URLs:', fileData.urls); // Открываем редактор в новом окне var win = window.open(editUrl, 'nextcloud_editor', 'width=1200,height=800,scrollbars=yes,resizable=yes'); if (win) { console.log('✅ Editor opened successfully'); // Показываем уведомление об успехе if (typeof app !== 'undefined' && app.helper && app.helper.showSuccessNotification) { app.helper.showSuccessNotification({ message: 'Редактор открыт! Файл: ' + fileData.file_name }); } else { // alert('✅ Редактор открыт! Файл: ' + fileData.file_name); } // Показываем информацию о файле showFileInfo(fileData); } else { console.log('❌ Failed to open editor window - popup blocked or error'); console.log('📋 Showing modal with alternative URLs'); // Показываем модальное окно с альтернативными вариантами showModalWithUrls(editUrl, fileData); } } // Функция для отображения информации о файле function showFileInfo(fileData) { // Проверяем, есть ли нужные поля в fileData var location = 'Неизвестно'; var nextcloudPath = 'Не указан'; var status = 'Не указан'; if (fileData.file_location && fileData.file_location.type) { location = fileData.file_location.type === 's3' ? 'S3 хранилище' : 'Локальное хранилище'; } if (fileData.nextcloud_path) { nextcloudPath = fileData.nextcloud_path; } if (fileData.message) { status = fileData.message; } var infoHtml = `
Информация о файле

Файл: ${fileData.file_name}

Расположение: ${location}

Путь в Nextcloud: ${nextcloudPath}

Статус: ${status}

`; // Если есть альтернативные URL, добавляем их if (fileData.urls) { infoHtml += `
Альтернативные способы открытия:
`; Object.keys(fileData.urls).forEach(function(key) { const label = key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); const isRecommended = key === fileData.recommended; const btnClass = isRecommended ? 'btn-success' : 'btn-default'; const icon = isRecommended ? 'fa-star' : 'fa-external-link'; infoHtml += ` ${label}${isRecommended ? ' (рекомендуется)' : ''} `; }); infoHtml += `
`; } // Добавляем информацию в модальное окно или показываем отдельно if ($('#nextcloudEditModal').length) { $('#nextcloudEditModal .modal-body').prepend(infoHtml); } else { // Создаём временное уведомление var notification = $('
' + infoHtml + '
'); $('body').append(notification); // Автоматически скрываем через 5 секунд setTimeout(function() { notification.fadeOut(function() { notification.remove(); }); }, 5000); } } // Функция для отображения ошибок function showError(message) { console.error('🚨 Error:', message); if (typeof app !== 'undefined' && app.helper && app.helper.showErrorNotification) { app.helper.showErrorNotification({ message: message }); } else { alert('❌ Ошибка: ' + message); } } // Функция для показа модального окна с URL function showModalWithUrls(editUrl, fileData) { console.log('📋 Showing modal with URLs for file:', fileData.file_name); if (fileData.urls) { // Используем существующую функцию showEditOptions showEditOptions(fileData.urls, fileData.file_name, fileData.record_id); } else { // Если нет альтернативных URL, показываем ошибку showError('Не удалось открыть редактор. Попробуйте открыть файл вручную в Nextcloud.'); } } /** * Показ вариантов открытия файла */ function showEditOptions(urls, fileName, recordId) { console.log('🎯 Showing edit options for:', fileName); console.log('📋 Available URLs:', urls); var buttonsHtml = ''; // Если urls - это объект (новый формат) if (typeof urls === 'object' && !Array.isArray(urls)) { Object.keys(urls).forEach(function(key, index) { const label = key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); const url = urls[key]; const isRecommended = key === 'correct_path'; const btnClass = isRecommended ? 'btn-success' : 'btn-primary'; const icon = isRecommended ? 'fa-star' : 'fa-external-link'; buttonsHtml += ` ${label}${isRecommended ? ' (рекомендуется)' : ''} `; }); } else { // Старый формат - массив URL var labels = [ 'С параметром openfile', 'С action=edit', 'С edit=true', 'Через RichDocuments', 'Через OnlyOffice' ]; var icons = ['fa-file', 'fa-edit', 'fa-pencil', 'fa-file-text', 'fa-file-word-o']; var colors = ['btn-primary', 'btn-success', 'btn-info', 'btn-warning', 'btn-danger']; urls.forEach(function(url, index) { buttonsHtml += ` ${labels[index]} `; }); } var modalHtml = ` `; // Удаляем старое модальное окно $('#nextcloudEditModal').remove(); // Добавляем новое $('body').append(modalHtml); $('#nextcloudEditModal').modal('show'); } /** * Синхронизация изменений файла (заглушка) */ function syncFileChanges(recordId, fileName) { console.log('Syncing file changes for:', recordId, fileName); if (typeof app !== 'undefined' && app.helper && app.helper.showSuccessNotification) { app.helper.showSuccessNotification({ message: 'Изменения синхронизированы!' }); } else { alert('✅ Изменения синхронизированы!'); } } /** * Открытие редактора в новом окне */ function openInNewWindow(editUrl) { window.open(editUrl, 'nextcloud_editor', 'width=1200,height=800,scrollbars=yes,resizable=yes,toolbar=no,location=no'); console.log('Opened Nextcloud editor in new window'); } // Алиас функции для обратной совместимости function editInNextcloud(recordId, fileName) { console.log('📝 editInNextcloud called (alias)'); return openNextcloudEditor(recordId, fileName); } // Автоматическое подключение при загрузке страницы $(document).ready(function() { console.log('Nextcloud Editor integration loaded'); // Добавляем CSS стили для модального окна if (!$('#nextcloud-editor-styles').length) { $('