Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OP-545: Twig templates for blocks and pages #534

Merged
merged 8 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions features/admin/adding_block.feature
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,12 @@ Feature: Adding blocks
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
Scenario: Adding block with content template
Given there is an existing content 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 select "Homepage" content 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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@managing_templates
@managing_content_templates
Feature: Adding cms templates
In order to create templates
As an Administrator
Expand Down
21 changes: 18 additions & 3 deletions features/admin/adding_page.feature
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,30 @@ Feature: Adding new page
And I should see newly created "Multiple media" content element in Content elements section

@ui @javascript
Scenario: Adding page with template
Given there is an existing template named "Homepage" with "Page" type that contains "Textarea, Single media" content elements
Scenario: Adding page with content template
Given there is an existing content template named "Homepage" with "Page" type that contains "Textarea, Single media" content elements
When I go to the create page page
And I fill the code with "my_page"
And I fill the slug with "my_page"
And I fill the name with "My page"
And I select "Homepage" template
And I select "Homepage" content template
And I confirm that I want to use this template
And I add it
Then I should be notified that the page 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

@ui @javascript
Scenario: Adding page with with a custom template
Given there is an existing template with "@SyliusCmsPlugin/Shop/Page/custom.html.twig" value
When I go to the create page page
And I fill the code with "my_page"
And I fill the slug with "my-page"
And I fill the name with "My page"
And I select "United States" channel
And I select "@SyliusCmsPlugin/Shop/Page/custom.html.twig" template
And I add it
Then I should be notified that the page has been created
And I go to the "my-page" page
And The rendered page should contain custom layout code

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@managing_templates
@managing_content_templates
Feature: Managing cms templates
In order to manage existing templates
As an Administrator
Expand Down
2 changes: 1 addition & 1 deletion features/admin/managing_pages.feature
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Feature: Managing cms pages
@ui
Scenario: Deleting page
Given there is a page in the store
When I go to the pages page
When I go to the cms pages page
And I delete this page
Then I should be notified that the page has been deleted
And I should see empty list of pages
Expand Down
2 changes: 1 addition & 1 deletion spec/Menu/ContentManagementMenuBuilderSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function it_build_menu(
->addChild('templates', ['route' => 'sylius_cms_admin_template_index'])
->willReturn($cmsRootMenuItem)
;
$cmsRootMenuItem->setLabel('sylius_cms.ui.templates')->willReturn($cmsRootMenuItem);
$cmsRootMenuItem->setLabel('sylius_cms.ui.content_templates')->willReturn($cmsRootMenuItem);
$cmsRootMenuItem->setLabelAttribute('icon', 'clone')->shouldBeCalled();

$cmsRootMenuItem
Expand Down
52 changes: 52 additions & 0 deletions spec/Provider/ResourceTemplateProviderSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace spec\Sylius\CmsPlugin\Provider;

use PhpSpec\ObjectBehavior;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Sylius\CmsPlugin\Provider\ResourceTemplateProvider;
use Sylius\CmsPlugin\Provider\ResourceTemplateProviderInterface;

final class ResourceTemplateProviderSpec extends ObjectBehavior
{
public function let(ParameterBagInterface $parameterBag): void
{
$parameterBag->get('sylius_cms.templates.pages')->willReturn([
'@CustomTemplate/Page.html.twig' => '@CustomTemplate/Page.html.twig',
]);

$parameterBag->get('sylius_cms.templates.blocks')->willReturn([
'@CustomTemplate/Block.html.twig' => '@CustomTemplate/Block.html.twig',
]);

$this->beConstructedWith($parameterBag);
}

public function it_is_initializable(): void
{
$this->shouldHaveType(ResourceTemplateProvider::class);
}

public function it_implements_resource_template_provider_interface(): void
{
$this->shouldImplement(ResourceTemplateProviderInterface::class);
}

public function it_returns_default_and_custom_page_templates(): void
{
$this->getPageTemplates()->shouldReturn([
'sylius.ui.default' => '@SyliusCmsPlugin/Shop/Page/show.html.twig',
'@CustomTemplate/Page.html.twig' => '@CustomTemplate/Page.html.twig',
]);
}

public function it_returns_default_and_custom_block_templates(): void
{
$this->getBlockTemplates()->shouldReturn([
'sylius.ui.default' => '@SyliusCmsPlugin/Shop/Block/show.html.twig',
'@CustomTemplate/Block.html.twig' => '@CustomTemplate/Block.html.twig',
]);
}
}
10 changes: 7 additions & 3 deletions src/Controller/BlockController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use FOS\RestBundle\View\View;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\CmsPlugin\Entity\BlockInterface;
use Sylius\CmsPlugin\Renderer\ContentElementRendererStrategyInterface;
use Sylius\CmsPlugin\Resolver\BlockResourceResolverInterface;
use Sylius\Component\Resource\ResourceActions;
use Symfony\Component\HttpFoundation\Request;
Expand Down Expand Up @@ -40,9 +41,7 @@ public function renderBlockAction(Request $request): Response
return $this->viewHandler->handle($configuration, View::create($block));
}

$template = $request->get('template') ?? self::BLOCK_TEMPLATE;

return $this->render($template, [
return $this->render($block->getTemplate() ?? self::BLOCK_TEMPLATE, [
'configuration' => $configuration,
'metadata' => $this->metadata,
'resource' => $block,
Expand Down Expand Up @@ -70,8 +69,13 @@ public function previewAction(Request $request): Response
return $this->viewHandler->handle($configuration, View::create($block));
}

/** @var ContentElementRendererStrategyInterface $contentElementRendererStrategy */
$contentElementRendererStrategy = $this->get('sylius_cms.content_element_renderer_strategy');

return $this->render($configuration->getTemplate(ResourceActions::CREATE . '.html'), [
'resource' => $block,
'template' => $block->getTemplate(),
'content' => $contentElementRendererStrategy->render($block),
$this->metadata->getName() => $block,
]);
}
Expand Down
40 changes: 40 additions & 0 deletions src/Controller/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
use FOS\RestBundle\View\View;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\CmsPlugin\Entity\PageInterface;
use Sylius\CmsPlugin\Repository\PageRepositoryInterface;
use Sylius\CmsPlugin\Resolver\PageResourceResolverInterface;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Sylius\Component\Locale\Context\LocaleContextInterface;
use Sylius\Component\Resource\ResourceActions;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand All @@ -22,6 +25,42 @@ final class PageController extends ResourceController

public const FILTER = 'sylius_admin_product_original';

public const DEFAULT_TEMPLATE = '@SyliusCmsPlugin/Shop/Page/show.html.twig';

public function showAction(Request $request): Response
{
$configuration = $this->getRequestConfiguration($request);

$this->isGrantedOr403($configuration, ResourceActions::SHOW);

$slug = $request->attributes->get('slug');

/** @var PageRepositoryInterface $pageRepository */
$pageRepository = $this->get('sylius_cms.repository.page');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather inject services via dependency injection than getting them directly from container

Copy link
Author

@jkindly jkindly Sep 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me too, but the problem here is that PageController extends ResourceController and constructor will looks like:
public function __construct(MetadataInterface $metadata, RequestConfigurationFactoryInterface $requestConfigurationFactory, ?ViewHandlerInterface $viewHandler, RepositoryInterface $repository, FactoryInterface $factory, NewResourceFactoryInterface $newResourceFactory, ObjectManager $manager, SingleResourceProviderInterface $singleResourceProvider, ResourcesCollectionProviderInterface $resourcesFinder, ResourceFormFactoryInterface $resourceFormFactory, RedirectHandlerInterface $redirectHandler, FlashHelperInterface $flashHelper, AuthorizationCheckerInterface $authorizationChecker, EventDispatcherInterface $eventDispatcher, ?StateMachineInterface $stateMachine, ResourceUpdateHandlerInterface $resourceUpdateHandler, ResourceDeleteHandlerInterface $resourceDeleteHandler) { parent::__construct($metadata, $requestConfigurationFactory, $viewHandler, $repository, $factory, $newResourceFactory, $manager, $singleResourceProvider, $resourcesFinder, $resourceFormFactory, $redirectHandler, $flashHelper, $authorizationChecker, $eventDispatcher, $stateMachine, $resourceUpdateHandler, $resourceDeleteHandler); }

So, I can't use DI here.


/** @var LocaleContextInterface $localeContext */
$localeContext = $this->get('sylius.context.locale');

/** @var ChannelContextInterface $channelContext */
$channelContext = $this->get('sylius.context.channel');

Assert::notNull($channelContext->getChannel()->getCode());

$page = $pageRepository->findOneEnabledBySlugAndChannelCode(
$slug,
$localeContext->getLocaleCode(),
$channelContext->getChannel()->getCode(),
);

if (null === $page) {
throw $this->createNotFoundException('Page not found');
}

return $this->render($page->getTemplate() ?? self::DEFAULT_TEMPLATE, [
'page' => $page,
]);
}

public function renderLinkAction(Request $request): Response
{
$configuration = $this->getRequestConfiguration($request);
Expand Down Expand Up @@ -78,6 +117,7 @@ public function previewAction(Request $request): Response
return $this->render($configuration->getTemplate(ResourceActions::CREATE . '.html'), [
'resource' => $page,
'preview' => true,
'template' => $page->getTemplate() ?? self::DEFAULT_TEMPLATE,
$this->metadata->getName() => $page,
]);
}
Expand Down
20 changes: 20 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public function getConfigTreeBuilder(): TreeBuilder
$rootNode = $treeBuilder->getRootNode();

$this->addResourcesSection($rootNode);
$this->addTemplatesSection($rootNode);

return $treeBuilder;
}
Expand Down Expand Up @@ -196,4 +197,23 @@ private function addResourcesSection(ArrayNodeDefinition $node): void
->end()
;
}

private function addTemplatesSection(ArrayNodeDefinition $node): void
{
$node
->children()
->arrayNode('templates')
->addDefaultsIfNotSet()
->children()
->arrayNode('pages')
->scalarPrototype()->end()
->end()
->arrayNode('blocks')
->scalarPrototype()->end()
->end()
->end()
->end()
->end()
;
}
}
5 changes: 5 additions & 0 deletions src/DependencyInjection/SyliusCmsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ final class SyliusCmsExtension extends AbstractResourceExtension implements Prep

public function load(array $configs, ContainerBuilder $container): void
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);

$container->setParameter('sylius_cms.templates.pages', $config['templates']['pages']);
$container->setParameter('sylius_cms.templates.blocks', $config['templates']['blocks']);
}

public function prepend(ContainerBuilder $container): void
Expand Down
12 changes: 12 additions & 0 deletions src/Entity/Block.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public function __construct()

protected ?string $name;

protected ?string $template = null;

public function getId(): ?int
{
return $this->id;
Expand All @@ -62,4 +64,14 @@ public function setName(?string $name): void
{
$this->name = $name;
}

public function getTemplate(): ?string
{
return $this->template;
}

public function setTemplate(?string $template): void
{
$this->template = $template;
}
}
4 changes: 4 additions & 0 deletions src/Entity/BlockInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ public function setCode(?string $code): void;
public function getName(): ?string;

public function setName(?string $name): void;

public function getTemplate(): ?string;

public function setTemplate(?string $template): void;
}
12 changes: 12 additions & 0 deletions src/Entity/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class Page implements PageInterface

protected ?string $name = null;

protected ?string $template = null;

protected ?\DateTimeImmutable $publishAt;

public function __construct()
Expand Down Expand Up @@ -161,6 +163,16 @@ public function setName(?string $name): void
$this->name = $name;
}

public function getTemplate(): ?string
{
return $this->template;
}

public function setTemplate(?string $template): void
{
$this->template = $template;
}

public function getTitle(): ?string
{
/** @var PageTranslationInterface $pageTranslationInterface */
Expand Down
4 changes: 4 additions & 0 deletions src/Entity/PageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public function getName(): ?string;

public function setName(?string $name): void;

public function getTemplate(): ?string;

public function setTemplate(?string $template): void;

public function getTitle(): ?string;

public function setTitle(?string $title): void;
Expand Down
10 changes: 9 additions & 1 deletion src/Form/Type/BlockType.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Sylius\Bundle\TaxonomyBundle\Form\Type\TaxonAutocompleteChoiceType;
use Sylius\CmsPlugin\Entity\BlockInterface;
use Sylius\CmsPlugin\Provider\ResourceTemplateProviderInterface;
use Sylius\Component\Locale\Model\LocaleInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
Expand All @@ -23,6 +24,7 @@ final class BlockType extends AbstractResourceType

public function __construct(
private RepositoryInterface $localeRepository,
private ResourceTemplateProviderInterface $templateProvider,
string $dataClass,
array $validationGroups = [],
) {
Expand All @@ -48,6 +50,11 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
->add('name', TextType::class, [
'label' => 'sylius_cms.ui.name',
])
->add('templates', ChoiceType::class, [
'label' => 'sylius_cms.ui.template',
'choices' => $this->templateProvider->getBlockTemplates(),
'mapped' => false,
])
->add('collections', CollectionAutocompleteChoiceType::class, [
'label' => 'sylius_cms.ui.collections',
'multiple' => true,
Expand Down Expand Up @@ -90,7 +97,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'multiple' => true,
'help' => 'sylius_cms.ui.display_for_taxons.help',
])
->add('template', TemplateBlockAutocompleteChoiceType::class, [
->add('contentTemplate', TemplateBlockAutocompleteChoiceType::class, [
'label' => false,
'mapped' => false,
])
Expand All @@ -105,6 +112,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
;

PageType::addContentElementLocaleListener($builder);
PageType::addTemplateListener($builder);
}

public function getBlockPrefix(): string
Expand Down
Loading
Loading