173 lines
6.1 KiB
PHP
173 lines
6.1 KiB
PHP
<?php
|
|
/**
|
|
* Merge DOCX template: load from S3, substitute {{field}} and {{ModuleName__field}}, output DOCX.
|
|
*/
|
|
|
|
class OnlyOfficeTemplates_MergeService
|
|
{
|
|
protected $s3;
|
|
protected $config;
|
|
|
|
public function __construct(OnlyOfficeTemplates_S3Helper $s3, array $config)
|
|
{
|
|
$this->s3 = $s3;
|
|
$this->config = $config;
|
|
}
|
|
|
|
/**
|
|
* Build placeholder map for a record: current module fields + related (Account, Contact, etc.).
|
|
*
|
|
* @param string $module
|
|
* @param int $recordId
|
|
* @return array [ 'fieldname' => value, 'Account__accountname' => value, ... ]
|
|
*/
|
|
public function buildPlaceholders($module, $recordId)
|
|
{
|
|
$adb = PearDatabase::getInstance();
|
|
$focus = CRMEntity::getInstance($module);
|
|
$focus->id = $recordId;
|
|
$focus->retrieve_entity_info($recordId, $module);
|
|
$fields = $focus->column_fields;
|
|
|
|
$map = [];
|
|
foreach ($fields as $k => $v) {
|
|
if ($v === null || $v === '') {
|
|
$v = '';
|
|
}
|
|
$map[$k] = is_string($v) ? $v : (string)$v;
|
|
}
|
|
$map = array_merge($map, $this->getRelatedModuleFields($module, $recordId, $focus));
|
|
return $map;
|
|
}
|
|
|
|
/**
|
|
* Get related entity fields (Account, Contact, etc.) for placeholder {{ModuleName__fieldname}}.
|
|
*/
|
|
protected function getRelatedModuleFields($module, $recordId, CRMEntity $focus)
|
|
{
|
|
$map = [];
|
|
$relFields = $this->getRelationFieldNames($module);
|
|
foreach ($relFields as $relModule => $fieldName) {
|
|
$relId = isset($focus->column_fields[$fieldName]) ? $focus->column_fields[$fieldName] : null;
|
|
if (empty($relId)) {
|
|
continue;
|
|
}
|
|
$relFocus = CRMEntity::getInstance($relModule);
|
|
$relFocus->retrieve_entity_info($relId, $relModule);
|
|
foreach ($relFocus->column_fields as $k => $v) {
|
|
if ($v === null || $v === '') {
|
|
$v = '';
|
|
}
|
|
$map[$relModule . '__' . $k] = is_string($v) ? $v : (string)$v;
|
|
}
|
|
}
|
|
return $map;
|
|
}
|
|
|
|
/**
|
|
* Common relation field names per module (account_id, contact_id, related_to, parent_id, etc.).
|
|
*/
|
|
/** @return array [ 'RelatedModule' => 'local_field_name', ... ] */
|
|
protected function getRelationFieldNames($module)
|
|
{
|
|
$known = [
|
|
'Project' => ['Accounts' => 'account_id', 'Contacts' => 'contact_id'],
|
|
'Contacts' => ['Accounts' => 'account_id'],
|
|
'Leads' => ['Accounts' => 'account_id'],
|
|
'Potentials' => ['Accounts' => 'related_to', 'Contacts' => 'contact_id'],
|
|
'Invoice' => ['Accounts' => 'account_id', 'Contacts' => 'contact_id'],
|
|
'Quotes' => ['Accounts' => 'account_id', 'Contacts' => 'contact_id'],
|
|
'SalesOrder' => ['Accounts' => 'account_id', 'Contacts' => 'contact_id'],
|
|
'PurchaseOrder' => ['Vendors' => 'vendor_id', 'Contacts' => 'contact_id'],
|
|
'HelpDesk' => ['Accounts' => 'parent_id', 'Contacts' => 'contact_id'],
|
|
'Accounts' => [],
|
|
];
|
|
if (isset($known[$module])) {
|
|
return $known[$module];
|
|
}
|
|
$out = [];
|
|
if (in_array($module, ['Contacts', 'Leads', 'Potentials', 'Invoice', 'Quotes', 'SalesOrder'])) {
|
|
$out['Accounts'] = 'account_id';
|
|
if ($module !== 'Accounts') {
|
|
$out['Contacts'] = 'contact_id';
|
|
}
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Merge template: download from S3, replace placeholders, save to temp file.
|
|
*
|
|
* @param string $s3Key template S3 key
|
|
* @param array $placeholders [ 'field' => 'value', 'Account__name' => 'value' ]
|
|
* @return string path to merged DOCX file
|
|
*/
|
|
public function mergeToFile($s3Key, array $placeholders)
|
|
{
|
|
$path = dirname(dirname(dirname(__DIR__)));
|
|
if (!class_exists('PhpOffice\PhpWord\IOFactory')) {
|
|
if (is_file($path . '/vendor/autoload.php')) {
|
|
require_once $path . '/vendor/autoload.php';
|
|
}
|
|
}
|
|
$tempDir = sys_get_temp_dir() . '/oot_' . uniqid();
|
|
if (!is_dir($tempDir)) {
|
|
mkdir($tempDir, 0755, true);
|
|
}
|
|
$templatePath = $tempDir . '/template.docx';
|
|
$this->s3->downloadToFile($s3Key, $templatePath);
|
|
|
|
$phpWord = \PhpOffice\PhpWord\IOFactory::load($templatePath);
|
|
$this->replaceInPhpWord($phpWord, $placeholders);
|
|
$outPath = $tempDir . '/merged.docx';
|
|
$writer = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
|
|
$writer->save($outPath);
|
|
@unlink($templatePath);
|
|
return $outPath;
|
|
}
|
|
|
|
/**
|
|
* Replace {{placeholder}} in all text elements.
|
|
*/
|
|
protected function replaceInPhpWord(\PhpOffice\PhpWord\PhpWord $phpWord, array $placeholders)
|
|
{
|
|
foreach ($phpWord->getSections() as $section) {
|
|
foreach ($section->getElements() as $element) {
|
|
$this->replaceInElement($element, $placeholders);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function replaceInElement($element, array $placeholders)
|
|
{
|
|
if ($element instanceof \PhpOffice\PhpWord\Element\Text) {
|
|
$text = $element->getText();
|
|
$text = $this->replacePlaceholders($text, $placeholders);
|
|
$element->setText($text);
|
|
return;
|
|
}
|
|
if ($element instanceof \PhpOffice\PhpWord\Element\TextRun) {
|
|
foreach ($element->getElements() as $el) {
|
|
$this->replaceInElement($el, $placeholders);
|
|
}
|
|
return;
|
|
}
|
|
if ($element instanceof \PhpOffice\PhpWord\Element\TextBreak) {
|
|
return;
|
|
}
|
|
if (method_exists($element, 'getElements')) {
|
|
foreach ($element->getElements() as $el) {
|
|
$this->replaceInElement($el, $placeholders);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function replacePlaceholders($text, array $placeholders)
|
|
{
|
|
foreach ($placeholders as $key => $value) {
|
|
$text = str_replace('{{' . $key . '}}', $value, $text);
|
|
}
|
|
return $text;
|
|
}
|
|
}
|