pdf refactoring and fixes
This commit is contained in:
@@ -29,18 +29,32 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\{
|
||||
Exceptions\Forbidden,
|
||||
Exceptions\BadRequest,
|
||||
};
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\Base,
|
||||
Api\Request,
|
||||
Acl,
|
||||
};
|
||||
|
||||
use Espo\Services\Pdf as Service;
|
||||
|
||||
use StdClass;
|
||||
|
||||
class Pdf extends Base
|
||||
class Pdf
|
||||
{
|
||||
private $service;
|
||||
|
||||
private $acl;
|
||||
|
||||
public function __construct(Service $service, Acl $acl)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->acl = $acl;
|
||||
}
|
||||
|
||||
public function postActionMassPrint(Request $request): StdClass
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
@@ -66,8 +80,7 @@ class Pdf extends Base
|
||||
}
|
||||
|
||||
return (object) [
|
||||
'id' => $this->getServiceFactory()
|
||||
->create('Pdf')
|
||||
'id' => $this->service
|
||||
->massGenerate(
|
||||
$data->entityType,
|
||||
$data->idList,
|
||||
|
||||
@@ -32,12 +32,17 @@ namespace Espo\Core\Formula\Functions\ExtGroup\PdfGroup;
|
||||
use Espo\Core\Formula\{
|
||||
Functions\BaseFunction,
|
||||
ArgumentList,
|
||||
Exceptions\Error,
|
||||
};
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
|
||||
use Espo\Tools\Pdf\Data;
|
||||
|
||||
use Espo\Core\Di;
|
||||
|
||||
use Exception;
|
||||
|
||||
class GenerateType extends BaseFunction implements
|
||||
Di\EntityManagerAware,
|
||||
Di\ServiceFactoryAware
|
||||
@@ -61,12 +66,15 @@ class GenerateType extends BaseFunction implements
|
||||
if (!$entityType || !is_string($entityType)) {
|
||||
$this->throwBadArgumentType(1, 'string');
|
||||
}
|
||||
|
||||
if (!$id || !is_string($id)) {
|
||||
$this->throwBadArgumentType(2, 'string');
|
||||
}
|
||||
|
||||
if (!$templateId || !is_string($templateId)) {
|
||||
$this->throwBadArgumentType(3, 'string');
|
||||
}
|
||||
|
||||
if ($fileName && !is_string($fileName)) {
|
||||
$this->throwBadArgumentType(4, 'string');
|
||||
}
|
||||
@@ -75,21 +83,25 @@ class GenerateType extends BaseFunction implements
|
||||
|
||||
try {
|
||||
$entity = $em->getEntity($entityType, $id);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->log("Message: " . $e->getMessage() . ".");
|
||||
return null;
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if (!$entity) {
|
||||
$this->log("Record {$entityType} {$id} does not exist.");
|
||||
return null;
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$template = $em->getEntity('Template', $templateId);
|
||||
|
||||
if (!$template) {
|
||||
$this->log("Template {$templateId} does not exist.");
|
||||
return null;
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if ($fileName) {
|
||||
@@ -100,11 +112,17 @@ class GenerateType extends BaseFunction implements
|
||||
$fileName = Util::sanitizeFileName($entity->get('name')) . '.pdf';
|
||||
}
|
||||
|
||||
$data = Data::fromNothing()->withAcl(false);
|
||||
|
||||
try {
|
||||
$contents = $this->serviceFactory->create('Pdf')->buildFromTemplate($entity, $template);
|
||||
} catch (\Exception $e) {
|
||||
$contents = $this->serviceFactory
|
||||
->create('Pdf')
|
||||
->generate($entity, $template, $data);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->log("Error while generating. Message: " . $e->getMessage() . ".", 'error');
|
||||
return null;
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$attachment = $em->createEntity('Attachment', [
|
||||
@@ -116,6 +134,6 @@ class GenerateType extends BaseFunction implements
|
||||
'role' => 'Attachment',
|
||||
]);
|
||||
|
||||
return $attachment->id;
|
||||
return $attachment->getId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,12 @@ use Espo\Core\Utils\DateTime;
|
||||
|
||||
class Factory
|
||||
{
|
||||
protected $injectableFactory;
|
||||
private $injectableFactory;
|
||||
|
||||
protected $dateTime;
|
||||
private $dateTime;
|
||||
|
||||
public function __construct(InjectableFactory $injectableFactory, DateTime $dateTime) {
|
||||
public function __construct(InjectableFactory $injectableFactory, DateTime $dateTime)
|
||||
{
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
@@ -46,6 +47,7 @@ class Factory
|
||||
public function create(bool $skipAcl = false, ?string $timezone = null): Htmlizer
|
||||
{
|
||||
$with = [];
|
||||
|
||||
if ($skipAcl) {
|
||||
$with['acl'] = null;
|
||||
}
|
||||
@@ -53,6 +55,7 @@ class Factory
|
||||
if ($timezone) {
|
||||
$dateTime = clone($this->dateTime);
|
||||
$dateTime->setTimezone($timezone);
|
||||
|
||||
$with['dateTime'] = $dateTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -133,10 +133,6 @@ class Htmlizer
|
||||
|
||||
$data = get_object_vars($entity->getValueMap());
|
||||
|
||||
//$data = $entity->toArray();
|
||||
|
||||
//print_r($data);
|
||||
|
||||
$attributeList = $entity->getAttributeList();
|
||||
|
||||
$forbiddenAttributeList = [];
|
||||
|
||||
@@ -34,21 +34,23 @@ use Espo\Core\{
|
||||
Exceptions\BadRequest,
|
||||
EntryPoint\EntryPoint,
|
||||
ORM\EntityManager,
|
||||
ServiceFactory,
|
||||
Api\Request,
|
||||
Api\Response,
|
||||
Utils\Util,
|
||||
};
|
||||
|
||||
use Espo\Services\Pdf as Service;
|
||||
|
||||
class Pdf implements EntryPoint
|
||||
{
|
||||
protected $entityManager;
|
||||
private $entityManager;
|
||||
|
||||
protected $serviceFactory;
|
||||
private $service;
|
||||
|
||||
public function __construct(EntityManager $entityManager, ServiceFactory $serviceFactory)
|
||||
public function __construct(EntityManager $entityManager, Service $service)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
public function run(Request $request, Response $response): void
|
||||
@@ -68,8 +70,26 @@ class Pdf implements EntryPoint
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$this->serviceFactory->create('Pdf')->buildFromTemplate($entity, $template, true);
|
||||
$contents = $this->service->generate($entity, $template);
|
||||
|
||||
exit;
|
||||
$fileName = Util::sanitizeFileName(
|
||||
$entity->get('name') ?? 'unnamed'
|
||||
);
|
||||
|
||||
$fileName = $fileName . '.pdf';
|
||||
|
||||
$response
|
||||
->setHeader('Content-Type', 'application/pdf')
|
||||
->setHeader('Cache-Control', 'private, must-revalidate, post-check=0, pre-check=0, max-age=1')
|
||||
->setHeader('Pragma', 'public')
|
||||
->setHeader('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT')
|
||||
->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT')
|
||||
->setHeader('Content', 'inline; filename="' . basename($fileName) . '"');
|
||||
|
||||
if (!$request->getServerParam('HTTP_ACCEPT_ENCODING')) {
|
||||
$response->setHeader('Content-Length', $contents->getLength());
|
||||
}
|
||||
|
||||
$response->writeBody($contents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,18 +36,16 @@ use Espo\Core\Exceptions\{
|
||||
};
|
||||
|
||||
use Espo\Core\{
|
||||
ServiceFactory,
|
||||
Acl,
|
||||
Acl\Table,
|
||||
Utils\Config,
|
||||
Utils\Metadata,
|
||||
Utils\Language,
|
||||
Utils\Util,
|
||||
Htmlizer\Htmlizer,
|
||||
Htmlizer\Factory as HtmlizerFactory,
|
||||
ORM\EntityManager,
|
||||
ORM\Entity,
|
||||
Pdf\Tcpdf,
|
||||
Select\SelectBuilderFactory,
|
||||
Record\ServiceContainer,
|
||||
};
|
||||
|
||||
use Espo\{
|
||||
@@ -57,60 +55,57 @@ use Espo\{
|
||||
Tools\Pdf\Data,
|
||||
};
|
||||
|
||||
use Espo\ORM\{
|
||||
QueryParams\Select,
|
||||
};
|
||||
use Espo\Entities\Template;
|
||||
|
||||
use DateTime;
|
||||
use StdClass;
|
||||
|
||||
class Pdf
|
||||
{
|
||||
protected const DEFAULT_ENGINE = 'Tcpdf';
|
||||
|
||||
protected $removeMassFilePeriod = '1 hour';
|
||||
|
||||
protected $config;
|
||||
private $config;
|
||||
|
||||
protected $serviceFactory;
|
||||
private $metadata;
|
||||
|
||||
protected $metadata;
|
||||
private $entityManager;
|
||||
|
||||
protected $entityManager;
|
||||
private $acl;
|
||||
|
||||
protected $acl;
|
||||
private $defaultLanguage;
|
||||
|
||||
protected $defaultLanguage;
|
||||
private $selectBuilderFactory;
|
||||
|
||||
protected $htmlizerFactory;
|
||||
private $builder;
|
||||
|
||||
protected $selectBuilderFactory;
|
||||
|
||||
protected $builder;
|
||||
private $serviceContanier;
|
||||
|
||||
public function __construct(
|
||||
Config $config,
|
||||
ServiceFactory $serviceFactory,
|
||||
Metadata $metadata,
|
||||
EntityManager $entityManager,
|
||||
Acl $acl,
|
||||
Language $defaultLanguage,
|
||||
HtmlizerFactory $htmlizerFactory,
|
||||
SelectBuilderFactory $selectBuilderFactory,
|
||||
Builder $builder
|
||||
Builder $builder,
|
||||
ServiceContainer $serviceContanier
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->serviceFactory = $serviceFactory;
|
||||
$this->metadata = $metadata;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->acl = $acl;
|
||||
$this->defaultLanguage = $defaultLanguage;
|
||||
$this->htmlizerFactory = $htmlizerFactory;
|
||||
$this->selectBuilderFactory = $selectBuilderFactory;
|
||||
$this->builder = $builder;
|
||||
$this->serviceContanier = $serviceContanier;
|
||||
}
|
||||
|
||||
public function generateMailMerge(
|
||||
string $entityType,
|
||||
iterable $entityList,
|
||||
Entity $template,
|
||||
Template $template,
|
||||
string $name,
|
||||
?string $campaignId = null
|
||||
): string {
|
||||
@@ -121,12 +116,7 @@ class Pdf
|
||||
$collection[] = $entity;
|
||||
}
|
||||
|
||||
if ($this->serviceFactory->checkExists($entityType)) {
|
||||
$service = $this->serviceFactory->create($entityType);
|
||||
}
|
||||
else {
|
||||
$service = $this->serviceFactory->create('Record');
|
||||
}
|
||||
$service = $this->serviceContanier->get($entityType);
|
||||
|
||||
foreach ($entityList as $entity) {
|
||||
$service->loadAdditionalFields($entity);
|
||||
@@ -140,7 +130,7 @@ class Pdf
|
||||
|
||||
$printer = $this->builder
|
||||
->setTemplate($templateWrapper)
|
||||
->setEngine('Tcpdf')
|
||||
->setEngine(self::DEFAULT_ENGINE)
|
||||
->build();
|
||||
|
||||
$contents = $printer->printCollection($collection);
|
||||
@@ -170,11 +160,7 @@ class Pdf
|
||||
bool $checkAcl = false
|
||||
): string {
|
||||
|
||||
if ($this->serviceFactory->checkExists($entityType)) {
|
||||
$service = $this->serviceFactory->create($entityType);
|
||||
} else {
|
||||
$service = $this->serviceFactory->create('Record');
|
||||
}
|
||||
$service = $this->serviceContanier->get($entityType);
|
||||
|
||||
$maxCount = $this->config->get('massPrintPdfMaxCount');
|
||||
|
||||
@@ -224,7 +210,7 @@ class Pdf
|
||||
|
||||
$templateWrapper = new TemplateWrapper($template);
|
||||
|
||||
$engine = $this->config->get('pdfEngine') ?? 'Tcpdf';
|
||||
$engine = $this->config->get('pdfEngine') ?? self::DEFAULT_ENGINE;
|
||||
|
||||
$printer = $this->builder
|
||||
->setTemplate($templateWrapper)
|
||||
@@ -265,7 +251,7 @@ class Pdf
|
||||
return $attachment->id;
|
||||
}
|
||||
|
||||
public function removeMassFileJob($data)
|
||||
public function removeMassFileJob(StdClass $data): void
|
||||
{
|
||||
if (empty($data->id)) {
|
||||
return;
|
||||
@@ -284,20 +270,38 @@ class Pdf
|
||||
$this->entityManager->removeEntity($attachment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate PDF. ACL check is processed if `$data` is null.
|
||||
*/
|
||||
public function generate(Entity $entity, Template $template, ?Data $data = null): string
|
||||
{
|
||||
return $this->buildFromTemplateInternal($entity, $template, false, null, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function buildFromTemplate(
|
||||
Entity $entity,
|
||||
Entity $template,
|
||||
Template $template,
|
||||
bool $displayInline = false,
|
||||
?array $additionalData = null
|
||||
): ?string {
|
||||
|
||||
return $this->buildFromTemplateInternal($entity, $template, $displayInline, $additionalData);
|
||||
}
|
||||
|
||||
private function buildFromTemplateInternal(
|
||||
Entity $entity,
|
||||
Entity $template,
|
||||
bool $displayInline = false,
|
||||
?array $additionalData = null,
|
||||
?Data $data = null
|
||||
): ?string {
|
||||
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
if ($this->serviceFactory->checkExists($entityType)) {
|
||||
$service = $this->serviceFactory->create($entityType);
|
||||
} else {
|
||||
$service = $this->serviceFactory->create('Record');
|
||||
}
|
||||
$service = $this->serviceContanier->get($entityType);
|
||||
|
||||
$service->loadAdditionalFields($entity);
|
||||
|
||||
@@ -309,17 +313,31 @@ class Pdf
|
||||
throw new Error("Not matching entity types.");
|
||||
}
|
||||
|
||||
if (!$this->acl->check($entity, 'read') || !$this->acl->check($template, 'read')) {
|
||||
throw new Forbidden();
|
||||
$applyAcl = true;
|
||||
|
||||
if ($data) {
|
||||
$applyAcl = $data->applyAcl();
|
||||
}
|
||||
|
||||
if ($applyAcl) {
|
||||
if (
|
||||
!$this->acl->check($entity, Table::ACTION_READ) ||
|
||||
!$this->acl->check($template, Table::ACTION_READ)
|
||||
) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
$templateWrapper = new TemplateWrapper($template);
|
||||
|
||||
$data = Data::createFromArray([
|
||||
'additionalTemplateData' => $additionalData,
|
||||
]);
|
||||
if (!$data) {
|
||||
$data = Data
|
||||
::fromNothing()
|
||||
->withAdditionalTemplateData($additionalData ?? [])
|
||||
->withAcl($applyAcl);
|
||||
}
|
||||
|
||||
$engine = $this->config->get('pdfEngine') ?? 'Tcpdf';
|
||||
$engine = $this->config->get('pdfEngine') ?? self::DEFAULT_ENGINE;
|
||||
|
||||
$printer = $this->builder
|
||||
->setTemplate($templateWrapper)
|
||||
@@ -337,7 +355,10 @@ class Pdf
|
||||
return $contents->getString();
|
||||
}
|
||||
|
||||
protected function displayInline(Entity $entity, Contents $contents)
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
private function displayInline(Entity $entity, Contents $contents): void
|
||||
{
|
||||
$fileName = Util::sanitizeFileName(
|
||||
$entity->get('name') ?? 'unnamed'
|
||||
@@ -358,9 +379,4 @@ class Pdf
|
||||
|
||||
echo $contents->getString();
|
||||
}
|
||||
|
||||
protected function createHtmlizer()
|
||||
{
|
||||
return $this->htmlizerFactory->create();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,22 +36,43 @@ class Data
|
||||
{
|
||||
private $additionalTemplateData = [];
|
||||
|
||||
private $applyAcl = false;
|
||||
|
||||
public function setAdditionalTemplateData(array $additionalTemplateData)
|
||||
{
|
||||
$this->additionalTemplateData = $additionalTemplateData;
|
||||
}
|
||||
|
||||
public function applyAcl(): bool
|
||||
{
|
||||
return $this->applyAcl;
|
||||
}
|
||||
|
||||
public function getAdditionalTemplateData(): array
|
||||
{
|
||||
return $this->additionalTemplateData;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): Data
|
||||
public function withAdditionalTemplateData(array $additionalTemplateData): self
|
||||
{
|
||||
$obj = new self();
|
||||
$obj = clone $this;
|
||||
|
||||
$obj->additionalTemplateData = $data['additionalTemplateData'] ?? [];
|
||||
$obj->additionalTemplateData = $additionalTemplateData;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function withAcl(bool $applyAcl = true): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
$obj->applyAcl = $applyAcl;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public static function fromNothing(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@ class EntityProcessor
|
||||
{
|
||||
$additionalData = $data->getAdditionalTemplateData();
|
||||
|
||||
$htmlizer = $this->htmlizerFactory->create();
|
||||
$htmlizer = $data->applyAcl() ?
|
||||
$this->htmlizerFactory->create() :
|
||||
$this->htmlizerFactory->createNoAcl();
|
||||
|
||||
$fontFace = $this->config->get('pdfFontFace', $this->fontFace);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user