pdf filename
This commit is contained in:
@@ -37,12 +37,16 @@ use Espo\Core\Formula\ArgumentList;
|
||||
use Espo\Core\Formula\Exceptions\Error;
|
||||
use Espo\Core\Formula\Functions\BaseFunction;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Pdf\Params;
|
||||
use Espo\Core\Di;
|
||||
|
||||
use Espo\Tools\Pdf\Result;
|
||||
use Espo\Tools\Pdf\Service;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class GenerateType extends BaseFunction implements
|
||||
Di\EntityManagerAware,
|
||||
Di\InjectableFactoryAware,
|
||||
@@ -92,38 +96,29 @@ class GenerateType extends BaseFunction implements
|
||||
}
|
||||
|
||||
if (!$entity) {
|
||||
$this->log("Record {$entityType} {$id} does not exist.");
|
||||
$this->log("Record $entityType $id does not exist.");
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
/** @var ?Template $template */
|
||||
$template = $em->getEntityById(Template::ENTITY_TYPE, $templateId);
|
||||
$template = $em->getRDBRepositoryByClass(Template::class)->getById($templateId);
|
||||
|
||||
if (!$template) {
|
||||
$this->log("Template {$templateId} does not exist.");
|
||||
$this->log("Template $templateId does not exist.");
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if ($fileName) {
|
||||
if (!str_ends_with($fileName, '.pdf')) {
|
||||
$fileName .= '.pdf';
|
||||
}
|
||||
} else {
|
||||
$fileName = Util::sanitizeFileName($entity->get(Field::NAME)) . '.pdf';
|
||||
}
|
||||
|
||||
$params = Params::create()->withAcl(false);
|
||||
|
||||
try {
|
||||
$service = $this->injectableFactory->create(Service::class);
|
||||
$service = $this->injectableFactory->create(Service::class);
|
||||
|
||||
$contents = $service->generate(
|
||||
$entity->getEntityType(),
|
||||
$entity->getId(),
|
||||
$template->getId(),
|
||||
$params
|
||||
try {
|
||||
$result = $service->generate(
|
||||
entityType: $entity->getEntityType(),
|
||||
id: $entity->getId(),
|
||||
templateId: $template->getId(),
|
||||
params: $params,
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$this->log("Error while generating. Message: " . $e->getMessage() . ".", 'error');
|
||||
@@ -131,20 +126,41 @@ class GenerateType extends BaseFunction implements
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
/** @var Attachment $attachment */
|
||||
$attachment = $em->getNewEntity(Attachment::ENTITY_TYPE);
|
||||
$fileName = $this->prepareFilename($fileName, $result, $entity);
|
||||
|
||||
$attachment = $em->getRDBRepositoryByClass(Attachment::class)->getNew();
|
||||
|
||||
$attachment
|
||||
->setName($fileName)
|
||||
->setType('application/pdf')
|
||||
->setSize($contents->getStream()->getSize())
|
||||
->setSize($result->getStream()->getSize())
|
||||
->setRelated(LinkParent::create($entityType, $id))
|
||||
->setRole(Attachment::ROLE_ATTACHMENT);
|
||||
|
||||
$em->saveEntity($attachment);
|
||||
|
||||
$this->fileStorageManager->putStream($attachment, $contents->getStream());
|
||||
$this->fileStorageManager->putStream($attachment, $result->getStream());
|
||||
|
||||
return $attachment->getId();
|
||||
}
|
||||
|
||||
private function composeFilename(Entity $entity): string
|
||||
{
|
||||
$defaultName = $entity->get(Field::NAME) ?? $entity->getId();
|
||||
|
||||
return Util::sanitizeFileName($defaultName) . '.pdf';
|
||||
}
|
||||
|
||||
private function prepareFilename(mixed $fileName, Result $result, Entity $entity): string
|
||||
{
|
||||
if ($fileName) {
|
||||
if (!str_ends_with($fileName, '.pdf')) {
|
||||
$fileName .= '.pdf';
|
||||
}
|
||||
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
return $result->getFilename() ?? $this->composeFilename($entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +53,9 @@ class Template extends Entity
|
||||
{
|
||||
return $this->get('status') === self::STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
public function getFilename(): ?string
|
||||
{
|
||||
return $this->get('filename');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ use Espo\Core\Name\Field;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Entities\Template;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Tools\Pdf\Service;
|
||||
|
||||
class Pdf implements EntryPoint
|
||||
@@ -69,11 +70,9 @@ class Pdf implements EntryPoint
|
||||
throw new NotFound("Template not found.");
|
||||
}
|
||||
|
||||
$contents = $this->service->generate($entityType, $entityId, $templateId);
|
||||
$result = $this->service->generate($entityType, $entityId, $templateId);
|
||||
|
||||
$fileName = Util::sanitizeFileName($entity->get(Field::NAME) ?? 'unnamed');
|
||||
|
||||
$fileName = $fileName . '.pdf';
|
||||
$fileName = $result->getFilename() ?? $this->composeFileName($entity);
|
||||
|
||||
$response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
@@ -83,9 +82,16 @@ class Pdf implements EntryPoint
|
||||
->setHeader('Content-Disposition', 'inline; filename="' . basename($fileName) . '"');
|
||||
|
||||
if (!$request->getServerParam('HTTP_ACCEPT_ENCODING')) {
|
||||
$response->setHeader('Content-Length', (string) $contents->getStream()->getSize());
|
||||
$response->setHeader('Content-Length', (string) $result->getStream()->getSize());
|
||||
}
|
||||
|
||||
$response->writeBody($contents->getStream());
|
||||
$response->writeBody($result->getStream());
|
||||
}
|
||||
|
||||
private function composeFileName(Entity $entity): string
|
||||
{
|
||||
$defaultName = $entity->get(Field::NAME) ?? $entity->getId();
|
||||
|
||||
return Util::sanitizeFileName($defaultName) . '.pdf';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
"fontFace": "Font",
|
||||
"title": "Title",
|
||||
"style": "Style",
|
||||
"status": "Status"
|
||||
"status": "Status",
|
||||
"filename": "Filename"
|
||||
},
|
||||
"links": {
|
||||
},
|
||||
@@ -48,6 +49,7 @@
|
||||
"fontFace": {}
|
||||
},
|
||||
"tooltips": {
|
||||
"filename": "An optional filename. Entity attributes can be used as placeholders.\n\nAdditional placeholder:\n- `{{today}}`\n\nExample:\n`Document_{{today}}_{{name}}`",
|
||||
"footer": "Use {pageNumber} to print page number.",
|
||||
"variables": "Copy-paste needed placeholder to Header, Body or Footer."
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@
|
||||
},
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "title"}, false],
|
||||
[{"name": "title"}, {"name": "filename"}],
|
||||
[{"name":"printHeader"}, {"name":"headerPosition"}],
|
||||
[{"name":"header","fullWidth":true}],
|
||||
[{"name":"header"}],
|
||||
[{"name":"printFooter"}, {"name":"footerPosition"}],
|
||||
[{"name":"footer","fullWidth":true}],
|
||||
[{"name":"footer"}],
|
||||
[{"name": "style"}]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -120,6 +120,11 @@
|
||||
},
|
||||
"title": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"filename": {
|
||||
"type": "varchar",
|
||||
"maxLength": 150,
|
||||
"tooltip": true
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Pdf;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class Result implements Contents
|
||||
{
|
||||
public function __construct(
|
||||
private Contents $contents,
|
||||
private ?string $filename
|
||||
) {}
|
||||
|
||||
public function getStream(): StreamInterface
|
||||
{
|
||||
return $this->contents->getStream();
|
||||
}
|
||||
|
||||
public function getString(): string
|
||||
{
|
||||
return $this->contents->getString();
|
||||
}
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
return $this->contents->getLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* If null, it means that the template does not define the file name.
|
||||
*/
|
||||
public function getFilename(): ?string
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,11 @@ use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Record\ServiceContainer;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\DateTime;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Core\Utils\Util;
|
||||
use Espo\Entities\Template as TemplateEntity;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Tools\Pdf\Data\DataLoaderManager;
|
||||
|
||||
@@ -52,6 +55,7 @@ class Service
|
||||
private Config $config,
|
||||
private Builder $builder,
|
||||
private Metadata $metadata,
|
||||
private DateTime $dateTime,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -72,8 +76,8 @@ class Service
|
||||
string $id,
|
||||
string $templateId,
|
||||
?Params $params = null,
|
||||
?Data $data = null
|
||||
): Contents {
|
||||
?Data $data = null,
|
||||
): Result {
|
||||
|
||||
$params = $params ?? Params::create()->withAcl(true);
|
||||
|
||||
@@ -130,6 +134,44 @@ class Service
|
||||
->setEngine($engine)
|
||||
->build();
|
||||
|
||||
return $printer->printEntity($entity, $params, $data);
|
||||
$contents = $printer->printEntity($entity, $params, $data);
|
||||
|
||||
$filename = $this->prepareFilename($entity, $template, $params);
|
||||
|
||||
return new Result($contents, $filename);
|
||||
}
|
||||
|
||||
private function prepareFilename(Entity $entity, TemplateEntity $template, Params $params): ?string
|
||||
{
|
||||
$filename = $template->getFilename() ?? null;
|
||||
|
||||
if (!$filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$forbiddenAttributeList = $this->acl->getScopeForbiddenAttributeList($entity->getEntityType());
|
||||
|
||||
foreach ($entity->getAttributeList() as $attribute) {
|
||||
$value = $entity->get($attribute);
|
||||
|
||||
if (!is_scalar($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($params->applyAcl() && in_array($attribute, $forbiddenAttributeList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filename = str_replace('{{' . $attribute . '}}', (string) $value, $filename);
|
||||
}
|
||||
|
||||
$today = $this->dateTime->getTodayString(null, DateTime::SYSTEM_DATE_FORMAT);
|
||||
$filename = str_replace('{{today}}', $today, $filename);
|
||||
|
||||
if (!str_ends_with(strtolower($filename), '.pdf')) {
|
||||
$filename .= '.pdf';
|
||||
}
|
||||
|
||||
return Util::sanitizeFileName($filename) ?: null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user