diff --git a/composer.json b/composer.json index 9852f771..5d2cc210 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ "phpunit/phpunit": "^9.5.13", "psalm/plugin-phpunit": "^0.16", "psalm/plugin-symfony": "^3.0", - "rector/rector": "^0.13", + "rector/rector": "^0.14", "sonata-project/doctrine-extensions": "^1.10.1", "symfony/browser-kit": "^4.4 || ^5.4 || ^6.0", "symfony/debug": "^4.4 || ^5.4 || ^6.0", diff --git a/src/Block/Service/AbstractMenuBlockService.php b/src/Block/Service/AbstractMenuBlockService.php new file mode 100644 index 00000000..3c6c34b0 --- /dev/null +++ b/src/Block/Service/AbstractMenuBlockService.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\BlockBundle\Block\Service; + +use Knp\Menu\ItemInterface; +use Sonata\BlockBundle\Block\BlockContextInterface; +use Sonata\BlockBundle\Form\Mapper\FormMapper; +use Sonata\BlockBundle\Model\BlockInterface; +use Sonata\Form\Type\ImmutableArrayType; +use Sonata\Form\Validator\ErrorElement; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\OptionsResolver\OptionsResolver; + +abstract class AbstractMenuBlockService extends AbstractBlockService implements EditableBlockService +{ + public function execute(BlockContextInterface $blockContext, ?Response $response = null): Response + { + $template = $blockContext->getTemplate(); + \assert(null !== $template); + + $responseSettings = [ + 'menu' => $this->getMenu($blockContext), + 'menu_options' => $this->getMenuOptions($blockContext->getSettings()), + 'block' => $blockContext->getBlock(), + 'context' => $blockContext, + ]; + + // NEXT_MAJOR: remove + if ('private' === $blockContext->getSetting('cache_policy')) { + return $this->renderPrivateResponse($template, $responseSettings, $response); + } + + return $this->renderResponse($template, $responseSettings, $response); + } + + public function configureCreateForm(FormMapper $form, BlockInterface $block): void + { + $this->configureEditForm($form, $block); + } + + public function configureEditForm(FormMapper $form, BlockInterface $block): void + { + $form->add('settings', ImmutableArrayType::class, [ + 'keys' => $this->getFormSettingsKeys(), + 'translation_domain' => 'SonataBlockBundle', + ]); + } + + public function validate(ErrorElement $errorElement, BlockInterface $block): void + { + } + + public function configureSettings(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'title' => '', + // NEXT_MAJOR: Remove. + 'cache_policy' => 'public', + 'template' => '@SonataBlock/Block/block_core_menu.html.twig', + 'safe_labels' => false, + 'current_class' => 'active', + 'first_class' => false, + 'last_class' => false, + 'menu_template' => null, + ]); + + // NEXT_MAJOR: Remove setDeprecated. + $resolver->setDeprecated( + 'cache_policy', + ...$this->deprecationParameters( + '4.12', + 'Option "cache_policy" is deprecated since sonata-project/block-bundle 4.12 and will be removed in 5.0.' + ) + ); + } + + /** + * @return array, array}> + */ + protected function getFormSettingsKeys(): array + { + return [ + ['title', TextType::class, [ + 'required' => false, + 'label' => 'form.label_title', + 'translation_domain' => 'SonataBlockBundle', + ]], + ['cache_policy', ChoiceType::class, [ + 'label' => 'form.label_cache_policy', + 'translation_domain' => 'SonataBlockBundle', + 'choices' => ['public', 'private'], + ]], + ['safe_labels', CheckboxType::class, [ + 'required' => false, + 'label' => 'form.label_safe_labels', + 'translation_domain' => 'SonataBlockBundle', + ]], + ['current_class', TextType::class, [ + 'required' => false, + 'label' => 'form.label_current_class', + 'translation_domain' => 'SonataBlockBundle', + ]], + ['first_class', TextType::class, [ + 'required' => false, + 'label' => 'form.label_first_class', + 'translation_domain' => 'SonataBlockBundle', + ]], + ['last_class', TextType::class, [ + 'required' => false, + 'label' => 'form.label_last_class', + 'translation_domain' => 'SonataBlockBundle', + ]], + ['menu_template', TextType::class, [ + 'required' => false, + 'label' => 'form.label_menu_template', + 'translation_domain' => 'SonataBlockBundle', + ]], + ]; + } + + /** + * Gets the menu to render. + * + * @return ItemInterface|string + */ + abstract protected function getMenu(BlockContextInterface $blockContext); + + /** + * Replaces setting keys with knp menu item options keys. + * + * @param array $settings + * + * @return array + */ + private function getMenuOptions(array $settings): array + { + $mapping = [ + 'current_class' => 'currentClass', + 'first_class' => 'firstClass', + 'last_class' => 'lastClass', + 'safe_labels' => 'allow_safe_labels', + 'menu_template' => 'template', + ]; + + $options = []; + + foreach ($settings as $key => $value) { + if (\array_key_exists($key, $mapping) && null !== $value) { + $options[$mapping[$key]] = $value; + } + } + + return $options; + } + + /** + * This class is a BC layer for deprecation messages for symfony/options-resolver < 5.1. + * Remove this class when dropping support for symfony/options-resolver < 5.1. + * + * @return mixed[] + */ + private function deprecationParameters(string $version, string $message): array + { + // @phpstan-ignore-next-line + if (method_exists(OptionsResolver::class, 'define')) { + return [ + 'sonata-project/block-bundle', + $version, + $message, + ]; + } + + return [$message]; + } +} diff --git a/src/Block/Service/ContainerBlockService.php b/src/Block/Service/ContainerBlockService.php index 5fd80ea5..e422d1a6 100644 --- a/src/Block/Service/ContainerBlockService.php +++ b/src/Block/Service/ContainerBlockService.php @@ -46,16 +46,20 @@ public function configureEditForm(FormMapper $form, BlockInterface $block): void ['code', TextType::class, [ 'required' => false, 'label' => 'form.label_code', + 'translation_domain' => 'SonataBlockBundle', ]], ['layout', TextareaType::class, [ 'label' => 'form.label_layout', + 'translation_domain' => 'SonataBlockBundle', ]], ['class', TextType::class, [ 'required' => false, 'label' => 'form.label_class', + 'translation_domain' => 'SonataBlockBundle', ]], ['template', ContainerTemplateType::class, [ 'label' => 'form.label_template', + 'translation_domain' => 'SonataBlockBundle', ]], ], 'translation_domain' => 'SonataBlockBundle', diff --git a/src/Block/Service/MenuBlockService.php b/src/Block/Service/MenuBlockService.php index 1f155c0a..952bd379 100644 --- a/src/Block/Service/MenuBlockService.php +++ b/src/Block/Service/MenuBlockService.php @@ -13,21 +13,15 @@ namespace Sonata\BlockBundle\Block\Service; -use Knp\Menu\ItemInterface; use Knp\Menu\Provider\MenuProviderInterface; use Sonata\BlockBundle\Block\BlockContextInterface; -use Sonata\BlockBundle\Form\Mapper\FormMapper; use Sonata\BlockBundle\Menu\MenuRegistryInterface; use Sonata\BlockBundle\Meta\Metadata; use Sonata\BlockBundle\Meta\MetadataInterface; use Sonata\BlockBundle\Model\BlockInterface; -use Sonata\Form\Type\ImmutableArrayType; use Sonata\Form\Validator\ErrorElement; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\Form\FormTypeInterface; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\OptionsResolver\OptionsResolver; use Twig\Environment; @@ -36,7 +30,7 @@ * * @final since sonata-project/block-bundle 4.x */ -class MenuBlockService extends AbstractBlockService implements EditableBlockService +class MenuBlockService extends AbstractMenuBlockService implements EditableBlockService { private MenuProviderInterface $menuProvider; @@ -53,39 +47,6 @@ public function __construct( $this->menuRegistry = $menuRegistry; } - public function execute(BlockContextInterface $blockContext, ?Response $response = null): Response - { - $template = $blockContext->getTemplate(); - \assert(null !== $template); - - $responseSettings = [ - 'menu' => $this->getMenu($blockContext), - 'menu_options' => $this->getMenuOptions($blockContext->getSettings()), - 'block' => $blockContext->getBlock(), - 'context' => $blockContext, - ]; - - // NEXT_MAJOR: remove - if ('private' === $blockContext->getSetting('cache_policy')) { - return $this->renderPrivateResponse($template, $responseSettings, $response); - } - - return $this->renderResponse($template, $responseSettings, $response); - } - - public function configureCreateForm(FormMapper $form, BlockInterface $block): void - { - $this->configureEditForm($form, $block); - } - - public function configureEditForm(FormMapper $form, BlockInterface $block): void - { - $form->add('settings', ImmutableArrayType::class, [ - 'keys' => $this->getFormSettingsKeys(), - 'translation_domain' => 'SonataBlockBundle', - ]); - } - public function validate(ErrorElement $errorElement, BlockInterface $block): void { $name = $block->getSetting('menu_name'); @@ -99,28 +60,42 @@ public function validate(ErrorElement $errorElement, BlockInterface $block): voi public function configureSettings(OptionsResolver $resolver): void { + parent::configureSettings($resolver); + $resolver->setDefaults([ - 'title' => '', - // NEXT_MAJOR: Remove. - 'cache_policy' => 'public', - 'template' => '@SonataBlock/Block/block_core_menu.html.twig', 'menu_name' => '', - 'safe_labels' => false, - 'current_class' => 'active', - 'first_class' => false, - 'last_class' => false, + // NEXT_MAJOR: Remove. 'current_uri' => null, + // NEXT_MAJOR: Remove. 'menu_class' => 'list-group', + // NEXT_MAJOR: Remove. 'children_class' => 'list-group-item', - 'menu_template' => null, ]); // NEXT_MAJOR: Remove setDeprecated. $resolver->setDeprecated( - 'cache_policy', + 'current_uri', ...$this->deprecationParameters( - '4.12', - 'Option "cache_policy" is deprecated since sonata-project/block-bundle 4.12 and will be removed in 5.0.' + '4.x', + 'Option "current_uri" is deprecated since sonata-project/block-bundle 4.x and will be removed in 5.0.' + ) + ); + + // NEXT_MAJOR: Remove setDeprecated. + $resolver->setDeprecated( + 'menu_class', + ...$this->deprecationParameters( + '4.x', + 'Option "menu_class" is deprecated since sonata-project/block-bundle 4.x and will be removed in 5.0.' + ) + ); + + // NEXT_MAJOR: Remove setDeprecated. + $resolver->setDeprecated( + 'children_class', + ...$this->deprecationParameters( + '4.x', + 'Option "children_class" is deprecated since sonata-project/block-bundle 4.x and will be removed in 5.0.' ) ); } @@ -132,109 +107,50 @@ public function getMetadata(): MetadataInterface ]); } - /** - * @return array, array}> - */ - private function getFormSettingsKeys(): array + protected function getFormSettingsKeys(): array { $choiceOptions = [ 'required' => false, - 'label' => 'form.label_url', - 'choice_translation_domain' => 'SonataBlockBundle', + 'label' => 'form.label_menu_name', + 'translation_domain' => 'SonataBlockBundle', ]; $choiceOptions['choices'] = array_flip($this->menuRegistry->getAliasNames()); - return [ - ['title', TextType::class, [ - 'required' => false, - 'label' => 'form.label_title', - ]], - ['cache_policy', ChoiceType::class, [ - 'label' => 'form.label_cache_policy', - 'choices' => ['public', 'private'], - ]], - ['menu_name', ChoiceType::class, $choiceOptions], - ['safe_labels', CheckboxType::class, [ - 'required' => false, - 'label' => 'form.label_safe_labels', - ]], - ['current_class', TextType::class, [ - 'required' => false, - 'label' => 'form.label_current_class', - ]], - ['first_class', TextType::class, [ - 'required' => false, - 'label' => 'form.label_first_class', - ]], - ['last_class', TextType::class, [ - 'required' => false, - 'label' => 'form.label_last_class', - ]], - ['menu_class', TextType::class, [ - 'required' => false, - 'label' => 'form.label_menu_class', - ]], - ['children_class', TextType::class, [ - 'required' => false, - 'label' => 'form.label_children_class', - ]], - ['menu_template', TextType::class, [ - 'required' => false, - 'label' => 'form.label_menu_template', - ]], - ]; + return array_merge( + parent::getFormSettingsKeys(), + [ + ['menu_name', ChoiceType::class, $choiceOptions], + // NEXT_MAJOR: Remove this and the related translations. + ['menu_class', TextType::class, [ + 'required' => false, + 'label' => 'form.label_menu_class', + 'translation_domain' => 'SonataBlockBundle', + ]], + // NEXT_MAJOR: Remove this and the related translations. + ['children_class', TextType::class, [ + 'required' => false, + 'label' => 'form.label_children_class', + 'translation_domain' => 'SonataBlockBundle', + ]], + ] + ); } - /** - * Gets the menu to render. - * - * @return ItemInterface|string - */ - private function getMenu(BlockContextInterface $blockContext) + protected function getMenu(BlockContextInterface $blockContext) { $settings = $blockContext->getSettings(); return $settings['menu_name']; } - /** - * Replaces setting keys with knp menu item options keys. - * - * @param array $settings - * - * @return array - */ - private function getMenuOptions(array $settings): array - { - $mapping = [ - 'current_class' => 'currentClass', - 'first_class' => 'firstClass', - 'last_class' => 'lastClass', - 'safe_labels' => 'allow_safe_labels', - 'menu_template' => 'template', - ]; - - $options = []; - - foreach ($settings as $key => $value) { - if (\array_key_exists($key, $mapping) && null !== $value) { - $options[$mapping[$key]] = $value; - } - } - - return $options; - } - /** * This class is a BC layer for deprecation messages for symfony/options-resolver < 5.1. * Remove this class when dropping support for symfony/options-resolver < 5.1. * - * @param string|\Closure $message - * * @return mixed[] */ - private function deprecationParameters(string $version, $message): array + private function deprecationParameters(string $version, string $message): array { // @phpstan-ignore-next-line if (method_exists(OptionsResolver::class, 'define')) { diff --git a/src/Block/Service/RssBlockService.php b/src/Block/Service/RssBlockService.php index 74089f46..073dfd39 100644 --- a/src/Block/Service/RssBlockService.php +++ b/src/Block/Service/RssBlockService.php @@ -57,21 +57,26 @@ public function configureEditForm(FormMapper $form, BlockInterface $block): void ['url', UrlType::class, [ 'required' => false, 'label' => 'form.label_url', + 'translation_domain' => 'SonataBlockBundle', ]], ['title', TextType::class, [ 'label' => 'form.label_title', + 'translation_domain' => 'SonataBlockBundle', 'required' => false, ]], ['translation_domain', TextType::class, [ 'label' => 'form.label_translation_domain', + 'translation_domain' => 'SonataBlockBundle', 'required' => false, ]], ['icon', TextType::class, [ 'label' => 'form.label_icon', + 'translation_domain' => 'SonataBlockBundle', 'required' => false, ]], ['class', TextType::class, [ 'label' => 'form.label_class', + 'translation_domain' => 'SonataBlockBundle', 'required' => false, ]], ], diff --git a/src/Block/Service/TemplateBlockService.php b/src/Block/Service/TemplateBlockService.php index a7f021f5..002a799b 100644 --- a/src/Block/Service/TemplateBlockService.php +++ b/src/Block/Service/TemplateBlockService.php @@ -50,6 +50,7 @@ public function configureEditForm(FormMapper $form, BlockInterface $block): void 'keys' => [ ['template', null, [ 'label' => 'form.label_template', + 'translation_domain' => 'SonataBlockBundle', ]], ], 'translation_domain' => 'SonataBlockBundle', diff --git a/src/Block/Service/TextBlockService.php b/src/Block/Service/TextBlockService.php index f082c8ab..70b1014d 100644 --- a/src/Block/Service/TextBlockService.php +++ b/src/Block/Service/TextBlockService.php @@ -51,6 +51,7 @@ public function configureEditForm(FormMapper $form, BlockInterface $block): void 'keys' => [ ['content', TextareaType::class, [ 'label' => 'form.label_content', + 'translation_domain' => 'SonataBlockBundle', ]], ], 'translation_domain' => 'SonataBlockBundle', diff --git a/src/Resources/translations/SonataBlockBundle.de.xliff b/src/Resources/translations/SonataBlockBundle.de.xliff index 675a970b..51709a92 100644 --- a/src/Resources/translations/SonataBlockBundle.de.xliff +++ b/src/Resources/translations/SonataBlockBundle.de.xliff @@ -54,6 +54,10 @@ form.label_cache_policy Cache Policy + + form.label_menu_name + Menu + form.label_safe_labels Sichere Bezeichner diff --git a/src/Resources/translations/SonataBlockBundle.en.xliff b/src/Resources/translations/SonataBlockBundle.en.xliff index ecae67ce..22de3b7b 100644 --- a/src/Resources/translations/SonataBlockBundle.en.xliff +++ b/src/Resources/translations/SonataBlockBundle.en.xliff @@ -54,6 +54,10 @@ form.label_cache_policy Cache policy + + form.label_menu_name + Menu + form.label_safe_labels Safe labels diff --git a/src/Resources/translations/SonataBlockBundle.fr.xliff b/src/Resources/translations/SonataBlockBundle.fr.xliff index cb02058f..4598025e 100644 --- a/src/Resources/translations/SonataBlockBundle.fr.xliff +++ b/src/Resources/translations/SonataBlockBundle.fr.xliff @@ -54,6 +54,10 @@ form.label_cache_policy Cache Policy + + form.label_menu_name + Nom du menu + form.label_safe_labels Libellés sûrs diff --git a/src/Resources/translations/SonataBlockBundle.hu.xliff b/src/Resources/translations/SonataBlockBundle.hu.xliff index 289807e1..ecc7a209 100644 --- a/src/Resources/translations/SonataBlockBundle.hu.xliff +++ b/src/Resources/translations/SonataBlockBundle.hu.xliff @@ -54,6 +54,10 @@ form.label_cache_policy Gyorsítótár politika + + form.label_menu_name + Menü + form.label_safe_labels Biztonságos címkék diff --git a/src/Resources/translations/SonataBlockBundle.it.xliff b/src/Resources/translations/SonataBlockBundle.it.xliff index bf8d5491..5c114e19 100644 --- a/src/Resources/translations/SonataBlockBundle.it.xliff +++ b/src/Resources/translations/SonataBlockBundle.it.xliff @@ -54,6 +54,10 @@ form.label_cache_policy Politica cache + + form.label_menu_name + Menù + form.label_safe_labels Label sicure diff --git a/src/Resources/translations/SonataBlockBundle.nl.xliff b/src/Resources/translations/SonataBlockBundle.nl.xliff index 096f01d6..cf7ddcaa 100644 --- a/src/Resources/translations/SonataBlockBundle.nl.xliff +++ b/src/Resources/translations/SonataBlockBundle.nl.xliff @@ -54,6 +54,10 @@ form.label_cache_policy Cache beleid + + form.label_menu_name + Menu + form.label_safe_labels Veilige labels diff --git a/src/Resources/translations/SonataBlockBundle.ru.xliff b/src/Resources/translations/SonataBlockBundle.ru.xliff index c36e9397..355cde05 100644 --- a/src/Resources/translations/SonataBlockBundle.ru.xliff +++ b/src/Resources/translations/SonataBlockBundle.ru.xliff @@ -22,6 +22,50 @@ sonata.block.template Шаблон + + form.label_url + URL + + + form.label_title + Заголовок + + + form.label_class + CSS-класс + + + form.label_menu_name + Меню + + + form.label_safe_labels + Безопасные заголовки + + + form.label_current_class + CSS-класс "текущий" + + + form.label_first_class + CSS-класс "первый" + + + form.label_last_class + CSS-класс "последний" + + + form.label_menu_class + CSS-класс меню + + + form.label_children_class + Класс CSS для дочених элементов + + + form.label_menu_template + Шаблон меню + diff --git a/tests/Block/Service/MenuBlockServiceTest.php b/tests/Block/Service/MenuBlockServiceTest.php index e11b1a25..a5da500d 100644 --- a/tests/Block/Service/MenuBlockServiceTest.php +++ b/tests/Block/Service/MenuBlockServiceTest.php @@ -60,8 +60,8 @@ public function testBuildEditForm(): void $choiceOptions = [ 'required' => false, - 'label' => 'form.label_url', - 'choice_translation_domain' => 'SonataBlockBundle', + 'label' => 'form.label_menu_name', + 'translation_domain' => 'SonataBlockBundle', ]; $choiceOptions['choices'] = [ @@ -74,39 +74,48 @@ public function testBuildEditForm(): void ['title', TextType::class, [ 'required' => false, 'label' => 'form.label_title', + 'translation_domain' => 'SonataBlockBundle', ]], ['cache_policy', ChoiceType::class, [ 'label' => 'form.label_cache_policy', + 'translation_domain' => 'SonataBlockBundle', 'choices' => ['public', 'private'], ]], - ['menu_name', ChoiceType::class, $choiceOptions], ['safe_labels', CheckboxType::class, [ 'required' => false, 'label' => 'form.label_safe_labels', + 'translation_domain' => 'SonataBlockBundle', ]], ['current_class', TextType::class, [ 'required' => false, 'label' => 'form.label_current_class', + 'translation_domain' => 'SonataBlockBundle', ]], ['first_class', TextType::class, [ 'required' => false, 'label' => 'form.label_first_class', + 'translation_domain' => 'SonataBlockBundle', ]], ['last_class', TextType::class, [ 'required' => false, 'label' => 'form.label_last_class', + 'translation_domain' => 'SonataBlockBundle', + ]], + ['menu_template', TextType::class, [ + 'required' => false, + 'label' => 'form.label_menu_template', + 'translation_domain' => 'SonataBlockBundle', ]], + ['menu_name', ChoiceType::class, $choiceOptions], ['menu_class', TextType::class, [ 'required' => false, 'label' => 'form.label_menu_class', + 'translation_domain' => 'SonataBlockBundle', ]], ['children_class', TextType::class, [ 'required' => false, 'label' => 'form.label_children_class', - ]], - ['menu_template', TextType::class, [ - 'required' => false, - 'label' => 'form.label_menu_template', + 'translation_domain' => 'SonataBlockBundle', ]], ], 'translation_domain' => 'SonataBlockBundle',