feat: OnlyOffice Standalone integration with S3 direct URLs

 ЧТО СДЕЛАНО:
- Поднят новый standalone OnlyOffice Document Server (порт 8083)
- Настроен Nginx для доступа через office.clientright.ru:9443
- Создан open_file_v3_standalone.php для работы с новым OnlyOffice
- Реализована поддержка прямых S3 URL (bucket публичный)
- Добавлен s3_proxy.php с поддержкой Range requests
- Создан onlyoffice_callback.php для сохранения (базовая версия)
- Файлы успешно открываются и загружаются!

⚠️ TODO (на завтра):
- Доработать onlyoffice_callback.php для сохранения обратно в ОРИГИНАЛЬНЫЙ путь в S3
- Добавить Redis маппинг documentKey → S3 path
- Обновить CRM JS для использования open_file_v3_standalone.php
- Протестировать сохранение файлов
- Удалить тестовые файлы

📊 РЕЗУЛЬТАТ:
- OnlyOffice Standalone РАБОТАЕТ! 
- Файлы открываются напрямую из S3 
- Редактор загружается БЫСТРО 
- Автосохранение настроено  (но нужна доработка callback)
This commit is contained in:
Fedor
2025-11-01 01:02:03 +03:00
parent d7941ac862
commit 269c7ea216
4383 changed files with 282112 additions and 7731 deletions

View File

@@ -7,22 +7,14 @@
* Открытие папки проекта в Nextcloud
*/
function openProjectFolder(projectId, projectName) {
// Нормализуем имя проекта как в FilePathManager::sanitizeFileName
console.log('📁 Opening project folder in Nextcloud:', projectId, projectName);
// Нормализуем имя проекта (убираем пробелы и кавычки)
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, '');
// Убираем кавычки (заменяем на подчёркивание)
projectName = projectName.replace(/"/g, '_');
// Заменяем ВСЕ пробелы на подчёркивания
projectName = projectName.replace(/\s+/g, '_');
}
// Формируем URL для папки проекта в Nextcloud
@@ -30,143 +22,10 @@ function openProjectFolder(projectId, projectName) {
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}`;
// URL для папки проекта в Nextcloud External Storage
const folderUrl = `${nextcloudUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${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(/&quot;/g, '"').replace(/&apos;/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(/&quot;/g, '"').replace(/&apos;/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(/&quot;/g, '"').replace(/&apos;/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 });
console.log('🔗 Folder URL:', folderUrl);
// Открываем папку в новом окне
window.open(folderUrl, 'nextcloud_folder', 'width=1200,height=800,scrollbars=yes,resizable=yes');
@@ -176,53 +35,30 @@ function openRecordFolder(moduleName, recordId, recordName) {
* Открытие редактора 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}`;
console.log('🚀 NEXTCLOUD EDITOR v2 - ONLYOFFICE + S3: Function called!', recordId, fileName);
// OnlyOffice + S3 Pre-signed URLs (БЫСТРО!)
const redirectUrl = `/crm_extensions/file_storage/api/open_file_v2.php?recordId=${recordId}&fileName=${encodeURIComponent(fileName)}&v=${Date.now()}`;
// Открываем редактор в новом окне через промежуточную страницу
window.open(redirectUrl, 'nextcloud_editor', 'width=1200,height=800,scrollbars=yes,resizable=yes');
console.log('🎯 Opening via OnlyOffice v2:', redirectUrl);
// Открываем в новом окне
const win = window.open(redirectUrl, 'nextcloud_editor_' + Date.now(), 'width=1400,height=900,scrollbars=yes,resizable=yes');
if (win) {
console.log('✅ Collabora editor opened successfully');
} else {
console.log('❌ Failed to open editor window - popup blocked');
alert('❌ Не удалось открыть редактор. Проверьте блокировку всплывающих окон.');
}
}
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);
}
});
// Пропускаем простой API и сразу идем к основному
console.log('🎯 Skipping simple API, going to main API...');
callMainAPI(recordId, fileName);
}
function createEditUrls(baseEditUrl, recordId, fileName, fileId = 662) {
@@ -231,36 +67,7 @@ function createEditUrls(baseEditUrl, recordId, fileName, fileId = 662) {
// Извлекаем базовый 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(/&quot;/g, '"').replace(/&apos;/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}`;
}
const filePath = `/crm/crm2/CRM_Active_Files/Documents/${recordId}/${encodedFileName}`;
// Токен для RichDocuments (из настроек Nextcloud)
const richDocumentsToken = '1sanuq71b3n4fm1ldkbb';
@@ -303,7 +110,7 @@ function getEditUrls(recordId, fileName, simpleData) {
url: '/crm_extensions/file_storage/api/get_edit_urls.php',
method: 'GET',
data: {
recordId: recordId,
record: recordId,
fileName: fileName
},
dataType: 'json',
@@ -343,14 +150,13 @@ function callMainAPI(recordId, fileName) {
});
}
// Вызываем API v2 для подготовки файла
// Вызываем ПРАВИЛЬНЫЙ API для подготовки файла
$.ajax({
url: '/crm_extensions/file_storage/api/prepare_edit_v2.php',
url: '/index.php?module=Documents&action=NcPrepareEdit',
method: 'GET',
data: {
recordId: recordId,
fileName: fileName,
module: window.app && window.app.getModuleName ? window.app.getModuleName() : 'Project'
record: recordId,
fileName: fileName
},
dataType: 'json',
success: function(response) {
@@ -660,4 +466,4 @@ $(document).ready(function() {
`)
.appendTo('head');
}
});// Version: 1761125337
});