Search code examples
phpsymfonyfosuserbundlehtmlpurifier

Symfony FosUserBundle and HTMLPurifierBundle


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.


Solution

  • 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