diff --git a/composer.json b/composer.json index 477b3772..0f8bdb44 100644 --- a/composer.json +++ b/composer.json @@ -13,12 +13,14 @@ "doctrine/orm": "^2.7", "doctrine/persistence": "^1.3 || ^2.2", "fakerphp/faker": "^1.9.0", + "knplabs/gaufrette": "^0.8", "knplabs/knp-menu": "^3.2", "knplabs/knp-snappy": "^1.2", "knplabs/knp-snappy-bundle": "^1.7", "sylius/resource-bundle": "^1.6", "symfony/config": "^4.4 || ^5.0", "symfony/dependency-injection": "^4.4 || ^5.0", + "symfony/dom-crawler": "^4.4 || ^5.0", "symfony/event-dispatcher": "^4.4 || ^5.0", "symfony/form": "^4.4 || ^5.0", "symfony/http-foundation": "^4.4 || ^5.0.7", diff --git a/src/Controller/Action/Admin/GenerateEncodedExamplePdfAction.php b/src/Controller/Action/Admin/GenerateEncodedExamplePdfAction.php new file mode 100644 index 00000000..6ed791b1 --- /dev/null +++ b/src/Controller/Action/Admin/GenerateEncodedExamplePdfAction.php @@ -0,0 +1,53 @@ +giftCardFactory = $giftCardFactory; + $this->giftCardConfigurationRepository = $giftCardConfigurationRepository; + $this->giftCardPdfGenerator = $giftCardPdfGenerator; + $this->formFactory = $formFactory; + } + + public function __invoke(Request $request, int $id): Response + { + $giftCard = $this->giftCardFactory->createExample(); + /** @var GiftCardConfigurationInterface|null $giftCardConfiguration */ + $giftCardConfiguration = $this->giftCardConfigurationRepository->find($id); + Assert::isInstanceOf($giftCardConfiguration, GiftCardConfigurationInterface::class); + + $form = $this->formFactory->create(GiftCardConfigurationType::class, $giftCardConfiguration); + $form->handleRequest($request); + + $pdfContent = $this->giftCardPdfGenerator->generateAndGetContent($giftCard, $giftCardConfiguration); + + return new Response(\base64_encode($pdfContent)); + } +} diff --git a/src/Controller/Action/Admin/LoadDefaultPdfCssAction.php b/src/Controller/Action/Admin/LoadDefaultPdfCssAction.php new file mode 100644 index 00000000..9cd4a8b3 --- /dev/null +++ b/src/Controller/Action/Admin/LoadDefaultPdfCssAction.php @@ -0,0 +1,54 @@ +giftCardFactory = $giftCardFactory; + $this->giftCardConfigurationRepository = $giftCardConfigurationRepository; + $this->giftCardPdfGenerator = $giftCardPdfGenerator; + $this->defaultPdfCssProvider = $defaultPdfCssProvider; + } + + public function __invoke(int $id): JsonResponse + { + $giftCard = $this->giftCardFactory->createExample(); + /** @var GiftCardConfigurationInterface|null $giftCardConfiguration */ + $giftCardConfiguration = $this->giftCardConfigurationRepository->find($id); + Assert::isInstanceOf($giftCardConfiguration, GiftCardConfigurationInterface::class); + + $defaultCss = $this->defaultPdfCssProvider->getDefaultCss(); + $giftCardConfiguration->setPdfRenderingCss($defaultCss); + + $pdfContent = $this->giftCardPdfGenerator->generateAndGetContent($giftCard, $giftCardConfiguration); + + return new JsonResponse([ + 'css' => $defaultCss, + 'pdfContent' => \base64_encode($pdfContent), + ]); + } +} diff --git a/src/DependencyInjection/SetonoSyliusGiftCardExtension.php b/src/DependencyInjection/SetonoSyliusGiftCardExtension.php index 5be8a8df..b83e1892 100644 --- a/src/DependencyInjection/SetonoSyliusGiftCardExtension.php +++ b/src/DependencyInjection/SetonoSyliusGiftCardExtension.php @@ -49,6 +49,12 @@ public function load(array $configs, ContainerBuilder $container): void $config['pdf_rendering']['available_page_sizes'] ); + // Load default CSS file + $container->setParameter( + 'setono_sylius_gift_card.default_css_file', + '@SetonoSyliusGiftCardPlugin/Shop/GiftCard/defaultGiftCardConfiguration.css.twig' + ); + $this->registerResources('setono_sylius_gift_card', $config['driver'], $config['resources'], $container); $loader->load('services.xml'); diff --git a/src/Factory/GiftCardFactory.php b/src/Factory/GiftCardFactory.php index f2353270..6b6974b6 100644 --- a/src/Factory/GiftCardFactory.php +++ b/src/Factory/GiftCardFactory.php @@ -14,6 +14,7 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Model\OrderInterface; +use Sylius\Component\Currency\Context\CurrencyContextInterface; use Sylius\Component\Resource\Factory\FactoryInterface; use Webmozart\Assert\Assert; @@ -27,16 +28,20 @@ final class GiftCardFactory implements GiftCardFactoryInterface private DateTimeProvider $dateTimeProvider; + private CurrencyContextInterface $currencyContext; + public function __construct( FactoryInterface $decoratedFactory, GiftCardCodeGeneratorInterface $giftCardCodeGenerator, GiftCardChannelConfigurationProviderInterface $giftCardChannelConfigurationProvider, - DateTimeProvider $dateTimeProvider + DateTimeProvider $dateTimeProvider, + CurrencyContextInterface $currencyContext ) { $this->decoratedFactory = $decoratedFactory; $this->giftCardCodeGenerator = $giftCardCodeGenerator; $this->giftCardChannelConfigurationProvider = $giftCardChannelConfigurationProvider; $this->dateTimeProvider = $dateTimeProvider; + $this->currencyContext = $currencyContext; } public function createNew(): GiftCardInterface @@ -114,4 +119,13 @@ public function createFromOrderItemUnitAndCart( return $giftCard; } + + public function createExample(): GiftCardInterface + { + $giftCard = $this->createNew(); + $giftCard->setAmount(1500); + $giftCard->setCurrencyCode($this->currencyContext->getCurrencyCode()); + + return $giftCard; + } } diff --git a/src/Factory/GiftCardFactoryInterface.php b/src/Factory/GiftCardFactoryInterface.php index 941d575a..ca6efd9d 100644 --- a/src/Factory/GiftCardFactoryInterface.php +++ b/src/Factory/GiftCardFactoryInterface.php @@ -24,4 +24,9 @@ public function createFromOrderItemUnitAndCart( OrderItemUnitInterface $orderItemUnit, OrderInterface $cart ): GiftCardInterface; + + /** + * Create an example GiftCard that is used to generate the example PDF for configuration live rendering + */ + public function createExample(): GiftCardInterface; } diff --git a/src/Form/Type/GiftCardConfigurationType.php b/src/Form/Type/GiftCardConfigurationType.php index 89ee978b..c877279f 100644 --- a/src/Form/Type/GiftCardConfigurationType.php +++ b/src/Form/Type/GiftCardConfigurationType.php @@ -9,6 +9,7 @@ use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; +use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -82,6 +83,9 @@ public function buildForm(FormBuilderInterface $builder, array $options): void return $value; }, ]); + $builder->add('pdfRenderingCss', TextareaType::class, [ + 'label' => 'setono_sylius_gift_card.form.gift_card_configuration.pdf_rendering_css', + ]); $builder->get('defaultValidityPeriod')->addModelTransformer( new CallbackTransformer( function (?string $period): array { diff --git a/src/Generator/GiftCardCodeGenerator.php b/src/Generator/GiftCardCodeGenerator.php index eb9de287..8ec1baaf 100644 --- a/src/Generator/GiftCardCodeGenerator.php +++ b/src/Generator/GiftCardCodeGenerator.php @@ -31,6 +31,7 @@ public function generate(): string do { // if we didn't remove the 'hard to read' characters we would only have to // generate codeLength / 2 bytes because hex uses two characters to represent one byte + /** @psalm-suppress ArgumentTypeCoercion */ $code = bin2hex(random_bytes($this->codeLength)); $code = preg_replace('/[01]/', '', $code); // remove hard to read characters $code = mb_strtoupper(mb_substr($code, 0, $this->codeLength)); diff --git a/src/Generator/GiftCardPdfGenerator.php b/src/Generator/GiftCardPdfGenerator.php index ccde4cb9..cebd272b 100644 --- a/src/Generator/GiftCardPdfGenerator.php +++ b/src/Generator/GiftCardPdfGenerator.php @@ -4,6 +4,7 @@ namespace Setono\SyliusGiftCardPlugin\Generator; +use Gaufrette\FilesystemInterface; use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse; use Knp\Snappy\GeneratorInterface; use Setono\SyliusGiftCardPlugin\Model\GiftCardConfigurationInterface; @@ -19,14 +20,22 @@ class GiftCardPdfGenerator implements GiftCardPdfGeneratorInterface private PdfRenderingOptionsProviderInterface $renderingOptionsProvider; + private GiftCardPdfPathGeneratorInterface $giftCardPdfPathGenerator; + + private FilesystemInterface $filesystem; + public function __construct( Environment $twig, GeneratorInterface $snappy, - PdfRenderingOptionsProviderInterface $renderingOptionsProvider + PdfRenderingOptionsProviderInterface $renderingOptionsProvider, + GiftCardPdfPathGeneratorInterface $giftCardPdfPathGenerator, + FilesystemInterface $filesystem ) { $this->twig = $twig; $this->snappy = $snappy; $this->renderingOptionsProvider = $renderingOptionsProvider; + $this->giftCardPdfPathGenerator = $giftCardPdfPathGenerator; + $this->filesystem = $filesystem; } public function generatePdfResponse( @@ -42,4 +51,39 @@ public function generatePdfResponse( return new PdfResponse($this->snappy->getOutputFromHtml($html, $renderingOptions), 'gift_card.pdf'); } + + public function generateAndGetContent( + GiftCardInterface $giftCard, + GiftCardConfigurationInterface $giftCardChannelConfiguration + ): string { + $html = $this->twig->render('@SetonoSyliusGiftCardPlugin/Shop/GiftCard/pdf.html.twig', [ + 'giftCard' => $giftCard, + 'configuration' => $giftCardChannelConfiguration, + ]); + $renderingOptions = $this->renderingOptionsProvider->getRenderingOptions($giftCardChannelConfiguration); + + return $this->snappy->getOutputFromHtml($html, $renderingOptions); + } + + public function generateAndSavePdf( + GiftCardInterface $giftCard, + GiftCardConfigurationInterface $giftCardChannelConfiguration + ): string { + $html = $this->twig->render('@SetonoSyliusGiftCardPlugin/Shop/GiftCard/pdf.html.twig', [ + 'giftCard' => $giftCard, + 'configuration' => $giftCardChannelConfiguration, + ]); + $renderingOptions = $this->renderingOptionsProvider->getRenderingOptions($giftCardChannelConfiguration); + + $filePath = $this->giftCardPdfPathGenerator->generatePath($giftCardChannelConfiguration); + $pdfContent = $this->snappy->getOutputFromHtml($html, $renderingOptions); + + $this->filesystem->write( + $filePath, + $pdfContent, + true + ); + + return $filePath; + } } diff --git a/src/Generator/GiftCardPdfGeneratorInterface.php b/src/Generator/GiftCardPdfGeneratorInterface.php index b458dc81..b6c3aea8 100644 --- a/src/Generator/GiftCardPdfGeneratorInterface.php +++ b/src/Generator/GiftCardPdfGeneratorInterface.php @@ -14,4 +14,14 @@ public function generatePdfResponse( GiftCardInterface $giftCard, GiftCardConfigurationInterface $giftCardChannelConfiguration ): PdfResponse; + + public function generateAndGetContent( + GiftCardInterface $giftCard, + GiftCardConfigurationInterface $giftCardChannelConfiguration + ): string; + + public function generateAndSavePdf( + GiftCardInterface $giftCard, + GiftCardConfigurationInterface $giftCardChannelConfiguration + ): string; } diff --git a/src/Generator/GiftCardPdfPathGenerator.php b/src/Generator/GiftCardPdfPathGenerator.php new file mode 100644 index 00000000..a55b0218 --- /dev/null +++ b/src/Generator/GiftCardPdfPathGenerator.php @@ -0,0 +1,23 @@ +getId(); + Assert::integer($giftCardChannelConfigurationId); + + return sprintf( + 'gift_card_configuration_pdf_%d.pdf', + $giftCardChannelConfigurationId + ); + } +} diff --git a/src/Generator/GiftCardPdfPathGeneratorInterface.php b/src/Generator/GiftCardPdfPathGeneratorInterface.php new file mode 100644 index 00000000..1908d9b4 --- /dev/null +++ b/src/Generator/GiftCardPdfPathGeneratorInterface.php @@ -0,0 +1,12 @@ +images = new ArrayCollection(); @@ -201,4 +203,14 @@ public function setOrientation(?string $orientation): void { $this->orientation = $orientation; } + + public function getPdfRenderingCss(): ?string + { + return $this->pdfRenderingCss; + } + + public function setPdfRenderingCss(?string $pdfRenderingCss): void + { + $this->pdfRenderingCss = $pdfRenderingCss; + } } diff --git a/src/Model/GiftCardConfigurationInterface.php b/src/Model/GiftCardConfigurationInterface.php index 0dfe6cb7..ec98524e 100644 --- a/src/Model/GiftCardConfigurationInterface.php +++ b/src/Model/GiftCardConfigurationInterface.php @@ -50,4 +50,8 @@ public function setPageSize(?string $pageSize): void; public function getOrientation(): ?string; public function setOrientation(?string $orientation): void; + + public function getPdfRenderingCss(): ?string; + + public function setPdfRenderingCss(?string $pdfRenderingCss): void; } diff --git a/src/Provider/DefaultPdfCssProvider.php b/src/Provider/DefaultPdfCssProvider.php new file mode 100644 index 00000000..30c0abe5 --- /dev/null +++ b/src/Provider/DefaultPdfCssProvider.php @@ -0,0 +1,25 @@ +defaultCssFile = $defaultCssFile; + $this->twig = $twig; + } + + public function getDefaultCss(): string + { + return $this->twig->render($this->defaultCssFile); + } +} diff --git a/src/Provider/DefaultPdfCssProviderInterface.php b/src/Provider/DefaultPdfCssProviderInterface.php new file mode 100644 index 00000000..0492f9cb --- /dev/null +++ b/src/Provider/DefaultPdfCssProviderInterface.php @@ -0,0 +1,10 @@ +> + */ + public function getAvailableOptions(): array + { + return [ + '%logo_url%' => [ + 'hint' => 'setono_sylius_gift_card.form.gift_card_configuration.pdf_live_rendering.css_option.logo_url_hint', + 'accessPath' => 'path', + ], + ]; + } + + public function getOptionsValue(array $context): array + { + $availableOptions = $this->getAvailableOptions(); + $options = []; + foreach ($availableOptions as $optionName => $definition) { + /** @psalm-suppress MixedAssignment */ + $options[$optionName] = $context[$definition['accessPath']]; + } + + return $options; + } + + public function getOptionsHint(): array + { + $availableOptions = $this->getAvailableOptions(); + $hints = []; + foreach ($availableOptions as $optionName => $definition) { + $hints[$optionName] = $definition['hint']; + } + + return $hints; + } +} diff --git a/src/Provider/PdfAvailableCssOptionProviderInterface.php b/src/Provider/PdfAvailableCssOptionProviderInterface.php new file mode 100644 index 00000000..2db2dfd7 --- /dev/null +++ b/src/Provider/PdfAvailableCssOptionProviderInterface.php @@ -0,0 +1,14 @@ + + diff --git a/src/Resources/config/routes/admin_ajax.yaml b/src/Resources/config/routes/admin_ajax.yaml index 60341cca..0f6532a4 100644 --- a/src/Resources/config/routes/admin_ajax.yaml +++ b/src/Resources/config/routes/admin_ajax.yaml @@ -24,3 +24,17 @@ setono_sylius_gift_card_admin_ajax_customer_by_email: repository: method: findBy arguments: [email: $email] + +setono_sylius_gift_card_admin_ajax_generate_pdf: + path: /gift-card-configurations/{id}/generate-pdf + methods: [ PUT ] + controller: setono_sylius_gift_card.controller.action.generate_pdf + requirements: + id: \d+ + +setono_sylius_gift_card_admin_ajax_load_default_pdf_css: + path: /gift-card-configurations/{id}/load-default-pdf-css + methods: [ POST ] + controller: setono_sylius_gift_card.controller.action.load_default_pdf_css + requirements: + id: \d+ diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 7a16b9fb..4c050bb3 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -11,6 +11,7 @@ + @@ -19,6 +20,7 @@ + diff --git a/src/Resources/config/services/controller.xml b/src/Resources/config/services/controller.xml index b0325c7d..7fa10c50 100644 --- a/src/Resources/config/services/controller.xml +++ b/src/Resources/config/services/controller.xml @@ -55,6 +55,20 @@ - + + + + + + + + + + + + + diff --git a/src/Resources/config/services/factory.xml b/src/Resources/config/services/factory.xml index f0232c53..1c6a479e 100644 --- a/src/Resources/config/services/factory.xml +++ b/src/Resources/config/services/factory.xml @@ -11,6 +11,7 @@ + + + + + + %setono_sylius_gift_card.code_length% + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/services/misc.xml b/src/Resources/config/services/misc.xml index 8de4e97f..695852c4 100644 --- a/src/Resources/config/services/misc.xml +++ b/src/Resources/config/services/misc.xml @@ -3,29 +3,6 @@ xmlns="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - - %setono_sylius_gift_card.code_length% - - - - - - - - - - - - - - - diff --git a/src/Resources/config/services/provider.xml b/src/Resources/config/services/provider.xml index 957d3cbd..df93bdaa 100644 --- a/src/Resources/config/services/provider.xml +++ b/src/Resources/config/services/provider.xml @@ -15,18 +15,31 @@ - + alias="setono_sylius_gift_card.provider.date_period_unit"/> + + + + + + %setono_sylius_gift_card.default_css_file% + + + diff --git a/src/Resources/config/services/twig.xml b/src/Resources/config/services/twig.xml new file mode 100644 index 00000000..3ea4f75c --- /dev/null +++ b/src/Resources/config/services/twig.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/public/setono-sylius-gift-card-live-pdf-rendering.js b/src/Resources/public/setono-sylius-gift-card-live-pdf-rendering.js new file mode 100644 index 00000000..2b87d375 --- /dev/null +++ b/src/Resources/public/setono-sylius-gift-card-live-pdf-rendering.js @@ -0,0 +1,42 @@ +(function ($) { + 'use strict'; + + $.fn.extend({ + applyPdfChanges: function () { + const $element = $(this); + const url = $element.data('url'); + + $element.on('click', function (event) { + event.preventDefault(); + + $.ajax(url, { + method: 'POST', + data: $('form[name="setono_sylius_gift_card_gift_card_configuration"]').serialize(), + success(response) { + $('.js-ssgc-live-render-container').html(``); + }, + }); + }); + }, + }); + + $.fn.extend({ + loadDefaultPdfCss: function () { + const $element = $(this); + const url = $element.data('url'); + + $element.on('click', function (event) { + event.preventDefault(); + + $.ajax(url, { + method: 'POST', + success(response) { + $('#setono_sylius_gift_card_gift_card_configuration_pdfRenderingCss').val(response.css); + const src = document.getElementById('js-ssgc-live-render-box').src; + $('.js-ssgc-live-render-container').html(``); + }, + }); + }); + }, + }); +})(jQuery); diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml index a2e46faa..8fba0f36 100644 --- a/src/Resources/translations/messages.en.yml +++ b/src/Resources/translations/messages.en.yml @@ -27,6 +27,11 @@ setono_sylius_gift_card: default: Default configuration default_validity_period: Default validity period enabled: Enabled + pdf_live_rendering: + css_option: + logo_url_hint: Displays the path of the logo URL + pdf_rendering_css: CSS used to render the PDF + pdf_rendering_html: HTML used to render the PDF product: gift_card: Gift card gift_card_amount_configurable: Gift card amount configurable @@ -35,6 +40,7 @@ setono_sylius_gift_card: add_gift_card_to_order: Add gift card amount: Amount applied_gift_cards: Applied gift cards + apply: Apply CSS average_amount: Average amount bought_at_order: Bought at order channel_configurations: Channel configurations @@ -48,16 +54,21 @@ setono_sylius_gift_card: enter_gift_card_code: Enter gift card code edit_gift_card_configuration: Edit gift card configuration gift_card: Gift card - gift_card_configurations: Gift card configurations - gift_cards_balance: Gift cards balance gift_card_channel_does_not_match_channel: The gift card was bought on another channel, please use the gift card on %channel% gift_card_code_is_invalid: Gift card code is invalid + gift_card_configuration: + pdf_live_rendering: + hint_title: Available variables + title: PDF Live rendering + gift_card_configurations: Gift card configurations gift_cards: Gift cards gift_card_search: Gift card search gift_cards_adjustment_total: Gift cards total + gift_cards_balance: Gift cards balance has_no_gift_card: Has no gift card initial_amount: Initial amount list_orders: List orders + load_default_css: Load default CSS manage_gift_cards: Manage gift cards manage_gift_card_configurations: Manage gift card configurations new_gift_card: New gift card diff --git a/src/Resources/views/Admin/GiftCardConfiguration/Update/_javascripts.html.twig b/src/Resources/views/Admin/GiftCardConfiguration/Update/_javascripts.html.twig new file mode 100644 index 00000000..0e5da75b --- /dev/null +++ b/src/Resources/views/Admin/GiftCardConfiguration/Update/_javascripts.html.twig @@ -0,0 +1,7 @@ + + diff --git a/src/Resources/views/Admin/GiftCardConfiguration/_form.html.twig b/src/Resources/views/Admin/GiftCardConfiguration/_form.html.twig index 1ccd3219..1ed821b9 100644 --- a/src/Resources/views/Admin/GiftCardConfiguration/_form.html.twig +++ b/src/Resources/views/Admin/GiftCardConfiguration/_form.html.twig @@ -25,3 +25,45 @@ + +
+ {{ 'setono_sylius_gift_card.ui.gift_card_configuration.pdf_live_rendering.title'|trans }} + +
+
+
+
{{ 'setono_sylius_gift_card.ui.gift_card_configuration.pdf_live_rendering.hint_title'|trans }}
+
    + {% for optionName, hint in ssgc_get_pdf_options_hint() %} +
  • {{ optionName }} - {{ hint|trans }}
  • + {% endfor %} +
+
+ {{ form_row(form.pdfRenderingCss) }} +
+ {% if resource is not null %} + + + {% endif %} +
+ + + + +
+
+ +
+
+
diff --git a/src/Resources/views/Shop/GiftCard/defaultGiftCardConfiguration.css.twig b/src/Resources/views/Shop/GiftCard/defaultGiftCardConfiguration.css.twig new file mode 100644 index 00000000..80077620 --- /dev/null +++ b/src/Resources/views/Shop/GiftCard/defaultGiftCardConfiguration.css.twig @@ -0,0 +1,10 @@ +body { + font-family: sans-serif; + font-size: 3rem; + background: transparent url("%logo_url%") no-repeat left bottom; + background-size: 100px; +} + +img { + display: block; +} diff --git a/src/Resources/views/Shop/GiftCard/pdf.html.twig b/src/Resources/views/Shop/GiftCard/pdf.html.twig index 67edf041..3f39bf26 100644 --- a/src/Resources/views/Shop/GiftCard/pdf.html.twig +++ b/src/Resources/views/Shop/GiftCard/pdf.html.twig @@ -9,16 +9,7 @@ diff --git a/src/Twig/Extension/PdfExtension.php b/src/Twig/Extension/PdfExtension.php new file mode 100644 index 00000000..b8ca3539 --- /dev/null +++ b/src/Twig/Extension/PdfExtension.php @@ -0,0 +1,28 @@ +cssOptionProvider = $cssOptionProvider; + $this->twig = $twig; + $this->giftCardPdfGenerator = $giftCardPdfGenerator; + $this->giftCardFactory = $giftCardFactory; + } + + public function replaceCssOptions(string $rawCss, array $twigContext): string + { + $options = $this->cssOptionProvider->getOptionsValue($twigContext); + + try { + /** @psalm-suppress UndefinedFunction */ + $replacedCss = twig_replace_filter($rawCss, $options); + Assert::string($replacedCss); + + return $replacedCss; + } catch (RuntimeError $e) { + return $rawCss; + } + } + + public function getOptionsHint(): array + { + return $this->cssOptionProvider->getOptionsHint(); + } + + public function getPdfTemplateContent(): string + { + $templateWrapper = $this->twig->load('@SetonoSyliusGiftCardPlugin/Shop/GiftCard/pdf.html.twig'); + \preg_match( + '/(.|\n)*<\/body>/', + $templateWrapper->getSourceContext()->getCode(), + $bodyContent + ); + + $domCrawler = new Crawler($bodyContent[0]); + + return $domCrawler->filter('html > body')->html(); + } + + public function getBase64EncodedExamplePdfContent(GiftCardConfigurationInterface $giftCardChannelConfiguration): string + { + $giftCard = $this->giftCardFactory->createExample(); + + $content = $this->giftCardPdfGenerator->generateAndGetContent($giftCard, $giftCardChannelConfiguration); + + return \base64_encode($content); + } +} diff --git a/tests/Unit/Controller/Action/Admin/GenerateEncodedExamplePdfActionTest.php b/tests/Unit/Controller/Action/Admin/GenerateEncodedExamplePdfActionTest.php new file mode 100644 index 00000000..a75c34ac --- /dev/null +++ b/tests/Unit/Controller/Action/Admin/GenerateEncodedExamplePdfActionTest.php @@ -0,0 +1,55 @@ +prophesize(GiftCardFactoryInterface::class); + $exampleGiftCardFactory->createExample()->willReturn($giftCard); + $giftCardConfiguration = new GiftCardConfiguration(); + $giftCardConfigurationRepository = $this->prophesize(RepositoryInterface::class); + $giftCardConfigurationRepository->find($id)->willReturn($giftCardConfiguration); + + $request = new Request(); + $form = $this->prophesize(FormInterface::class); + $formFactory = $this->prophesize(FormFactoryInterface::class); + $formFactory->create(GiftCardConfigurationType::class, $giftCardConfiguration)->willReturn($form); + $form->handleRequest($request)->shouldBeCalled(); + + $giftCardPdfGenerator = $this->prophesize(GiftCardPdfGeneratorInterface::class); + $giftCardPdfGenerator->generateAndGetContent($giftCard, $giftCardConfiguration)->shouldBeCalled(); + + $action = new GenerateEncodedExamplePdfAction( + $exampleGiftCardFactory->reveal(), + $giftCardConfigurationRepository->reveal(), + $giftCardPdfGenerator->reveal(), + $formFactory->reveal() + ); + $response = $action($request, $id); + $this->assertEquals(null, $response->getContent()); + } +} diff --git a/tests/Unit/Controller/Action/Admin/LoadDefaultPdfCssActionTest.php b/tests/Unit/Controller/Action/Admin/LoadDefaultPdfCssActionTest.php new file mode 100644 index 00000000..5f1d8f0f --- /dev/null +++ b/tests/Unit/Controller/Action/Admin/LoadDefaultPdfCssActionTest.php @@ -0,0 +1,49 @@ +prophesize(GiftCardFactoryInterface::class); + $exampleGiftCardFactory->createExample()->willReturn($giftCard); + $giftCardConfiguration = new GiftCardConfiguration(); + $giftCardConfigurationRepository = $this->prophesize(RepositoryInterface::class); + $giftCardConfigurationRepository->find($id)->willReturn($giftCardConfiguration); + $defaultCssProvider = $this->prophesize(DefaultPdfCssProviderInterface::class); + $defaultCssProvider->getDefaultCss()->willReturn('body {background-color: red;}'); + + $giftCardPdfGenerator = $this->prophesize(GiftCardPdfGeneratorInterface::class); + $giftCardPdfGenerator->generateAndGetContent($giftCard, $giftCardConfiguration)->shouldBeCalled(); + + $action = new LoadDefaultPdfCssAction( + $exampleGiftCardFactory->reveal(), + $giftCardConfigurationRepository->reveal(), + $giftCardPdfGenerator->reveal(), + $defaultCssProvider->reveal() + ); + $jsonResponse = $action($id); + $response = \json_decode($jsonResponse->getContent(), true); + $this->assertEquals('body {background-color: red;}', $response['css']); + } +} diff --git a/tests/Unit/Factory/GiftCardFactoryTest.php b/tests/Unit/Factory/GiftCardFactoryTest.php index 3e1ca617..2f7fda8f 100644 --- a/tests/Unit/Factory/GiftCardFactoryTest.php +++ b/tests/Unit/Factory/GiftCardFactoryTest.php @@ -16,6 +16,7 @@ use Sylius\Bundle\ShippingBundle\Provider\DateTimeProvider; use Sylius\Component\Core\Model\Channel; use Sylius\Component\Core\Model\Customer; +use Sylius\Component\Currency\Context\CurrencyContextInterface; use Sylius\Component\Resource\Factory\FactoryInterface; use Tests\Setono\SyliusGiftCardPlugin\Application\Model\Order; use Tests\Setono\SyliusGiftCardPlugin\Application\Model\OrderItem; @@ -35,6 +36,7 @@ public function it_creates_a_new_gift_card_with_code(): void $decoratedFactory = $this->prophesize(FactoryInterface::class); $giftCardCodeGenerator = $this->prophesize(GiftCardCodeGeneratorInterface::class); $configurationProvider = $this->prophesize(GiftCardChannelConfigurationProviderInterface::class); + $currencyContext = $this->prophesize(CurrencyContextInterface::class); $decoratedFactory->createNew()->willReturn($giftCard); $giftCardCodeGenerator->generate()->willReturn('super-code'); @@ -43,7 +45,8 @@ public function it_creates_a_new_gift_card_with_code(): void $decoratedFactory->reveal(), $giftCardCodeGenerator->reveal(), $configurationProvider->reveal(), - new Calendar() + new Calendar(), + $currencyContext->reveal() ); $createdGiftCard = $factory->createNew(); @@ -62,6 +65,7 @@ public function it_creates_a_new_gift_card_for_channel(): void $decoratedFactory = $this->prophesize(FactoryInterface::class); $giftCardCodeGenerator = $this->prophesize(GiftCardCodeGeneratorInterface::class); $configurationProvider = $this->prophesize(GiftCardChannelConfigurationProviderInterface::class); + $currencyContext = $this->prophesize(CurrencyContextInterface::class); $decoratedFactory->createNew()->willReturn($giftCard); $giftCardCodeGenerator->generate()->willReturn('super-code'); @@ -70,7 +74,8 @@ public function it_creates_a_new_gift_card_for_channel(): void $decoratedFactory->reveal(), $giftCardCodeGenerator->reveal(), $configurationProvider->reveal(), - new Calendar() + new Calendar(), + $currencyContext->reveal() ); $createdGiftCard = $factory->createForChannel($channel); @@ -94,6 +99,7 @@ public function it_creates_a_new_gift_card_for_channel_with_expiration_date(): v $giftCardCodeGenerator = $this->prophesize(GiftCardCodeGeneratorInterface::class); $configurationProvider = $this->prophesize(GiftCardChannelConfigurationProviderInterface::class); $calendar = $this->prophesize(DateTimeProvider::class); + $currencyContext = $this->prophesize(CurrencyContextInterface::class); $decoratedFactory->createNew()->willReturn($giftCard); $giftCardCodeGenerator->generate()->willReturn('super-code'); @@ -104,7 +110,8 @@ public function it_creates_a_new_gift_card_for_channel_with_expiration_date(): v $decoratedFactory->reveal(), $giftCardCodeGenerator->reveal(), $configurationProvider->reveal(), - $calendar->reveal() + $calendar->reveal(), + $currencyContext->reveal() ); $createdGiftCard = $factory->createForChannel($channel); @@ -124,6 +131,7 @@ public function it_creates_a_new_gift_card_for_channel_from_admin(): void $decoratedFactory = $this->prophesize(FactoryInterface::class); $giftCardCodeGenerator = $this->prophesize(GiftCardCodeGeneratorInterface::class); $configurationProvider = $this->prophesize(GiftCardChannelConfigurationProviderInterface::class); + $currencyContext = $this->prophesize(CurrencyContextInterface::class); $decoratedFactory->createNew()->willReturn($giftCard); $giftCardCodeGenerator->generate()->willReturn('super-code'); @@ -132,7 +140,8 @@ public function it_creates_a_new_gift_card_for_channel_from_admin(): void $decoratedFactory->reveal(), $giftCardCodeGenerator->reveal(), $configurationProvider->reveal(), - new Calendar() + new Calendar(), + $currencyContext->reveal() ); $createdGiftCard = $factory->createForChannelFromAdmin($channel); @@ -158,6 +167,7 @@ public function it_creates_a_new_gift_card_for_order_item_unit_and_cart(): void $decoratedFactory = $this->prophesize(FactoryInterface::class); $giftCardCodeGenerator = $this->prophesize(GiftCardCodeGeneratorInterface::class); $configurationProvider = $this->prophesize(GiftCardChannelConfigurationProviderInterface::class); + $currencyContext = $this->prophesize(CurrencyContextInterface::class); $decoratedFactory->createNew()->willReturn($giftCard); $giftCardCodeGenerator->generate()->willReturn('super-code'); @@ -166,7 +176,8 @@ public function it_creates_a_new_gift_card_for_order_item_unit_and_cart(): void $decoratedFactory->reveal(), $giftCardCodeGenerator->reveal(), $configurationProvider->reveal(), - new Calendar() + new Calendar(), + $currencyContext->reveal() ); $createdGiftCard = $factory->createFromOrderItemUnitAndCart($orderItemUnit->reveal(), $cart); @@ -202,6 +213,7 @@ public function it_creates_a_new_gift_card_for_order_item_unit(): void $decoratedFactory = $this->prophesize(FactoryInterface::class); $giftCardCodeGenerator = $this->prophesize(GiftCardCodeGeneratorInterface::class); $configurationProvider = $this->prophesize(GiftCardChannelConfigurationProviderInterface::class); + $currencyContext = $this->prophesize(CurrencyContextInterface::class); $decoratedFactory->createNew()->willReturn($giftCard); $giftCardCodeGenerator->generate()->willReturn('super-code'); @@ -210,7 +222,8 @@ public function it_creates_a_new_gift_card_for_order_item_unit(): void $decoratedFactory->reveal(), $giftCardCodeGenerator->reveal(), $configurationProvider->reveal(), - new Calendar() + new Calendar(), + $currencyContext->reveal() ); $createdGiftCard = $factory->createFromOrderItemUnit($orderItemUnit->reveal()); @@ -224,4 +237,31 @@ public function it_creates_a_new_gift_card_for_order_item_unit(): void $this->assertFalse($createdGiftCard->isEnabled()); $this->assertSame(GiftCardInterface::ORIGIN_ORDER, $createdGiftCard->getOrigin()); } + + /** + * @test + */ + public function it_creates_example_gift_card(): void + { + $decoratedFactory = $this->prophesize(FactoryInterface::class); + $giftCardCodeGenerator = $this->prophesize(GiftCardCodeGeneratorInterface::class); + $configurationProvider = $this->prophesize(GiftCardChannelConfigurationProviderInterface::class); + $currencyContext = $this->prophesize(CurrencyContextInterface::class); + + $giftCard = new GiftCard(); + $decoratedFactory->createNew()->willReturn($giftCard); + $giftCardCodeGenerator->generate()->willReturn('super-code'); + $currencyContext->getCurrencyCode()->willReturn('USD'); + + $factory = new GiftCardFactory( + $decoratedFactory->reveal(), + $giftCardCodeGenerator->reveal(), + $configurationProvider->reveal(), + new Calendar(), + $currencyContext->reveal() + ); + $returnedGiftCard = $factory->createExample(); + $this->assertEquals(1500, $returnedGiftCard->getAmount()); + $this->assertEquals('USD', $returnedGiftCard->getCurrencyCode()); + } } diff --git a/tests/Unit/Generator/GiftCardCodeGeneratorTest.php b/tests/Unit/Generator/GiftCardCodeGeneratorTest.php new file mode 100644 index 00000000..68dc19f5 --- /dev/null +++ b/tests/Unit/Generator/GiftCardCodeGeneratorTest.php @@ -0,0 +1,29 @@ +prophesize(GiftCardRepositoryInterface::class); + + $giftCardCodeGenerator = new GiftCardCodeGenerator($giftCardRepository->reveal(), $codeLength); + + $code = $giftCardCodeGenerator->generate(); + $this->assertEquals($codeLength, \strlen($code)); + } +} diff --git a/tests/Unit/Generator/GiftCardPdfGeneratorTest.php b/tests/Unit/Generator/GiftCardPdfGeneratorTest.php index 150d3863..a4ac659a 100644 --- a/tests/Unit/Generator/GiftCardPdfGeneratorTest.php +++ b/tests/Unit/Generator/GiftCardPdfGeneratorTest.php @@ -4,10 +4,12 @@ namespace Tests\Setono\SyliusGiftCardPlugin\Unit\Generator; +use Gaufrette\Filesystem; use Knp\Snappy\Pdf; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use Setono\SyliusGiftCardPlugin\Generator\GiftCardPdfGenerator; +use Setono\SyliusGiftCardPlugin\Generator\GiftCardPdfPathGeneratorInterface; use Setono\SyliusGiftCardPlugin\Model\GiftCard; use Setono\SyliusGiftCardPlugin\Model\GiftCardConfiguration; use Setono\SyliusGiftCardPlugin\Provider\PdfRenderingOptionsProviderInterface; @@ -28,6 +30,8 @@ public function it_generates_pdf_response_for_gift_card(): void $twig = $this->prophesize(Environment::class); $snappy = $this->prophesize(Pdf::class); $renderingOptionsProvider = $this->prophesize(PdfRenderingOptionsProviderInterface::class); + $pathGenerator = $this->prophesize(GiftCardPdfPathGeneratorInterface::class); + $filesystem = $this->prophesize(Filesystem::class); $renderingOptionsProvider->getRenderingOptions($giftCardChannelConfiguration)->willReturn([]); $twig->render('@SetonoSyliusGiftCardPlugin/Shop/GiftCard/pdf.html.twig', [ @@ -39,11 +43,86 @@ public function it_generates_pdf_response_for_gift_card(): void $giftCardPdfGenerator = new GiftCardPdfGenerator( $twig->reveal(), $snappy->reveal(), - $renderingOptionsProvider->reveal() + $renderingOptionsProvider->reveal(), + $pathGenerator->reveal(), + $filesystem->reveal() ); $response = $giftCardPdfGenerator->generatePdfResponse($giftCard, $giftCardChannelConfiguration); $this->assertEquals('application/pdf', $response->headers->get('Content-type')); $this->assertEquals('super GiftCard template', $response->getContent()); } + + /** + * @test + */ + public function it_generates_pdf_and_gets_content(): void + { + $giftCard = new GiftCard(); + $giftCardChannelConfiguration = new GiftCardConfiguration(); + + $twig = $this->prophesize(Environment::class); + $snappy = $this->prophesize(Pdf::class); + $renderingOptionsProvider = $this->prophesize(PdfRenderingOptionsProviderInterface::class); + $pathGenerator = $this->prophesize(GiftCardPdfPathGeneratorInterface::class); + $filesystem = $this->prophesize(Filesystem::class); + + $renderingOptionsProvider->getRenderingOptions($giftCardChannelConfiguration)->willReturn([]); + $twig->render('@SetonoSyliusGiftCardPlugin/Shop/GiftCard/pdf.html.twig', [ + 'giftCard' => $giftCard, + 'configuration' => $giftCardChannelConfiguration, + ])->willReturn('super GiftCard template'); + $snappy->getOutputFromHtml('super GiftCard template', [])->willReturn('super GiftCard template'); + + $giftCardPdfGenerator = new GiftCardPdfGenerator( + $twig->reveal(), + $snappy->reveal(), + $renderingOptionsProvider->reveal(), + $pathGenerator->reveal(), + $filesystem->reveal() + ); + $content = $giftCardPdfGenerator->generateAndGetContent($giftCard, $giftCardChannelConfiguration); + + $this->assertEquals('super GiftCard template', $content); + } + + /** + * @test + */ + public function it_generates_and_saves_pdf(): void + { + $giftCard = new GiftCard(); + $giftCardChannelConfiguration = new GiftCardConfiguration(); + + $twig = $this->prophesize(Environment::class); + $snappy = $this->prophesize(Pdf::class); + $renderingOptionsProvider = $this->prophesize(PdfRenderingOptionsProviderInterface::class); + $pathGenerator = $this->prophesize(GiftCardPdfPathGeneratorInterface::class); + $pathGenerator->generatePath($giftCardChannelConfiguration)->willReturn('super/path.pdf'); + $filesystem = $this->prophesize(Filesystem::class); + + $renderingOptionsProvider->getRenderingOptions($giftCardChannelConfiguration)->willReturn([]); + $twig->render('@SetonoSyliusGiftCardPlugin/Shop/GiftCard/pdf.html.twig', [ + 'giftCard' => $giftCard, + 'configuration' => $giftCardChannelConfiguration, + ])->willReturn('super GiftCard template'); + $snappy->getOutputFromHtml('super GiftCard template', [])->willReturn('super GiftCard template'); + + $filesystem->write( + 'super/path.pdf', + 'super GiftCard template', + true + )->shouldBeCalled(); + + $giftCardPdfGenerator = new GiftCardPdfGenerator( + $twig->reveal(), + $snappy->reveal(), + $renderingOptionsProvider->reveal(), + $pathGenerator->reveal(), + $filesystem->reveal() + ); + $filePath = $giftCardPdfGenerator->generateAndSavePdf($giftCard, $giftCardChannelConfiguration); + + $this->assertEquals('super/path.pdf', $filePath); + } } diff --git a/tests/Unit/Generator/GiftCardPdfPathGeneratorTest.php b/tests/Unit/Generator/GiftCardPdfPathGeneratorTest.php new file mode 100644 index 00000000..f5ba1f45 --- /dev/null +++ b/tests/Unit/Generator/GiftCardPdfPathGeneratorTest.php @@ -0,0 +1,29 @@ +prophesize(GiftCardConfigurationInterface::class); + $giftCardChannelConfiguration->getId()->willReturn(10); + + $giftCardPdfPathGenerator = new GiftCardPdfPathGenerator(); + $path = $giftCardPdfPathGenerator->generatePath($giftCardChannelConfiguration->reveal()); + + $this->assertEquals('gift_card_configuration_pdf_10.pdf', $path); + } +} diff --git a/tests/Unit/Model/GiftCardConfigurationTest.php b/tests/Unit/Model/GiftCardConfigurationTest.php index 750a655e..b6ea52f0 100644 --- a/tests/Unit/Model/GiftCardConfigurationTest.php +++ b/tests/Unit/Model/GiftCardConfigurationTest.php @@ -30,5 +30,8 @@ public function it_has_properties(): void $giftCardConfiguration->setOrientation('Landscape'); $this->assertEquals('Landscape', $giftCardConfiguration->getOrientation()); + + $giftCardConfiguration->setPdfRenderingCss('body { color: red; }'); + $this->assertEquals('body { color: red; }', $giftCardConfiguration->getPdfRenderingCss()); } } diff --git a/tests/Unit/Provider/DefaultPdfCssProviderTest.php b/tests/Unit/Provider/DefaultPdfCssProviderTest.php new file mode 100644 index 00000000..c3754606 --- /dev/null +++ b/tests/Unit/Provider/DefaultPdfCssProviderTest.php @@ -0,0 +1,30 @@ +prophesize(Environment::class); + $twig->render($cssTemplateFile)->willReturn($css); + + $defaultPdfCssProvider = new DefaultPdfCssProvider($cssTemplateFile, $twig->reveal()); + + $this->assertEquals($css, $defaultPdfCssProvider->getDefaultCss()); + } +} diff --git a/tests/Unit/Provider/PdfAvailableCssOptionProviderTest.php b/tests/Unit/Provider/PdfAvailableCssOptionProviderTest.php new file mode 100644 index 00000000..e1b9485c --- /dev/null +++ b/tests/Unit/Provider/PdfAvailableCssOptionProviderTest.php @@ -0,0 +1,65 @@ + [ + 'hint' => 'setono_sylius_gift_card.form.gift_card_configuration.pdf_live_rendering.css_option.logo_url_hint', + 'accessPath' => 'path', + ], + ]; + $result = $pdfAvailableCssOptionProvider->getAvailableOptions(); + + $this->assertEquals($expected, $result); + } + + /** + * @test + */ + public function it_provides_options_value(): void + { + $pdfAvailableCssOptionProvider = new PdfAvailableCssOptionProvider(); + + $expected = [ + '%logo_url%' => 'https://test.com', + ]; + $context = [ + 'path' => 'https://test.com', + ]; + $result = $pdfAvailableCssOptionProvider->getOptionsValue($context); + + $this->assertEquals($expected, $result); + } + + /** + * @test + */ + public function it_provides_options_hint(): void + { + $pdfAvailableCssOptionProvider = new PdfAvailableCssOptionProvider(); + + $expected = [ + '%logo_url%' => 'setono_sylius_gift_card.form.gift_card_configuration.pdf_live_rendering.css_option.logo_url_hint', + ]; + $result = $pdfAvailableCssOptionProvider->getOptionsHint(); + + $this->assertEquals($expected, $result); + } +} diff --git a/tests/Unit/Twig/Extension/PdfRuntimeTest.php b/tests/Unit/Twig/Extension/PdfRuntimeTest.php new file mode 100644 index 00000000..b852b871 --- /dev/null +++ b/tests/Unit/Twig/Extension/PdfRuntimeTest.php @@ -0,0 +1,47 @@ +prophesize(PdfAvailableCssOptionProviderInterface::class); + $twig = $this->prophesize(Environment::class); + $giftCardPdfGenerator = $this->prophesize(GiftCardPdfGeneratorInterface::class); + $giftCardPdfGenerator->generateAndGetContent($giftCard, $giftCardConfiguration)->willReturn($pdfContent); + $giftCardFactory = $this->prophesize(GiftCardFactoryInterface::class); + $giftCardFactory->createExample()->willReturn($giftCard); + + $runtime = new PdfRuntime( + $cssOptionProvider->reveal(), + $twig->reveal(), + $giftCardPdfGenerator->reveal(), + $giftCardFactory->reveal() + ); + $base64Content = $runtime->getBase64EncodedExamplePdfContent($giftCardConfiguration); + + $this->assertEquals(\base64_encode($pdfContent), $base64Content); + } +}