Création d'un espace mon compte avec FOSUserBundle et SonataUserBundle [SF2 back-office part 2.]

Création d'un espace mon compte avec FOSUserBundle et SonataUserBundle pour Symonfy 2.3

Publié le 04/10/2014

1 - Création d'un Bundle CMS

On va créer un bundle CMS pour gérer l'affichage des pages de notre site. Pour simplifier cette partie on va générer des pages avec un contenu statique. Le route vont passer le controller Template et on pourra se passer de la création de controller pour l'affichage des pages front de notre site.

php app/console generate:bundle --namespace= BlogTest/CmsBundle

2 - Création du template "base" pour le CMSBundle

Création du template base incluant la navigation et le block de connexion à l'espace "mon compte" :

nano /app/Resources/views/base.html.twig


<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>{% block title %}Title page{% endblock %}</title>
    {% block stylesheets %}{% endblock %}
    <link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
</head>
<body>
{% block header %}
    <ul>
        <li><a href="{{ path('homepage') }}">Home</a></li>
        <li><a href="{{ path('imprint') }}">Mentions légales</a></li>
        {% if is_granted('IS_AUTHENTICATED_FULLY') %}
            <li><a href="{{ path('fos_user_security_logout') }}">Déconnexion</a></li>
            <li>Bonjour, {{ app.user }}</li>
        {% else %}
            <li><a href="{{ path('fos_user_registration_register') }}">Inscription</a></li>
            <li><a href="{{ path('fos_user_security_login') }}">Connexion</a></li>
        {% endif %}
    </ul>
{% endblock %}
{% block flash %}

    {% for flashMessage in app.session.flashbag.get('notice') %}
        <div class="flash-notice">
            {{ flashMessage }}
        </div>
    {% endfor %}

    {% for flashMessage in app.session.flashbag.get('error') %}
        <div class="flash-error">
            {{ flashMessage }}
        </div>
    {% endfor %}

{% endblock %}
{% block rubric %}{% endblock %}
{% block body %}{% endblock %}
{% block footer %}{% endblock footer %}
{% block javascripts %}{% endblock %}
</body>
</html>

3 - Création des routes et vues pour le CMSBundle

On va créer un layout pour le bundle et une vue pour les pages index et imprint.

Création du layout :

nano src/BlogTest/CmsBundle/Resources/views/layout.html.twig


{% extends '::base.html.twig' %}

Création de la vue index :

nano src/BlogTest/CmsBundle/Resources/views/Cms/index.html.twig


{% extends '@BlogTestCms/layout.html.twig' %}

{% block body %}Home{% endblock %}

{% block rubric %}
    <p>Here some content...</p>
{% endblock %}

Création de la vue imprint :


{% extends '@BlogTestCms/layout.html.twig' %}

{% block body %}
    <p>Imprint</p>
{% endblock %}

Ajout des routes :

nano src/BlogTest/CmsBundle/Resources/config/routing.yml


homepage:
    path:     /
    defaults: { _controller: FrameworkBundle:Template:template, template: BlogTestCmsBundle:Cms:index.html.twig }
    requirements:
        _method: GET

imprint:
    path:   /imprint
    defaults: { _controller: FrameworkBundle:Template:template, template: BlogTestCmsBundle:Cms:imprint.html.twig }
    requirements:
        _method: GET

On dispose maintenant d'un front opérationnel. On va pouvoir "brancher" l'action login de FOSUserBundke et surcharger les vues show qui affichera l'espace "mon compte" correspondant à la route sonata_user_profile_edit.

4 - Modification du fichier config.yml

On modifie le fichier de configuration config.yml pour permettre la surcharge de FOSUserBundle et SonataUserBundle, ainsi que l'envoie des email au format html.

nano app/config/config.yml


# FOS User Bundle
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: BlogTest\UserBundle\Entity\User
    service:
        mailer: fos_user.mailer.twig_swift
    registration:
        form:
            type: blogtest_sonata_user_register
            validation_groups: [Register, Default, Registration]
        confirmation:
            enabled: true
            template: BlogTestUserBundle:Registration:email.html.twig
    profile:
        form:
            type: blogtest_sonata_user_profile
            #validation_groups: [Profile, Default]
    resetting:
        email:
            template: BlogTestUserBundle:Resetting:email.html.twig
    group:
        group_class: BlogTest\UserBundle\Entity\Group
    from_email:
        address: pa@foulquier.info
        sender_name: paf

5 - Héritage de SonataUserBundle pour notre UserBundle

On appel le parent du bundle SonataUserBundle pour notre UserBundle.

nano src/BlogTest/UserBundle/BlogTestUserBundle.php


<?php

namespace BlogTest\UserBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class BlogTestUserBundle extends Bundle
{
    /**
     * {@inheritdoc}
     */
    public function getParent()
    {
        return 'SonataUserBundle';
    }
}

6 - Surcharge des routes de FOSUserBundle dans le UserBundle

On surcharge les routes du FOSUserBundle de façon a personnaliser nos url propres en ajoutant les routes suivantes :

nano src/BlogTest/UserBundle/Resources/config/routing.yml


# SECURITY
fos_user_security_login:
    pattern: /connexion
    defaults: { _controller: FOSUserBundle:Security:login }
    requirements:
        _method: GET

fos_user_security_check:
    pattern: /connexion/verification
    defaults: { _controller: FOSUserBundle:Security:check }
    requirements:
        _method: POST

fos_user_security_logout:
    pattern: /deconnexion
    defaults: { _controller: FOSUserBundle:Security:logout }
    requirements:
        _method: GET

# REGISTRATION
fos_user_registration_register:
    pattern: /inscription
    defaults: { _controller: FOSUserBundle:Registration:register }
    requirements:
        _method: GET|POST

fos_user_registration_check_email:
    pattern: /inscription/e-mail
    defaults: { _controller: FOSUserBundle:Registration:checkEmail }
    requirements:
        _method: GET

fos_user_registration_confirm:
    pattern: /inscription/confirmation/{token}
    defaults: { _controller: FOSUserBundle:Registration:confirm }
    requirements:
        _method: GET

fos_user_registration_confirmed:
    pattern: /inscription/confirmation
    defaults: { _controller: FOSUserBundle:Registration:confirmed }
    requirements:
        _method: GET

# RESETTING PASSWORD
fos_user_resetting_request:
    pattern: /mot-de-passe-oublie/reinitialisation
    defaults: { _controller: FOSUserBundle:Resetting:request }
    requirements:
        _method: GET

fos_user_resetting_send_email:
    pattern: /mot-de-passe-oublie/e-mail
    defaults: { _controller: FOSUserBundle:Resetting:sendEmail }
    requirements:
        _method: GET|POST

fos_user_resetting_check_email:
    pattern: /mot-de-passe-oublie/verification
    defaults: { _controller: FOSUserBundle:Resetting:checkEmail }
    requirements:
        _method: GET

fos_user_resetting_reset:
    pattern: /mot-de-passe-oublie/{token}
    defaults: { _controller: FOSUserBundle:Resetting:reset }
    requirements:
        _method: GET|POST

# PROFILE
fos_user_profile_show:
    pattern: /profile
    defaults: { _controller: FOSUserBundle:Profile:show }
    requirements:
        _method: GET

fos_user_profile_edit:
    pattern: /profile/editer
    defaults: { _controller: FOSUserBundle:Profile:edit }
    requirements:
        _method: GET|POST

7 - Surcharge des vues controller Profile (espace privé utilisateur) du bundle SonataUserBundle (qui surcharge le bundle FOSUserBundle) :

On va créer dans notre bundle UserBundle les vues show.html.twig et edit.html.twig afficheront les pages : "mon compte" et le formulaires d'édition de données "mon compte".

On a commence par la création d'un layout :

nano scr/TestBlog/UserBundle/Resources/views/layout.html.twig


{% extends '::base.html.twig' %}

Création de la vue show :

nano scr/TestBlog/UserBundle/Resources/views/Profile/show.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    Nom d'utilisateur : {{ app.user.username }}<br />
    Adresse e-mail : {{ app.user.email }}<br />
    <a href="{{ path('fos_user_profile_edit') }}">Editer</a>
{% endblock %}
 

Création de la vue edit :

nano scr/TestBlog/UserBundle/Resources/views/Profile/edit.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    <form action="{{ path('fos_user_profile_edit') }}" {{ form_enctype(form) }} method="post">
        {{ form_widget(form) }}
        <input type="submit" value="Mettre à jour" />
    </form>
{% endblock %}
 

8 - Création des FormType d'inscription et de modifications des informations utilisateurs :

On va créer un FormType pour l'inscription de nos utilisateurs depuis la partie publique de notre site web.

Création du FormType ProfileType :

nano src/BlogTest/UserBundle/Form/Type/ProfileType.php


<?php
/**
 * Created by PhpStorm.
 * User: gerard
 * Date: 09/09/2014
 * Time: 10:16
 */

namespace BlogTest\UserBundle\Form\Type;

use FOS\UserBundle\Form\Type\ProfileFormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class ProfileType extends ProfileFormType
{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('gender', 'choice', array(
                'label' => 'profile.fields.gender',
                'translation_domain' => 'forms',
                'choices' => array(
                    'm' => 'M.',
                    'f' => 'Mme'
                )
            ))
            ->add('firstname', 'text', array(
                'label' => 'profile.fields.firstname',
                'translation_domain' => 'forms'
            ))
            ->add('address', 'textarea', array(
                'label' => 'profile.fields.address',
                'translation_domain' => 'forms'
            ))
            ->add('zipCode', 'text', array(
                'label' => 'profile.fields.zip_code',
                'translation_domain' => 'forms'
            ))
            ->add('lastname', 'text', array(
                'label' => 'profile.fields.lastname',
                'translation_domain' => 'forms'
            ))
            ->add('phone', 'text', array(
                'label' => 'profile.fields.phone',
                'translation_domain' => 'forms'
            ))
            ->add('email', 'email', array(
                'label' => 'profile.fields.email',
                'translation_domain' => 'forms'
            ))
            ->add('plainPassword', 'repeated', array(
                'first_options'  => array(
                    'label' => 'profile.fields.password_first'
                ),
                'second_options' => array(
                    'label' => 'profile.fields.password_second'
                ),
                'required' => false,
                'translation_domain' => 'forms'
            ))
        ;
    }

    public function setDefaultOption(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'validation_groups' => array('Default', 'Account')
        ));
    }

    public function getName()
    {
        return 'blogtest_sonata_user_profile';
    }
} 

Création du FormType RegisterType :

nano src/BlogTest/UserBundle/Form/Type/RegisterType.php


<?php
/**
 * Created by PhpStorm.
 * User: gerard
 * Date: 15/09/2014
 * Time: 15:03
 */

namespace BlogTest\UserBundle\Form\Type;

use FOS\UserBundle\Form\Type\RegistrationFormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class RegisterType extends RegistrationFormType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {

        parent::buildForm($builder, $options);

        $builder
            ->add('firstname', 'text', array(
                'label' => 'profile.fields.firstname',
                'translation_domain' => 'forms'
            ))
        ;
    }

    public function setDefaultOption(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'validation_groups' => array('Default', 'Register')
        ));
    }

    public function getName()
    {
        return 'blogtest_sonata_user_register';
    }
} 

9 - Ajout des formulaires en tant que services


services:
    user.form.profile.type:
        class: BlogTest\UserBundle\Form\Type\ProfileType
        parent: fos_user.profile.form.type
        tags:
            - { name: form.type, alias: blogtest_sonata_user_profile }

    user.form.register.type:
        class: BlogTest\UserBundle\Form\Type\RegisterType
        parent: fos_user.registration.form.type
        tags:
            - { name: form.type, alias: blogtest_sonata_user_register }

10 - Création des Handler pour les formulaires ProfileType

Création du handler pour le formulaire ProfilType :

nano src/BlogTest/UserBundle/Form/Handler/ProfileFormHandler


<?php
/**
 * Created by PhpStorm.
 * User: gerard
 * Date: 09/09/2014
 * Time: 10:31
 */

namespace BlogTest\UserBundle\Form\Handler;

use Sonata\UserBundle\Form\Handler\ProfileFormHandler as BaseHandler;

class ProfileFormHandler extends BaseHandler
{
    protected $form;
    protected $request;
    protected $templating;

    public function __construct($request, $form, $templating)
    {
        $this->request = $request;
        $this->form = $form;
        $this->templating = $templating;
    }
} 

11 - Ajout des traductions

Traduction des formulaires :

nano src/BlogTest/UserBundle/Resources/translations/forms.fr.yml


profile:
    fields:
        gender: Civilité
        firstname: Prénom
        lastname: Nom
        address: Adresse
        zip_code: Code postal
        phone: Téléphone
        email: E-mail
        password_first: Mot de passe
        password_second: Mot de passe (validation)

register:
    fields:
        firstname: Nom

Traduction du contenu des email de FOSUserBundle :

nano src/BlogTest/UserBundle/Resources/translations/FOSUserBundle.fr.yml


# Resetting account
resetting:
    email:
        subject: |
            [Vet-Tune] Réinitialisation de votre mot de passe
        message:  |
            <p>Bonjour %username%,</p>
            <p>Pour réinitialiser votre mot de passe, merci de cliquer sur le lien suivant :</p>
            <p style="text-align:center"><a href="%confirmationUrl%">%confirmationUrl%</a></p>
            <p>Cordialement,<br />Vet-Tune.</p>

# Registration
registration:
    email:
        subject: |
            [Vet-Tune] Bienvenue %username% !
        message:  |
            <p>Bonjour %username%,</p>
            <p>Pour valider votre compte utilisateur, merci de vous rendre sur :</p>
            <p style="text-align:center"><a href="%confirmationUrl%">%confirmationUrl%</a></p>
            <p>Cordialement,<br />Vet-Tune.</p>

Traduction des messages d'erreurs des des validateurs :

nano src/BlogTest/UserBundle/Resources/translations/validators.fr.yml


valid_phone_number: Cette valeur n'est pas un numéro de téléphone valide.
"'{{ value }}' is not a valid email.": "'{{ value }}' n'est pas une adresse e-mail valide."
not_empty_field: Le champs ne peut être vide.

12 - Surcharge des vues du controller Registration (inscription utilisateur) du bundle SonataUserBundle

On va créer dans notre UserBundle les vues qui vont permettre le process d'inscription d'un nouvel utilisateur depuis la partie publique du site :

scr/BlogTest/UserBundle/Resources/views/
	Registration/
		inc/
			email.html.twig
		checkEmail.html.twig
		confirmed.html.twig
		email.html.twig
		register.html.twig

Création du template de corps du email de validation :

nano src/BlogTest/UserBundle/Resources/views/Registration/inc/email.html.twig


<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Template mailing</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style type="text/css">
        /* Fonts and Content */
        body, td { font-family: 'Helvetica Neue', Arial, Helvetica, Geneva, sans-serif; font-size:14px; }
        body { background-color: #2A374E; margin: 0; padding: 0; -webkit-text-size-adjust:none; -ms-text-size-adjust:none; }
        h2{ padding-top:12px; /* ne fonctionnera pas sous Outlook 2007+ */color:#0E7693; font-size:22px; }

    </style>

</head>
<body style="margin:0px; padding:0px; -webkit-text-size-adjust:none;">

<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:rgb(42, 55, 78)" >
    <tbody>
    <tr>
        <td align="center" bgcolor="#2A374E">
            <table  cellpadding="0" cellspacing="0" border="0">
                <tbody>
                <tr>
                    <td class="w640"  width="640" height="10"></td>
                </tr>
                <tr>
                    <td class="w640"  width="640" height="10"></td>
                </tr>

                <!-- entete -->
                <tr class="pagetoplogo">
                    <td class="w640"  width="640">
                        <table  class="w640"  width="640" cellpadding="0" cellspacing="0" border="0" bgcolor="#F2F0F0">
                            <tbody>
                            <tr>
                                <td class="w30"  width="30"></td>
                                <td  class="w580"  width="580" valign="middle" align="left">
                                    <div class="pagetoplogo-content">
                                        <img class="w580" style="text-decoration: none; display: block; color:#476688; font-size:30px;" src="logo.png" alt="Logo" width="482" height="108"/>
                                    </div>
                                </td>
                                <td class="w30" width="30"></td>
                            </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>

                <!-- separateur horizontal -->
                <tr>
                    <td class="w640" width="640" height="1" bgcolor="#d7d6d6"></td>
                </tr>

                <!-- contenu -->
                <tr class="content">
                    <td class="w640" class="w640" width="640" bgcolor="#ffffff">
                        <table class="w640"  width="640" cellpadding="0" cellspacing="0" border="0">
                            <tbody>
                            <tr>
                                <td  class="w30"  width="30"></td>
                                <td  class="w580"  width="580">
                                    <!-- une zone de contenu -->
                                    <table class="w580"  width="580" cellpadding="0" cellspacing="0" border="0">
                                        <tbody>
                                        <tr>
                                            <td class="w580"  width="580">
                                                <h2 style="color:#0E7693; font-size:22px; padding-top:12px;">Confirmation d'inscription</h2>

                                                <div align="left" class="article-content">
                                                    {{ 'registration.email.message'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}, 'FOSUserBundle')|raw }}
                                                </div>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td class="w580"  width="580" height="1" bgcolor="#c7c5c5"></td>
                                        </tr>
                                        </tbody>
                                    </table>




                                </td>
                                <td class="w30" class="w30"  width="30"></td>
                            </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>

                <!--  separateur horizontal de 15px de  haut-->
                <tr>
                    <td class="w640"  width="640" height="15" bgcolor="#ffffff"></td>
                </tr>

                <!-- pied de page -->
                <tr class="pagebottom">
                    <td class="w640"  width="640">
                        <table class="w640"  width="640" cellpadding="0" cellspacing="0" border="0" bgcolor="#c7c7c7">
                            <tbody>
                            <tr>
                                <td colspan="5" height="10"></td>
                            </tr>
                            <tr>
                                <td class="w30"  width="30"></td>
                                <td class="w580"  width="580" valign="top">
                                    <p align="right" class="pagebottom-content-left">
                                        <a style="color: #255D5C;" href="{{ url('homepage') }}"><span style="color: #255D5C;">Link</span></a>
                                    </p>
                                </td>

                                <td class="w30"  width="30"></td>
                            </tr>
                            <tr>
                                <td colspan="5" height="10"></td>
                            </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
                <tr>
                    <td class="w640"  width="640" height="60"></td>
                </tr>
                </tbody>
            </table>
        </td>
    </tr>
    </tbody>
</table>
</body>
</html>
 

Création de la vue checkEmail :

nano src/BlogTest/UserBundle/Resources/views/Registration/checkEmail.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    <p>{{ 'registration.check_email'|trans({'%email%': user.email}, 'FOSUserBundle') }}</p>
{% endblock %}

Création de la vue confirmed :

nano src/BlogTest/UserBundle/Resources/views/Registration/confirmed.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    <p>{{ 'registration.confirmed'|trans({'%username%': user.username}, 'FOSUserBundle') }}</p>
    {% if app.session is not empty %}
        {% set targetUrl = app.session.get('_security.' ~ app.security.token.providerKey ~ '.target_path') %}
        {% if targetUrl is not empty %}<p><a href="{{ targetUrl }}">{{ 'registration.back'|trans({}, 'FOSUserBundle') }}</a></p>{% endif %}
    {% endif %}
{% endblock %}

Création de la vue email de validation :

nano src/BlogTest/UserBundle/Resources/views/Registration/email.html.twig


{% block subject %}
    {% autoescape false %}
        {{ 'registration.email.subject'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}, 'FOSUserBundle') }}
    {% endautoescape %}
{% endblock %}

{% block body_text %}
    {% autoescape false %}
        {{ 'registration.email.message'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}, 'FOSUserBundle') }}
    {% endautoescape %}
{% endblock %}

{% block body_html %}
    {% include 'BlogTestUserBundle:Registration:inc/email.html.twig' %}
{% endblock %}

Création de la vue register :

nano src/BlogTest/UserBundle/Resources/views/Registration/register.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    <p>Inscription :</p>
    <form action="{{ path('fos_user_registration_register') }}" {{ form_enctype(form) }} method="POST" class="fos_user_registration_register">
        {{ form_widget(form) }}
        <div>
            <input type="submit" value="{{ 'registration.submit'|trans({}, 'FOSUserBundle') }}" />
        </div>
    </form>
{% endblock %}

13 - Surcharge des vues du controller Resetting (reset mot de passe utilisateur) du bundle SonataUserBundle

On passe maintenant à la surcharge des vues du controller Resetting pour la réinitialisation du mot de passe utilisateur : scr/BlogTest/UserBundle/Resources/views/ Resetting/ inc/ email.html.twig checkEmail.html.twig passwordAlreadyRequested.html.twig request.html.twig request_content.html.twig reset.html.twig reset_content.html.twig

Création du template du corps de l'email de réinitialisation de mot de passe:

nano src/BlogTest/UserBundle/Resources/views/Resetting/inc/email.html.twig


<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Template mailing</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style type="text/css">
        /* Fonts and Content */
        body, td { font-family: 'Helvetica Neue', Arial, Helvetica, Geneva, sans-serif; font-size:14px; }
        body { background-color: #2A374E; margin: 0; padding: 0; -webkit-text-size-adjust:none; -ms-text-size-adjust:none; }
        h2{ padding-top:12px; /* ne fonctionnera pas sous Outlook 2007+ */color:#0E7693; font-size:22px; }

    </style>

</head>
<body style="margin:0px; padding:0px; -webkit-text-size-adjust:none;">

<table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:rgb(42, 55, 78)" >
    <tbody>
    <tr>
        <td align="center" bgcolor="#2A374E">
            <table  cellpadding="0" cellspacing="0" border="0">
                <tbody>
                <tr>
                    <td class="w640"  width="640" height="10"></td>
                </tr>

                <tr>
                    <td class="w640"  width="640" height="10"></td>
                </tr>


                <!-- entete -->
                <tr class="pagetoplogo">
                    <td class="w640"  width="640">
                        <table  class="w640"  width="640" cellpadding="0" cellspacing="0" border="0" bgcolor="#F2F0F0">
                            <tbody>
                            <tr>
                                <td class="w30"  width="30"></td>
                                <td  class="w580"  width="580" valign="middle" align="left">
                                    <div class="pagetoplogo-content">
                                        <img class="w580" style="text-decoration: none; display: block; color:#476688; font-size:30px;" src="logo.png" alt="Logo" width="482" height="108"/>
                                    </div>
                                </td>
                                <td class="w30"  width="30"></td>
                            </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>

                <!-- separateur horizontal -->
                <tr>
                    <td  class="w640"  width="640" height="1" bgcolor="#d7d6d6"></td>
                </tr>

                <!-- contenu -->
                <tr class="content">
                    <td class="w640" class="w640"  width="640" bgcolor="#ffffff">
                        <table class="w640"  width="640" cellpadding="0" cellspacing="0" border="0">
                            <tbody>
                            <tr>
                                <td  class="w30"  width="30"></td>
                                <td  class="w580"  width="580">
                                    <!-- une zone de contenu -->
                                    <table class="w580"  width="580" cellpadding="0" cellspacing="0" border="0">
                                        <tbody>
                                        <tr>
                                            <td class="w580"  width="580">
                                                <h2 style="color:#0E7693; font-size:22px; padding-top:12px;">Reinitialisation du mot de passe</h2>

                                                <div align="left" class="article-content">
                                                    <p>{{ 'resetting.email.message'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}, 'FOSUserBundle')|raw }}</p>
                                                </div>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td class="w580"  width="580" height="1" bgcolor="#c7c5c5"></td>
                                        </tr>
                                        </tbody>
                                    </table>

                                </td>
                                <td class="w30" class="w30"  width="30"></td>
                            </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>

                <!--  separateur horizontal de 15px de  haut-->
                <tr>
                    <td class="w640"  width="640" height="15" bgcolor="#ffffff"></td>
                </tr>

                <!-- pied de page -->
                <tr class="pagebottom">
                    <td class="w640"  width="640">
                        <table class="w640"  width="640" cellpadding="0" cellspacing="0" border="0" bgcolor="#c7c7c7">
                            <tbody>
                            <tr>
                                <td colspan="5" height="10"></td>
                            </tr>
                            <tr>
                                <td class="w30"  width="30"></td>
                                <td class="w580"  width="580" valign="top">
                                    <p align="right" class="pagebottom-content-left">
                                        <a style="color:#255D5C;" href="{{ url('homepage') }}"><span style="color:#255D5C;">Link</span></a>
                                    </p>
                                </td>

                                <td class="w30"  width="30"></td>
                            </tr>
                            <tr>
                                <td colspan="5" height="10"></td>
                            </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
                <tr>
                    <td class="w640"  width="640" height="60"></td>
                </tr>
                </tbody>
            </table>
        </td>
    </tr>
    </tbody>
</table>
</body>
</html>

Création de la vue checkEmail :

nano src/BlogTest/UserBundle/Resources/views/Resetting/inc/checkEmail.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    <p>
        {{ 'resetting.check_email'|trans({'%email%': email}, 'FOSUserBundle') }}
    </p>
{% endblock %}

Création de la vue email :

nano src/BlogTest/UserBundle/Resources/views/Resetting/email.html.twig


{% block subject %}
    {% autoescape false %}
        {{ 'resetting.email.subject'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}, 'FOSUserBundle') }}
    {% endautoescape %}
{% endblock %}

{% block body_text %}
    {% autoescape false %}
        {{ 'resetting.email.message'|trans({'%username%': user.username, '%confirmationUrl%': confirmationUrl}, 'FOSUserBundle') }}
    {% endautoescape %}
{% endblock %}

{% block body_html %}
    {% include 'BlogTestUserBundle:Resetting:inc/email.html.twig' %}
{% endblock %}

Création de la vue passwordAlreadyRequested :

nano src/BlogTest/UserBundle/Resources/views/Resetting/passwordAlreadyRequested.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    <p>{{ 'resetting.password_already_requested'|trans({}, 'FOSUserBundle') }}</p>
{% endblock %}

Création de la vue request :

nano src/BlogTest/UserBundle/Resources/views/Resetting/request.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    {% include "FOSUserBundle:Resetting:request_content.html.twig" %}
{% endblock %}

Création de la vue request_content :

nano src/BlogTest/UserBundle/Resources/views/Resetting/request_content.html.twig


<form action="{{ path('fos_user_resetting_send_email') }}" method="POST" class="fos_user_resetting_request">
    <div>
        {% if invalid_username is defined %}
            <p>{{ 'resetting.request.invalid_username'|trans({'%username%': invalid_username}, 'FOSUserBundle') }}</p>
        {% endif %}
        <label for="username">{{ 'resetting.request.username'|trans({}, 'FOSUserBundle') }}</label>
        <input type="text" id="username" name="username" required="required" />
    </div>
    <div>
        <input type="submit" value="{{ 'resetting.request.submit'|trans({}, 'FOSUserBundle') }}" />
    </div>
</form>

Création de la vue reset :

nano src/BlogTest/UserBundle/Resources/views/Resetting/reset.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    {% include "FOSUserBundle:Resetting:reset_content.html.twig" %}
{% endblock %}

Création de la vue reset_content :

nano src/BlogTest/UserBundle/Resources/views/Resetting/reset_content.html.twig


<form action="{{ path('fos_user_resetting_reset', {'token': token}) }}" {{ form_enctype(form) }} method="POST" class="fos_user_resetting_reset">
    {{ form_widget(form) }}
    <div>
        <input type="submit" value="{{ 'resetting.reset.submit'|trans({}, 'FOSUserBundle') }}" />
    </div>
</form>

14 - Surcharge des vues du controller Security (login utilisateur) du bundle SonataUserBundle

On va créer dans notre UserBundle les vues qui vont permettre le process d'inscription d'un nouvel utilisateur depuis la partie publique du site :

scr/BlogTest/UserBundle/Resources/views/
	Security/
		login.html.twig

Création de la vue login :

nano src/BlogTest/UserBundle/Resources/views/Security/login.html.twig


{% extends 'BlogTestUserBundle::layout.html.twig' %}

{% block body %}
    <div class="row">
        <div class="col-md-6">
            <div class="panel panel-info">

                <div class="panel-heading">
                    <h2 class="panel-title">{{ 'title_user_authentication'|trans({}, 'SonataUserBundle') }}</h2>
                </div>

                <div class="panel-body">

                    {% if error %}
                        <div class="alert alert-danger alert-error">{{ error|trans({}, 'FOSUserBundle') }}</div>
                    {% endif %}

                    <form action="{{ path("fos_user_security_check") }}" method="post" role="form"
                          class="form-horizontal">
                        <input type="hidden" name="_csrf_token" value="{{ csrf_token }}"/>

                        <div class="form-group">
                            <label for="username"
                                   class="col-lg-3 col-sm-3 control-label">{{ 'security.login.username'|trans({}, 'FOSUserBundle') }}</label>

                            <div class="col-lg-9 col-sm-9"><input type="text" class="form-control" id="username"
                                                                  name="_username" value="{{ last_username }}"
                                                                  required="required"/></div>
                        </div>


                        <div class="form-group control-group">
                            <label for="password"
                                   class="col-lg-3 col-sm-3 control-label">{{ 'security.login.password'|trans({}, 'FOSUserBundle') }}</label>

                            <div class="col-lg-9 col-sm-9"><input type="password" class="form-control" id="password"
                                                                  name="_password" required="required"/></div>
                        </div>

                        <div class="form-group">
                            <div class="col-sm-offset-3 col-sm-9">
                                <div class="checkbox control-group">
                                    <label for="remember_me">
                                        <input type="checkbox" id="remember_me" name="_remember_me" value="on"/>
                                        {{ 'security.login.remember_me'|trans({}, 'FOSUserBundle') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="col-sm-offset-3 col-sm-9">
                                <input type="submit" id="_submit" name="_submit" class="btn btn-primary"
                                       value="{{ 'security.login.submit'|trans({}, 'FOSUserBundle') }}"/>
                            </div>
                        </div>
                    </form>
                </div>
            </div>

        </div>
    </div>
    <a href="{{ path('fos_user_resetting_request') }}">
        <strong>Reset password</strong>
    </a>
{% endblock %}

15 - Test des fonctionnalités mon compte

On va pouvoir tester nos fonctionnalités.

Grace à l'application mailcatcher il va nous être possible d'intercepter les mails sans passer par un smtp externe.

Dans le fichier app/config/parameters.yml, modifier le mailer_host comme suit :


mailer_host: 'localhost:1025'