diff --git a/src/lib/Form/EventSubscriber/FixUrlProtocolListener.php b/src/lib/Form/EventSubscriber/FixUrlProtocolListener.php new file mode 100644 index 00000000..480336be --- /dev/null +++ b/src/lib/Form/EventSubscriber/FixUrlProtocolListener.php @@ -0,0 +1,74 @@ +defaultProtocol = $defaultProtocol; + $this->fixUrlProtocolListener = new BaseFixUrlProtocolListener($defaultProtocol); + } + + public function onSubmit(FormEvent $event): void + { + $data = $event->getData(); + if (null === $this->defaultProtocol || empty($data) || !\is_string($data)) { + return; + } + + $protocol = explode(':', $data, 2)[0]; + if ($this->hasAuthority($protocol) && $this->hasAuthority($this->defaultProtocol)) { + $this->fixUrlProtocolListener->onSubmit($event); + + return; + } + + if (!$this->hasAuthority($protocol) && preg_match('~^(?:[/.]|[\w+.-]+:|[^:/?@#]++@)~', $data)) { + return; + } + + if ($this->hasAuthority($this->defaultProtocol)) { + $schemaSeparator = '://'; + $regExp = '~^(?:[/.]|[\w+.-]+//|[^:/?@#]++@)~'; + } else { + $schemaSeparator = ':'; + $regExp = '~^[\w+.-]+:~'; // allowing emails for non-http/https/file + } + + if (!preg_match($regExp, $data)) { + $event->setData($this->defaultProtocol . $schemaSeparator . $data); + } + } + + private function hasAuthority(string $protocol): bool + { + return !in_array($protocol, ['mailto', 'tel'], true); + } + + public static function getSubscribedEvents(): array + { + return [FormEvents::SUBMIT => 'onSubmit']; + } +} diff --git a/src/lib/Form/Type/FieldType/UrlFieldType.php b/src/lib/Form/Type/FieldType/UrlFieldType.php index 47fdf990..23c2c95e 100644 --- a/src/lib/Form/Type/FieldType/UrlFieldType.php +++ b/src/lib/Form/Type/FieldType/UrlFieldType.php @@ -9,6 +9,7 @@ namespace Ibexa\ContentForms\Form\Type\FieldType; use Ibexa\ContentForms\FieldType\DataTransformer\FieldValueTransformer; +use Ibexa\ContentForms\Form\EventSubscriber\FixUrlProtocolListener; use Ibexa\Contracts\Core\Repository\FieldTypeService; use JMS\TranslationBundle\Annotation\Desc; use Symfony\Component\Form\AbstractType; @@ -49,6 +50,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) [ 'label' => /** @Desc("URL") */ 'content.field_type.ezurl.link', 'required' => $options['required'], + 'default_protocol' => null, ] ) ->add( @@ -60,6 +62,8 @@ public function buildForm(FormBuilderInterface $builder, array $options) ] ) ->addModelTransformer(new FieldValueTransformer($this->fieldTypeService->getFieldType('ezurl'))); + + $builder->get('link')->addEventSubscriber(new FixUrlProtocolListener()); } public function configureOptions(OptionsResolver $resolver) diff --git a/tests/lib/Form/EventSubscriber/FixUrlProtocolListenerTest.php b/tests/lib/Form/EventSubscriber/FixUrlProtocolListenerTest.php new file mode 100644 index 00000000..8713b545 --- /dev/null +++ b/tests/lib/Form/EventSubscriber/FixUrlProtocolListenerTest.php @@ -0,0 +1,101 @@ +createMock(FormInterface::class); + $listener = new FixUrlProtocolListener($defaultProtocol); + + $event = new FormEvent($form, $inputData); + + $listener->onSubmit($event); + + self::assertSame($expectedData, $event->getData()); + } + + /** + * @return iterable + */ + public static function provideUrlCases(): iterable + { + return [ + 'adds http when protocol missing' => [ + self::DOMAIN, + self::URL_HTTP, + ], + 'does not modify https url' => [ + self::URL_HTTPS, + self::URL_HTTPS, + ], + 'does not modify http url' => [ + self::URL_HTTP, + self::URL_HTTP, + ], + 'keep relative url with leading / intact' => [ + self::URL_RELATIVE, + self::URL_RELATIVE, + ], + 'keeps ftp intact' => [ + self::URL_SFTP, + self::URL_SFTP, + ], + 'keeps tel intact' => [ + self::URL_TEL, + self::URL_TEL, + ], + 'adds default tel' => [ + self::TEL, + self::URL_TEL, + 'tel', + ], + 'keeps mailto intact' => [ + self::URL_MAILTO, + self::URL_MAILTO, + ], + 'adds default mailto' => [ + self::MAIL, + self::URL_MAILTO, + 'mailto', + ], + 'does nothing when link is empty string' => [ + '', + '', + ], + 'does nothing when data is null' => [ + null, + null, + ], + ]; + } +}