diff --git a/application/Espo/Modules/Crm/Classes/Pdf/Account/ExampleDataLoader.php b/application/Espo/Modules/Crm/Classes/Pdf/Account/ExampleDataLoader.php new file mode 100644 index 0000000000..4134739a1d --- /dev/null +++ b/application/Espo/Modules/Crm/Classes/Pdf/Account/ExampleDataLoader.php @@ -0,0 +1,48 @@ + 'TEST HELLO', + ]; + } +} diff --git a/application/Espo/Modules/Crm/Resources/metadata/pdfDefs/Account.json b/application/Espo/Modules/Crm/Resources/metadata/pdfDefs/Account.json new file mode 100644 index 0000000000..dfe1978eb0 --- /dev/null +++ b/application/Espo/Modules/Crm/Resources/metadata/pdfDefs/Account.json @@ -0,0 +1,5 @@ +{ + "dataLoaderClassNameList": [ + "Espo\\Modules\\Crm\\Classes\\Pdf\\Account\\ExampleDataLoader" + ] +} diff --git a/application/Espo/Services/Pdf.php b/application/Espo/Services/Pdf.php index dfaa9656ed..f13c9604bc 100644 --- a/application/Espo/Services/Pdf.php +++ b/application/Espo/Services/Pdf.php @@ -54,6 +54,9 @@ use Espo\{ Tools\Pdf\Contents, Tools\Pdf\TemplateWrapper, Tools\Pdf\Data, + Tools\Pdf\IdDataMap, + Tools\Pdf\Params, + Tools\Pdf\DataLoader\DataLoaderManager, }; use Espo\Entities\Template; @@ -63,9 +66,9 @@ use StdClass; class Pdf { - protected const DEFAULT_ENGINE = 'Tcpdf'; + private const DEFAULT_ENGINE = 'Tcpdf'; - protected $removeMassFilePeriod = '1 hour'; + private $removeMassFilePeriod = '1 hour'; private $config; @@ -83,6 +86,8 @@ class Pdf private $serviceContanier; + private $dataLoaderManager; + public function __construct( Config $config, Metadata $metadata, @@ -91,7 +96,8 @@ class Pdf Language $defaultLanguage, SelectBuilderFactory $selectBuilderFactory, Builder $builder, - ServiceContainer $serviceContanier + ServiceContainer $serviceContanier, + DataLoaderManager $dataLoaderManager ) { $this->config = $config; $this->metadata = $metadata; @@ -101,6 +107,7 @@ class Pdf $this->selectBuilderFactory = $selectBuilderFactory; $this->builder = $builder; $this->serviceContanier = $serviceContanier; + $this->dataLoaderManager = $dataLoaderManager; } public function generateMailMerge( @@ -117,24 +124,36 @@ class Pdf $collection[] = $entity; } + $params = Params::create()->withAcl(); + + $idDataMap = IdDataMap::create(); + $service = $this->serviceContanier->get($entityType); foreach ($entityList as $entity) { $service->loadAdditionalFields($entity); + $idDataMap->set( + $entity->getId(), + $this->dataLoaderManager->load($entity) + ); + + // deprecated if (method_exists($service, 'loadAdditionalFieldsForPdf')) { $service->loadAdditionalFieldsForPdf($entity); } } + $engine = $this->config->get('pdfEngine') ?? self::DEFAULT_ENGINE; + $templateWrapper = new TemplateWrapper($template); $printer = $this->builder ->setTemplate($templateWrapper) - ->setEngine(self::DEFAULT_ENGINE) + ->setEngine($engine) ->build(); - $contents = $printer->printCollection($collection); + $contents = $printer->printCollection($collection, $params, $idDataMap); $filename = Util::sanitizeFileName($name) . '.pdf'; @@ -177,6 +196,8 @@ class Pdf throw new NotFound(); } + $params = Params::create(); + if ($checkAcl) { if (!$this->acl->check($template)) { throw new Forbidden(); @@ -185,6 +206,8 @@ class Pdf if (!$this->acl->checkScope($entityType)) { throw new Forbidden(); } + + $params = $params->withAcl(); } $query = $this->selectBuilderFactory @@ -201,9 +224,17 @@ class Pdf ]) ->find(); + $idDataMap = IdDataMap::create(); + foreach ($collection as $entity) { $service->loadAdditionalFields($entity); + $idDataMap->set( + $entity->getId(), + $this->dataLoaderManager->load($entity) + ); + + // deprecated if (method_exists($service, 'loadAdditionalFieldsForPdf')) { $service->loadAdditionalFieldsForPdf($entity); } @@ -218,7 +249,7 @@ class Pdf ->setEngine($engine) ->build(); - $contents = $printer->printCollection($collection); + $contents = $printer->printCollection($collection, $params, $idDataMap); $entityTypeTranslated = $this->defaultLanguage->translate($entityType, 'scopeNamesPlural'); @@ -241,7 +272,7 @@ class Pdf 'serviceName' => 'Pdf', 'methodName' => 'removeMassFileJob', 'data' => [ - 'id' => $attachment->id + 'id' => $attachment->getId(), ], 'executeTime' => (new DateTime())->modify('+' . $this->removeMassFilePeriod)->format('Y-m-d H:i:s'), 'queue' => QueueName::Q1, @@ -249,7 +280,7 @@ class Pdf $this->entityManager->saveEntity($job); - return $attachment->id; + return $attachment->getId(); } public function removeMassFileJob(StdClass $data): void @@ -274,9 +305,9 @@ class Pdf /** * Generate PDF. ACL check is processed if `$data` is null. */ - public function generate(Entity $entity, Template $template, ?Data $data = null): string + public function generate(Entity $entity, Template $template, ?Params $params = null, ?Data $data = null): string { - return $this->buildFromTemplateInternal($entity, $template, false, null, $data); + return $this->buildFromTemplateInternal($entity, $template, false, null, $params, $data); } /** @@ -297,6 +328,7 @@ class Pdf Entity $template, bool $displayInline = false, ?array $additionalData = null, + ?Params $params = null, ?Data $data = null ): ?string { @@ -307,6 +339,7 @@ class Pdf $service->loadAdditionalFields($entity); if (method_exists($service, 'loadAdditionalFieldsForPdf')) { + // deprecated $service->loadAdditionalFieldsForPdf($entity); } @@ -316,7 +349,7 @@ class Pdf $applyAcl = true; - if ($data) { + if ($params) { $applyAcl = $data->applyAcl(); } @@ -332,12 +365,14 @@ class Pdf $templateWrapper = new TemplateWrapper($template); if (!$data) { - $data = Data - ::create() - ->withAdditionalTemplateData($additionalData ?? []) - ->withAcl($applyAcl); + $data = Data::create() + ->withAdditionalTemplateData( + (object) ($additionalData ?? []) + ); } + $data = $this->dataLoaderManager->load($entity, $data); + $engine = $this->config->get('pdfEngine') ?? self::DEFAULT_ENGINE; $printer = $this->builder @@ -345,7 +380,7 @@ class Pdf ->setEngine($engine) ->build(); - $contents = $printer->printEntity($entity, $data); + $contents = $printer->printEntity($entity, $params, $data); if ($displayInline) { $this->displayInline($entity, $contents); diff --git a/application/Espo/Tools/Pdf/CollectionPrinter.php b/application/Espo/Tools/Pdf/CollectionPrinter.php index 788591c308..e2e4b605b5 100644 --- a/application/Espo/Tools/Pdf/CollectionPrinter.php +++ b/application/Espo/Tools/Pdf/CollectionPrinter.php @@ -29,11 +29,9 @@ namespace Espo\Tools\Pdf; -use Espo\{ - ORM\Collection, -}; +use Espo\ORM\Collection; interface CollectionPrinter { - public function print(Template $template, Collection $collection, Data $data): Contents; + public function print(Template $template, Collection $collection, Params $params, IdDataMap $idDataMap): Contents; } diff --git a/application/Espo/Tools/Pdf/Data.php b/application/Espo/Tools/Pdf/Data.php index 1dceb29e8f..fb6654fe1f 100644 --- a/application/Espo/Tools/Pdf/Data.php +++ b/application/Espo/Tools/Pdf/Data.php @@ -29,44 +29,25 @@ namespace Espo\Tools\Pdf; -/** - * Data transfer object. - */ +use stdClass; + class Data { private $additionalTemplateData = []; - private $applyAcl = false; - - public function setAdditionalTemplateData(array $additionalTemplateData) + public function getAdditionalTemplateData(): stdClass { - $this->additionalTemplateData = $additionalTemplateData; + return (object) $this->additionalTemplateData; } - public function applyAcl(): bool - { - return $this->applyAcl; - } - - public function getAdditionalTemplateData(): array - { - return $this->additionalTemplateData; - } - - public function withAdditionalTemplateData(array $additionalTemplateData): self + public function withAdditionalTemplateData(stdClass $additionalTemplateData): self { $obj = clone $this; - $obj->additionalTemplateData = $additionalTemplateData; - - return $obj; - } - - public function withAcl(bool $applyAcl = true): self - { - $obj = clone $this; - - $obj->applyAcl = $applyAcl; + $obj->additionalTemplateData = array_merge( + $obj->additionalTemplateData, + get_object_vars($additionalTemplateData) + ); return $obj; } diff --git a/application/Espo/Tools/Pdf/DataLoader/DataLoader.php b/application/Espo/Tools/Pdf/DataLoader/DataLoader.php new file mode 100644 index 0000000000..4d799e1180 --- /dev/null +++ b/application/Espo/Tools/Pdf/DataLoader/DataLoader.php @@ -0,0 +1,39 @@ +metadata = $metadata; + $this->injectableFactory = $injectableFactory; + } + + public function load(Entity $entity, ?Data $data = null): Data + { + if (!$data) { + $data = Data::create(); + } + + $classNameList = $this->metadata->get(['pdfDefs', $entity->getEntityType(), 'dataLoaderClassNameList']) ?? []; + + foreach ($classNameList as $className) { + $loader = $this->createLoader($className); + + $loadedData = $loader->load($entity); + + $data = $data->withAdditionalTemplateData($loadedData); + } + + return $data; + } + + private function createLoader(string $className): DataLoader + { + return $this->injectableFactory->create($className); + } +} diff --git a/application/Espo/Tools/Pdf/EntityPrinter.php b/application/Espo/Tools/Pdf/EntityPrinter.php index 62d6cf9d48..f8dfbf8cc8 100644 --- a/application/Espo/Tools/Pdf/EntityPrinter.php +++ b/application/Espo/Tools/Pdf/EntityPrinter.php @@ -29,11 +29,9 @@ namespace Espo\Tools\Pdf; -use Espo\{ - ORM\Entity, -}; +use Espo\ORM\Entity; interface EntityPrinter { - public function print(Template $template, Entity $entity, Data $data): Contents; + public function print(Template $template, Entity $entity, Params $params, Data $data): Contents; } diff --git a/application/Espo/Tools/Pdf/IdDataMap.php b/application/Espo/Tools/Pdf/IdDataMap.php new file mode 100644 index 0000000000..2985cde34a --- /dev/null +++ b/application/Espo/Tools/Pdf/IdDataMap.php @@ -0,0 +1,50 @@ +map[$id] = $data; + } + + public function get(string $id): ?Data + { + return $this->map[$id] ?? null; + } + + public static function create(): self + { + return new self(); + } +} diff --git a/application/Espo/Tools/Pdf/Params.php b/application/Espo/Tools/Pdf/Params.php new file mode 100644 index 0000000000..9d83e50032 --- /dev/null +++ b/application/Espo/Tools/Pdf/Params.php @@ -0,0 +1,54 @@ +applyAcl; + } + + public function withAcl(bool $applyAcl = true): self + { + $obj = clone $this; + + $obj->applyAcl = $applyAcl; + + return $obj; + } + + public static function create(): self + { + return new self(); + } +} diff --git a/application/Espo/Tools/Pdf/PrinterController.php b/application/Espo/Tools/Pdf/PrinterController.php index 0cb06299c3..8883a428c1 100644 --- a/application/Espo/Tools/Pdf/PrinterController.php +++ b/application/Espo/Tools/Pdf/PrinterController.php @@ -42,11 +42,11 @@ use Espo\{ class PrinterController { - protected $template; + private $template; - protected $metadata; + private $metadata; - protected $injectableFactory; + private $injectableFactory; public function __construct( Metadata $metadata, @@ -61,21 +61,23 @@ class PrinterController $this->engine = $engine; } - public function printEntity(Entity $entity, ?Data $data = null): Contents + public function printEntity(Entity $entity, ?Params $params, ?Data $data = null): Contents { + $params = $params ?? new Params(); $data = $data ?? new Data(); - return $this->createPrinter('entity')->print($this->template, $entity, $data); + return $this->createPrinter('entity')->print($this->template, $entity, $params, $data); } - public function printCollection(Collection $collection, ?Data $data = null): Contents + public function printCollection(Collection $collection, ?Params $params, ?IdDataMap $IdDataMap = null): Contents { - $data = $data ?? new Data(); + $params = $params ?? new Params(); + $IdDataMap = $IdDataMap ?? new IdDataMap(); - return $this->createPrinter('collection')->print($this->template, $collection, $data); + return $this->createPrinter('collection')->print($this->template, $collection, $params, $IdDataMap); } - protected function createPrinter(string $type): object + private function createPrinter(string $type): object { $className = $this->metadata ->get(['app', 'pdfEngines', $this->engine, 'implementationClassNameMap', $type]) ?? null; diff --git a/application/Espo/Tools/Pdf/Tcpdf/EntityProcessor.php b/application/Espo/Tools/Pdf/Tcpdf/EntityProcessor.php index dfd9e2b849..72c8f9f3c1 100644 --- a/application/Espo/Tools/Pdf/Tcpdf/EntityProcessor.php +++ b/application/Espo/Tools/Pdf/Tcpdf/EntityProcessor.php @@ -35,6 +35,7 @@ use Espo\Core\Htmlizer\Factory as HtmlizerFactory; use Espo\ORM\Entity; use Espo\Tools\Pdf\Template; use Espo\Tools\Pdf\Data; +use Espo\Tools\Pdf\Params; use Espo\Tools\Pdf\Tcpdf\Tcpdf; class EntityProcessor @@ -53,11 +54,11 @@ class EntityProcessor $this->htmlizerFactory = $htmlizerFactory; } - public function process(Tcpdf $pdf, Template $template, Entity $entity, Data $data): void + public function process(Tcpdf $pdf, Template $template, Entity $entity, Params $params, Data $data): void { - $additionalData = $data->getAdditionalTemplateData(); + $additionalData = get_object_vars($data->getAdditionalTemplateData()); - $htmlizer = $data->applyAcl() ? + $htmlizer = $params->applyAcl() ? $this->htmlizerFactory->create() : $this->htmlizerFactory->createNoAcl(); diff --git a/application/Espo/Tools/Pdf/Tcpdf/TcpdfCollectionPrinter.php b/application/Espo/Tools/Pdf/Tcpdf/TcpdfCollectionPrinter.php index b7dd66606c..ab7eeb9bef 100644 --- a/application/Espo/Tools/Pdf/Tcpdf/TcpdfCollectionPrinter.php +++ b/application/Espo/Tools/Pdf/Tcpdf/TcpdfCollectionPrinter.php @@ -34,6 +34,8 @@ use Espo\Tools\Pdf\CollectionPrinter; use Espo\Tools\Pdf\Template; use Espo\Tools\Pdf\Contents; use Espo\Tools\Pdf\Data; +use Espo\Tools\Pdf\IdDataMap; +use Espo\Tools\Pdf\Params; use Espo\Tools\Pdf\Tcpdf\Tcpdf; class TcpdfCollectionPrinter implements CollectionPrinter @@ -45,7 +47,7 @@ class TcpdfCollectionPrinter implements CollectionPrinter $this->entityProcessor = $entityProcessor; } - public function print(Template $template, Collection $collection, Data $data): Contents + public function print(Template $template, Collection $collection, Params $params, IdDataMap $dataMap): Contents { $pdf = new Tcpdf(); @@ -54,7 +56,9 @@ class TcpdfCollectionPrinter implements CollectionPrinter foreach ($collection as $entity) { $pdf->startPageGroup(); - $this->entityProcessor->process($pdf, $template, $entity, $data); + $data = $dataMap->get($entity->getId()) ?? Data::create(); + + $this->entityProcessor->process($pdf, $template, $entity, $params, $data); } return new TcpdfContents($pdf); diff --git a/application/Espo/Tools/Pdf/Tcpdf/TcpdfEntityPrinter.php b/application/Espo/Tools/Pdf/Tcpdf/TcpdfEntityPrinter.php index abf47e32d7..1caea95dba 100644 --- a/application/Espo/Tools/Pdf/Tcpdf/TcpdfEntityPrinter.php +++ b/application/Espo/Tools/Pdf/Tcpdf/TcpdfEntityPrinter.php @@ -33,6 +33,7 @@ use Espo\ORM\Entity; use Espo\Tools\Pdf\EntityPrinter; use Espo\Tools\Pdf\Template; use Espo\Tools\Pdf\Contents; +use Espo\Tools\Pdf\Params; use Espo\Tools\Pdf\Data; use Espo\Tools\Pdf\Tcpdf\Tcpdf; @@ -45,11 +46,11 @@ class TcpdfEntityPrinter implements EntityPrinter $this->entityProcessor = $entityProcessor; } - public function print(Template $template, Entity $entity, Data $data): Contents + public function print(Template $template, Entity $entity, Params $params, Data $data): Contents { $pdf = new Tcpdf(); - $this->entityProcessor->process($pdf, $template, $entity, $data); + $this->entityProcessor->process($pdf, $template, $entity, $params, $data); return new TcpdfContents($pdf); }