diff --git a/features/admin/adding_block.feature b/features/admin/adding_block.feature index 61550185..29dd78df 100644 --- a/features/admin/adding_block.feature +++ b/features/admin/adding_block.feature @@ -180,3 +180,16 @@ Feature: Adding blocks And I try to add it Then I should be notified that "Code, Name" fields are too long + @ui @javascript + Scenario: Adding block with template + Given there is an existing template named "Homepage" with "Block" type that contains "Textarea, Single media" content elements + When I go to the create block page + And I fill the code with "intro" + And I fill the name with "Intro" + And I select "Homepage" template + And I click button to use this template + And I confirm that I want to use this template + And I add it + Then I should be notified that the block has been created + And I should see newly created "Textarea" content element in Content elements section + And I should see newly created "Single media" content element in Content elements section diff --git a/features/admin/adding_template.feature b/features/admin/adding_template.feature index ef850a27..3197fb24 100644 --- a/features/admin/adding_template.feature +++ b/features/admin/adding_template.feature @@ -9,13 +9,21 @@ Feature: Adding cms templates And I am logged in as an administrator @ui - Scenario: Creating template + Scenario: Creating template with type page When I go to the create template page And I fill the name with "Test template" And I choose "Page" in Type field And I add it Then I should be notified that the template has been created + @ui + Scenario: Creating template with type block + When I go to the create template page + And I fill the name with "Test template" + And I choose "Block" in Type field + And I add it + Then I should be notified that the template has been created + @ui @javascript Scenario: Creating template with content elements When I go to the create template page diff --git a/spec/Menu/ContentManagementMenuBuilderSpec.php b/spec/Menu/ContentManagementMenuBuilderSpec.php index 19273075..c964b651 100755 --- a/spec/Menu/ContentManagementMenuBuilderSpec.php +++ b/spec/Menu/ContentManagementMenuBuilderSpec.php @@ -65,6 +65,11 @@ public function it_build_menu( $cmsRootMenuItem->setLabel('bitbag_sylius_cms_plugin.ui.media')->willReturn($cmsRootMenuItem); $cmsRootMenuItem->setLabelAttribute('icon', 'file')->shouldBeCalled(); + $menu->getChildren()->willReturn(['marketing' => $cmsRootMenuItem]); + $menu->getChild('bitbag_cms')->willReturn($cmsRootMenuItem); + + $menu->setChildren(['marketing' => $cmsRootMenuItem, 'bitbag_cms' => $cmsRootMenuItem])->willReturn($menu); + $this->buildMenu($menuBuilderEvent); } } diff --git a/src/Entity/Page.php b/src/Entity/Page.php index afad49de..d7854eba 100755 --- a/src/Entity/Page.php +++ b/src/Entity/Page.php @@ -29,11 +29,11 @@ class Page implements PageInterface __construct as protected initializeTranslationsCollection; } - protected ?int $id; + protected ?int $id = null; protected ?string $code = null; - protected ?string $name; + protected ?string $name = null; protected ?\DateTimeImmutable $publishAt; diff --git a/src/Entity/PageTranslation.php b/src/Entity/PageTranslation.php index 245e23ac..303355a3 100755 --- a/src/Entity/PageTranslation.php +++ b/src/Entity/PageTranslation.php @@ -18,7 +18,7 @@ class PageTranslation extends AbstractTranslation implements PageTranslationInte protected ?string $slug = null; - protected ?string $title; + protected ?string $title = null; protected ?string $metaKeywords; diff --git a/src/Form/Type/BlockType.php b/src/Form/Type/BlockType.php index d2472dc3..6713a45c 100755 --- a/src/Form/Type/BlockType.php +++ b/src/Form/Type/BlockType.php @@ -72,6 +72,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'multiple' => true, 'help' => 'bitbag_sylius_cms_plugin.ui.display_for_taxons.help', ]) + ->add('template', TemplateBlockAutocompleteChoiceType::class, [ + 'label' => false, + 'mapped' => false, + ]) ; } diff --git a/src/Form/Type/TemplateBlockAutocompleteChoiceType.php b/src/Form/Type/TemplateBlockAutocompleteChoiceType.php new file mode 100644 index 00000000..c0737319 --- /dev/null +++ b/src/Form/Type/TemplateBlockAutocompleteChoiceType.php @@ -0,0 +1,19 @@ + 'bitbag_sylius_cms_plugin.ui.type', 'choices' => [ 'bitbag_sylius_cms_plugin.ui.page' => 'page', + 'bitbag_sylius_cms_plugin.ui.block' => 'block', ], ]) ->add('contentElements', CollectionType::class, [ diff --git a/src/Menu/ContentManagementMenuBuilder.php b/src/Menu/ContentManagementMenuBuilder.php index 8862823f..c0e25f11 100755 --- a/src/Menu/ContentManagementMenuBuilder.php +++ b/src/Menu/ContentManagementMenuBuilder.php @@ -10,6 +10,7 @@ namespace BitBag\SyliusCmsPlugin\Menu; +use Knp\Menu\ItemInterface; use Sylius\Bundle\UiBundle\Menu\Event\MenuBuilderEvent; final class ContentManagementMenuBuilder @@ -62,5 +63,29 @@ public function buildMenu(MenuBuilderEvent $menuBuilderEvent): void ->setLabel('bitbag_sylius_cms_plugin.ui.media') ->setLabelAttribute('icon', 'file') ; + + $this->reorderMenu($menu, 'bitbag_cms', 'marketing'); + } + + private function reorderMenu(ItemInterface $menu, string $newItemKey, string $targetItemKey): void + { + $menuItems = $menu->getChildren(); + + $newMenuItem = $menu->getChild($newItemKey); + unset($menuItems[$newItemKey]); + + $targetPosition = array_search($targetItemKey, array_keys($menuItems), true); + + if (null !== $newMenuItem && false !== $targetPosition) { + $menuItems = array_slice($menuItems, 0, $targetPosition + 1, true) + + [$newItemKey => $newMenuItem] + + array_slice($menuItems, $targetPosition + 1, null, true); + + $menuItems = array_filter($menuItems, static function ($item) { + return $item instanceof ItemInterface; + }); + + $menu->setChildren($menuItems); + } } } diff --git a/src/Renderer/ContentElement/ProductsCarouselByTaxonContentElementRenderer.php b/src/Renderer/ContentElement/ProductsCarouselByTaxonContentElementRenderer.php index 59d8c573..14a47edb 100644 --- a/src/Renderer/ContentElement/ProductsCarouselByTaxonContentElementRenderer.php +++ b/src/Renderer/ContentElement/ProductsCarouselByTaxonContentElementRenderer.php @@ -35,8 +35,12 @@ public function render(ContentConfigurationInterface $contentConfiguration): str { $taxonCode = $contentConfiguration->getConfiguration()['products_carousel_by_taxon']; - /** @var TaxonInterface $taxon */ + /** @var TaxonInterface|null $taxon */ $taxon = $this->taxonRepository->findOneBy(['code' => $taxonCode]); + if (null === $taxon) { + return ''; + } + $products = $this->productRepository->findByTaxon($taxon); return $this->twig->render('@BitBagSyliusCmsPlugin/Shop/ContentElement/index.html.twig', [ diff --git a/src/Renderer/ContentElement/ProductsCarouselContentElementRenderer.php b/src/Renderer/ContentElement/ProductsCarouselContentElementRenderer.php index 83d014e5..180290e9 100644 --- a/src/Renderer/ContentElement/ProductsCarouselContentElementRenderer.php +++ b/src/Renderer/ContentElement/ProductsCarouselContentElementRenderer.php @@ -33,6 +33,9 @@ public function render(ContentConfigurationInterface $contentConfiguration): str $configuration = $contentConfiguration->getConfiguration(); $productsCodes = $configuration['products_carousel']['products']; $products = $this->productRepository->findBy(['code' => $productsCodes]); + if (empty($products)) { + return ''; + } return $this->twig->render('@BitBagSyliusCmsPlugin/Shop/ContentElement/index.html.twig', [ 'content_element' => '@BitBagSyliusCmsPlugin/Shop/ContentElement/_products_carousel.html.twig', diff --git a/src/Renderer/ContentElement/ProductsGridByTaxonContentElementRenderer.php b/src/Renderer/ContentElement/ProductsGridByTaxonContentElementRenderer.php index 3877ddda..c704ae08 100644 --- a/src/Renderer/ContentElement/ProductsGridByTaxonContentElementRenderer.php +++ b/src/Renderer/ContentElement/ProductsGridByTaxonContentElementRenderer.php @@ -35,8 +35,12 @@ public function render(ContentConfigurationInterface $contentConfiguration): str { $taxonCode = $contentConfiguration->getConfiguration()['products_grid_by_taxon']; - /** @var TaxonInterface $taxon */ + /** @var TaxonInterface|null $taxon */ $taxon = $this->taxonRepository->findOneBy(['code' => $taxonCode]); + if (null === $taxon) { + return ''; + } + $products = $this->productRepository->findByTaxon($taxon); return $this->twig->render('@BitBagSyliusCmsPlugin/Shop/ContentElement/index.html.twig', [ diff --git a/src/Renderer/ContentElement/SingleMediaContentElementRenderer.php b/src/Renderer/ContentElement/SingleMediaContentElementRenderer.php index 1b61dbdb..348b705e 100644 --- a/src/Renderer/ContentElement/SingleMediaContentElementRenderer.php +++ b/src/Renderer/ContentElement/SingleMediaContentElementRenderer.php @@ -33,6 +33,10 @@ public function supports(ContentConfigurationInterface $contentConfiguration): b public function render(ContentConfigurationInterface $contentConfiguration): string { $code = $contentConfiguration->getConfiguration()['single_media']; + if (null === $code) { + return ''; + } + $media = [ 'renderedContent' => $this->renderMediaRuntime->renderMedia($code), 'entity' => $this->mediaRepository->findOneBy(['code' => $code]), diff --git a/src/Resources/assets/admin/js/bitbag/bitbag-template.js b/src/Resources/assets/admin/js/bitbag/bitbag-template.js index aeea6ac5..6ca7984b 100644 --- a/src/Resources/assets/admin/js/bitbag/bitbag-template.js +++ b/src/Resources/assets/admin/js/bitbag/bitbag-template.js @@ -9,19 +9,20 @@ export class HandleTemplate { $(document).ready(() => { const cmsLoadTemplate = $('[data-bb-cms-load-template]'); const cmsPageTemplate = $('#bitbag_sylius_cms_plugin_page_template'); + const cmsBlockTemplate = $('#bitbag_sylius_cms_plugin_block_template'); cmsLoadTemplate.on('click', function (e) { e.preventDefault(); - if (!cmsPageTemplate.val()) { + if (!cmsPageTemplate.val() && !cmsBlockTemplate.val()) { return; } $('#load-template-confirmation-modal').modal('show'); }); - $('#load-template-confirmation-button').on('click', function (e) { - const templateId = cmsPageTemplate.val(); + $('#load-template-confirmation-button').on('click', function () { + const templateId = cmsPageTemplate.val() ?? cmsBlockTemplate.val(); if (!templateId) { return; } @@ -34,9 +35,6 @@ export class HandleTemplate { $.ajax({ url: endpointUrl, type: 'GET', - headers: { - 'X-Requested-With': 'XMLHttpRequest' - }, success: function(data) { if (data.status === 'success') { $('[id^="bitbag_sylius_cms_plugin_"][id$="contentElements"]') @@ -47,7 +45,9 @@ export class HandleTemplate { $('[data-form-collection="add"]').trigger('click'); }); - const elements = $('[id^="bitbag_sylius_cms_plugin_page_contentElements_"][id$="_type"]'); + const elements = $('[id^="bitbag_sylius_cms_plugin_"][id*="_contentElements_"][id$="_type"]').filter(function() { + return /_page_|_block_/.test(this.id); + }); $.each(data.content, function (index, element) { elements.eq(index).val(element.type); diff --git a/src/Resources/config/routing/admin/template.yml b/src/Resources/config/routing/admin/template.yml index 129cd1bb..9013de97 100755 --- a/src/Resources/config/routing/admin/template.yml +++ b/src/Resources/config/routing/admin/template.yml @@ -16,7 +16,7 @@ bitbag_sylius_cms_plugin_admin_template: type: sylius.resource bitbag_sylius_cms_plugin_admin_ajax_template_page_by_name_phrase: - path: /ajax/templates/search + path: /ajax/templates/page/search methods: [GET] defaults: _format: json @@ -30,7 +30,22 @@ bitbag_sylius_cms_plugin_admin_ajax_template_page_by_name_phrase: phrase: $phrase type: page -bitbag_sylius_cms_plugin_admin_ajax_template_page_by_id: +bitbag_sylius_cms_plugin_admin_ajax_template_block_by_name_phrase: + path: /ajax/templates/block/search + methods: [GET] + defaults: + _format: json + _controller: bitbag_sylius_cms_plugin.controller.template::indexAction + _sylius: + serialization_groups: [Autocomplete] + permission: true + repository: + method: findTemplatesByNamePart + arguments: + phrase: $phrase + type: block + +bitbag_sylius_cms_plugin_admin_ajax_template_by_id: path: /ajax/templates/id methods: [GET] defaults: @@ -43,7 +58,7 @@ bitbag_sylius_cms_plugin_admin_ajax_template_page_by_id: method: find arguments: [id: $id] -bitbag_sylius_cms_plugin_admin_ajax_template_page_content_by_id: +bitbag_sylius_cms_plugin_admin_ajax_template_content_by_id: path: /ajax/templates/content/{id} methods: [GET] defaults: diff --git a/src/Resources/views/Block/Crud/_form.html.twig b/src/Resources/views/Block/Crud/_form.html.twig index d53d42d7..e2905386 100755 --- a/src/Resources/views/Block/Crud/_form.html.twig +++ b/src/Resources/views/Block/Crud/_form.html.twig @@ -1,5 +1,6 @@ {% from '@BitBagSyliusCmsPlugin/Macro/translationForm.html.twig' import translationForm %} {% form_theme form '@BitBagSyliusCmsPlugin/Form/theme.html.twig' %} +{% include '@BitBagSyliusCmsPlugin/Modal/_loadTemplateConfirmation.html.twig' %}
@@ -22,6 +23,7 @@

{{ 'bitbag_sylius_cms_plugin.ui.content_elements.title'|trans }}

+ {% include '@BitBagSyliusCmsPlugin/Template/form.html.twig' with {ajax_url: path('bitbag_sylius_cms_plugin_admin_ajax_template_content_by_id', {'id': 'REPLACE_ID'}) } %}
{{ form_row(form.contentElements) }} diff --git a/src/Resources/views/Form/theme.html.twig b/src/Resources/views/Form/theme.html.twig index 4f31f175..1a33ad39 100755 --- a/src/Resources/views/Form/theme.html.twig +++ b/src/Resources/views/Form/theme.html.twig @@ -13,7 +13,11 @@ {% endblock %} {% block bitbag_template_page_autocomplete_choice_row %} - {{ form_row(form, {'remote_url': path('bitbag_sylius_cms_plugin_admin_ajax_template_page_by_name_phrase'), 'load_edit_url': path('bitbag_sylius_cms_plugin_admin_ajax_template_page_by_id')}) }} + {{ form_row(form, {'remote_url': path('bitbag_sylius_cms_plugin_admin_ajax_template_page_by_name_phrase'), 'load_edit_url': path('bitbag_sylius_cms_plugin_admin_ajax_template_by_id')}) }} +{% endblock %} + +{% block bitbag_template_block_autocomplete_choice_row %} + {{ form_row(form, {'remote_url': path('bitbag_sylius_cms_plugin_admin_ajax_template_block_by_name_phrase'), 'load_edit_url': path('bitbag_sylius_cms_plugin_admin_ajax_template_by_id')}) }} {% endblock %} {% block bitbag_media_autocomplete_choice_row %} diff --git a/src/Resources/views/Page/Crud/_form.html.twig b/src/Resources/views/Page/Crud/_form.html.twig index cc75f04c..5d74fe74 100755 --- a/src/Resources/views/Page/Crud/_form.html.twig +++ b/src/Resources/views/Page/Crud/_form.html.twig @@ -15,6 +15,10 @@ {{ form_row(form.channels) }} {{ form_row(form.collections) }} {{ form_row(form.publishAt) }} + + + {{ 'bitbag_sylius_cms_plugin.ui.preview'|trans }} +

{{ 'bitbag_sylius_cms_plugin.ui.seo'|trans }}

@@ -24,19 +28,7 @@

{{ 'bitbag_sylius_cms_plugin.ui.content_elements.title'|trans }}

-
-
{{ 'bitbag_sylius_cms_plugin.ui.use_page_template'|trans }}
-
-
- {{ form_row(form.template) }} -
- -
-
+ {% include '@BitBagSyliusCmsPlugin/Template/form.html.twig' with {ajax_url: path('bitbag_sylius_cms_plugin_admin_ajax_template_content_by_id', {'id': 'REPLACE_ID'}) } %}
{{ form_row(form.contentElements) }} diff --git a/src/Resources/views/Template/form.html.twig b/src/Resources/views/Template/form.html.twig new file mode 100644 index 00000000..c53272e2 --- /dev/null +++ b/src/Resources/views/Template/form.html.twig @@ -0,0 +1,13 @@ +
+
{{ 'bitbag_sylius_cms_plugin.ui.use_page_template'|trans }}
+
+
+ {{ form_row(form.template) }} +
+ +
+
diff --git a/tests/Behat/Context/Ui/Admin/BlockContext.php b/tests/Behat/Context/Ui/Admin/BlockContext.php index 607c2e30..ab024435 100755 --- a/tests/Behat/Context/Ui/Admin/BlockContext.php +++ b/tests/Behat/Context/Ui/Admin/BlockContext.php @@ -406,6 +406,30 @@ public function iShouldSeeEmptyListOfBlocks(): void $this->resolveCurrentPage()->isEmpty(); } + /** + * @Then I select :templateName template + */ + public function iSelectTemplate(string $templateName): void + { + $this->resolveCurrentPage()->selectTemplate($templateName); + } + + /** + * @Then I click button to use this template + */ + public function iClickButtonToUseThisTemplate(): void + { + $this->resolveCurrentPage()->useTemplate(); + } + + /** + * @Then I confirm that I want to use this template + */ + public function iConfirmThatIWantToUseThisTemplate(): void + { + $this->resolveCurrentPage()->confirmUseTemplate(); + } + /** * @return IndexPageInterface|CreatePageInterface|UpdatePageInterface|SymfonyPageInterface */ diff --git a/tests/Behat/Page/Admin/Block/CreatePage.php b/tests/Behat/Page/Admin/Block/CreatePage.php index 6e07b44b..a1dd1db6 100755 --- a/tests/Behat/Page/Admin/Block/CreatePage.php +++ b/tests/Behat/Page/Admin/Block/CreatePage.php @@ -255,6 +255,37 @@ public function addTaxonsListContentElementWithTaxons(array $taxons): void } } + public function selectTemplate(string $templateName): void + { + $dropdown = $this->getElement('template_select_dropdown'); + $dropdown->click(); + + $dropdown->waitFor(5, function () use ($templateName): bool { + return $this->hasElement('template_select_dropdown_item', [ + '%item%' => $templateName, + ]); + }); + + $item = $this->getElement('template_select_dropdown_item', [ + '%item%' => $templateName, + ]); + + $item->click(); + } + + public function useTemplate(): void + { + $this->getDocument()->findLink('Use this template')->click(); + } + + public function confirmUseTemplate(): void + { + $this->getDocument()->findById('load-template-confirmation-button')->click(); + $this->getDocument()->waitFor(2, function (): bool { + return '' !== $this->getDocument()->find('css', '[data-form-collection="list"]')->getHtml(); + }); + } + protected function getDefinedElements(): array { return array_merge( @@ -264,6 +295,8 @@ protected function getDefinedElements(): array 'association_dropdown_collection' => '.field > label:contains("Collections") ~ .sylius-autocomplete', 'association_dropdown_collection_item' => '.field > label:contains("Collections") ~ .sylius-autocomplete > div.menu > div.item:contains("%item%")', 'content_elements_add_button' => '#bitbag_sylius_cms_plugin_block_contentElements a[data-form-collection="add"]', + 'template_select_dropdown' => 'h5:contains("Use page template") ~ .column .field > .sylius-autocomplete', + 'template_select_dropdown_item' => 'h5:contains("Use page template") ~ .column .field > .sylius-autocomplete > div.menu > div.item:contains("%item%")', ], ); } diff --git a/tests/Behat/Page/Admin/Block/CreatePageInterface.php b/tests/Behat/Page/Admin/Block/CreatePageInterface.php index 54605051..24bda1f8 100755 --- a/tests/Behat/Page/Admin/Block/CreatePageInterface.php +++ b/tests/Behat/Page/Admin/Block/CreatePageInterface.php @@ -52,4 +52,10 @@ public function addProductsGridByTaxonContentElementWithTaxon(string $taxon): vo public function addTaxonsListContentElementWithTaxons(array $taxons): void; public function disable(): void; + + public function selectTemplate(string $templateName): void; + + public function useTemplate(): void; + + public function confirmUseTemplate(): void; } diff --git a/tests/Behat/Page/Admin/Page/CreatePageInterface.php b/tests/Behat/Page/Admin/Page/CreatePageInterface.php index adc032b6..3e5cc053 100755 --- a/tests/Behat/Page/Admin/Page/CreatePageInterface.php +++ b/tests/Behat/Page/Admin/Page/CreatePageInterface.php @@ -60,4 +60,6 @@ public function addTaxonsListContentElementWithTaxons(array $taxons): void; public function selectTemplate(string $templateName): void; public function useTemplate(): void; + + public function confirmUseTemplate(): void; } diff --git a/tests/Behat/Resources/suites/ui/managing_blocks.yml b/tests/Behat/Resources/suites/ui/managing_blocks.yml index 1645aa51..ea514393 100755 --- a/tests/Behat/Resources/suites/ui/managing_blocks.yml +++ b/tests/Behat/Resources/suites/ui/managing_blocks.yml @@ -11,6 +11,7 @@ default: - bitbag_sylius_cms_plugin.behat.context.setup.block - bitbag_sylius_cms_plugin.behat.context.setup.collection - bitbag_sylius_cms_plugin.behat.context.setup.media + - bitbag_sylius_cms_plugin.behat.context.setup.template - bitbag_sylius_cms_plugin.behat.context.ui.admin.block filters: diff --git a/tests/Integration/DataFixtures/ORM/TemplateRepositoryTest/test_it_finds_template_by_name.yml b/tests/Integration/DataFixtures/ORM/TemplateRepositoryTest/test_it_finds_template_by_name.yml index 184c5df1..0b25ada4 100644 --- a/tests/Integration/DataFixtures/ORM/TemplateRepositoryTest/test_it_finds_template_by_name.yml +++ b/tests/Integration/DataFixtures/ORM/TemplateRepositoryTest/test_it_finds_template_by_name.yml @@ -8,3 +8,12 @@ BitBag\SyliusCmsPlugin\Entity\Template: template3: name: 'template3-name' type: 'page' + template4: + name: 'template4-name' + type: 'block' + template5: + name: 'template5-name' + type: 'block' + template6: + name: 'template6-name' + type: 'block' diff --git a/tests/Integration/Repository/TemplateRepositoryTest.php b/tests/Integration/Repository/TemplateRepositoryTest.php index ad10ad89..9608cfd7 100644 --- a/tests/Integration/Repository/TemplateRepositoryTest.php +++ b/tests/Integration/Repository/TemplateRepositoryTest.php @@ -36,6 +36,20 @@ public function test_it_finds_template_page_by_name_part(): void self::assertCount(3, $template); } + public function test_it_finds_template_block_by_name_part(): void + { + $this->loadFixturesFromFile('TemplateRepositoryTest/test_it_finds_template_by_name.yml'); + + $repository = $this->getRepository(); + + $phrase = 'template'; + $type = 'block'; + $template = $repository->findTemplatesByNamePart($phrase, $type); + + self::assertIsArray($template); + self::assertCount(3, $template); + } + private function getRepository(): TemplateRepositoryInterface { /** @var TemplateRepositoryInterface $repository */