diff --git a/.gitignore b/.gitignore index 095f17e..e8fe863 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ /Tests/Application/var/* /vendor/ +.env.local +.env.*.local /composer.phar /composer.lock .php_cs.cache diff --git a/.travis.yml b/.travis.yml index 6663f7c..074f299 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ cache: matrix: include: - - php: 7.2 + - php: 7.3 env: - COMPOSER_FLAGS="--prefer-lowest --prefer-stable --prefer-dist --no-interaction" - php: 7.4 diff --git a/DependencyInjection/CompilerPass/ImageFormatCompilerPass.php b/DependencyInjection/CompilerPass/ImageFormatCompilerPass.php index 67cbeca..63fa245 100644 --- a/DependencyInjection/CompilerPass/ImageFormatCompilerPass.php +++ b/DependencyInjection/CompilerPass/ImageFormatCompilerPass.php @@ -11,8 +11,8 @@ namespace Sulu\Bundle\ThemeBundle\DependencyInjection\CompilerPass; -use Liip\ThemeBundle\ActiveTheme; use Sulu\Bundle\MediaBundle\DependencyInjection\AbstractImageFormatCompilerPass; +use Sylius\Bundle\ThemeBundle\Repository\ThemeRepositoryInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -25,14 +25,13 @@ class ImageFormatCompilerPass extends AbstractImageFormatCompilerPass */ protected function getFiles(ContainerBuilder $container) { - $files = []; - - /** @var ActiveTheme $activeTheme */ - $activeTheme = $container->get('liip_theme.active_theme'); + /** @var ThemeRepositoryInterface $themeRepository */ + $themeRepository = $container->get('sylius.repository.theme'); $bundles = $container->getParameter('kernel.bundles'); $configPath = 'config/image-formats.xml'; - foreach ($activeTheme->getThemes() as $theme) { + $files = []; + foreach ($themeRepository->findAll() as $theme) { foreach ($bundles as $bundleName => $bundle) { $bundleReflection = new \ReflectionClass($bundle); $fileName = $bundleReflection->getFileName(); @@ -44,7 +43,7 @@ protected function getFiles(ContainerBuilder $container) $path = sprintf( '%s/Resources/themes/%s/%s', dirname($fileName), - $theme, + $theme->getName(), $configPath ); diff --git a/DependencyInjection/CompilerPass/WebspaceStructureProviderCompilerPass.php b/DependencyInjection/CompilerPass/WebspaceStructureProviderCompilerPass.php deleted file mode 100644 index 55eac93..0000000 --- a/DependencyInjection/CompilerPass/WebspaceStructureProviderCompilerPass.php +++ /dev/null @@ -1,35 +0,0 @@ -hasDefinition('sulu.content.webspace_structure_provider')) { - return; - } - - $definition = $container->getDefinition('sulu.content.webspace_structure_provider'); - $definition->setClass(WebspaceStructureProvider::class); - $definition->addArgument(new Reference('sulu_core.webspace.webspace_manager')); - $definition->addArgument(new Reference('liip_theme.active_theme')); - } -} diff --git a/EventListener/SetThemeEventListener.php b/EventListener/SetThemeEventListener.php index 5bb8ab6..ae06cad 100644 --- a/EventListener/SetThemeEventListener.php +++ b/EventListener/SetThemeEventListener.php @@ -11,9 +11,10 @@ namespace Sulu\Bundle\ThemeBundle\EventListener; -use Liip\ThemeBundle\ActiveTheme; use Sulu\Bundle\PreviewBundle\Preview\Events\PreRenderEvent; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Sylius\Bundle\ThemeBundle\Context\SettableThemeContext; +use Sylius\Bundle\ThemeBundle\Repository\ThemeRepositoryInterface; +use Symfony\Component\HttpKernel\Event\RequestEvent; /** * Listener which applies the configured theme. @@ -21,19 +22,25 @@ class SetThemeEventListener { /** - * @var ActiveTheme + * @var ThemeRepositoryInterface */ - private $activeTheme; + private $themeRepository; - public function __construct(ActiveTheme $activeTheme) + /** + * @var SettableThemeContext + */ + private $themeContext; + + public function __construct(ThemeRepositoryInterface $themeRepository, SettableThemeContext $themeContext) { - $this->activeTheme = $activeTheme; + $this->themeRepository = $themeRepository; + $this->themeContext = $themeContext; } /** * Set the active theme if there is a portal. */ - public function setActiveThemeOnRequest(GetResponseEvent $event): void + public function setActiveThemeOnRequest(RequestEvent $event): void { if (null === ($attributes = $event->getRequest()->get('_sulu')) || null === ($webspace = $attributes->getAttribute('webspace')) @@ -42,7 +49,10 @@ public function setActiveThemeOnRequest(GetResponseEvent $event): void return; } - $this->activeTheme->setName($theme); + $theme = $this->themeRepository->findOneByName($theme); + if (null !== $theme) { + $this->themeContext->setTheme($theme); + } } /** @@ -50,6 +60,9 @@ public function setActiveThemeOnRequest(GetResponseEvent $event): void */ public function setActiveThemeOnPreviewPreRender(PreRenderEvent $event): void { - $this->activeTheme->setName($event->getAttribute('webspace')->getTheme()); + $theme = $this->themeRepository->findOneByName($event->getAttribute('webspace')->getTheme()); + if (null !== $theme) { + $this->themeContext->setTheme($theme); + } } } diff --git a/README.md b/README.md index 66d9be8..2d39b6d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,152 @@ -# SuluThemeBundle +

SuluThemeBundle

-[![Build Status](https://travis-ci.org/sulu/SuluThemeBundle.svg?branch=develop)](https://travis-ci.org/sulu/SuluThemeBundle) +

+ + Official Sulu Bundle Badge + +

-This package enables a basic theming for sulu. It uses liip/theme-bundle to extend the discovery feature of twig. +

+ + GitHub tag (latest SemVer) + + + GitHub license + + + Sulu compatibility + +

+
+ +The **SuluThemeBundle** provides the functionality to add multiple themes for different look and feel using multiple +webspaces in the [Sulu](https://sulu.io/) content management system. + +To achieve this, the bundle uses the [SyliusThemeBundle](https://github.com/Sylius/SyliusThemeBundle) to render different +twig templates and asset files. Each webspace can have it's own theme. + +## 🚀  Installation and Usage + +### Install the bundle + +Execute the following [composer](https://getcomposer.org/) command to add the bundle to the dependencies of your +project: + +```bash +composer require sulu/theme-bundle +``` + +### Enable the bundle + +Enable the bundle by adding it to the list of registered bundles in the `config/bundles.php` file of your project: + +```php +return [ + /* ... */ + Sulu\Bundle\ThemeBundle\SuluThemeBundle::class => ['all' => true], +]; +``` + + +### Configure the SyliusThemeBundle + +In order to use the bundle you have to add at least the following default configuration: + +```yaml +# ./config/packages/sylius_theme.yaml + +sylius_theme: + sources: + filesystem: ~ +``` + +By default, the bundle seeks for the themes in the `%kernel.project_dir%/themes` directory and looks for a +theme configuration file named `composer.json`. This can be changed via the yaml configuration: + +```yaml +sylius_theme: + sources: + filesystem: + filename: theme.json +``` + +For more detailed information about the configuration sources go to the [SyliusThemeBundle documentation](https://github.com/Sylius/SyliusThemeBundle/blob/master/docs/configuration_sources.md). + +### Configure your themes + +Every theme must have its own configuration file in form of a `theme.json`. +Go to the [Theme Configuration Reference](https://github.com/Sylius/SyliusThemeBundle/blob/master/docs/theme_configuration_reference.md) +for the detailed documentation about the configuration options. + +The minimal configuration for a theme would be the following: + +```json +// ./themes//theme.json + +{ + "name": "vendor/" +} +``` + +It is important, that the `name` matches the naming convention of composer (`vendor/name`). + +### Create a theme +First you have to create the directory `themes` inside your project. +To create a theme you have to create a new directory in the themes folder with the name of the new theme. +In the newly created directory you have to add the theme configuration file `theme.json`. +See [Configure your themes](#configure-your-themes). Additonally you have to create the `templates` directory next to +the `theme.json`. Afterwards you have to fill this folder with all the used templates and assets for this theme. + +This results in the following project structure: + +``` +ProjectName +├── composer.json +├── assets +├── bin +├── config +├── templates +├── themes +│ ├── +│ │ ├── templates +│ │ │ └── base.html.twig +│ │ └── theme.json +│ └── +│ ├── templates +│ │ └── base.html.twig +│ └── theme.json +├── ... +└── ... +``` + +### Add one of your themes to a webspace + +Each webspace can use a different theme. A theme can be enabled for a specific webspace by adding the theme name +`vendor/theme-name` to your webspace: + +```xml + + + example.com + example + ... + vendor/theme-name + ... + +``` + +## ❤️  Support and Contributions + +The Sulu content management system is a **community-driven open source project** backed by various partner companies. +We are committed to a fully transparent development process and **highly appreciate any contributions**. + +In case you have questions, we are happy to welcome you in our official [Slack channel](https://sulu.io/services-and-support). +If you found a bug or miss a specific feature, feel free to **file a new issue** with a respective title and description +on the the [sulu/SuluThemeBundle](https://github.com/sulu/SuluThemeBundle) repository. + + +## 📘  License + +The Sulu content management system is released under the under terms of the [MIT License](LICENSE). diff --git a/Resources/config/admin.xml b/Resources/config/admin.xml index 07e7fef..ed3827a 100644 --- a/Resources/config/admin.xml +++ b/Resources/config/admin.xml @@ -5,7 +5,8 @@ - + + diff --git a/Resources/config/website.xml b/Resources/config/website.xml index 8b59130..c5aab40 100644 --- a/Resources/config/website.xml +++ b/Resources/config/website.xml @@ -2,11 +2,11 @@ - - + + diff --git a/StructureProvider/WebspaceStructureProvider.php b/StructureProvider/WebspaceStructureProvider.php deleted file mode 100644 index bc7b686..0000000 --- a/StructureProvider/WebspaceStructureProvider.php +++ /dev/null @@ -1,83 +0,0 @@ -webspaceManager = $webspaceManager; - $this->activeTheme = $activeTheme; - } - - /** - * {@inheritdoc} - * - * @return StructureInterface[] - */ - protected function loadStructures($webspaceKey): array - { - $before = $this->activeTheme->getName(); - $webspace = $this->webspaceManager->findWebspaceByKey($webspaceKey); - - if (!$webspace) { - return []; - } - - if (null !== $webspace->getTheme()) { - $this->activeTheme->setName($webspace->getTheme()); - } - - $structures = []; - $keys = []; - /** @var PageBridge $page */ - foreach ($this->structureManager->getStructures() as $page) { - $template = sprintf('%s.html.twig', $page->getView()); - if ($this->templateExists($template)) { - $keys[] = $page->getKey(); - $structures[] = $page; - } - } - $this->activeTheme->setName($before); - $this->cache->save($webspaceKey, $keys); - - return $structures; - } -} diff --git a/SuluThemeBundle.php b/SuluThemeBundle.php index fa7d173..06a2727 100644 --- a/SuluThemeBundle.php +++ b/SuluThemeBundle.php @@ -12,7 +12,6 @@ namespace Sulu\Bundle\ThemeBundle; use Sulu\Bundle\ThemeBundle\DependencyInjection\CompilerPass\ImageFormatCompilerPass; -use Sulu\Bundle\ThemeBundle\DependencyInjection\CompilerPass\WebspaceStructureProviderCompilerPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -21,8 +20,6 @@ class SuluThemeBundle extends Bundle public function build(ContainerBuilder $container): void { parent::build($container); - - $container->addCompilerPass(new WebspaceStructureProviderCompilerPass()); $container->addCompilerPass(new ImageFormatCompilerPass()); } } diff --git a/Tests/Application/Kernel.php b/Tests/Application/Kernel.php index 2e02eba..c1c0db2 100644 --- a/Tests/Application/Kernel.php +++ b/Tests/Application/Kernel.php @@ -11,9 +11,9 @@ namespace Sulu\Bundle\ThemeBundle\Tests\Application; -use Liip\ThemeBundle\LiipThemeBundle; use Sulu\Bundle\TestBundle\Kernel\SuluTestKernel; use Sulu\Bundle\ThemeBundle\SuluThemeBundle; +use Sylius\Bundle\ThemeBundle\SyliusThemeBundle; use Symfony\Component\Config\Loader\LoaderInterface; class Kernel extends SuluTestKernel @@ -34,7 +34,7 @@ public function registerBundles() return array_merge( parent::registerBundles(), [ - new LiipThemeBundle(), + new SyliusThemeBundle(), new SuluThemeBundle(), ] ); diff --git a/Tests/Application/config/sylius_theme.yaml b/Tests/Application/config/sylius_theme.yaml new file mode 100644 index 0000000..4d34199 --- /dev/null +++ b/Tests/Application/config/sylius_theme.yaml @@ -0,0 +1,3 @@ +sylius_theme: + sources: + test: ~ diff --git a/Tests/Unit/EventListener/SetThemeEventListenerTest.php b/Tests/Unit/EventListener/SetThemeEventListenerTest.php index c1d38f6..b7b5f5c 100644 --- a/Tests/Unit/EventListener/SetThemeEventListenerTest.php +++ b/Tests/Unit/EventListener/SetThemeEventListenerTest.php @@ -11,32 +11,29 @@ namespace Sulu\Bundle\ThemeBundle\Tests\Unit\EventListener; -use Liip\ThemeBundle\ActiveTheme; use PHPUnit\Framework\TestCase; use Prophecy\Prophecy\ObjectProphecy; use Sulu\Bundle\PreviewBundle\Preview\Events\PreRenderEvent; use Sulu\Bundle\ThemeBundle\EventListener\SetThemeEventListener; use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes; use Sulu\Component\Webspace\Webspace; +use Sylius\Bundle\ThemeBundle\Context\SettableThemeContext; +use Sylius\Bundle\ThemeBundle\Model\ThemeInterface; +use Sylius\Bundle\ThemeBundle\Repository\ThemeRepositoryInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\RequestEvent; class SetThemeEventListenerTest extends TestCase { /** - * @var ObjectProphecy|ActiveTheme + * @var ThemeRepositoryInterface|ObjectProphecy */ - private $activeTheme; + private $themeRepository; /** - * @var string + * @var SettableThemeContext */ - private $theme = 'test'; - - /** - * @var ObjectProphecy|Webspace - */ - private $webspace; + private $themeContext; /** * @var SetThemeEventListener @@ -45,84 +42,135 @@ class SetThemeEventListenerTest extends TestCase public function setUp(): void { - $this->activeTheme = $this->prophesize(ActiveTheme::class); - $this->webspace = $this->prophesize(Webspace::class); - $this->webspace->getTheme()->willReturn($this->theme); + $this->themeRepository = $this->prophesize(ThemeRepositoryInterface::class); + $this->themeContext = new SettableThemeContext(); - $this->listener = new SetThemeEventListener($this->activeTheme->reveal()); + $this->listener = new SetThemeEventListener($this->themeRepository->reveal(), $this->themeContext); } public function testEventListener(): void { + /** @var Webspace|ObjectProphecy webspace */ + $webspace = $this->prophesize(Webspace::class); + $webspace->getTheme()->willReturn('theme/name'); + + /** @var ThemeInterface|ObjectProphecy $theme */ + $theme = $this->prophesize(ThemeInterface::class); + + /** @var Request|ObjectProphecy $request */ $request = $this->prophesize(Request::class); + /** @var RequestAttributes|ObjectProphecy $attributes */ $attributes = $this->prophesize(RequestAttributes::class); - $attributes->getAttribute('webspace')->willReturn($this->webspace->reveal()); + $attributes->getAttribute('webspace')->willReturn($webspace->reveal()); $request->get('_sulu')->willReturn($attributes->reveal()); - $event = $this->prophesize(GetResponseEvent::class); + /** @var RequestEvent|ObjectProphecy $event */ + $event = $this->prophesize(RequestEvent::class); $event->getRequest()->willReturn($request->reveal()); $event->isMasterRequest()->willReturn(true); - $this->activeTheme->setName($this->theme)->shouldBeCalled(); + $this->themeRepository->findOneByName('theme/name') + ->shouldBeCalled() + ->willReturn($theme->reveal()); $this->listener->setActiveThemeOnRequest($event->reveal()); + + $this->assertSame($theme->reveal(), $this->themeContext->getTheme()); } public function testEventListenerNotMaster(): void { + /** @var Webspace|ObjectProphecy webspace */ + $webspace = $this->prophesize(Webspace::class); + $webspace->getTheme()->willReturn('theme/name'); + + /** @var ThemeInterface|ObjectProphecy $theme */ + $theme = $this->prophesize(ThemeInterface::class); + + /** @var Request|ObjectProphecy $request */ $request = $this->prophesize(Request::class); + /** @var RequestAttributes|ObjectProphecy $attributes */ $attributes = $this->prophesize(RequestAttributes::class); - $attributes->getAttribute('webspace')->willReturn($this->webspace->reveal()); + $attributes->getAttribute('webspace')->willReturn($webspace->reveal()); $request->get('_sulu')->willReturn($attributes->reveal()); - $event = $this->prophesize(GetResponseEvent::class); + /** @var RequestEvent|ObjectProphecy $event */ + $event = $this->prophesize(RequestEvent::class); $event->getRequest()->willReturn($request->reveal()); $event->isMasterRequest()->willReturn(false); - $this->activeTheme->setName($this->theme)->shouldBeCalled(); + $this->themeRepository->findOneByName('theme/name') + ->shouldBeCalled() + ->willReturn($theme->reveal()); $this->listener->setActiveThemeOnRequest($event->reveal()); + + $this->assertSame($theme->reveal(), $this->themeContext->getTheme()); } public function testEventListenerNoWebspace(): void { + /** @var Request|ObjectProphecy $request */ $request = $this->prophesize(Request::class); + /** @var RequestAttributes|ObjectProphecy $attributes */ $attributes = $this->prophesize(RequestAttributes::class); $attributes->getAttribute('webspace')->willReturn(null); $request->get('_sulu')->willReturn($attributes->reveal()); - $event = $this->prophesize(GetResponseEvent::class); + /** @var RequestEvent|ObjectProphecy $event */ + $event = $this->prophesize(RequestEvent::class); $event->getRequest()->willReturn($request->reveal()); $event->isMasterRequest()->willReturn(true); - $this->activeTheme->setName($this->theme)->shouldNotBeCalled(); + $this->themeRepository->findOneByName('theme/name') + ->shouldNotBeCalled(); $this->listener->setActiveThemeOnRequest($event->reveal()); + + $this->assertNull($this->themeContext->getTheme()); } public function testEventListenerNoAttributes(): void { + /** @var Request|ObjectProphecy $request */ $request = $this->prophesize(Request::class); + /* @var RequestAttributes|ObjectProphecy $attributes */ $request->get('_sulu')->willReturn(null); - $event = $this->prophesize(GetResponseEvent::class); + /** @var RequestEvent|ObjectProphecy $event */ + $event = $this->prophesize(RequestEvent::class); $event->getRequest()->willReturn($request->reveal()); $event->isMasterRequest()->willReturn(true); - $this->activeTheme->setName($this->theme)->shouldNotBeCalled(); + $this->themeRepository->findOneByName('theme/name') + ->shouldNotBeCalled(); $this->listener->setActiveThemeOnRequest($event->reveal()); + + $this->assertNull($this->themeContext->getTheme()); } public function testEventListenerOnPreview(): void { + /** @var ThemeInterface|ObjectProphecy $theme */ + $theme = $this->prophesize(ThemeInterface::class); + + /** @var Webspace|ObjectProphecy webspace */ + $webspace = $this->prophesize(Webspace::class); + $webspace->getTheme()->willReturn('theme/name'); + + /** @var RequestAttributes|ObjectProphecy $attributes */ $attributes = $this->prophesize(RequestAttributes::class); - $attributes->getAttribute('webspace', null)->willReturn($this->webspace->reveal()); + $attributes->getAttribute('webspace', null)->willReturn($webspace->reveal()); - $this->activeTheme->setName($this->theme)->shouldBeCalled(); + $this->themeRepository->findOneByName('theme/name') + ->shouldBeCalled() + ->willReturn($theme->reveal()); $this->listener->setActiveThemeOnPreviewPreRender( new PreRenderEvent($attributes->reveal()) ); + + $this->assertSame($theme->reveal(), $this->themeContext->getTheme()); } } diff --git a/Tests/Unit/StructureProvider/WebspaceStructureProviderTest.php b/Tests/Unit/StructureProvider/WebspaceStructureProviderTest.php deleted file mode 100644 index 832cbc7..0000000 --- a/Tests/Unit/StructureProvider/WebspaceStructureProviderTest.php +++ /dev/null @@ -1,124 +0,0 @@ -generateStructure('t1', 'MyBundle:default:t1'), - $this->generateStructure('t2', 'MyBundle:default:t2'), - $this->generateStructure('t3', 'MyBundle:default:t3'), - ]; - - $theme = 'test'; - - $webspace = $this->prophesize('Sulu\Component\Webspace\Webspace'); - $webspace->getTheme()->willReturn($theme); - - $twigLoader = $this->prophesize('\Twig_ExistsLoaderInterface'); - $twigLoader->exists('MyBundle:default:t1.html.twig')->willReturn(false); - $twigLoader->exists('MyBundle:default:t2.html.twig')->willReturn(true)->shouldBeCalled(); - $twigLoader->exists('MyBundle:default:t3.html.twig')->willReturn(false); - - $twig = $this->prophesize('\Twig_Environment'); - $twig->getLoader()->willReturn($twigLoader->reveal()); - - $structureManager = $this->prophesize('Sulu\Component\Content\Compat\StructureManagerInterface'); - $structureManager->getStructures()->willReturn($structures); - - $webspaceManager = $this->prophesize('Sulu\Component\Webspace\Manager\WebspaceManagerInterface'); - $webspaceManager->findWebspaceByKey('sulu_io')->willReturn($webspace->reveal()); - - $activeTheme = $this->prophesize('Liip\ThemeBundle\ActiveTheme'); - $activeTheme->getName()->willReturn('before'); - $activeTheme->setName('test')->shouldBeCalled(); - $activeTheme->setName('before')->shouldBeCalled(); - - $structureProvider = new WebspaceStructureProvider( - $twig->reveal(), - $structureManager->reveal(), - $cache, - $webspaceManager->reveal(), - $activeTheme->reveal() - ); - - $result = $structureProvider->getStructures('sulu_io'); - - $this->assertCount(1, $result); - $this->assertEquals($structures[1], $result[0]); - - $this->assertTrue($cache->contains('sulu_io')); - $this->assertEquals(['t2'], $cache->fetch('sulu_io')); - } - - public function testGetStructuresCached(): void - { - $cache = new ArrayCache(); - $cache->save('sulu_io', ['t1', 't3']); - - $structures = [ - $this->generateStructure('t1', 'MyBundle:default:t1'), - $this->generateStructure('t2', 'MyBundle:default:t2'), - $this->generateStructure('t3', 'MyBundle:default:t3'), - ]; - - $twig = $this->prophesize('\Twig_Environment'); - $twig->getLoader()->shouldNotBeCalled(); - - $structureManager = $this->prophesize('Sulu\Component\Content\Compat\StructureManagerInterface'); - $structureManager->getStructures()->shouldNotBeCalled(); - $structureManager->getStructure('t1')->willReturn($structures[0]); - $structureManager->getStructure('t2')->shouldNotBeCalled(); - $structureManager->getStructure('t3')->willReturn($structures[2]); - - $webspaceManager = $this->prophesize('Sulu\Component\Webspace\Manager\WebspaceManagerInterface'); - $webspaceManager->findWebspaceByKey('sulu_io')->shouldNotBeCalled(); - - $activeTheme = $this->prophesize('Liip\ThemeBundle\ActiveTheme'); - $activeTheme->getName()->shouldNotBeCalled(); - $activeTheme->setName('test')->shouldNotBeCalled(); - $activeTheme->setName('before')->shouldNotBeCalled(); - - $structureProvider = new WebspaceStructureProvider( - $twig->reveal(), - $structureManager->reveal(), - $cache, - $webspaceManager->reveal(), - $activeTheme->reveal() - ); - - $result = $structureProvider->getStructures('sulu_io'); - - $this->assertCount(2, $result); - $this->assertEquals($structures[0]->getKey(), $result[0]->getKey()); - $this->assertEquals($structures[2]->getKey(), $result[1]->getKey()); - } - - private function generateStructure(string $key, string $view): StructureInterface - { - $mock = $this->prophesize('Sulu\Component\Content\Compat\Structure\PageBridge'); - - $mock->getKey()->willReturn($key); - $mock->getView()->willReturn($view); - - return $mock->reveal(); - } -} diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 0000000..0487b85 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,111 @@ +# Upgrade + +## 3.0 + +### Switch from LiipThemeBundle to SyliusThemeBundle + +To achieve Symfony 5 compatibility the ThemingBundle has to be changed from [LiipThemeBundle](https://github.com/liip/LiipThemeBundle) to [SyliusThemeBundle](https://github.com/Sylius/SyliusThemeBundle). + +#### Remove the old theme bundle and install the SyliusThemeBundle: + +```bash +# Remove old theme-bundle +composer remove liip/theme-bundle --no-update + +# Install new theme-bundle +composer require sylius/theme-bundle:"^2.0" --no-update +composer require sulu/theme-bundle:"^3.0" +``` + +#### Remove old configuration + +The old `liip_theme.yaml` configuration needs to be removed: + +```diff +- liip_theme: +- themes: ['project', 'awesome'] +- active_theme: 'awesome' +``` + +In the next step you see how you configure the **awesome** theme using the SyliusThemeBundle. + +#### Configure the SyliusThemeBundle: + +In order to use the bundle you have to add the following default configuration: + +```yaml +# ./config/packages/sylius_theme.yaml + +sylius_theme: + sources: + filesystem: ~ +``` + +By default, the bundle seeks for the themes in the `%kernel.project_dir%/themes` directory and looks for a configuration +file named `composer.json`. This can be changed via the yaml configuration: + +```yaml +sylius_theme: + sources: + filesystem: + filename: theme.json +``` + +#### Convert Theme Configuration + +In the SyliusThemeBundle every theme must have its own configuration file in form of a `theme.json`. +Add a `theme.json` file and add the following minimal configuration: + +```diff ++ { ++ "name": "app/awesome" ++ } +``` + +Go to the [Theme Configuration Reference](https://github.com/Sylius/SyliusThemeBundle/blob/master/docs/theme_configuration_reference.md) +for the detailed documentation about the configuration options. + +Most likely you have to change the theme name. It is important, that the `name` matches the naming convention of composer (`vendor/name`). +Furthermore the `theme.json` has to be moved into the directory for this specific theme. + +For example: `%kernel.project_dir%/themes/awesome/theme.json` + +#### Update project structure + +Your templates have to be placed in a `templates` directory next to the `theme.json` file. + +For example: `%kernel.project_dir%/themes//templates` + +This results in the following project structure: + +``` +ProjectName +├── composer.json +├── assets +├── bin +├── config +├── templates +├── themes +│ ├── awesome +│ │ ├── templates +│ │ │ └── base.html.twig +│ │ └── theme.json +│ └── +│ ├── templates +│ │ └── base.html.twig +│ └── theme.json +├── ... +└── ... +``` + +As you can see in the project structure, each theme must have their own `theme.json` configuration file next to the +templates directory. + +#### Update webspace configuration + +If your old theme name didn't match the naming convention you also have to change it in the `webspace.xml` to the new one. + +```diff +- awesome ++ app/awesome +``` diff --git a/composer.json b/composer.json index 0e9e37e..9aa9a3e 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "sulu/theme-bundle", "type": "sulu-bundle", - "description": "The Sulu Bundle which provides themes", + "description": "The Sulu Bundle which provides theming functionality.", "license": "MIT", "keywords": [ "sulu", @@ -14,26 +14,24 @@ } ], "require": { - "php": "^7.2", - "liip/theme-bundle": "^1.5", + "php": "^7.3", "sulu/sulu": "^2.0", - "symfony/config": "^4.3", - "symfony/dependency-injection": "^4.3", - "symfony/http-kernel": "^4.3" + "symfony/config": "^4.4 || ^5.0", + "symfony/dependency-injection": "^4.4 || ^5.0", + "symfony/http-kernel": "^4.4 || ^5.0", + "sylius/theme-bundle": "^2.0" }, "require-dev": { "jackalope/jackalope-doctrine-dbal": "^1.3", - "phpunit/phpunit": "^8.0", + "symfony/dotenv": "^4.4 || ^5.0", + "phpunit/phpunit": "^8.5.9", "jangregor/phpstan-prophecy": "^0.8", "phpstan/phpstan": "^0.12", "phpstan/phpstan-doctrine": "^0.12", "phpstan/phpstan-phpunit": "^0.12", "phpstan/phpstan-symfony": "^0.12", - "symfony/dotenv": "^4.3", - "thecodingmachine/phpstan-strict-rules": "^0.12" - }, - "conflict": { - "liip/theme-bundle": "1.6.0" + "thecodingmachine/phpstan-strict-rules": "^0.12", + "symfony/phpunit-bridge": "^5.1" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 8aac5b0..f7f0df6 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,4 +16,14 @@ + + + + + + + + + +