13

I would like to activate the email confirmation on FOSUserBundle /profile/edit

In /profile/edit you are already logged in and you can edit the username or the email address entering the current password. Now I would like to send a confirmation email if the user edit the email address :)

In the FOSUserBundle Configuration Reference I haven't found a setting to do this... https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/configuration_reference.md

These are my current settings:

fos_user:
  db_driver: orm
  firewall_name: main
  user_class: Acme\CoreBundle\Entity\User
  registration:
    confirmation: { enabled: true }
  from_email:
    address: noreply@%domain%
    sender_name: %site_name% Staff
  resetting:
    token_ttl: %reset_password_ttl%

Someone can help me?

Francesco Casula
  • 26,184
  • 15
  • 132
  • 131

2 Answers2

17

Using new FOSUserBundle's events (FOS 2.0 feature), you can create a ChangeProfileListener that handle email changes.

The idea : use the same logic as a registration process : disable our user, send him a token (and logging-him out for our case).

There is two things to do :

  • create an event suscriber
  • overwrite the mail.txt.twig to replace the "registration" message by a neutral "email confirmation" message.

Here is my listener, don't forget to replace namespace.

EventListener/ChangeProfileListener.php:

<?php

// src/Fuz/HomeBundle/EventListener/ChangeProfileListener.php

namespace Fuz\HomeBundle\EventListener;

use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Mailer\MailerInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class ChangeProfileListener implements EventSubscriberInterface
{

    private $mailer;
    private $tokenGenerator;
    private $router;
    private $session;
    private $tokenStorage;

    public function __construct(MailerInterface $mailer, TokenGeneratorInterface $tokenGenerator,
       UrlGeneratorInterface $router, SessionInterface $session, TokenStorageInterface $tokenStorage)
    {
        $this->mailer = $mailer;
        $this->tokenGenerator = $tokenGenerator;
        $this->router = $router;
        $this->session = $session;
        $this->tokenStorage = $tokenStorage;
    }

    public static function getSubscribedEvents()
    {
        return array(
                FOSUserEvents::PROFILE_EDIT_INITIALIZE => 'onProfileEditInitialize',
                FOSUserEvents::PROFILE_EDIT_SUCCESS => 'onProfileEditSuccess',
        );
    }

    public function onProfileEditInitialize(GetResponseUserEvent $event)
    {
        // required, because when Success's event is called, session already contains new email
        $this->email = $event->getUser()->getEmail();
    }

    public function onProfileEditSuccess(FormEvent $event)
    {
        $user = $event->getForm()->getData();
        if ($user->getEmail() !== $this->email)
        {
            // disable user
            $user->setEnabled(false);

            // send confirmation token to new email
            $user->setConfirmationToken($this->tokenGenerator->generateToken());
            $this->mailer->sendConfirmationEmailMessage($user);

            // force user to log-out
            $this->tokenStorage->setToken();

            // redirect user to check email page
            $this->session->set('fos_user_send_confirmation_email/email', $user->getEmail());
            $url = $this->router->generate('fos_user_registration_check_email');
            $event->setResponse(new RedirectResponse($url));
        }
    }

}

services.yml :

parameters:
    fuz_home.email_change.listener.class: Fuz\HomeBundle\EventListener\ChangeProfileListener

services:
      fuz_home.email_change.listener:
          class: %fuz_home.email_change.listener.class%
          arguments: ['@fos_user.mailer', '@fos_user.util.token_generator', '@router', '@session', '@security.token_storage']
          tags:
            - { name: kernel.event_subscriber }

About overwritting email template, that's just creating app/Resources/FOSUserBundle/views/Registration/email.txt.twig and put, for example :

{% block subject %}
Email Confirmation
{% endblock %}

{% block body_text %}

Welcome to example.com, {{ user.username }}!                                                

To confirm your email, please follow this link:                                                                    
{{ confirmationUrl }}

You will be able to log-in using the username or email you given:                                        

Username : {{ user.username }}                                                                                                         
Email    : {{ user.email }}                                                                                                 

If you received this e-mail in error just ignore this message. No further actions are required from you.                  



                                                     *****                                                           

                                                 See you soon!
{% endblock %}
Alain Tiemblo
  • 36,099
  • 17
  • 121
  • 153
  • 2
    This solution does have a problem though, when the user changes the email the db is updated. If the email is wrong how do they get back into the system? Would it not be better to store the new email in a different field? – lookbadgers Mar 08 '16 at 09:39
4

The functionality to send a confirmation email only exists in

RegistrationFormHandler->onSucces

where that configuration variable is passed and checked. Similar functionality doesn't exist in the profile/edit:

ProfileFormHandler->onSuccess.

You would therefore need to override the FOS ProfileFormHandler and add this functionality yourself. This is covered in the FOSUserBundle documentation: Overriding Forms.

Gottlieb Notschnabel
  • 9,408
  • 18
  • 74
  • 116
MadManMonty
  • 816
  • 1
  • 7
  • 25