diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index 17598fa95815c..bd239ff0d5693 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -74,6 +74,10 @@ + + + + diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 0d7d7efed0c6c..ef53183696c4d 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -17,6 +17,7 @@ CHANGELOG is deprecated. The method will be added to the interface in 6.0. * Added a `rounding_mode` option for the PercentType and correctly round the value when submitted * Deprecated `Symfony\Component\Form\Extension\Validator\Util\ServerParams` in favor of its parent class `Symfony\Component\Form\Util\ServerParams` + * Added the `html5` option to the `ColorType` to validate the input 5.0.0 ----- diff --git a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php index e3eae88b33543..04e9dd45861f4 100644 --- a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php +++ b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php @@ -75,7 +75,7 @@ protected function loadTypes() new Type\ResetType(), new Type\CurrencyType(), new Type\TelType(), - new Type\ColorType(), + new Type\ColorType($this->translator), new Type\WeekType(), ]; } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ColorType.php b/src/Symfony/Component/Form/Extension/Core/Type/ColorType.php index 9c2734ead6f40..b4fe44d0e6eb8 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ColorType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ColorType.php @@ -12,9 +12,68 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Contracts\Translation\TranslatorInterface; class ColorType extends AbstractType { + /** + * @see https://www.w3.org/TR/html52/sec-forms.html#color-state-typecolor + */ + private const HTML5_PATTERN = '/^#[0-9a-f]{6}$/i'; + + private $translator; + + public function __construct(TranslatorInterface $translator = null) + { + $this->translator = $translator; + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if (!$options['html5']) { + return; + } + + $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event): void { + $value = $event->getData(); + if (null === $value || '' === $value) { + return; + } + + if (\is_string($value) && preg_match(self::HTML5_PATTERN, $value)) { + return; + } + + $messageTemplate = 'This value is not a valid HTML5 color.'; + $messageParameters = [ + '{{ value }}' => is_scalar($value) ? (string) $value : \gettype($value), + ]; + $message = $this->translator ? $this->translator->trans($messageTemplate, $messageParameters, 'validators') : $messageTemplate; + + $event->getForm()->addError(new FormError($message, $messageTemplate, $messageParameters)); + }); + } + + /** + * {@inheritdoc} + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'html5' => false, + ]); + + $resolver->setAllowedTypes('html5', 'bool'); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Form/Resources/translations/validators.en.xlf b/src/Symfony/Component/Form/Resources/translations/validators.en.xlf index b8542d319ddec..89814258d145a 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.en.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.en.xlf @@ -14,6 +14,10 @@ The CSRF token is invalid. Please try to resubmit the form. The CSRF token is invalid. Please try to resubmit the form. + + This value is not a valid HTML5 color. + This value is not a valid HTML5 color. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf b/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf index 21f9010143afc..a32c83fc93026 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf @@ -14,6 +14,10 @@ The CSRF token is invalid. Please try to resubmit the form. Le jeton CSRF est invalide. Veuillez renvoyer le formulaire. + + This value is not a valid HTML5 color. + Cette valeur n'est pas une couleur HTML5 valide. + diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ColorTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ColorTypeTest.php new file mode 100644 index 0000000000000..1a83b44b65c6b --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ColorTypeTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\Type\ColorType; +use Symfony\Component\Form\FormError; + +final class ColorTypeTest extends BaseTypeTest +{ + const TESTED_TYPE = ColorType::class; + + /** + * @dataProvider validationShouldPassProvider + */ + public function testValidationShouldPass(bool $html5, ?string $submittedValue) + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'html5' => $html5, + 'trim' => true, + ]); + + $form->submit($submittedValue); + + $this->assertEmpty($form->getErrors()); + } + + public function validationShouldPassProvider() + { + return [ + [false, 'foo'], + [false, null], + [false, ''], + [false, ' '], + [true, '#000000'], + [true, '#abcabc'], + [true, '#BbBbBb'], + [true, '#1Ee54d'], + [true, ' #1Ee54d '], + [true, null], + [true, ''], + [true, ' '], + ]; + } + + /** + * @dataProvider validationShouldFailProvider + */ + public function testValidationShouldFail(string $expectedValueParameterValue, ?string $submittedValue, bool $trim = true) + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'html5' => true, + 'trim' => $trim, + ]); + + $form->submit($submittedValue); + + $expectedFormError = new FormError('This value is not a valid HTML5 color.', 'This value is not a valid HTML5 color.', [ + '{{ value }}' => $expectedValueParameterValue, + ]); + $expectedFormError->setOrigin($form); + + $this->assertEquals([$expectedFormError], iterator_to_array($form->getErrors())); + } + + public function validationShouldFailProvider() + { + return [ + ['foo', 'foo'], + ['000000', '000000'], + ['#abcabg', '#abcabg'], + ['#12345', '#12345'], + [' #ffffff ', ' #ffffff ', false], + ]; + } + + public function testSubmitNull($expected = null, $norm = null, $view = null) + { + parent::testSubmitNull($expected, $norm, ''); + } +}