#}
diff --git a/EMS/core-bundle/src/Resources/views/form/fields.html.twig b/EMS/core-bundle/src/Resources/views/form/fields.html.twig
index e424292d6..d9e865321 100644
--- a/EMS/core-bundle/src/Resources/views/form/fields.html.twig
+++ b/EMS/core-bundle/src/Resources/views/form/fields.html.twig
@@ -457,15 +457,6 @@
{{ parent() }}
{% endblock form_errors %}
-{% block choice_errors %}
- {% if form.parent is defined and form.parent.vars.helptext is defined and form.parent.vars.helptext %}
-
- {% endif %}
- {{ block('form_errors') }}
-{% endblock choice_errors %}
-
-
-
{% block tabsfieldtype_row -%}
{#
#}
{#
#}
diff --git a/EMS/helpers/src/Html/Sanitizer/HtmlSanitizerClass.php b/EMS/helpers/src/Html/Sanitizer/HtmlSanitizerClass.php
index 11c3ad386..789b30a59 100644
--- a/EMS/helpers/src/Html/Sanitizer/HtmlSanitizerClass.php
+++ b/EMS/helpers/src/Html/Sanitizer/HtmlSanitizerClass.php
@@ -31,6 +31,10 @@ public function sanitizeAttribute(string $element, string $attribute, string $va
$classes = \explode(' ', $value);
$classNames = \array_filter($classes, 'trim');
+ if (\count($this->settings['replace']) > 0) {
+ $classNames = \array_map(fn (string $className) => $this->settings['replace'][$className] ?? $className, $classNames);
+ }
+
if (\count($this->settings['allow']) > 0) {
$classNames = \array_filter($classNames, fn (string $className) => \in_array($className, $this->settings['allow']));
}
@@ -39,10 +43,6 @@ public function sanitizeAttribute(string $element, string $attribute, string $va
$classNames = \array_filter($classNames, fn (string $className) => !\in_array($className, $this->settings['drop']));
}
- if (\count($this->settings['replace']) > 0) {
- $classNames = \array_map(fn (string $className) => $this->settings['replace'][$className] ?? $className, $classNames);
- }
-
return \count($classNames) > 0 ? \implode(' ', $classNames) : null;
}
}
diff --git a/EMS/helpers/src/Image/SmartCrop.php b/EMS/helpers/src/Image/SmartCrop.php
index da6756085..3bfea1609 100644
--- a/EMS/helpers/src/Image/SmartCrop.php
+++ b/EMS/helpers/src/Image/SmartCrop.php
@@ -316,7 +316,7 @@ private function importance(array $crop, int $x, int $y): float
private function thirds(float $x): float
{
- $x = (($x - (1 / 3) + 1.0) % 2.0 * 0.5 - 0.5) * 16;
+ $x = ((int) ($x - (1 / 3) + 1.0) % 2.0 * 0.5 - 0.5) * 16;
return \max(1.0 - $x * $x, 0.0);
}
diff --git a/EMS/helpers/tests/Unit/File/FileAiTest.php b/EMS/helpers/tests/Unit/File/FileAiTest.php
new file mode 100644
index 000000000..d7b6163a2
--- /dev/null
+++ b/EMS/helpers/tests/Unit/File/FileAiTest.php
@@ -0,0 +1,49 @@
+assertInstanceOf(File::class, $file);
+ $this->assertEquals('file.txt', $file->name);
+ $this->assertEquals('txt', $file->extension);
+ $this->assertIsInt($file->size);
+ $this->assertEquals('text/plain', $file->mimeType);
+ }
+
+ public function testFromFilename()
+ {
+ $file = File::fromFilename(self::TEST_FILE_PATH);
+ $this->assertInstanceOf(File::class, $file);
+ }
+
+ public function testChunk()
+ {
+ $file = new File(new \SplFileInfo(self::TEST_FILE_PATH));
+ $chunks = $file->chunk(0, 1024);
+
+ foreach ($chunks as $chunk) {
+ $this->assertLessThanOrEqual(1024, \strlen($chunk));
+ }
+ }
+}
diff --git a/EMS/helpers/tests/Unit/File/FolderAiTest.php b/EMS/helpers/tests/Unit/File/FolderAiTest.php
new file mode 100644
index 000000000..2a578b0ea
--- /dev/null
+++ b/EMS/helpers/tests/Unit/File/FolderAiTest.php
@@ -0,0 +1,36 @@
+testFolderPath = \sys_get_temp_dir().DIRECTORY_SEPARATOR.'/path/to/test/folder';
+ }
+
+ public function testGetRealPathWithExistingDirectory()
+ {
+ \mkdir($this->testFolderPath, 0777, true);
+ $realPath = Folder::getRealPath($this->testFolderPath);
+ $this->assertEquals(\realpath($this->testFolderPath), $realPath);
+ }
+
+ public function testGetRealPathWithNonExistingDirectory()
+ {
+ $realPath = Folder::getRealPath($this->testFolderPath);
+ $this->assertEquals(\realpath($this->testFolderPath), $realPath);
+ }
+
+ protected function tearDown(): void
+ {
+ \rmdir($this->testFolderPath);
+ }
+}
diff --git a/EMS/helpers/tests/Unit/File/TempFileAiTest.php b/EMS/helpers/tests/Unit/File/TempFileAiTest.php
new file mode 100644
index 000000000..e67a264b1
--- /dev/null
+++ b/EMS/helpers/tests/Unit/File/TempFileAiTest.php
@@ -0,0 +1,46 @@
+assertInstanceOf(TempFile::class, $tempFile);
+ $this->assertTrue(\file_exists($tempFile->path));
+ }
+
+ public function testCreateNamed()
+ {
+ $name = 'testfile.txt';
+ $tempFile = TempFile::createNamed($name);
+ $this->assertInstanceOf(TempFile::class, $tempFile);
+ $this->assertEquals(\sys_get_temp_dir().DIRECTORY_SEPARATOR.'EMS_temp_file_'.$name, $tempFile->path);
+ }
+
+ public function testLoadFromStream()
+ {
+ $tempFile = TempFile::create();
+ $stream = $this->createMock(StreamInterface::class);
+ $stream->method('eof')->willReturnOnConsecutiveCalls(false, true);
+ $stream->method('read')->willReturn('Test content');
+
+ $tempFile->loadFromStream($stream);
+ $this->assertTrue(\file_exists($tempFile->path));
+ $this->assertEquals('Test content', \file_get_contents($tempFile->path));
+ }
+
+ public function testClean()
+ {
+ $tempFile = TempFile::create();
+ $tempFile->clean();
+ $this->assertFalse(\file_exists($tempFile->path));
+ }
+}
diff --git a/EMS/helpers/tests/Unit/Html/Sanitizer/HtmlSanitizerClassAiTest.php b/EMS/helpers/tests/Unit/Html/Sanitizer/HtmlSanitizerClassAiTest.php
new file mode 100644
index 000000000..7a7372f92
--- /dev/null
+++ b/EMS/helpers/tests/Unit/Html/Sanitizer/HtmlSanitizerClassAiTest.php
@@ -0,0 +1,57 @@
+sanitizer = new HtmlSanitizerClass([
+ 'allow' => ['allowed-class', 'new-class'],
+ 'drop' => ['dropped-class'],
+ 'replace' => ['old-class' => 'new-class'],
+ ]);
+ }
+
+ public function testGetSupportedElements(): void
+ {
+ $this->assertNull($this->sanitizer->getSupportedElements());
+ }
+
+ public function testGetSupportedAttributes(): void
+ {
+ $expectedAttributes = ['class'];
+ $this->assertEquals($expectedAttributes, $this->sanitizer->getSupportedAttributes());
+ }
+
+ public function testSanitizeAttribute(): void
+ {
+ $config = $this->createMock(HtmlSanitizerConfig::class);
+ $element = 'div';
+ $attribute = 'class';
+
+ // Test allow
+ $value = 'allowed-class';
+ $this->assertEquals('allowed-class', $this->sanitizer->sanitizeAttribute($element, $attribute, $value, $config));
+
+ // Test drop
+ $value = 'dropped-class';
+ $this->assertNull($this->sanitizer->sanitizeAttribute($element, $attribute, $value, $config));
+
+ // Test replace
+ $value = 'old-class';
+ $this->assertEquals('new-class', $this->sanitizer->sanitizeAttribute($element, $attribute, $value, $config));
+
+ // Test multiple classes
+ $value = 'allowed-class dropped-class old-class';
+ $this->assertEquals('allowed-class new-class', $this->sanitizer->sanitizeAttribute($element, $attribute, $value, $config));
+ }
+}
diff --git a/EMS/helpers/tests/Unit/Html/Sanitizer/HtmlSanitizerLinkAiTest.php b/EMS/helpers/tests/Unit/Html/Sanitizer/HtmlSanitizerLinkAiTest.php
new file mode 100644
index 000000000..2b626be31
--- /dev/null
+++ b/EMS/helpers/tests/Unit/Html/Sanitizer/HtmlSanitizerLinkAiTest.php
@@ -0,0 +1,62 @@
+sanitizer = new HtmlSanitizerLink();
+ }
+
+ public function testSanitizeAttributeWithEmsScheme()
+ {
+ $element = 'a';
+ $attribute = 'href';
+ $value = 'ems://some-resource';
+ $config = new HtmlSanitizerConfig();
+
+ $sanitizedValue = $this->sanitizer->sanitizeAttribute($element, $attribute, $value, $config);
+ $this->assertEquals('ems://some-resource', $sanitizedValue);
+ }
+
+ public function testSanitizeAttributeWithHttpScheme()
+ {
+ $element = 'a';
+ $attribute = 'href';
+ $value = 'http://example.com';
+ $config = new HtmlSanitizerConfig();
+
+ $sanitizedValue = $this->sanitizer->sanitizeAttribute($element, $attribute, $value, $config);
+ $this->assertEquals('http://example.com', $sanitizedValue);
+ }
+
+ public function testSanitizeAttributeWithJavascriptScheme()
+ {
+ $element = 'a';
+ $attribute = 'href';
+ $value = 'javascript:alert(1)';
+ $config = new HtmlSanitizerConfig();
+
+ $sanitizedValue = $this->sanitizer->sanitizeAttribute($element, $attribute, $value, $config);
+ $this->assertNull($sanitizedValue);
+ }
+
+ public function testGetSupportedElements()
+ {
+ $this->assertNull($this->sanitizer->getSupportedElements());
+ }
+
+ public function testGetSupportedAttributes()
+ {
+ $this->assertEquals(['src', 'href', 'lowsrc', 'background', 'ping'], $this->sanitizer->getSupportedAttributes());
+ }
+}
diff --git a/EMS/helpers/tests/Unit/Image/SmartCropAiTest.php b/EMS/helpers/tests/Unit/Image/SmartCropAiTest.php
new file mode 100644
index 000000000..d12ce5f10
--- /dev/null
+++ b/EMS/helpers/tests/Unit/Image/SmartCropAiTest.php
@@ -0,0 +1,53 @@
+image = \imagecreatetruecolor(200, 200);
+ }
+
+ protected function tearDown(): void
+ {
+ \imagedestroy($this->image);
+ }
+
+ public function testConstructor()
+ {
+ $smartCrop = new SmartCrop($this->image, $this->cropWidth, $this->cropHeight);
+ $this->assertInstanceOf(SmartCrop::class, $smartCrop);
+ }
+
+ public function testAnalyse()
+ {
+ $smartCrop = new SmartCrop($this->image, $this->cropWidth, $this->cropHeight);
+ $result = $smartCrop->analyse();
+ $this->assertIsArray($result);
+ $this->assertArrayHasKey('topCrop', $result);
+ }
+
+ public function testCrop()
+ {
+ $smartCrop = new SmartCrop($this->image, $this->cropWidth, $this->cropHeight);
+ $cropped = $smartCrop->crop(50, 50, $this->cropWidth, $this->cropHeight);
+ $this->assertInstanceOf(SmartCrop::class, $cropped);
+ }
+
+ public function testGet()
+ {
+ $smartCrop = new SmartCrop($this->image, $this->cropWidth, $this->cropHeight);
+ $resultImage = $smartCrop->get();
+ $this->assertInstanceOf(\GdImage::class, $resultImage);
+ }
+}
diff --git a/EMS/helpers/tests/Unit/Standard/AccessorAiTest.php b/EMS/helpers/tests/Unit/Standard/AccessorAiTest.php
new file mode 100644
index 000000000..4df3af692
--- /dev/null
+++ b/EMS/helpers/tests/Unit/Standard/AccessorAiTest.php
@@ -0,0 +1,19 @@
+assertSame('[field][subfield]', Accessor::fieldPathToPropertyPath('field.subfield'));
+ $this->assertSame('[field][0]', Accessor::fieldPathToPropertyPath('field[0]'));
+ $this->assertSame('[field][subfield][0]', Accessor::fieldPathToPropertyPath('field.subfield[0]'));
+ $this->assertSame('[field][sub][0]', Accessor::fieldPathToPropertyPath('field.sub[0]'));
+ }
+}
diff --git a/EMS/helpers/tests/Unit/Standard/Base64AiTest.php b/EMS/helpers/tests/Unit/Standard/Base64AiTest.php
new file mode 100644
index 000000000..8d593755e
--- /dev/null
+++ b/EMS/helpers/tests/Unit/Standard/Base64AiTest.php
@@ -0,0 +1,31 @@
+assertEquals(\base64_encode($originalString), $encodedString);
+ }
+
+ public function testDecode()
+ {
+ $encodedString = \base64_encode('Test String');
+ $decodedString = Base64::decode($encodedString);
+ $this->assertEquals('Test String', $decodedString);
+ }
+
+ public function testDecodeThrowsRuntimeExceptionOnInvalidBase64()
+ {
+ $this->expectException(\RuntimeException::class);
+ Base64::decode('@');
+ }
+}
diff --git a/EMS/helpers/tests/Unit/Standard/ColorAiTest.php b/EMS/helpers/tests/Unit/Standard/ColorAiTest.php
new file mode 100644
index 000000000..bf63197b3
--- /dev/null
+++ b/EMS/helpers/tests/Unit/Standard/ColorAiTest.php
@@ -0,0 +1,107 @@
+assertEquals(60, $color->getRed());
+ $this->assertEquals(141, $color->getGreen());
+ $this->assertEquals(188, $color->getBlue());
+ }
+
+ public function testConstructorWithStandardHtmlColor()
+ {
+ $color = new Color('blue');
+ $this->assertEquals(0, $color->getRed());
+ $this->assertEquals(0, $color->getGreen());
+ $this->assertEquals(255, $color->getBlue());
+ }
+
+ public function testConstructorWithHexColor()
+ {
+ $color = new Color('#FF5733');
+ $this->assertEquals(255, $color->getRed());
+ $this->assertEquals(87, $color->getGreen());
+ $this->assertEquals(51, $color->getBlue());
+ }
+
+ public function testGetSetRed()
+ {
+ $color = new Color('#000000');
+ $color->setRed(123);
+ $this->assertEquals(123, $color->getRed());
+ }
+
+ public function testGetSetGreen()
+ {
+ $color = new Color('#000000');
+ $color->setGreen(123);
+ $this->assertEquals(123, $color->getGreen());
+ }
+
+ public function testGetSetBlue()
+ {
+ $color = new Color('#000000');
+ $color->setBlue(123);
+ $this->assertEquals(123, $color->getBlue());
+ }
+
+ public function testGetSetAlpha()
+ {
+ $color = new Color('#000000');
+ $color->setAlpha(123);
+ $this->assertEquals(123, $color->getAlpha());
+ }
+
+ public function testGetColorId()
+ {
+ $color = new Color('#000000');
+ $image = \imagecreatetruecolor(100, 100);
+ $colorId = $color->getColorId($image);
+ $this->assertIsInt($colorId);
+ \imagedestroy($image);
+ }
+
+ public function testRelativeLuminance()
+ {
+ $color = new Color('#FFFFFF');
+ $this->assertEquals(1.0, $color->relativeLuminance(), '', 0.01);
+ }
+
+ public function testContrastRatio()
+ {
+ $color1 = new Color('#FFFFFF');
+ $color2 = new Color('#000000');
+ $this->assertEquals(21, $color1->contrastRatio($color2), '', 0.01);
+ }
+
+ public function testGetComplementary()
+ {
+ $color = new Color('#FFFFFF');
+ $complementary = $color->getComplementary();
+ $this->assertEquals(0, $complementary->getRed());
+ $this->assertEquals(0, $complementary->getGreen());
+ $this->assertEquals(0, $complementary->getBlue());
+ }
+
+ public function testGetRGB()
+ {
+ $color = new Color('#FF5733');
+ $this->assertEquals('#FF5733', $color->getRGB());
+ }
+
+ public function testGetRGBA()
+ {
+ $color = new Color('#FF5733');
+ $color->setAlpha(127);
+ $this->assertEquals('#FF57337F', $color->getRGBA());
+ }
+}
diff --git a/EMS/helpers/tests/Unit/Standard/DateTimeAiTest.php b/EMS/helpers/tests/Unit/Standard/DateTimeAiTest.php
new file mode 100644
index 000000000..d088cf2e0
--- /dev/null
+++ b/EMS/helpers/tests/Unit/Standard/DateTimeAiTest.php
@@ -0,0 +1,39 @@
+assertInstanceOf(\DateTimeImmutable::class, $dateTime);
+ $this->assertEquals('2024-01-01T00:00:00+00:00', $dateTime->format(\DateTimeInterface::ATOM));
+ }
+
+ public function testCreateThrowsRuntimeExceptionOnInvalidTime()
+ {
+ $this->expectException(\RuntimeException::class);
+ DateTime::create('invalid-time-string');
+ }
+
+ public function testCreateFromFormat()
+ {
+ $timeString = '2024-01-01T00:00:00+00:00';
+ $dateTime = DateTime::createFromFormat($timeString, \DateTimeInterface::ATOM);
+ $this->assertInstanceOf(\DateTimeImmutable::class, $dateTime);
+ $this->assertEquals($timeString, $dateTime->format(\DateTimeInterface::ATOM));
+ }
+
+ public function testCreateFromFormatThrowsRuntimeExceptionOnInvalidFormat()
+ {
+ $this->expectException(\RuntimeException::class);
+ DateTime::createFromFormat('2024-01-01 00:00:00', 'invalid-format');
+ }
+}
diff --git a/EMS/helpers/tests/Unit/Standard/HashAiTest.php b/EMS/helpers/tests/Unit/Standard/HashAiTest.php
new file mode 100644
index 000000000..31e23f54f
--- /dev/null
+++ b/EMS/helpers/tests/Unit/Standard/HashAiTest.php
@@ -0,0 +1,41 @@
+assertEquals(\sha1($originalString), $hashedString);
+ }
+
+ public function testStringHashWithPrefix()
+ {
+ $originalString = 'Test String';
+ $prefix = 'prefix_';
+ $hashedString = Hash::string($originalString, $prefix);
+ $this->assertEquals($prefix.\sha1($originalString), $hashedString);
+ }
+
+ public function testArrayHash()
+ {
+ $array = ['key' => 'value'];
+ $hashedArray = Hash::array($array);
+ $this->assertEquals(\sha1(\json_encode($array)), $hashedArray);
+ }
+
+ public function testArrayHashWithPrefix()
+ {
+ $array = ['key' => 'value'];
+ $prefix = 'prefix_';
+ $hashedArray = Hash::array($array, $prefix);
+ $this->assertEquals($prefix.\sha1(\json_encode($array)), $hashedArray);
+ }
+}
diff --git a/EMS/helpers/tests/Unit/Standard/TypeAiTest.php b/EMS/helpers/tests/Unit/Standard/TypeAiTest.php
index a90d74189..15c24a576 100644
--- a/EMS/helpers/tests/Unit/Standard/TypeAiTest.php
+++ b/EMS/helpers/tests/Unit/Standard/TypeAiTest.php
@@ -9,31 +9,49 @@
class TypeAiTest extends TestCase
{
- public function testStringReturnsString(): void
+ public function testStringWithValidString()
{
- $input = 'test';
- $result = Type::string($input);
- $this->assertSame($input, $result);
+ $this->assertEquals('test', Type::string('test'));
}
- public function testStringThrowsExceptionForNonString(): void
+ public function testStringWithInvalidType()
{
$this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage("Expect a string got 'integer'");
Type::string(123);
}
- public function testIntegerReturnsInteger(): void
+ public function testIntegerWithValidInteger()
{
- $input = 123;
- $result = Type::integer($input);
- $this->assertSame($input, $result);
+ $this->assertEquals(123, Type::integer(123));
}
- public function testIntegerThrowsExceptionForNonInteger(): void
+ public function testIntegerWithInvalidType()
{
$this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage("Expect an integer got 'string'");
Type::integer('test');
}
+
+ public function testArrayWithValidArray()
+ {
+ $this->assertEquals(['key' => 'value'], Type::array(['key' => 'value']));
+ }
+
+ public function testArrayWithInvalidType()
+ {
+ $this->expectException(\RuntimeException::class);
+ Type::array('not an array');
+ }
+
+ public function testGdImageWithValidGdImage()
+ {
+ $image = \imagecreatetruecolor(100, 100);
+ $this->assertInstanceOf(\GdImage::class, Type::gdImage($image));
+ \imagedestroy($image);
+ }
+
+ public function testGdImageWithInvalidType()
+ {
+ $this->expectException(\RuntimeException::class);
+ Type::gdImage('not a gd image');
+ }
}
diff --git a/docs/dev/helpers/standard.md b/docs/dev/helpers/standard.md
index f5074b466..a6978f386 100644
--- a/docs/dev/helpers/standard.md
+++ b/docs/dev/helpers/standard.md
@@ -101,9 +101,9 @@ $settings = [
],
'drop_elements' => [ 'iframe' ], // remove all `iframe` elements and content
'classes' => [
- 'allow' => ['my-class', 'my-second-class'],
+ 'allow' => ['my-class', 'my-second-class', 'new-class'],
'drop' => ['delete', 'remove'],
- 'replace' => ['test' => 'example'],
+ 'replace' => ['old-class' => 'new-class'], // IMPORTANT: add new-class in the 'allow' array
]
]
```