vendor/pimcore/pimcore/lib/Web2Print/Processor.php line 47

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Web2Print;
  15. use Pimcore\Config;
  16. use Pimcore\Event\DocumentEvents;
  17. use Pimcore\Event\Model\DocumentEvent;
  18. use Pimcore\Helper\Mail;
  19. use Pimcore\Logger;
  20. use Pimcore\Messenger\GenerateWeb2PrintPdfMessage;
  21. use Pimcore\Model;
  22. use Pimcore\Model\Document;
  23. use Pimcore\Web2Print\Exception\CancelException;
  24. use Pimcore\Web2Print\Exception\NotPreparedException;
  25. use Pimcore\Web2Print\Processor\HeadlessChrome;
  26. use Pimcore\Web2Print\Processor\PdfReactor;
  27. use Pimcore\Web2Print\Processor\WkHtmlToPdf;
  28. use Symfony\Component\Lock\LockFactory;
  29. use Symfony\Component\Lock\LockInterface;
  30. use Twig\Sandbox\SecurityError;
  31. abstract class Processor
  32. {
  33.     /**
  34.      * @var LockInterface|null
  35.      */
  36.     private static $lock null;
  37.     /**
  38.      * @return Processor
  39.      *
  40.      * @throws \Exception
  41.      */
  42.     public static function getInstance()
  43.     {
  44.         $config Config::getWeb2PrintConfig();
  45.         if ($config->get('generalTool') === 'pdfreactor') {
  46.             return new PdfReactor();
  47.         } elseif ($config->get('generalTool') === 'wkhtmltopdf') {
  48.             return new WkHtmlToPdf();
  49.         } elseif ($config->get('generalTool') === 'headlesschrome') {
  50.             return new HeadlessChrome();
  51.         } else {
  52.             throw new \Exception('Invalid Configuration - ' $config->get('generalTool'));
  53.         }
  54.     }
  55.     /**
  56.      * @param int $documentId
  57.      * @param array $config
  58.      *
  59.      * @return bool
  60.      *
  61.      * @throws \Exception
  62.      */
  63.     public function preparePdfGeneration($documentId$config)
  64.     {
  65.         $document $this->getPrintDocument($documentId);
  66.         if (Model\Tool\TmpStore::get($document->getLockKey())) {
  67.             throw new \Exception('Process with given document already running.');
  68.         }
  69.         Model\Tool\TmpStore::add($document->getLockKey(), true);
  70.         $jobConfig = new \stdClass();
  71.         $jobConfig->documentId $documentId;
  72.         $jobConfig->config $config;
  73.         $this->saveJobConfigObjectFile($jobConfig);
  74.         $this->updateStatus($documentId0'prepare_pdf_generation');
  75.         $disableBackgroundExecution $config['disableBackgroundExecution'] ?? false;
  76.         if (!$disableBackgroundExecution) {
  77.             \Pimcore::getContainer()->get('messenger.bus.pimcore-core')->dispatch(
  78.                 new GenerateWeb2PrintPdfMessage($jobConfig->documentId)
  79.             );
  80.             return true;
  81.         }
  82.         return (bool)self::getInstance()->startPdfGeneration($jobConfig->documentId);
  83.     }
  84.     /**
  85.      * @param int $documentId
  86.      *
  87.      * @return string|null
  88.      *
  89.      * @throws Model\Element\ValidationException
  90.      * @throws NotPreparedException
  91.      */
  92.     public function startPdfGeneration($documentId)
  93.     {
  94.         $jobConfigFile $this->loadJobConfigObject($documentId);
  95.         if (!$jobConfigFile) {
  96.             throw new NotPreparedException('PDF Generation for document ' $documentId ' is not prepared.');
  97.         }
  98.         $document $this->getPrintDocument($documentId);
  99.         $lock $this->getLock($document);
  100.         // check if there is already a generating process running, wait if so ...
  101.         $lock->acquire(true);
  102.         $pdf null;
  103.         try {
  104.             $preEvent = new DocumentEvent($document, [
  105.                 'processor' => $this,
  106.                 'jobConfig' => $jobConfigFile->config,
  107.             ]);
  108.             \Pimcore::getEventDispatcher()->dispatch($preEventDocumentEvents::PRINT_PRE_PDF_GENERATION);
  109.             $pdf $this->buildPdf($document$jobConfigFile->config);
  110.             file_put_contents($document->getPdfFileName(), $pdf);
  111.             $postEvent = new DocumentEvent($document, [
  112.                 'filename' => $document->getPdfFileName(),
  113.                 'pdf' => $pdf,
  114.             ]);
  115.             \Pimcore::getEventDispatcher()->dispatch($postEventDocumentEvents::PRINT_POST_PDF_GENERATION);
  116.             $document->setLastGenerated((time() + 1));
  117.             $document->setLastGenerateMessage('');
  118.             $document->save();
  119.         } catch (CancelException $e) {
  120.             Logger::debug($e->getMessage());
  121.         } catch (\Exception $e) {
  122.             Logger::err((string) $e);
  123.             $document->setLastGenerateMessage($e->getMessage());
  124.             $document->save();
  125.         }
  126.         $lock->release();
  127.         Model\Tool\TmpStore::delete($document->getLockKey());
  128.         @unlink(static::getJobConfigFile($documentId));
  129.         return $pdf;
  130.     }
  131.     /**
  132.      * @param Document\PrintAbstract $document
  133.      * @param object $config
  134.      *
  135.      * @return string
  136.      *
  137.      * @throws \Exception
  138.      */
  139.     abstract protected function buildPdf(Document\PrintAbstract $document$config);
  140.     /**
  141.      * @param \stdClass $jobConfig
  142.      *
  143.      * @return bool
  144.      */
  145.     protected function saveJobConfigObjectFile($jobConfig)
  146.     {
  147.         file_put_contents(static::getJobConfigFile($jobConfig->documentId), json_encode($jobConfig));
  148.         return true;
  149.     }
  150.     /**
  151.      * @param int $documentId
  152.      *
  153.      * @return \stdClass|null
  154.      */
  155.     protected function loadJobConfigObject($documentId)
  156.     {
  157.         $file = static::getJobConfigFile($documentId);
  158.         if (file_exists($file)) {
  159.             return json_decode(file_get_contents($file));
  160.         }
  161.         return null;
  162.     }
  163.     /**
  164.      * @param int $documentId
  165.      *
  166.      * @return Document\PrintAbstract
  167.      *
  168.      * @throws \Exception
  169.      */
  170.     protected function getPrintDocument($documentId)
  171.     {
  172.         $document Document\PrintAbstract::getById($documentId);
  173.         if (empty($document)) {
  174.             throw new \Exception('PrintDocument with ' $documentId ' not found.');
  175.         }
  176.         return $document;
  177.     }
  178.     /**
  179.      * @param int $processId
  180.      *
  181.      * @return string
  182.      */
  183.     public static function getJobConfigFile($processId)
  184.     {
  185.         return PIMCORE_SYSTEM_TEMP_DIRECTORY DIRECTORY_SEPARATOR 'pdf-creation-job-' $processId '.json';
  186.     }
  187.     /**
  188.      * @return array
  189.      */
  190.     abstract public function getProcessingOptions();
  191.     /**
  192.      * @param int $documentId
  193.      * @param int $status
  194.      * @param string $statusUpdate
  195.      *
  196.      * @throws CancelException
  197.      */
  198.     protected function updateStatus($documentId$status$statusUpdate)
  199.     {
  200.         $jobConfig $this->loadJobConfigObject($documentId);
  201.         if (!$jobConfig) {
  202.             throw new CancelException('PDF Generation for document ' $documentId ' is canceled.');
  203.         }
  204.         $jobConfig->status $status;
  205.         $jobConfig->statusUpdate $statusUpdate;
  206.         $this->saveJobConfigObjectFile($jobConfig);
  207.     }
  208.     /**
  209.      * @param int $documentId
  210.      *
  211.      * @return array|null
  212.      */
  213.     public function getStatusUpdate($documentId)
  214.     {
  215.         $jobConfig $this->loadJobConfigObject($documentId);
  216.         if ($jobConfig) {
  217.             return [
  218.                 'status' => $jobConfig->status,
  219.                 'statusUpdate' => $jobConfig->statusUpdate,
  220.             ];
  221.         }
  222.         return null;
  223.     }
  224.     /**
  225.      * @param int $documentId
  226.      *
  227.      * @throws \Exception
  228.      */
  229.     public function cancelGeneration($documentId)
  230.     {
  231.         $document Document\PrintAbstract::getById($documentId);
  232.         if (empty($document)) {
  233.             throw new \Exception('Document with id ' $documentId ' not found.');
  234.         }
  235.         $this->getLock($document)->release();
  236.         Model\Tool\TmpStore::delete($document->getLockKey());
  237.         @unlink(static::getJobConfigFile($documentId));
  238.     }
  239.     /**
  240.      * @param string $html
  241.      * @param array $params
  242.      *
  243.      * @return string
  244.      *
  245.      * @throws \Exception
  246.      */
  247.     protected function processHtml($html$params)
  248.     {
  249.         $document $params['document'] ?? null;
  250.         $hostUrl $params['hostUrl'] ?? null;
  251.         $templatingEngine \Pimcore::getContainer()->get('pimcore.templating.engine.delegating');
  252.         try {
  253.             $twig $templatingEngine->getTwigEnvironment(true);
  254.             $template $twig->createTemplate((string) $html);
  255.             $html $twig->render($template$params);
  256.         } catch (SecurityError $e) {
  257.             Logger::err((string) $e);
  258.             throw new \Exception(sprintf('Failed rendering the print template: %s. Please check your twig sandbox security policy or contact the administrator.'$e->getMessage()));
  259.         } finally {
  260.             $templatingEngine->disableSandboxExtensionFromTwigEnvironment();
  261.         }
  262.         return Mail::setAbsolutePaths($html$document$hostUrl);
  263.     }
  264.     /**
  265.      * @param Document\PrintAbstract $document
  266.      *
  267.      * @return LockInterface
  268.      */
  269.     protected function getLock(Document\PrintAbstract $document): LockInterface
  270.     {
  271.         if (!self::$lock) {
  272.             self::$lock \Pimcore::getContainer()->get(LockFactory::class)->createLock($document->getLockKey());
  273.         }
  274.         return self::$lock;
  275.     }
  276.     /**
  277.      * Returns the generated pdf file. Its path or data depending supplied parameter
  278.      *
  279.      * @param string $html
  280.      * @param array $params
  281.      * @param bool $returnFilePath return the path to the pdf file or the content
  282.      *
  283.      * @return string
  284.      */
  285.     abstract public function getPdfFromString($html$params = [], $returnFilePath false);
  286. }