From d3ec14a6d1e0359c507ecdcd9b308c2f7ae1dbe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20D=C4=99bi=C5=84ski?= Date: Thu, 7 Aug 2025 11:38:14 +0200 Subject: [PATCH 1/2] IBX-10425: Translated string is returned as XML --- composer.json | 1 + phpstan-baseline.neon | 12 +++++++ src/lib/Encoder.php | 35 +++++++++++++++++++ .../Encoder/Field/PageBuilderFieldEncoder.php | 34 ++++++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/composer.json b/composer.json index 1bba1f4..9a8f189 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "require": { "php": "^7.4 || ^8.0", "ext-libxml": "*", + "ext-dom": "*", "symfony/yaml": "^5.0", "symfony/routing": "^5.0", "symfony/console": "^5.0", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d3ecc67..0bf6eba 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -30,6 +30,18 @@ parameters: count: 1 path: src/bundle/Form/TranslationAddDataTransformer.php + - + message: '#^Call to an undefined method DOMNode\:\:getAttribute\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/lib/Encoder.php + + - + message: '#^Call to an undefined method DOMNode\:\:getAttribute\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/lib/Encoder/Field/PageBuilderFieldEncoder.php + - message: '#^Cannot call method setValue\(\) on Ibexa\\Contracts\\FieldTypePage\\FieldType\\LandingPage\\Model\\Attribute\|null\.$#' identifier: method.nonObject diff --git a/src/lib/Encoder.php b/src/lib/Encoder.php index c4aab29..090d658 100644 --- a/src/lib/Encoder.php +++ b/src/lib/Encoder.php @@ -8,6 +8,9 @@ namespace Ibexa\AutomatedTranslation; +use DOMCdataSection; +use DOMDocument; +use DOMXPath; use Ibexa\AutomatedTranslation\Encoder\Field\FieldEncoderManager; use Ibexa\AutomatedTranslation\Exception\EmptyTranslatedFieldException; use Ibexa\Bundle\AutomatedTranslation\Event\FieldDecodeEvent; @@ -18,7 +21,9 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Content; use Ibexa\Contracts\Core\Repository\Values\Content\Field; use Ibexa\Core\FieldType\Value; +use Ibexa\FieldTypeRichText\FieldType\RichText\Value as RichTextValue; use InvalidArgumentException; +use RuntimeException; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; @@ -118,6 +123,36 @@ public function encode(Content $content): string $encoder = new XmlEncoder(); $payload = $encoder->encode($results, XmlEncoder::FORMAT); + + $dom = new DOMDocument(); + $dom->loadXML($payload); + $xpath = new DOMXPath($dom); + $textNodes = $xpath->query('//text()'); + if ($textNodes !== false) { + foreach ($textNodes as $textNode) { + if ($textNode instanceof DOMCdataSection) { + $parent = $textNode->parentNode; + if ($parent === null) { + continue; + } + + $type = $parent->getAttribute('type'); + + if ($type !== RichTextValue::class) { + $newText = $dom->createTextNode($textNode->data); + $parent->replaceChild($newText, $textNode); + } + } + } + $payload = $dom->saveXML(); + + if (!$payload) { + throw new RuntimeException( + sprintf('Saving XML failed after removing CDATA, error: %s', preg_last_error_msg()) + ); + } + } + // here Encoder has decorated with CDATA, we don't want the CDATA return str_replace( [''], diff --git a/src/lib/Encoder/Field/PageBuilderFieldEncoder.php b/src/lib/Encoder/Field/PageBuilderFieldEncoder.php index e9588b7..22bb9d3 100644 --- a/src/lib/Encoder/Field/PageBuilderFieldEncoder.php +++ b/src/lib/Encoder/Field/PageBuilderFieldEncoder.php @@ -8,6 +8,9 @@ namespace Ibexa\AutomatedTranslation\Encoder\Field; +use DOMCdataSection; +use DOMDocument; +use DOMXPath; use Ibexa\AutomatedTranslation\Encoder\BlockAttribute\BlockAttributeEncoderManager; use Ibexa\AutomatedTranslation\Exception\EmptyTranslatedAttributeException; use Ibexa\Contracts\AutomatedTranslation\Encoder\Field\FieldEncoderInterface; @@ -15,7 +18,9 @@ use Ibexa\Core\FieldType\Value as APIValue; use Ibexa\FieldTypePage\FieldType\LandingPage\Value; use Ibexa\FieldTypePage\FieldType\Page\Block\Definition\BlockDefinitionFactoryInterface; +use Ibexa\FieldTypeRichText\FieldType\RichText\Value as RichTextValue; use InvalidArgumentException; +use RuntimeException; use Symfony\Component\Serializer\Encoder\XmlEncoder; final class PageBuilderFieldEncoder implements FieldEncoderInterface @@ -84,6 +89,35 @@ public function encode(Field $field): string XmlEncoder::ROOT_NODE_NAME => 'blocks', ]); + $dom = new DOMDocument(); + $dom->loadXML($payload); + $xpath = new DOMXPath($dom); + $textNodes = $xpath->query('//text()'); + if ($textNodes !== false) { + foreach ($textNodes as $textNode) { + if ($textNode instanceof DOMCdataSection) { + $parent = $textNode->parentNode; + if ($parent === null) { + continue; + } + + $type = $parent->getAttribute('type'); + + if ($type !== RichTextValue::class) { + $newText = $dom->createTextNode($textNode->data); + $parent->replaceChild($newText, $textNode); + } + } + } + $payload = $dom->saveXML(); + + if (!$payload) { + throw new RuntimeException( + sprintf('Saving XML failed after removing CDATA, error: %s', preg_last_error_msg()) + ); + } + } + $payload = str_replace('' . "\n", '', $payload); $payload = str_replace( From e3db293619c93d87791bf15ab0bf977257f729cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20D=C4=99bi=C5=84ski?= Date: Thu, 7 Aug 2025 11:51:26 +0200 Subject: [PATCH 2/2] Corrected phpstan for 7.4 and higher --- phpstan-baseline-7.4.neon | 7 +++++++ phpstan-baseline-gte-8.0.neon | 7 +++++++ phpstan-baseline.neon | 6 ------ phpstan-baseline.neon.php | 19 +++++++++++++++++++ phpstan.neon | 1 + 5 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 phpstan-baseline-7.4.neon create mode 100644 phpstan-baseline-gte-8.0.neon create mode 100644 phpstan-baseline.neon.php diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon new file mode 100644 index 0000000..1178cb3 --- /dev/null +++ b/phpstan-baseline-7.4.neon @@ -0,0 +1,7 @@ +parameters: + ignoreErrors: + - + message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(mixed\)\: bool\)\|null, Closure\(mixed\)\: \(array\|null\) given\.$#' + identifier: argument.type + count: 1 + path: src/bundle/DependencyInjection/IbexaAutomatedTranslationExtension.php \ No newline at end of file diff --git a/phpstan-baseline-gte-8.0.neon b/phpstan-baseline-gte-8.0.neon new file mode 100644 index 0000000..46bfb61 --- /dev/null +++ b/phpstan-baseline-gte-8.0.neon @@ -0,0 +1,7 @@ +parameters: + ignoreErrors: + - + message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(mixed\)\: bool\)\|null, Closure\(mixed\)\: array given\.$#' + identifier: argument.type + count: 1 + path: src/bundle/DependencyInjection/IbexaAutomatedTranslationExtension.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0bf6eba..6179edf 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,11 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(mixed\)\: bool\)\|null, Closure\(mixed\)\: \(array\|null\) given\.$#' - identifier: argument.type - count: 1 - path: src/bundle/DependencyInjection/IbexaAutomatedTranslationExtension.php - - message: '#^Access to protected property Ibexa\\AdminUi\\Form\\Data\\ContentTranslationData\:\:\$content\.$#' identifier: property.protected diff --git a/phpstan-baseline.neon.php b/phpstan-baseline.neon.php new file mode 100644 index 0000000..7d0bd65 --- /dev/null +++ b/phpstan-baseline.neon.php @@ -0,0 +1,19 @@ +