Files
crm.clientright.ru/crm_extensions/nextcloud_editor/js/nextcloud-editor.js
Fedor 3fb2ad5f60 feat: Project file migration and Nextcloud integration
- Added project file migration script with sanitization (underscores)
- Fixed Nextcloud editor integration (urldecode, basename fix)
- Added 'Open Project Folder in Nextcloud' button
- 223 projects migrated (completed + archived)
- URL decoding fix for Cyrillic filenames
2025-10-22 18:29:02 +03:00

495 lines
21 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Nextcloud Editor Integration JavaScript
* 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) {
// ПРОСТОЕ РЕШЕНИЕ - используем промежуточную страницу для редиректа!
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) {
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);
const 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 для подготовки файла
$.ajax({
url: '/crm_extensions/file_storage/api/prepare_edit.php',
method: 'GET',
data: {
recordId: recordId,
fileName: fileName
},
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 = `
<div class="alert alert-info" style="margin: 10px 0;">
<h5><i class="fa fa-info-circle"></i> Информация о файле</h5>
<p><strong>Файл:</strong> ${fileData.file_name}</p>
<p><strong>Расположение:</strong> ${location}</p>
<p><strong>Путь в Nextcloud:</strong> ${nextcloudPath}</p>
<p><strong>Статус:</strong> ${status}</p>
</div>
`;
// Если есть альтернативные URL, добавляем их
if (fileData.urls) {
infoHtml += `
<div class="alert alert-warning" style="margin: 10px 0;">
<h6><i class="fa fa-link"></i> Альтернативные способы открытия:</h6>
<div class="btn-group-vertical" style="width: 100%;">
`;
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 += `
<a href="${fileData.urls[key]}" target="_blank" class="btn ${btnClass} btn-sm" style="margin: 2px; text-align: left;">
<i class="fa ${icon}"></i> ${label}${isRecommended ? ' (рекомендуется)' : ''}
</a>
`;
});
infoHtml += `
</div>
</div>
`;
}
// Добавляем информацию в модальное окно или показываем отдельно
if ($('#nextcloudEditModal').length) {
$('#nextcloudEditModal .modal-body').prepend(infoHtml);
} else {
// Создаём временное уведомление
var notification = $('<div class="alert alert-info" style="position: fixed; top: 20px; right: 20px; z-index: 9999; max-width: 400px;">' + infoHtml + '</div>');
$('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 += `
<a href="${url}" target="_blank" class="btn ${btnClass} btn-sm" style="margin: 3px; display: block;">
<i class="fa ${icon}"></i> ${label}${isRecommended ? ' (рекомендуется)' : ''}
</a>
`;
});
} 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 += `
<a href="${url}" target="_blank" class="btn ${colors[index]} btn-sm" style="margin: 3px; display: block;">
<i class="fa ${icons[index]}"></i> ${labels[index]}
</a>
`;
});
}
var modalHtml = `
<div class="modal fade" id="nextcloudEditModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
<i class="fa fa-edit"></i> Варианты открытия файла
</h4>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<p><strong>Файл:</strong> ${fileName}</p>
<p>Попробуйте эти варианты для прямого открытия в редакторе:</p>
<div class="btn-group-vertical" style="width: 100%;">
${buttonsHtml}
</div>
<div class="alert alert-info" style="margin-top: 15px;">
<small><strong>💡 Совет:</strong> Попробуйте варианты сверху вниз. Один из них должен открыть файл сразу в редакторе!</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button>
</div>
</div>
</div>
</div>
`;
// Удаляем старое модальное окно
$('#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) {
$('<style id="nextcloud-editor-styles">')
.html(`
.nextcloud-edit-btn {
background: #0082c9;
border-color: #0082c9;
}
.nextcloud-edit-btn:hover {
background: #006ba6;
border-color: #006ba6;
}
`)
.appendTo('head');
}
});// Version: 1761125337