Symfony FosUserBundle and HTMLPurifierBundle - php

On my personal Symfony3.2 project I want to use HtmlPurifier into my UserProfile Form in order to to prvent XSS Attacks.
Therefore as described in: https://github.com/Exercise/HTMLPurifierBundle I modified the UserProfileFormType accorditly:
namespace AppUserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class UserProfileFormType extends AbstractType
{
private $purifierTransformer;
public function __construct(DataTransformerInterface $purifierTransformer)
{
$this->purifierTransformer = $purifierTransformer;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->addViewTransformer($this->purifierTransformer);
$builder->add('name',TextType::class,array('label'=>'profile.first_name','required' => false));
$builder->add('surname',TextType::class,array('label'=>'profile.surnname','required' => false));
$builder->remove('username');
$builder->add('username',TextType::class,['required' => false]);
$builder->add('email',TextType::class,['required' => false]);
$builder->add('description',TextareaType::class,['required' => false]);
$builder->remove('current_password');
}
public function getParent()
{
return 'FOS\UserBundle\Form\Type\ProfileFormType';
}
public function getBlockPrefix()
{
return 'app_user_profile';
}
// For Symfony 2.x
public function getName()
{
return $this->getBlockPrefix();
}
}
And also used these entries in services.yml entries:
services:
app_user.html_purifier:
class: Exercise\HTMLPurifierBundle\Form\HTMLPurifierTransformer
arguments: ["#exercise_html_purifier.default"]
app_user.user_profile_form:
class: AppUserBundle\Form\Type\UserProfileFormType
arguments: ["#app_user.html_purifier"]
tags:
- { name: form.type, alias: app_user_profile }
Also I put these entries in config.yml:
fos_user:
db_driver: orm # other valid values are 'mongodb' and 'couchdb'
firewall_name: main
user_class: '%user_class%'
from_email:
address: "somemail#example.com"
sender_name: "App Mailer"
registration:
confirmation:
enabled: false
profile:
form:
type: AppUserBundle\Form\Type\UserProfileFormType
exercise_html_purifier:
default:
Cache.SerializerPath: '%kernel.cache_dir%/htmlpurifier'
With these options I get the following error message:
Type error: Argument 1 passed to AppUserBundle\Form\Type\UserProfileFormType::__construct() must be an instance of Symfony\Component\Form\DataTransformerInterface, none given, called in /home/pcmagas/Kwdikas/php/apps/symfonyAdminLTE/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistry.php on line 85
I also tried to change the following value in src/AppUserBundle/Resources/config.yml:
fos_user:
#Some configuration
profile:
form:
type: AppUserBundle\Form\Type\UserProfileFormType
With this one:
fos_user
#Some configuration
profile:
form:
type: app_user.user_profile_form
That returns the following error:
Could not load type "app_user.user_profile_form"
Do you have any Idea how I will solve this one? As long as I Understand the prob is that the FosUserBundle cannot load the proper service somehow. Do you have any idea how I will tell to load the appropriate service to the configuration?
Edit 1:
On UserProfileFormType method I replaced the:
$builder->add('description',TextareaType::class,['required' => false]);
With:
$builder->add('description',PurifiedTextAreaType::class,['required' => false]);
And I got the following error:
Type error: Argument 1 passed to AppUserBundle\Form\Type\PurifiedTextAreaType::__construct() must implement interface Symfony\Component\Form\DataTransformerInterface, none given, called in /home/pcmagas/Kwdikas/php/apps/symfonyAdminLTE/var/cache/dev/appDevDebugProjectContainer.php on line 389
Please keep in mind that I created the AppUserBundle/Form/Type/PurifiedTextAreaType.php:
namespace AppUserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class PurifiedTextAreaType extends AbstractType
{
private $purifierTransformer;
public function __construct(DataTransformerInterface $purifierTransformer)
{
$this->purifierTransformer = $purifierTransformer;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addViewTransformer($this->purifierTransformer);
}
public function getParent()
{
return 'textarea';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'compound' => false,
));
}
public function getName()
{
return 'purified_textarea';
}
}
I also forgot to mention that I use a different bundle in order to do any sort of user management and handling named AppUserBudnle all the custom forms are in this bundle.

I think that, the best way to use this extension is as described in the tutorial, with a single Textarea which you can call in your final form as your own TextAreaType:
I see this mistake in your description, there are the namespace but "UserProfileFormType" class name is missing.
services:
app_user.user_profile_form:
class: AppUserBundle\Form\Type
arguments: ["#app_user.html_purifier"]
tags:
- { name: form.type, alias: app_user_profile }
In this configuration
fos_user:
db_driver: orm # other valid values are 'mongodb' and 'couchdb'
firewall_name: main
user_class: '%user_class%'
from_email:
address: "somemail#example.com"
sender_name: "App Mailer"
registration:
confirmation:
enabled: false
profile:
form:
type: AppUserBundle\Form\Type\UserProfileFormType
You must overwrite profile form type and then call it by name instead of by class name (which may construct you form without argument) :
Have a look there: http://symfony.com/doc/current/bundles/FOSUserBundle/overriding_forms.html#overriding-a-form-type

As Manuel DUVERNON says in the end I created this FormType that represents a HtmlPurified TextArea:
namespace AppUserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Exercise\HTMLPurifierBundle\Form\HTMLPurifierTransformer;
use HTMLPurifier;
class PurifiedTextAreaType extends AbstractType
{
private $purifierTransformer;
/**
* I Do not Dependency Inject the HtmlPurifier because I want to Use it as
* FormType in FosUserBundle's extended forms in the wat that is described in:
* https://symfony.com/doc/master/bundles/FOSUserBundle/overriding_forms.html
*
* And the method described above does not favour Dependency Injection.
*/
public function __construct()
{
$this->purifierTransformer = new HTMLPurifierTransformer(new \HTMLPurifier());
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addViewTransformer($this->purifierTransformer);
}
public function getParent()
{
return 'Symfony\Component\Form\Extension\Core\Type\TextareaType';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'compound' => false,
));
}
public function getName()
{
return 'purified_textarea';
}
}
Please keep in mind NOT TO load the HTMLPurifierTransformer via Dependency Injection because the FosUserBundle's FormBuilder cannot load the extended form via dependency Injection. Also keep in mind to use the namespace of the FormType you are extending (in my case the Symfony\Component\Form\Extension\Core\Type\TextareaType) because FormBuilder also has a limitation into a specific interface.

Related

cannot load the TranslatorInterface in my formType

I try to introduce some translation into a ChatBundle in order to follow the changes of the _locale of the hosting app in Symfony 4.
So in the formBuilder i try to inject the TranslatorInterface as such:
// lib/ChatBundle/Form/ChatMessageType.php
namespace bornToBeAlive\ChatBundle\Form;
use bornToBeAlive\ChatBundle\Entity\ChatMessage;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Translation\TranslatorInterface;
class ChatMessageType extends AbstractType
{
private $trans;
public function __construct(TranslatorInterface $trans)
{
$this->trans = $trans;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content', null, [
'attr'=> ['placeholder' => $this->trans->trans('placeholder',[],'chat')]
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ChatMessage::class,
]);
}
}
but when I try to run my show action:
public function show(): Response
{
$message = new ChatMessage();
$form = $this->createForm(ChatMessageType::class, $message);
return $this->render('#Chat/show.html.twig', [
'form' => $form->createView(),
]);
}
I get the following error :
Too few arguments to function bornToBeAlive\ChatBundle\Form\ChatMessageType::__construct(), 0 passed in ../vendor/symfony/form/FormRegistry.php on line 92 and exactly 1 expected
I'm surprised because I use this technique when I'm in my host app for the other type. did I do something wrong ?
According to the Symfony 4.4 Form documentation :
If you're using the default services.yaml configuration, this example will already work! Otherwise, create a service for this form class and tag it with form.type.
services:
# default configuration for services in *this* file
_defaults:
autowire: true
autoconfigure: true
public: false
If autowiring is not working as expected, you can define form as service, like this
# config/services.yaml
app.form.corporation_type:
class: bornToBeAlive\ChatBundle\Entity\ChatMessageType
arguments: ["#translator"]
tags:
- { name: form.type }

Display tagged services in Symfony ChoiceType

I implemented a system that lets an admin configure some additional behaviors (that are actually Symfony Services) from a form. At the moment I was using an EntityType, in order to let the admin select one or more services from a table in the data base.
$builder->add('services', EntityType::class, array(
'class' => 'AppBundle:Services',
'multiple' => true,
));
But since I'm registering the services in Symfony itself I just thought that there should be a way to get the services from the container (or similar) and create a new ServiceTagType so I don't have to add them in both the data base and the services.yml and I can do something like:
$builder->add('services', ServiceTagType::class, array(
'tag' => 'some.service.tag',
'multiple' => true,
));
Reading here and there I found out that you can tag services but you can only get the tagged services list when the container is being compiled... I'm struggling trying to find a workaround but it's been no use.
First of all, you have to create the ServiceTagType (I assume it will extend the ChoiceType and not the EntityType) :
How to create a custom type is documented here.
// src/AppBundle/Form/Type/ServiceTagType.php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class ServiceTagType extends AbstractType
{
private $tags;
public function setTags($tags)
{
$this->tags = $tags;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'choices' => $this->tags
));
}
public function getParent()
{
return ChoiceType::class;
}
}
Register your ServiceTagType as a service (Because you need to provide the tags with setter injection)
# services.yml
app.form_type.service_tag:
class: AppBundle\Form\Type\ServiceTagType
tags:
- { name: form.type }
Then, as Bourvill suggest, you can collect your tags in a Compiler pass.
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class TagsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->has('app.form_type.service_tag')) {
return;
}
$definition = $container->findDefinition(
'app.form_type.service_tag'
);
$taggedServicesIds = array_keys($container->findTaggedServiceIds(
'app.tagged_for_service_tag_type'
));
$taggedServices = array_fill_keys($taggedServicesIds ,$taggedServicesIds);
$definition->addMethodCall('setTags',$taggedServices );
}
}
And don't forget to register this Compiler pass
To register a CompilerPass in the FullStack Framework:
class AppBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new TagsCompilerPass());
}
}

How can I use the Translator service outside of a controller in Symfony 3?

I have a form type:
<?php
// src/AppBundle/Form/ProductType.php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class ProductType extends AbstractType
{
private $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('save', SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Product',
));
}
}
As you see I am already attempting to set up my form type to inject the translator. In my services I have:
parameters:
# parameter_name: value
services:
app.form.product:
class: AppBundle\Form\ProductType
arguments: ["#translator"]
But am receiving the following error:
Catchable Fatal Error: Argument 1 passed to AppBundle\Form\ProductType::__construct() must implement interface
Symfony\Component\Translation\TranslatorInterface, none given, called
in
/path/to/symfony/bundle/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistry.php
on line 85 and defined....
Can someone tell me what gives? I am pretty sure the service type is wrong but cant find the one I need to save my life.
Check in the service definition of your form that you have correctly tag the service as a form.type as described here in the doc.
In according with the news announcement, from the version 2.6 the translator component is defined as service like translator.default.
As Example, you should have something like:
services:
app.form.product:
class: AppBundle\Form\ProductType
arguments: ["#translator.default"]
tags:
- { name: form.type }
hope this help

Symfony Custom Validator not loading Dependency

Error:
...ThemeValidator::__construct() must be of the type array, null given...
For some reason the Service is not being called, but the Class is being loaded directly.
validation.yml
theme:
- NotBlank: ~
- DashboardHub\Bundle\AppBundle\Validator\Constraints\ThemeValidator: ~
Validator Class
<?php
namespace DashboardHub\Bundle\AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class ThemeValidator extends ConstraintValidator
{
protected $config;
public function __construct(array $config)
{
$this->config = $config;
}
public function validatedBy()
{
return 'theme.validator';
}
public function validate($value, Constraint $constraint)
{
var_dump($this->config); exit;
}
// ...
service.yml
dashboardhub_app_main.validator.constraints.theme:
class: DashboardHub\Bundle\AppBundle\Validator\Constraints\ThemeValidator
arguments: ["%dashboard_hub_app%"]
tags:
- { name: validator.constraint_validator, alias: theme.validator }
Edit
parameters:
dashboard_hub_app:
themes:
Github: DashboardHubAppBundle:Template:Github.html.twig
GithubTravis: DashboardHubAppBundle:Template:GithubTravis.html.twig
Edit2
It works find when used in the Form Service
dashboardhub_app_main.form.type.dashboard:
class: DashboardHub\Bundle\AppBundle\Form\DashboardType
arguments: ["%dashboard_hub_app%"]
tags:
- { name: form.type, alias: dashboard }
parameters:
dashboard_hub_app: -- !!!!! IS NULL yes
funny :)
parameters follow one by one, so you need to be careful, paramenters && themes - are different.
to inject an array you need provide a value for this paramenter, but i guess you need to inject this one
themes:
Github: DashboardHubAppBundle:Template:Github.html.twig
GithubTravis: DashboardHubAppBundle:Template:GithubTravis.html.twig
arguments: ["%themes%"]
and result will be an array inside your Validator class
array (size=2)
'Github' => string 'DashboardHubAppBundle:Template:Github.html.twig' (length=47)
'GithubTravis' => string 'DashboardHubAppBundle:Template:GithubTravis.html.twig' (length=53)

FOSUserBundle - Overriding forms: could not load type "user_registration"

I created my own form type at Me\MyBundle\Form\Type\UserFormRegistrationType:
namespace Me\MyBundle\Form\Type;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UserFormRegistrationType extends BaseType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
// all my unique fields
}
public function getName()
{
return 'user_registration';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Me\MyBundle\Entity\User'));
}
}
I have the following in my services.yml:
services:
me_my.registration.form.type:
class: Me\MyBundle\Form\Type\UserFormRegistrationType
arguments: [%fos_user.model.user.class%]
tags:
- { name: form.type, alias: user_registration }
And the following added to my config.yml:
# other config stuff
fos_user:
# database stuff, general config
registration:
form:
type: user_registration
Yet, when I try to access my registration form/page, I get:
Could not load type "user_registration"
Any hint to what I'm obviously missing? It's not a firewall issue. I had one, but tweaking my security.yml fixed it. This is a pure not found error. Very annoying, as I believe I followed the docs to the letter.
You should not use aliases anymore
In your config file :
Before :
registration:
form:
type: user_registration
New :
registration:
form:
type: 'CoreBundle\Form\Type\RegistrationFormType'
In your src/CoreBundle/Form/Type/RegistrationFormType.php: getParent() function should be :
public function getParent()
{
return "FOS\UserBundle\Form\Type\RegistrationFormType";
}
Don't forget the use on the top of the file :
use FOS\UserBundle\Form\Type\RegistrationFormType;
Hi here is the explanation :
https://stackoverflow.com/a/53048060/7888453
Official :
https://github.com/symfony/symfony/blob/3.4/UPGRADE-3.0.md#form
Take a look at this guys problem and then the solution that he provides to his own question. I went off of his code and modified it to match the names within my files and it worked!
How to override register form FosUserBundle?
The docs declined to mention/remind that the service needs to be registered in config.yml.

Categories