<?php declare(strict_types=1);
namespace Uandi\UandiEfbDownloadCenter\Controller;
use Mpdf\MpdfException;
use Psr\Log\LoggerInterface;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Controller\StorefrontController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Mime\FileinfoMimeTypeGuesser;
use Symfony\Component\Routing\Annotation\Route;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Uandi\UandiEfbDownloadCenter\Service\PdfRenderer;
/**
* @Route(defaults={"_routeScope"={"storefront"}})
*/
class PdfController extends StorefrontController
{
private EntityRepository $productRepository;
private PdfRenderer $pdfRenderer;
private LoggerInterface $logger;
public function __construct(
EntityRepository $productRepository,
PdfRenderer $pdfRenderer,
LoggerInterface $logger
) {
$this->productRepository = $productRepository;
$this->pdfRenderer = $pdfRenderer;
$this->logger = $logger;
}
/**
* Find the product with the supplied product number, render a datasheet and return for download.
* Required GET parameter: productNumber
*
* @Route("/download/datasheet/", name="uandi.downloadcenter.pdf.datasheet", methods={"GET"})
*
* @param Request $request
* @param SalesChannelContext $salesChannelContext
*
* @return BinaryFileResponse|RedirectResponse
*/
public function dataSheetPdf(
Request $request,
SalesChannelContext $salesChannelContext
): BinaryFileResponse|RedirectResponse {
$product = $this->findProduct($request, $salesChannelContext);
if ($product == null) {
return $this->createErrorRedirectResponse($request, 'uandiDownloadCenter.productNotFound');
}
try {
$pdf = $this->pdfRenderer->renderDataSheetPdf($product, $salesChannelContext);
} catch (MpdfException|LoaderError|RuntimeError|SyntaxError $e) {
$this->logger->error(
sprintf(
'%s: Product %s: Exception on rendering datasheet: %s, Trace: %s',
__METHOD__,
$product->getProductNumber(),
$e->getMessage(),
$e->getTraceAsString()
)
);
return $this->createErrorRedirectResponse($request, "uandiDownloadCenter.pdfRenderError");
}
if ($pdf === null) {
return $this->createErrorRedirectResponse($request, "uandiDownloadCenter.pdfRenderError");
}
return $this->createPdfDownloadResponse($pdf, 'datasheet', $product, $request->getLocale());
}
/**
* Find the product with the supplied product number, call ERP API,
* render conformity declaration pdf, and return for download.
* Required POST parameter: productNumber
*
* @Route(
* "/download/conformitydeclaration/", name="uandi.downloadcenter.pdf.conformitydeclaration", methods={"GET"}
* )
*
* @param Request $request
* @param SalesChannelContext $salesChannelContext
*
* @return BinaryFileResponse|RedirectResponse
*/
public function conformityDeclarationPdf(
Request $request,
SalesChannelContext $salesChannelContext
): BinaryFileResponse|RedirectResponse {
$product = $this->findProduct($request, $salesChannelContext);
if ($product == null) {
return $this->createErrorRedirectResponse($request, "uandiDownloadCenter.productNotFound");
}
try {
$pdf = $this->pdfRenderer->renderConfirmityDeclarationPdf(
[$product],
$salesChannelContext
);
} catch (MpdfException|SyntaxError|RuntimeError|LoaderError $e) {
$this->logger->error(
sprintf(
'%s: Product %s: Exception on rendering declaration of conformity: %s, Trace: %s',
__METHOD__,
$product->getProductNumber(),
$e->getMessage(),
$e->getTraceAsString()
)
);
return $this->createErrorRedirectResponse($request, "uandiDownloadCenter.pdfRenderError");
}
if ($pdf === null | $pdf === false) {
return $this->createErrorRedirectResponse($request, "uandiDownloadCenter.pdfRenderError");
}
return $this->createPdfDownloadResponse($pdf, 'declarationofconformity', $product, $request->getLocale());
}
/**
* Find the product with the supplied product number, call ERP API,
* render UKCA conformity declaration pdf, and return for download.
* Required POST parameter: productNumber
*
* @Route(
* "/download/ukca/", name="uandi.downloadcenter.pdf.ukca", methods={"GET"}
* )
*
* @param Request $request
* @param SalesChannelContext $salesChannelContext
*
* @return BinaryFileResponse|RedirectResponse
*/
public function ukcaPdf(
Request $request,
SalesChannelContext $salesChannelContext
): BinaryFileResponse|RedirectResponse {
$product = $this->findProduct($request, $salesChannelContext);
if ($product == null) {
return $this->createErrorRedirectResponse($request, "uandiDownloadCenter.productNotFound");
}
try {
$pdf = $this->pdfRenderer->renderUkcaPdf(
[$product],
$salesChannelContext
);
} catch (MpdfException|SyntaxError|RuntimeError|LoaderError $e) {
$this->logger->error(
sprintf(
'%s: Product %s: Exception on rendering ukca: %s, Trace: %s',
__METHOD__,
$product->getProductNumber(),
$e->getMessage(),
$e->getTraceAsString()
)
);
return $this->createErrorRedirectResponse($request, "uandiDownloadCenter.pdfRenderError");
}
if ($pdf === null | $pdf === false) {
return $this->createErrorRedirectResponse($request, "uandiDownloadCenter.pdfRenderError");
}
return $this->createPdfDownloadResponse($pdf, 'ukca', $product, $request->getLocale());
}
/**
* Find product for the customer entered product number.
*
* @param Request $request
* @param SalesChannelContext $salesChannelContext
*
* @return mixed|null
*/
private function findProduct(Request $request, SalesChannelContext $salesChannelContext): ?ProductEntity
{
$productSearchQuery = $request->get('productNumber');
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('productNumber', $productSearchQuery));
$criteria->addAssociations(['properties', 'cover', 'media']);
return $this->productRepository->search($criteria, $salesChannelContext->getContext())->first();
}
/**
* Return pdf stream as a file download response.
*
* @param string $pdf
* @param string $type
* @param ProductEntity $productEntity
* @param string $locale
* @return BinaryFileResponse
*/
private function createPdfDownloadResponse(
string $pdf,
string $type,
ProductEntity $productEntity,
string $locale
): BinaryFileResponse {
$tmpFile = tempnam("/tmp", $type);
file_put_contents($tmpFile, $pdf);
$response = new BinaryFileResponse($tmpFile);
$mimeTypeGuesser = new FileinfoMimeTypeGuesser();
$response->headers->set('Content-Type', $mimeTypeGuesser->guessMimeType($tmpFile));
$language = $this->getDocumentLanguage($locale);
$documentType = match ($type) {
'ukca' => '_UKCA_',
'declarationofconformity' => '_CE_',
default => '_',
};
$fileName = $productEntity->getProductNumber() . $documentType . $language . '.pdf';
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $fileName);
return $response;
}
/**
* Redirect user to previous page adding flash message with error
* - Add timestamp in url to avoid cache and show error message
*
* @param Request $request
* @param string $snippet
* @return RedirectResponse
*/
private function createErrorRedirectResponse(Request $request, string $snippet): RedirectResponse
{
$this->addFlash(self::DANGER, $this->trans($snippet));
$previousPageUrl = parse_url($request->headers->get('referer') ?? '/');
$redirectPage = sprintf('%s?%s', $previousPageUrl['path'], http_build_query(['c' => time()]));
return $this->redirect($redirectPage);
}
/**
* Get language code according to requirements.
*
* @param string $locale
* @return string
*/
private function getDocumentLanguage(string $locale): string
{
if (!empty($locale)) {
list($langCode, $country) = explode('-', $locale);
$langCode = mb_strtoupper($langCode);
if ($langCode == 'DA') {
$langCode = 'EN';
}
return $langCode;
}
return 'de';
}
}