Files
crm.clientright.ru/crm_extensions/file_storage/api/create_from_template.php

252 lines
8.9 KiB
PHP
Raw Normal View History

<?php
/**
* Создание документа из шаблона Nextcloud
*
* Алгоритм:
* 1. Получаем шаблон из Nextcloud через WebDAV
* 2. Заполняем переменные через PHPWord
* 3. Сохраняем готовый документ в папку проекта
* 4. Открываем в OnlyOffice
*/
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'] ?? '';
$templateName = $_GET['templateName'] ?? ''; // Имя шаблона (например, "pretenziya.docx")
$variables = json_decode($_GET['variables'] ?? '{}', true); // Переменные для заполнения
if (empty($module) || empty($recordId) || empty($fileName) || empty($templateName)) {
die(json_encode(['success' => false, 'error' => 'Не указаны обязательные параметры']));
}
// 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;
// ONLYOFFICE хранит шаблоны в папке /Templates/ в корне пользователя
// Путь к шаблону в Nextcloud
$templatePath = "/Templates/{$templateName}";
$templateWebDAVUrl = $nextcloudUrl . '/remote.php/dav/files/' . $username . $templatePath;
// Путь к готовому документу
$fileType = pathinfo($templateName, PATHINFO_EXTENSION);
$ncPath = "/crm/crm2/CRM_Active_Files/Documents/{$moduleFolder}/{$folderName}/{$fileName}.{$fileType}";
error_log("=== CREATE FROM TEMPLATE ===");
error_log("Template: {$templateName}");
error_log("Variables: " . json_encode($variables, JSON_UNESCAPED_UNICODE));
error_log("Output path: {$ncPath}");
// 1. СКАЧИВАЕМ ШАБЛОН ИЗ NEXTCLOUD
$ch = curl_init($templateWebDAVUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$templateContent = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || empty($templateContent)) {
die(json_encode(['success' => false, 'error' => "Шаблон не найден: {$templateName}"]));
}
error_log("✅ Template downloaded (" . strlen($templateContent) . " bytes)");
// 2. ЗАПОЛНЯЕМ ПЕРЕМЕННЫЕ В ШАБЛОНЕ
$filledContent = fillTemplateVariables($templateContent, $variables, $fileType);
// 3. СОХРАНЯЕМ В S3
$s3Path = ltrim($ncPath, '/');
$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';
try {
$result = $s3Client->putObject([
'Bucket' => $bucket,
'Key' => $s3Path,
'Body' => $filledContent,
'ContentType' => getContentType($fileType)
]);
error_log("✅ File saved to S3: {$s3Path}");
} catch (Exception $e) {
error_log("Failed to save to S3: " . $e->getMessage());
die(json_encode(['success' => false, 'error' => "Ошибка сохранения: " . $e->getMessage()]));
}
// 4. ПУБЛИКУЕМ СОБЫТИЕ В REDIS
try {
$redis = new Predis\Client([
'scheme' => 'tcp',
'host' => 'crm.clientright.ru',
'port' => 6379,
'password' => 'CRM_Redis_Pass_2025_Secure!'
]);
$event = json_encode([
'type' => 'file_created',
'source' => 'crm_template',
'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());
}
// 5. ОТКРЫВАЕМ В ONLYOFFICE
$s3Url = 'https://s3.twcstorage.ru/' . $bucket . '/' . $s3Path;
$redirectUrl = '/crm_extensions/file_storage/api/open_file_v2.php?recordId=' . urlencode($recordId) . '&fileName=' . urlencode($s3Url);
header('Location: ' . $redirectUrl);
exit;
/**
* Заполняет переменные в шаблоне
*
* Поддерживает два формата:
* 1. Простая замена {VARIABLE_NAME} значение
* 2. PHPWord для сложных документов
*/
function fillTemplateVariables($content, $variables, $fileType) {
if ($fileType === 'docx') {
// Используем PHPWord для DOCX
return fillDocxTemplate($content, $variables);
} else {
// Для других форматов - простая замена
return fillSimpleTemplate($content, $variables);
}
}
/**
* Заполнение DOCX через PHPWord
*/
function fillDocxTemplate($content, $variables) {
// Сохраняем во временный файл
$tempFile = tempnam(sys_get_temp_dir(), 'template_') . '.docx';
file_put_contents($tempFile, $content);
try {
$phpWord = \PhpOffice\PhpWord\IOFactory::load($tempFile);
// Заменяем переменные во всех секциях
foreach ($phpWord->getSections() as $section) {
foreach ($section->getElements() as $element) {
if ($element instanceof \PhpOffice\PhpWord\Element\Text) {
$text = $element->getText();
$text = replaceVariables($text, $variables);
$element->setText($text);
} elseif ($element instanceof \PhpOffice\PhpWord\Element\TextRun) {
foreach ($element->getElements() as $textElement) {
if ($textElement instanceof \PhpOffice\PhpWord\Element\Text) {
$text = $textElement->getText();
$text = replaceVariables($text, $variables);
$textElement->setText($text);
}
}
}
}
}
// Сохраняем результат
$writer = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
$outputFile = tempnam(sys_get_temp_dir(), 'output_') . '.docx';
$writer->save($outputFile);
$result = file_get_contents($outputFile);
// Удаляем временные файлы
unlink($tempFile);
unlink($outputFile);
return $result;
} catch (Exception $e) {
error_log("PHPWord error: " . $e->getMessage());
// Fallback на простую замену
unlink($tempFile);
return fillSimpleTemplate($content, $variables);
}
}
/**
* Простая замена переменных {VAR} значение
*/
function fillSimpleTemplate($content, $variables) {
foreach ($variables as $key => $value) {
$content = str_replace('{' . strtoupper($key) . '}', $value, $content);
$content = str_replace('{{' . strtoupper($key) . '}}', $value, $content);
}
return $content;
}
/**
* Универсальная замена переменных
*/
function replaceVariables($text, $variables) {
foreach ($variables as $key => $value) {
$text = str_replace('{' . strtoupper($key) . '}', $value, $text);
$text = str_replace('{{' . strtoupper($key) . '}}', $value, $text);
}
return $text;
}
/**
* Определяет 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',
];
return $types[$fileType] ?? 'application/octet-stream';
}