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'; }