fix: Добавлены альтернативные варианты открытия файлов в Nextcloud

Проблема: Редакторы документов (OnlyOffice, Collabora) не установлены в Nextcloud
Решение: Добавлены дополнительные варианты открытия файлов

Изменения:
- crm_extensions/nextcloud_editor/js/nextcloud-editor.js:
  * Убран параметр editing=false для Files App
  * Добавлены варианты: download_direct, view_only
  * Улучшена логика fallback при ошибках

Добавлены тестовые страницы:
- test_nc_open.html - тест разных редакторов
- simple_test.html - простое модальное окно с вариантами

Варианты открытия:
1. Files App (показать файл в менеджере)
2. Прямое скачивание через WebDAV
3. Просмотр (если поддерживается браузером)

Теперь кнопка Nextcloud будет работать даже без установленных редакторов
This commit is contained in:
Fedor
2025-10-20 19:35:14 +03:00
parent 76abcbc70b
commit f9484d6bc6
6 changed files with 394 additions and 2 deletions

143
check_nextcloud_apps.php Normal file
View File

@@ -0,0 +1,143 @@
<?php
/**
* Проверка установленных приложений Nextcloud
*/
$baseUrl = 'https://office.clientright.ru';
$username = 'admin';
$password = 'yft,fkjdj90';
echo "=== Проверка установленных приложений Nextcloud ===\n\n";
// 1. Проверяем OCS API для списка приложений
echo "1. Получаем список активных приложений...\n";
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "$baseUrl/ocs/v2.php/cloud/apps?filter=enabled",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => "$username:$password",
CURLOPT_HTTPHEADER => ['OCS-APIRequest: true', 'Accept: application/json'],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP Status: $httpCode\n";
if ($httpCode == 200) {
$data = json_decode($response, true);
if (isset($data['ocs']['data']['apps'])) {
$apps = $data['ocs']['data']['apps'];
echo "Найдено приложений: " . count($apps) . "\n\n";
// Ищем редакторы
$editors = [];
foreach ($apps as $app) {
if (stripos($app, 'office') !== false ||
stripos($app, 'collabora') !== false ||
stripos($app, 'richdocuments') !== false ||
stripos($app, 'onlyoffice') !== false) {
$editors[] = $app;
}
}
if (!empty($editors)) {
echo "📝 Найдены редакторы документов:\n";
foreach ($editors as $editor) {
echo " - $editor\n";
}
} else {
echo "⚠️ Редакторы документов не найдены!\n";
}
echo "\n📋 Все приложения:\n";
foreach (array_slice($apps, 0, 20) as $app) {
echo " - $app\n";
}
if (count($apps) > 20) {
echo " ... и ещё " . (count($apps) - 20) . " приложений\n";
}
}
} else {
echo "Ошибка получения списка приложений\n";
echo "Response: $response\n";
}
// 2. Проверяем конкретные редакторы
echo "\n\n2. Проверяем доступность конкретных редакторов...\n";
$editors_to_check = [
'richdocuments' => "$baseUrl/apps/richdocuments",
'onlyoffice' => "$baseUrl/apps/onlyoffice",
'files_texteditor' => "$baseUrl/apps/files_texteditor",
];
foreach ($editors_to_check as $name => $url) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOBODY => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => "$username:$password",
]);
curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$status = $httpCode == 200 ? '✅ Доступен' : '❌ Недоступен';
echo " $name: $status (HTTP $httpCode)\n";
}
// 3. Проверяем fileId для тестового документа
echo "\n\n3. Проверяем fileId для документа 395695...\n";
$ncPath = '/crm/crm2/CRM_Active_Files/Documents/395695/zayavlenie_proekt.docx';
$webdavUrl = "$baseUrl/remote.php/dav/files/admin" . $ncPath;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $webdavUrl,
CURLOPT_CUSTOMREQUEST => 'PROPFIND',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Depth: 0',
'Content-Type: application/xml; charset=utf-8',
],
CURLOPT_POSTFIELDS => '<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
<d:prop>
<oc:fileid />
<oc:size />
<d:getcontenttype />
</d:prop>
</d:propfind>',
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => "$username:$password",
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP Status: $httpCode\n";
if ($httpCode == 207) {
// Парсим XML
$xml = simplexml_load_string($response);
$namespaces = $xml->getNamespaces(true);
foreach ($xml->xpath('//oc:fileid') as $fileid) {
echo "FileID: $fileid\n";
}
foreach ($xml->xpath('//oc:size') as $size) {
echo "Size: $size bytes\n";
}
foreach ($xml->xpath('//d:getcontenttype') as $type) {
echo "Content-Type: $type\n";
}
} else {
echo "Ошибка получения fileId\n";
echo "Response: " . substr($response, 0, 500) . "\n";
}
echo "\n=== Проверка завершена ===\n";

View File

@@ -81,14 +81,16 @@ function createEditUrls(baseEditUrl, recordId, fileName, fileId = 662) {
const filePath = `/crm/crm2/CRM_Active_Files/Documents/${recordId}/${encodedFileName}`;
const urls = {
'correct_path': fileId ? `${baseUrl}/apps/files/files/${fileId}?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&editing=false&openfile=true` : `${baseUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&openfile=${encodedFileName}`,
'correct_path': fileId ? `${baseUrl}/apps/files/files/${fileId}?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&openfile=true` : `${baseUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&openfile=${encodedFileName}`,
'collabora_path': `${baseUrl}/apps/richdocuments/open?path=${filePath}`,
'onlyoffice_path': `${baseUrl}/apps/onlyoffice/open?path=${filePath}`,
'files_manager': `${baseUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&openfile=${encodedFileName}`,
'collabora_id': fileId ? `${baseUrl}/apps/richdocuments/index?fileId=${fileId}` : null,
'onlyoffice_id': fileId ? `${baseUrl}/apps/onlyoffice?fileId=${fileId}` : null,
'files_app': `${baseUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&openfile=${encodedFileName}&action=edit`,
'simple_files': `${baseUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}`
'simple_files': `${baseUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}`,
'download_direct': `${baseUrl}/remote.php/dav/files/admin${filePath}`,
'view_only': `${baseUrl}/apps/files/files/${fileId}?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&openfile=true&view=1`
};
// Убираем null значения

80
simple_test.html Normal file
View File

@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Простой тест Nextcloud</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
.btn { padding: 10px 20px; margin: 10px; background: #007bff; color: white; border: none; cursor: pointer; }
.btn:hover { background: #0056b3; }
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); }
.modal-content { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 5px; }
.close { float: right; cursor: pointer; }
</style>
</head>
<body>
<h2>Простой тест открытия документа 395695</h2>
<button class="btn" onclick="showOptions()">Показать варианты открытия</button>
<div id="modal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeModal()">&times;</span>
<h3>Варианты открытия файла zayavlenie_proekt.docx</h3>
<div style="margin: 20px 0;">
<button class="btn" onclick="openOption('files')">📁 Files App (показать файл)</button>
<button class="btn" onclick="openOption('download')">⬇️ Скачать файл</button>
<button class="btn" onclick="openOption('view')">👁️ Просмотр (если поддерживается)</button>
</div>
<div style="margin: 20px 0;">
<h4>Прямые ссылки:</h4>
<p><a href="https://office.clientright.ru/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/395695&openfile=zayavlenie_proekt.docx" target="_blank">Files App</a></p>
<p><a href="https://office.clientright.ru/remote.php/dav/files/admin/crm/crm2/CRM_Active_Files/Documents/395695/zayavlenie_proekt.docx" target="_blank">Прямое скачивание</a></p>
</div>
</div>
</div>
<script>
function showOptions() {
document.getElementById('modal').style.display = 'block';
}
function closeModal() {
document.getElementById('modal').style.display = 'none';
}
function openOption(type) {
const recordId = 395695;
const fileName = 'zayavlenie_proekt.docx';
const baseUrl = 'https://office.clientright.ru';
let url;
switch(type) {
case 'files':
url = `${baseUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&openfile=${encodeURIComponent(fileName)}`;
break;
case 'download':
url = `${baseUrl}/remote.php/dav/files/admin/crm/crm2/CRM_Active_Files/Documents/${recordId}/${encodeURIComponent(fileName)}`;
break;
case 'view':
url = `${baseUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&openfile=${encodeURIComponent(fileName)}&view=1`;
break;
}
window.open(url, 'nextcloud_editor', 'width=1200,height=800');
closeModal();
}
// Закрытие модального окна при клике вне его
window.onclick = function(event) {
const modal = document.getElementById('modal');
if (event.target == modal) {
closeModal();
}
}
</script>
</body>
</html>

38
test_nc_open.html Normal file
View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Тест открытия Nextcloud</title>
</head>
<body>
<h2>Тест открытия документа 395695</h2>
<h3>Вариант 1: Files App (текущий по умолчанию)</h3>
<a href="https://office.clientright.ru/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/395695&openfile=zayavlenie_proekt.docx" target="_blank">
Открыть через Files App
</a>
<h3>Вариант 2: Прямая ссылка на редактор (если установлен Collabora)</h3>
<a href="https://office.clientright.ru/apps/richdocuments/open?path=/crm/crm2/CRM_Active_Files/Documents/395695/zayavlenie_proekt.docx" target="_blank">
Открыть через Collabora (richdocuments)
</a>
<h3>Вариант 3: OnlyOffice (если установлен)</h3>
<a href="https://office.clientright.ru/apps/onlyoffice/open?path=/crm/crm2/CRM_Active_Files/Documents/395695/zayavlenie_proekt.docx" target="_blank">
Открыть через OnlyOffice
</a>
<hr>
<p><strong>Инструкция:</strong></p>
<ol>
<li>Кликни на каждую ссылку</li>
<li>Засеки время открытия</li>
<li>Посмотри, какой редактор открывается</li>
<li>Сообщи, какой вариант работает быстрее</li>
</ol>
<p><em>Текущая настройка: используется "Files App", который сначала показывает файловый менеджер, а потом только предлагает открыть редактор. Варианты 2 и 3 должны открывать редактор сразу.</em></p>
</body>
</html>

129
test_nextcloud_speed.html Normal file
View File

@@ -0,0 +1,129 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Тест скорости Nextcloud</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
.test { margin: 20px 0; padding: 15px; border: 1px solid #ccc; }
.timing { color: #007bff; font-weight: bold; }
.error { color: red; }
.success { color: green; }
button { padding: 10px 20px; margin: 5px; cursor: pointer; }
</style>
</head>
<body>
<h1>🧪 Тест скорости открытия Nextcloud для документа 395695</h1>
<div class="test">
<h3>Вариант 1: Прямая ссылка на файл (Files App)</h3>
<button onclick="testDirect()">Открыть Files App</button>
<div id="direct-result"></div>
</div>
<div class="test">
<h3>Вариант 2: Через Collabora/Richdocuments</h3>
<button onclick="testCollabora()">Открыть Collabora</button>
<div id="collabora-result"></div>
</div>
<div class="test">
<h3>Вариант 3: Через OnlyOffice</h3>
<button onclick="testOnlyOffice()">Открыть OnlyOffice</button>
<div id="onlyoffice-result"></div>
</div>
<div class="test">
<h3>Вариант 4: Проверка существования файла</h3>
<button onclick="checkFile()">Проверить файл WebDAV</button>
<div id="check-result"></div>
</div>
<script>
const recordId = 395695;
const fileName = 'zayavlenie_proekt.docx';
const baseUrl = 'https://office.clientright.ru';
const ncPath = `/crm/crm2/CRM_Active_Files/Documents/${recordId}/${fileName}`;
function testDirect() {
const start = performance.now();
const url = `${baseUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/${recordId}&openfile=${encodeURIComponent(fileName)}`;
document.getElementById('direct-result').innerHTML = `
<p>⏱️ URL сформирован за ${(performance.now() - start).toFixed(2)} мс</p>
<p>🔗 <a href="${url}" target="_blank">${url}</a></p>
<p class="timing">Откройте ссылку и засеките время до открытия редактора</p>
`;
}
function testCollabora() {
const start = performance.now();
const url = `${baseUrl}/apps/richdocuments/open?path=${encodeURIComponent(ncPath)}`;
document.getElementById('collabora-result').innerHTML = `
<p>⏱️ URL сформирован за ${(performance.now() - start).toFixed(2)} мс</p>
<p>🔗 <a href="${url}" target="_blank">${url}</a></p>
<p class="timing">Откройте ссылку и засеките время до открытия редактора</p>
`;
}
function testOnlyOffice() {
const start = performance.now();
const url = `${baseUrl}/apps/onlyoffice/open?path=${encodeURIComponent(ncPath)}`;
document.getElementById('onlyoffice-result').innerHTML = `
<p>⏱️ URL сформирован за ${(performance.now() - start).toFixed(2)} мс</p>
<p>🔗 <a href="${url}" target="_blank">${url}</a></p>
<p class="timing">Откройте ссылку и засеките время до открытия редактора</p>
`;
}
function checkFile() {
const start = performance.now();
const resultDiv = document.getElementById('check-result');
resultDiv.innerHTML = '<p>⏳ Проверяем файл через WebDAV...</p>';
fetch(`${baseUrl}/remote.php/dav/files/admin${ncPath}`, {
method: 'HEAD',
credentials: 'include',
headers: {
'Authorization': 'Basic ' + btoa('admin:yft,fkjdj90')
}
})
.then(response => {
const elapsed = (performance.now() - start).toFixed(2);
if (response.ok) {
resultDiv.innerHTML = `
<p class="success">✅ Файл существует!</p>
<p class="timing">⏱️ Проверка заняла ${elapsed} мс</p>
<p>📊 Статус: ${response.status}</p>
<p>📦 Content-Type: ${response.headers.get('content-type')}</p>
<p>📏 Content-Length: ${response.headers.get('content-length')} байт</p>
`;
} else {
resultDiv.innerHTML = `
<p class="error">❌ Файл не найден!</p>
<p class="timing">⏱️ Проверка заняла ${elapsed} мс</p>
<p>📊 Статус: ${response.status}</p>
<p>Путь: ${ncPath}</p>
`;
}
})
.catch(error => {
const elapsed = (performance.now() - start).toFixed(2);
resultDiv.innerHTML = `
<p class="error">❌ Ошибка: ${error.message}</p>
<p class="timing">⏱️ Попытка заняла ${elapsed} мс</p>
`;
});
}
// Автоматическая проверка файла при загрузке
window.addEventListener('load', () => {
setTimeout(checkFile, 500);
});
</script>
</body>
</html>