diff --git a/composer.json b/composer.json index 1265bcbd..f54cc31b 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,7 @@ "ibexa/search": "~4.6.x-dev", "ibexa/solr": "~4.6.x-dev", "ibexa/test-core": "~4.6.x-dev", + "ibexa/test-rest": "~4.6.x-dev", "phpstan/phpstan": "^1.9", "phpstan/phpstan-symfony": "^1.2", "phpstan/phpstan-phpunit": "^1.3", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4924532e..ca9e51c8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -100,16 +100,6 @@ parameters: count: 1 path: src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php - - - message: "#^Method Ibexa\\\\Bundle\\\\FieldTypeRichText\\\\DependencyInjection\\\\IbexaFieldTypeRichTextExtension\\:\\:load\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\FieldTypeRichText\\\\DependencyInjection\\\\IbexaFieldTypeRichTextExtension\\:\\:load\\(\\) has parameter \\$configs with no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php - - message: "#^Method Ibexa\\\\Bundle\\\\FieldTypeRichText\\\\DependencyInjection\\\\IbexaFieldTypeRichTextExtension\\:\\:prepend\\(\\) has no return type specified\\.$#" count: 1 @@ -150,11 +140,6 @@ parameters: count: 1 path: src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php - - - message: "#^Parameter \\#1 \\$configuration of method Symfony\\\\Component\\\\DependencyInjection\\\\Extension\\\\Extension\\:\\:processConfiguration\\(\\) expects Symfony\\\\Component\\\\Config\\\\Definition\\\\ConfigurationInterface, Symfony\\\\Component\\\\Config\\\\Definition\\\\ConfigurationInterface\\|null given\\.$#" - count: 1 - path: src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php - - message: "#^Parameter \\#1 \\$filename of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parseFile\\(\\) expects string, string\\|false given\\.$#" count: 2 diff --git a/src/bundle/Controller/RichTextConfigController.php b/src/bundle/Controller/RichTextConfigController.php new file mode 100644 index 00000000..3c571ba7 --- /dev/null +++ b/src/bundle/Controller/RichTextConfigController.php @@ -0,0 +1,28 @@ +providerService = $providerService; + } + + public function loadConfigAction(): RichTextConfig + { + return new RichTextConfig($this->providerService->getConfiguration()); + } +} diff --git a/src/bundle/DependencyInjection/Configuration.php b/src/bundle/DependencyInjection/Configuration.php index e4fa52b2..5e67b3c9 100644 --- a/src/bundle/DependencyInjection/Configuration.php +++ b/src/bundle/DependencyInjection/Configuration.php @@ -18,18 +18,27 @@ class Configuration extends SiteAccessConfiguration { public const CUSTOM_TAG_ATTRIBUTE_TYPES = ['number', 'string', 'boolean', 'choice', 'link']; - /** - * Generates the configuration tree builder. - * - * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder - */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder(IbexaFieldTypeRichTextExtension::EXTENSION_NAME); $rootNode = $treeBuilder->getRootNode(); $sections = $rootNode->children(); + + $rootNode + ->children() + ->booleanNode('expose_config_as_global') + ->defaultTrue() + ->setDeprecated( + 'ibexa/fieldtype-richtext', + '4.6', + 'expose_config_as_global configuration is deprecated and will be removed in 5.0. ' + . 'Acquire RichText configuration via REST API instead.' + ) + ->end() + ->end(); + $this ->addEnabledAttributeTypesSection($sections); $this diff --git a/src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php b/src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php index 0da004ce..2ebbc298 100644 --- a/src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php +++ b/src/bundle/DependencyInjection/IbexaFieldTypeRichTextExtension.php @@ -14,15 +14,15 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension; use Symfony\Component\Yaml\Yaml; /** * Ibexa RichText Field Type Bundle extension. */ -class IbexaFieldTypeRichTextExtension extends Extension implements PrependExtensionInterface +class IbexaFieldTypeRichTextExtension extends ConfigurableExtension implements PrependExtensionInterface { public const EXTENSION_NAME = 'ibexa_fieldtype_richtext'; @@ -45,15 +45,15 @@ public function getAlias() } /** - * Load Ibexa RichText Field Type Bundle configuration. - * - * @param array $configs - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * - * @throws \Exception + * @param array $mergedConfig */ - public function load(array $configs, ContainerBuilder $container) + protected function loadInternal(array $mergedConfig, ContainerBuilder $container): void { + $container->setParameter( + 'ibexa.field_type.richtext.expose_config_as_global', + $mergedConfig['expose_config_as_global'], + ); + $settingsLoader = new Loader\YamlFileLoader( $container, new FileLocator(__DIR__ . '/../Resources/config/settings') @@ -82,10 +82,9 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('configuration.yaml'); $loader->load('api.yaml'); $loader->load('command.yaml'); + $loader->load('controller.yaml'); - $configuration = $this->getConfiguration($configs, $container); - $config = $this->processConfiguration($configuration, $configs); - $this->registerRichTextConfiguration($config, $container); + $this->registerRichTextConfiguration($mergedConfig, $container); (new IbexaEncoreConfigurationDumper($container))->dumpCustomConfiguration( self::WEBPACK_CONFIG_NAMES diff --git a/src/bundle/Resources/config/controller.yaml b/src/bundle/Resources/config/controller.yaml new file mode 100644 index 00000000..d2442f95 --- /dev/null +++ b/src/bundle/Resources/config/controller.yaml @@ -0,0 +1,9 @@ +services: + _defaults: + autoconfigure: true + autowire: true + public: false + + Ibexa\Bundle\FieldTypeRichText\Controller\RichTextConfigController: + tags: + - controller.service_arguments diff --git a/src/bundle/Resources/config/rest.yaml b/src/bundle/Resources/config/rest.yaml index 7aae8148..4fe9b4f2 100644 --- a/src/bundle/Resources/config/rest.yaml +++ b/src/bundle/Resources/config/rest.yaml @@ -9,3 +9,8 @@ services: - '@Ibexa\FieldTypeRichText\RichText\Converter\Html5Edit' tags: - { name: ibexa.rest.field_type.processor, alias: ezrichtext } + + Ibexa\FieldTypeRichText\REST\Output\ValueObjectVisitor\RichTextConfigVisitor: + parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor + tags: + - { name: ibexa.rest.output.value_object.visitor, type: Ibexa\FieldTypeRichText\REST\Value\RichTextConfig } diff --git a/src/bundle/Resources/config/routing_rest.yaml b/src/bundle/Resources/config/routing_rest.yaml new file mode 100644 index 00000000..341d2594 --- /dev/null +++ b/src/bundle/Resources/config/routing_rest.yaml @@ -0,0 +1,6 @@ +ibexa.rest.richtext_config: + path: /richtext-config + controller: 'Ibexa\Bundle\FieldTypeRichText\Controller\RichTextConfigController::loadConfigAction' + methods: [GET] + options: + expose: true diff --git a/src/bundle/Resources/config/templating.yaml b/src/bundle/Resources/config/templating.yaml index 42d26faf..e6831569 100644 --- a/src/bundle/Resources/config/templating.yaml +++ b/src/bundle/Resources/config/templating.yaml @@ -11,4 +11,6 @@ services: Ibexa\Bundle\FieldTypeRichText\Templating\Twig\Extension\YoutubeIdExtractorExtension: ~ - Ibexa\Bundle\FieldTypeRichText\Templating\Twig\Extension\RichTextConfigurationExtension: ~ + Ibexa\Bundle\FieldTypeRichText\Templating\Twig\Extension\RichTextConfigurationExtension: + arguments: + $exposeGlobals: '%ibexa.field_type.richtext.expose_config_as_global%' diff --git a/src/bundle/Templating/Twig/Extension/RichTextConfigurationExtension.php b/src/bundle/Templating/Twig/Extension/RichTextConfigurationExtension.php index e85b4013..caeb30f1 100644 --- a/src/bundle/Templating/Twig/Extension/RichTextConfigurationExtension.php +++ b/src/bundle/Templating/Twig/Extension/RichTextConfigurationExtension.php @@ -22,13 +22,28 @@ final class RichTextConfigurationExtension extends AbstractExtension implements /** @var \Ibexa\Contracts\FieldTypeRichText\Configuration\ProviderService */ private $configurationProvider; - public function __construct(ProviderService $configurationProvider) + private bool $exposeGlobals; + + public function __construct(ProviderService $configurationProvider, bool $exposeGlobals = false) { $this->configurationProvider = $configurationProvider; + $this->exposeGlobals = $exposeGlobals; } public function getGlobals(): array { + if (!$this->exposeGlobals) { + return []; + } + + trigger_deprecation( + 'ibexa/fieldtype-richtext', + '4.6', + 'Richtext configuration as global Twig variable is deprecated and will be removed in 5.0. ' + . 'Set bundle\'s configuration "ibexa_fieldtype_richtext.expose_config_as_global to false ' + . 'and acquire RichText configuration via REST API instead.', + ); + $config = $this->configurationProvider->getConfiguration(); return [ diff --git a/src/lib/REST/Output/ValueObjectVisitor/RichTextConfigVisitor.php b/src/lib/REST/Output/ValueObjectVisitor/RichTextConfigVisitor.php new file mode 100644 index 00000000..d758d960 --- /dev/null +++ b/src/lib/REST/Output/ValueObjectVisitor/RichTextConfigVisitor.php @@ -0,0 +1,28 @@ +startObjectElement('RichTextConfig'); + $visitor->setHeader('Content-Type', $generator->getMediaType('RichTextConfig')); + + foreach ($data->getConfig() as $namespace => $config) { + $generator->generateFieldTypeHash($namespace, $config); + } + + $generator->endObjectElement('RichTextConfig'); + } +} diff --git a/src/lib/REST/Value/RichTextConfig.php b/src/lib/REST/Value/RichTextConfig.php new file mode 100644 index 00000000..b771788e --- /dev/null +++ b/src/lib/REST/Value/RichTextConfig.php @@ -0,0 +1,33 @@ + */ + private array $config; + + /** + * @param array $config + */ + public function __construct(array $config) + { + $this->config = $config; + } + + /** + * @return array + */ + public function getConfig(): array + { + return $this->config; + } +} diff --git a/tests/bundle/DependencyInjection/Configuration/ConfigurationTest.php b/tests/bundle/DependencyInjection/Configuration/ConfigurationTest.php index 7c49c8a4..6d9708d9 100644 --- a/tests/bundle/DependencyInjection/Configuration/ConfigurationTest.php +++ b/tests/bundle/DependencyInjection/Configuration/ConfigurationTest.php @@ -74,6 +74,7 @@ public function providerForTestProcessingConfiguration(): array 'custom_tags' => [], 'custom_styles' => [], 'enabled_attribute_types' => ['number', 'string', 'boolean', 'choice', 'link'], + 'expose_config_as_global' => true, ], ], 'Alloy editor configs from multiple sources' => [ @@ -113,6 +114,7 @@ public function providerForTestProcessingConfiguration(): array 'custom_tags' => [], 'custom_styles' => [], 'enabled_attribute_types' => ['number', 'string', 'boolean', 'choice', 'link'], + 'expose_config_as_global' => true, ], ], ]; diff --git a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/000-attribute-type-number.yaml b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/000-attribute-type-number.yaml index 6ae7f80a..0b674fce 100644 --- a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/000-attribute-type-number.yaml +++ b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/000-attribute-type-number.yaml @@ -15,3 +15,4 @@ enabled_attribute_types: - 'boolean' - 'choice' - 'link' +expose_config_as_global: true diff --git a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/001-attribute-type-string.yaml b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/001-attribute-type-string.yaml index a6422979..7e9942c2 100644 --- a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/001-attribute-type-string.yaml +++ b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/001-attribute-type-string.yaml @@ -15,3 +15,4 @@ enabled_attribute_types: - 'boolean' - 'choice' - 'link' +expose_config_as_global: true diff --git a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/002-attribute-type-boolean.yaml b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/002-attribute-type-boolean.yaml index ca1254f0..a13028d7 100644 --- a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/002-attribute-type-boolean.yaml +++ b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/002-attribute-type-boolean.yaml @@ -15,3 +15,4 @@ enabled_attribute_types: - 'boolean' - 'choice' - 'link' +expose_config_as_global: true diff --git a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/003-attribute-type-choice.yaml b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/003-attribute-type-choice.yaml index e41bfec1..cbdc35c2 100644 --- a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/003-attribute-type-choice.yaml +++ b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/003-attribute-type-choice.yaml @@ -16,3 +16,4 @@ enabled_attribute_types: - 'boolean' - 'choice' - 'link' +expose_config_as_global: true diff --git a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/004-attribute-type-link.yaml b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/004-attribute-type-link.yaml index 9195a030..8c9fd438 100644 --- a/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/004-attribute-type-link.yaml +++ b/tests/bundle/DependencyInjection/Fixtures/custom_tags/output/004-attribute-type-link.yaml @@ -15,3 +15,4 @@ enabled_attribute_types: - 'boolean' - 'choice' - 'link' +expose_config_as_global: true diff --git a/tests/integration/REST/RichTextConfigTest.php b/tests/integration/REST/RichTextConfigTest.php new file mode 100644 index 00000000..9055424a --- /dev/null +++ b/tests/integration/REST/RichTextConfigTest.php @@ -0,0 +1,80 @@ +client->request('GET', '/api/ibexa/v2/richtext-config', [], [], [ + 'HTTP_ACCEPT' => $acceptHeader, + ]); + + $this->assertResponseIsSuccessful(); + $response = $this->client->getResponse(); + $content = $response->getContent(); + self::assertIsString($content); + + if ($type === 'xml') { + self::assertSame('application/vnd.ibexa.api.RichTextConfig+xml', $response->headers->get('Content-Type')); + self::assertResponseMatchesXmlSnapshot($content, $snapshot); + } elseif ($type === 'json') { + self::assertSame('application/vnd.ibexa.api.RichTextConfig+json', $response->headers->get('Content-Type')); + self::assertResponseMatchesJsonSnapshot($content, $snapshot); + } else { + throw new \LogicException(sprintf( + 'Unknown type: "%s". Expected one of: "%s"', + $type, + implode('", "', ['json', 'xml']), + )); + } + } + + /** + * @return iterable + */ + public static function provideForTestConfig(): iterable + { + yield 'application/vnd.ibexa.api.RichTextConfig+json' => [ + 'json', + 'application/vnd.ibexa.api.RichTextConfig+json', + __DIR__ . '/_output/RichTextConfig.json', + ]; + + yield 'application/json' => [ + 'json', + 'application/json', + __DIR__ . '/_output/RichTextConfig.json', + ]; + + yield 'application/vnd.ibexa.api.RichTextConfig+xml' => [ + 'xml', + 'application/vnd.ibexa.api.RichTextConfig+xml', + __DIR__ . '/_output/RichTextConfig.xml', + ]; + + yield 'application/xml' => [ + 'xml', + 'application/xml', + __DIR__ . '/_output/RichTextConfig.xml', + ]; + } +} diff --git a/tests/integration/REST/_output/RichTextConfig.json b/tests/integration/REST/_output/RichTextConfig.json new file mode 100644 index 00000000..13cf6351 --- /dev/null +++ b/tests/integration/REST/_output/RichTextConfig.json @@ -0,0 +1,21 @@ +{ + "RichTextConfig": { + "_media-type": "application\/vnd.ibexa.api.RichTextConfig+json", + "customStyles": [], + "customTags": [], + "alloyEditor": { + "extraPlugins": [], + "toolbars": [], + "classes": [], + "attributes": [], + "nativeAttributes": { + "table": [ + "border" + ] + } + }, + "CKEditor": { + "toolbar": [] + } + } +} \ No newline at end of file diff --git a/tests/integration/REST/_output/RichTextConfig.xml b/tests/integration/REST/_output/RichTextConfig.xml new file mode 100644 index 00000000..d0fba021 --- /dev/null +++ b/tests/integration/REST/_output/RichTextConfig.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + border + + + + + + + diff --git a/tests/integration/Resources/config.yaml b/tests/integration/Resources/config.yaml index 60ae5ec2..f2352ffc 100644 --- a/tests/integration/Resources/config.yaml +++ b/tests/integration/Resources/config.yaml @@ -15,6 +15,10 @@ ibexa: engine: '%env(SEARCH_ENGINE)%' connection: default + siteaccess: + groups: + admin_group: [] + system: default: languages: diff --git a/tests/integration/Resources/routing.yaml b/tests/integration/Resources/routing.yaml index 2956445d..10db3109 100644 --- a/tests/integration/Resources/routing.yaml +++ b/tests/integration/Resources/routing.yaml @@ -1,2 +1,6 @@ ibexa.user.default_profile_image.initials: path: /user/default_profile_image/initials.svg + +ibexa.fieldtype_richtext.rest: + resource: '@IbexaFieldTypeRichTextBundle/Resources/config/routing_rest.yaml' + prefix: '%ibexa.rest.path_prefix%'