✨ Исправление путей к папкам проектов в Nextcloud + создание файлов из CRM
🔧 Исправления: - Исправлены пути к папкам проектов: теперь /Documents/Project/{Name}_{Id} - Исправлена функция openProjectFolder() во всех JS файлах - Добавлены кнопки создания Word/Excel/PowerPoint из CRM (10 модулей) - Создание файлов напрямую в S3 с автоиндексацией через Redis - Исправлена ошибка 'Class Redis not found' (использован Predis) 📁 Изменённые файлы: - layouts/v7/lib/nextcloud-editor.js - crm_extensions/nextcloud_editor/js/nextcloud-editor.js - layouts/v7/lib/nextcloud-editor-v3.js - crm_extensions/file_storage/api/create_nextcloud_file.php - layouts/v7/modules/*/DetailViewHeaderTitle.tpl (10 модулей) - layouts/v7/modules/Documents/*.tpl (кнопки редактирования) 🎯 Результат: - Кнопка 'Папка в Nextcloud' открывает правильную папку - Создание файлов работает молниеносно (прямо в S3) - Redis события публикуются корректно - OnlyOffice открывается для редактирования Проект 391552 теперь открывается по правильному пути!
This commit is contained in:
233
crm_extensions/file_storage/api/create_nextcloud_file.php
Normal file
233
crm_extensions/file_storage/api/create_nextcloud_file.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
/**
|
||||
* Создание нового файла в Nextcloud
|
||||
* Создаёт пустой DOCX/XLSX/PPTX и открывает для редактирования
|
||||
*/
|
||||
|
||||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/shared/EnvLoader.php';
|
||||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/vendor/autoload.php';
|
||||
EnvLoader::load('/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/.env');
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
// Параметры
|
||||
$module = $_GET['module'] ?? '';
|
||||
$recordId = $_GET['recordId'] ?? '';
|
||||
$recordName = $_GET['recordName'] ?? '';
|
||||
$fileName = $_GET['fileName'] ?? '';
|
||||
$fileType = $_GET['fileType'] ?? 'docx';
|
||||
|
||||
if (empty($module) || empty($recordId) || empty($fileName)) {
|
||||
die("❌ Не указаны обязательные параметры");
|
||||
}
|
||||
|
||||
// Nextcloud credentials
|
||||
$nextcloudUrl = 'https://office.clientright.ru:8443';
|
||||
$username = 'admin';
|
||||
$password = 'office';
|
||||
|
||||
// Определяем папку модуля
|
||||
$moduleFolders = [
|
||||
'Project' => 'Project',
|
||||
'Contacts' => 'Contacts',
|
||||
'Accounts' => 'Accounts',
|
||||
'Invoice' => 'Invoice',
|
||||
'Quotes' => 'Quotes',
|
||||
'SalesOrder' => 'SalesOrder',
|
||||
'PurchaseOrder' => 'PurchaseOrder',
|
||||
'HelpDesk' => 'HelpDesk',
|
||||
'Leads' => 'Leads',
|
||||
'Potentials' => 'Potentials'
|
||||
];
|
||||
|
||||
$moduleFolder = $moduleFolders[$module] ?? 'Other';
|
||||
|
||||
// Формируем имя папки записи
|
||||
$recordName = preg_replace('/[\/\\\\:\*\?"<>\|]/', '_', $recordName); // Убираем недопустимые символы
|
||||
$folderName = $recordName . '_' . $recordId;
|
||||
|
||||
// Формируем путь к файлу в Nextcloud
|
||||
$ncPath = "/crm/crm2/CRM_Active_Files/Documents/{$moduleFolder}/{$folderName}/{$fileName}.{$fileType}";
|
||||
$webdavUrl = $nextcloudUrl . '/remote.php/dav/files/' . $username . $ncPath;
|
||||
|
||||
error_log("=== CREATE NEXTCLOUD FILE ===");
|
||||
error_log("Module: " . $module);
|
||||
error_log("Record ID: " . $recordId);
|
||||
error_log("File name: " . $fileName);
|
||||
error_log("File type: " . $fileType);
|
||||
error_log("Nextcloud path: " . $ncPath);
|
||||
error_log("WebDAV URL: " . $webdavUrl);
|
||||
|
||||
// СОЗДАЁМ ФАЙЛ ЧЕРЕЗ NEXTCLOUD OCS API (Direct Editing)
|
||||
// Используем встроенный API Nextcloud для создания нового файла
|
||||
|
||||
$templateMap = [
|
||||
'docx' => 'onlyoffice',
|
||||
'xlsx' => 'onlyoffice',
|
||||
'pptx' => 'onlyoffice'
|
||||
];
|
||||
|
||||
$editorId = $templateMap[$fileType] ?? 'onlyoffice';
|
||||
|
||||
// Используем OCS API v2 для создания нового файла
|
||||
$createUrl = $nextcloudUrl . '/ocs/v2.php/apps/files/api/v1/directEditing/create';
|
||||
|
||||
$postData = http_build_query([
|
||||
'path' => $ncPath,
|
||||
'editorId' => $editorId,
|
||||
'templateId' => '',
|
||||
'templateType' => $fileType
|
||||
]);
|
||||
|
||||
error_log("Creating file via OCS API: " . $createUrl);
|
||||
error_log("Post data: " . $postData);
|
||||
|
||||
// Вызываем API создания
|
||||
$ch = curl_init($createUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'OCS-APIRequest: true',
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
'Accept: application/json'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
error_log("OCS API response code: " . $httpCode);
|
||||
error_log("OCS API response: " . substr($response, 0, 500));
|
||||
|
||||
// Если API сработал - парсим ответ и получаем URL редактора
|
||||
if ($httpCode === 200) {
|
||||
$data = json_decode($response, true);
|
||||
if (isset($data['ocs']['data']['url'])) {
|
||||
$editorUrl = $data['ocs']['data']['url'];
|
||||
error_log("Got editor URL from API: " . $editorUrl);
|
||||
header('Location: ' . $nextcloudUrl . $editorUrl);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Если API не сработал - создаём файл НАПРЯМУЮ В S3 и открываем через OnlyOffice!
|
||||
error_log("OCS API failed, creating file directly in S3");
|
||||
|
||||
// Извлекаем S3 путь из Nextcloud пути
|
||||
// /crm/crm2/CRM_Active_Files/... → crm2/CRM_Active_Files/...
|
||||
$s3Path = ltrim($ncPath, '/');
|
||||
|
||||
// S3 credentials
|
||||
$s3Client = new Aws\S3\S3Client([
|
||||
'version' => 'latest',
|
||||
'region' => 'ru-1',
|
||||
'endpoint' => 'https://s3.twcstorage.ru',
|
||||
'use_path_style_endpoint' => true,
|
||||
'credentials' => [
|
||||
'key' => EnvLoader::getRequired('S3_ACCESS_KEY'),
|
||||
'secret' => EnvLoader::getRequired('S3_SECRET_KEY')
|
||||
],
|
||||
'suppress_php_deprecation_warning' => true
|
||||
]);
|
||||
|
||||
$bucket = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
|
||||
|
||||
// Создаём минимальный пустой файл
|
||||
$emptyContent = createEmptyFile($fileType);
|
||||
|
||||
error_log("Creating file in S3: " . $s3Path . " (" . strlen($emptyContent) . " bytes)");
|
||||
|
||||
// Загружаем файл в S3
|
||||
try {
|
||||
$result = $s3Client->putObject([
|
||||
'Bucket' => $bucket,
|
||||
'Key' => $s3Path,
|
||||
'Body' => $emptyContent,
|
||||
'ContentType' => getContentType($fileType)
|
||||
]);
|
||||
|
||||
error_log("✅ File created in S3!");
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Failed to create file in S3: " . $e->getMessage());
|
||||
die("❌ Не удалось создать файл в S3: " . $e->getMessage());
|
||||
}
|
||||
|
||||
// Формируем S3 URL
|
||||
$s3Url = 'https://s3.twcstorage.ru/' . $bucket . '/' . $s3Path;
|
||||
|
||||
error_log("S3 URL: " . $s3Url);
|
||||
|
||||
// Публикуем событие в Redis для индексации Nextcloud
|
||||
try {
|
||||
// Используем Predis (установлен через composer)
|
||||
$redis = new Predis\Client([
|
||||
'scheme' => 'tcp',
|
||||
'host' => '147.45.146.17',
|
||||
'port' => 6379,
|
||||
'password' => 'CRM_Redis_Pass_2025_Secure!'
|
||||
]);
|
||||
|
||||
$event = json_encode([
|
||||
'type' => 'file_created',
|
||||
'source' => 'crm_create_file',
|
||||
'path' => $s3Path,
|
||||
'timestamp' => time()
|
||||
]);
|
||||
|
||||
$redis->publish('crm:file:events', $event);
|
||||
error_log("✅ Published event to Redis");
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Redis publish failed: " . $e->getMessage());
|
||||
}
|
||||
|
||||
// Открываем файл НАПРЯМУЮ через OnlyOffice (быстро!)
|
||||
$redirectUrl = '/crm_extensions/file_storage/api/open_file_v2.php?recordId=' . urlencode($recordId) . '&fileName=' . urlencode($s3Url);
|
||||
|
||||
error_log("Redirecting to OnlyOffice: " . $redirectUrl);
|
||||
|
||||
// Редирект
|
||||
header('Location: ' . $redirectUrl);
|
||||
exit;
|
||||
|
||||
/**
|
||||
* Создаёт минимальное пустое содержимое для Office файла
|
||||
*/
|
||||
function createEmptyFile($fileType) {
|
||||
// Используем готовые минимальные шаблоны
|
||||
$templatePath = __DIR__ . '/../templates/empty.' . $fileType;
|
||||
|
||||
if (file_exists($templatePath)) {
|
||||
$content = file_get_contents($templatePath);
|
||||
error_log("Using template: " . $templatePath . " (" . strlen($content) . " bytes)");
|
||||
return $content;
|
||||
}
|
||||
|
||||
error_log("Template not found: " . $templatePath);
|
||||
|
||||
// Fallback: пустая строка (не будет работать, но хотя бы не упадёт)
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет Content-Type для файла
|
||||
*/
|
||||
function getContentType($fileType) {
|
||||
$types = [
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'doc' => 'application/msword',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'ppt' => 'application/vnd.ms-powerpoint'
|
||||
];
|
||||
return $types[$fileType] ?? 'application/octet-stream';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
97
crm_extensions/file_storage/api/nextcloud_open.php
Normal file
97
crm_extensions/file_storage/api/nextcloud_open.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/**
|
||||
* Открытие файла через Nextcloud + OnlyOffice
|
||||
* Для сравнения с прямым OnlyOffice
|
||||
*/
|
||||
|
||||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/shared/EnvLoader.php';
|
||||
EnvLoader::load('/var/www/fastuser/data/www/crm.clientright.ru/crm_extensions/.env');
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
$fileName = isset($_GET['fileName']) ? $_GET['fileName'] : '';
|
||||
$recordId = isset($_GET['recordId']) ? $_GET['recordId'] : '';
|
||||
|
||||
if (empty($fileName)) {
|
||||
die("❌ fileName не указан");
|
||||
}
|
||||
|
||||
// Извлекаем S3 путь
|
||||
$s3Path = '';
|
||||
if (strpos($fileName, 'http') === 0) {
|
||||
$fileName = urldecode($fileName);
|
||||
$bucketId = 'f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c';
|
||||
$pos = strpos($fileName, $bucketId . '/');
|
||||
if ($pos !== false) {
|
||||
$s3Path = substr($fileName, $pos + strlen($bucketId) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($s3Path)) {
|
||||
die("❌ Не удалось извлечь путь из URL");
|
||||
}
|
||||
|
||||
// Nextcloud credentials
|
||||
$nextcloudUrl = 'https://office.clientright.ru:8443';
|
||||
$username = 'admin';
|
||||
$password = 'office';
|
||||
|
||||
// Формируем WebDAV путь
|
||||
$ncPath = '/crm/' . $s3Path;
|
||||
$webdavUrl = $nextcloudUrl . '/remote.php/dav/files/' . $username . $ncPath;
|
||||
|
||||
error_log("=== NEXTCLOUD OPEN ===");
|
||||
error_log("S3 Path: " . $s3Path);
|
||||
error_log("Nextcloud WebDAV: " . $webdavUrl);
|
||||
|
||||
// Получаем fileId через PROPFIND
|
||||
$ch = curl_init($webdavUrl);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PROPFIND');
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Depth: 0',
|
||||
'Content-Type: application/xml'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, '<?xml version="1.0"?>
|
||||
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
|
||||
<d:prop>
|
||||
<oc:fileid/>
|
||||
</d:prop>
|
||||
</d:propfind>');
|
||||
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
error_log("PROPFIND HTTP код: " . $httpCode);
|
||||
|
||||
if ($httpCode !== 207) {
|
||||
die("❌ Файл не найден в Nextcloud (HTTP $httpCode). Возможно, он не проиндексирован.");
|
||||
}
|
||||
|
||||
// Извлекаем fileId из XML
|
||||
preg_match('/<oc:fileid>(\d+)<\/oc:fileid>/', $response, $matches);
|
||||
if (!isset($matches[1])) {
|
||||
die("❌ Не удалось получить fileId");
|
||||
}
|
||||
|
||||
$fileId = $matches[1];
|
||||
error_log("Получен fileId: " . $fileId);
|
||||
|
||||
// Извлекаем директорию из пути
|
||||
$dirPath = dirname($ncPath);
|
||||
|
||||
// Формируем URL для открытия в Nextcloud
|
||||
// Nextcloud автоматически откроет OnlyOffice для редактирования
|
||||
$redirectUrl = $nextcloudUrl . '/apps/files/files/' . $fileId . '?dir=' . urlencode($dirPath) . '&openfile=true';
|
||||
|
||||
error_log("Redirect to: " . $redirectUrl);
|
||||
|
||||
// Редирект в Nextcloud
|
||||
header('Location: ' . $redirectUrl);
|
||||
exit;
|
||||
?>
|
||||
|
||||
39
crm_extensions/file_storage/create_templates.php
Normal file
39
crm_extensions/file_storage/create_templates.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Создание минимальных пустых шаблонов Office файлов
|
||||
*/
|
||||
|
||||
require_once '/var/www/fastuser/data/www/crm.clientright.ru/vendor/autoload.php';
|
||||
|
||||
use PhpOffice\PhpWord\PhpWord;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpPresentation\PhpPresentation;
|
||||
|
||||
$templatesDir = __DIR__ . '/templates/';
|
||||
|
||||
// Создаём Word документ
|
||||
$phpWord = new PhpWord();
|
||||
$section = $phpWord->addSection();
|
||||
$section->addText('');
|
||||
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
|
||||
$objWriter->save($templatesDir . 'empty.docx');
|
||||
echo "✅ Created empty.docx\n";
|
||||
|
||||
// Создаём Excel таблицу
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setCellValue('A1', '');
|
||||
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
|
||||
$writer->save($templatesDir . 'empty.xlsx');
|
||||
echo "✅ Created empty.xlsx\n";
|
||||
|
||||
// Создаём PowerPoint презентацию
|
||||
$presentation = new PhpPresentation();
|
||||
$slide = $presentation->getActiveSlide();
|
||||
$writer = \PhpOffice\PhpPresentation\IOFactory::createWriter($presentation, 'PowerPoint2007');
|
||||
$writer->save($templatesDir . 'empty.pptx');
|
||||
echo "✅ Created empty.pptx\n";
|
||||
|
||||
echo "\n✅ Все шаблоны созданы!\n";
|
||||
?>
|
||||
|
||||
@@ -121,3 +121,4 @@ process.on('SIGTERM', () => {
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
|
||||
|
||||
BIN
crm_extensions/file_storage/templates/empty.docx
Normal file
BIN
crm_extensions/file_storage/templates/empty.docx
Normal file
Binary file not shown.
BIN
crm_extensions/file_storage/templates/empty.pptx
Normal file
BIN
crm_extensions/file_storage/templates/empty.pptx
Normal file
Binary file not shown.
BIN
crm_extensions/file_storage/templates/empty.xlsx
Normal file
BIN
crm_extensions/file_storage/templates/empty.xlsx
Normal file
Binary file not shown.
@@ -22,8 +22,8 @@ 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/${encodedFolderName}`;
|
||||
// URL для папки проекта в Nextcloud External Storage (со структурой Project/)
|
||||
const folderUrl = `${nextcloudUrl}/apps/files/?dir=/crm/crm2/CRM_Active_Files/Documents/Project/${encodedFolderName}`;
|
||||
|
||||
console.log('🔗 Folder URL:', folderUrl);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user