Commit ce1c605f authored by Sebastian Castro's avatar Sebastian Castro

Add location and newsletter attribute to User. Improve registration form

parent 6e51c06f
parameters:
app.version: 1.6.4
\ No newline at end of file
app.version: 1.6.5
\ No newline at end of file
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Application\Sonata\UserBundle\Controller;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Application\Sonata\UserBundle\Form\Type\RegistrationFormType;
use Biopen\CoreBundle\Document\User;
/**
* This class is inspired from the FOS RegistrationController.
*
* @author Hugo Briand <briand@ekino.com>
*/
class RegistrationFOSUser1Controller extends Controller
{
/**
* @return RedirectResponse|Response
*/
public function registerAction()
{
$user = $this->getUser();
if ($user instanceof UserInterface) {
$this->get('session')->getFlashBag()->set('sonata_user_error', 'sonata_user_already_authenticated');
return $this->redirect($this->generateUrl('sonata_user_profile_show'));
}
$form = $this->get('form.factory')->create(RegistrationFormType::class, new User());
$formHandler = $this->get('sonata.user.registration.form.handler');
$confirmationEnabled = $this->container->getParameter('fos_user.registration.confirmation.enabled');
$process = $formHandler->process($confirmationEnabled);
if ($process) {
$user = $form->getData();
$authUser = false;
if ($confirmationEnabled) {
$this->get('session')->set('fos_user_send_confirmation_email/email', $user->getEmail());
$url = $this->generateUrl('fos_user_registration_check_email');
} else {
$authUser = true;
$route = $this->get('session')->get('sonata_basket_delivery_redirect');
if (null !== $route) {
// NEXT_MAJOR: remove the if block
@trigger_error(<<<'EOT'
Setting a redirect url in the sonata_basket_delivery_redirect session variable
is deprecated since 3.2 and will no longer result in a redirection to this url in 4.0.
EOT
, E_USER_DEPRECATED);
$this->get('session')->remove('sonata_basket_delivery_redirect');
$url = $this->generateUrl($route);
} else {
$url = $this->get('session')->get('sonata_user_redirect_url');
}
}
if (!$url) {
$url = $this->generateUrl('sonata_user_profile_show');
}
$this->setFlash('fos_user_success', 'registration.flash.user_created');
$response = $this->redirect($url);
if ($authUser) {
$this->authenticateUser($user, $response);
}
return $response;
}
// NEXT_MAJOR: Inject $request in the method signature instead.
if ($this->has('request_stack')) {
$request = $this->get('request_stack')->getCurrentRequest();
} else {
$request = $this->get('request');
}
$this->get('session')->set('sonata_user_redirect_url', $request->headers->get('referer'));
return $this->render('FOSUserBundle:Registration:register.html.'.$this->getEngine(), [
'form' => $form->createView(),
]);
}
/**
* Tell the user to check his email provider.
*
* @return Response
*
* @throws NotFoundHttpException
*/
public function checkEmailAction()
{
$email = $this->get('session')->get('fos_user_send_confirmation_email/email');
$this->get('session')->remove('fos_user_send_confirmation_email/email');
$user = $this->get('fos_user.user_manager')->findUserByEmail($email);
if (null === $user) {
throw new NotFoundHttpException(sprintf('The user with email "%s" does not exist', $email));
}
return $this->render('FOSUserBundle:Registration:checkEmail.html.'.$this->getEngine(), [
'user' => $user,
]);
}
/**
* Receive the confirmation token from user email provider, login the user.
*
* @param string $token
*
* @return RedirectResponse
*
* @throws NotFoundHttpException
*/
public function confirmAction($token)
{
$user = $this->get('fos_user.user_manager')->findUserByConfirmationToken($token);
if (null === $user) {
throw new NotFoundHttpException(sprintf('The user with confirmation token "%s" does not exist', $token));
}
$user->setConfirmationToken(null);
$user->setEnabled(true);
$user->setLastLogin(new \DateTime());
$this->get('fos_user.user_manager')->updateUser($user);
if ($redirectRoute = $this->container->getParameter('sonata.user.register.confirm.redirect_route')) {
$response = $this->redirect($this->generateUrl(
$redirectRoute,
$this->container->getParameter('sonata.user.register.confirm.redirect_route_params')
));
} else {
$response = $this->redirect($this->generateUrl('fos_user_registration_confirmed'));
}
$this->authenticateUser($user, $response);
return $response;
}
/**
* Tell the user his account is now confirmed.
*
* @return Response
*
* @throws AccessDeniedException
*/
public function confirmedAction()
{
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
return $this->render('FOSUserBundle:Registration:confirmed.html.'.$this->getEngine(), [
'user' => $user,
]);
}
/**
* Authenticate a user with Symfony Security.
*
* @param UserInterface $user
* @param Response $response
*/
protected function authenticateUser(UserInterface $user, Response $response)
{
try {
$this->get('fos_user.security.login_manager')->loginUser(
$this->container->getParameter('fos_user.firewall_name'),
$user,
$response
);
} catch (AccountStatusException $ex) {
// We simply do not authenticate users which do not pass the user
// checker (not enabled, expired, etc.).
}
}
/**
* @param string $action
* @param string $value
*/
protected function setFlash($action, $value)
{
$this->get('session')->getFlashBag()->set($action, $value);
}
/**
* @return string
*/
protected function getEngine()
{
return $this->container->getParameter('fos_user.template.engine');
}
}
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Application\Sonata\UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class RegistrationFormType extends AbstractType
{
/**
* @var array
*/
protected $mergeOptions;
/**
* @var string
*/
private $class;
/**
* @param string $class The User class name
* @param array $mergeOptions Add options to elements
*/
public function __construct($class, array $mergeOptions = [])
{
$this->class = $class;
$this->mergeOptions = $mergeOptions;
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// NEXT_MAJOR: Keep FQCN when bumping Symfony requirement to 2.8+.
if (method_exists('Symfony\Component\Form\AbstractType', 'getBlockPrefix')) {
$textType = 'Symfony\Component\Form\Extension\Core\Type\TextType';
$emailType = 'Symfony\Component\Form\Extension\Core\Type\EmailType';
$repeatedType = 'Symfony\Component\Form\Extension\Core\Type\RepeatedType';
$passwordType = 'Symfony\Component\Form\Extension\Core\Type\PasswordType';
$choiceType = 'Symfony\Component\Form\Extension\Core\Type\ChoiceType';
} else {
$textType = 'text';
$emailType = 'email';
$repeatedType = 'repeated';
$passwordType = 'password';
$choiceType = 'choice';
}
$builder
->add('username', null, array_merge([
'label' => 'form.username',
'translation_domain' => 'SonataUserBundle',
], $this->mergeOptions))
->add('email', $emailType, array_merge([
'label' => 'form.email',
'translation_domain' => 'SonataUserBundle',
], $this->mergeOptions))
->add('plainPassword', $repeatedType, array_merge([
'type' => $passwordType,
'options' => ['translation_domain' => 'SonataUserBundle'],
'first_options' => array_merge([
'label' => 'form.password',
], $this->mergeOptions),
'second_options' => array_merge([
'label' => 'form.password_confirmation',
], $this->mergeOptions),
'invalid_message' => 'fos_user.password.mismatch',
], $this->mergeOptions))
->add('location', $textType, array_merge([
'label' => 'form.location',
'translation_domain' => 'SonataUserBundle',
'required' => false,
], $this->mergeOptions))
->add('newsletterFrequency', $choiceType, array_merge([
'label' => 'form.newsletterFrequency',
'translation_domain' => 'SonataUserBundle',
'choices' => array(
'Jamais' => 0,
'Chaque semaine' => 1,
'Chaque mois' => 2,
),
'expanded' => true, 'multiple' => false,
'required' => false, 'placeholder' => false,
'choices_as_values' => true,
], $this->mergeOptions))
;
}
/**
* {@inheritdoc}
*
* NEXT_MAJOR: remove this method.
*
* @deprecated Remove it when bumping requirements to Symfony 2.7+
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$this->configureOptions($resolver);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => $this->class,
'intention' => 'registration',
]);
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'biopen_user_registration';
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->getBlockPrefix();
}
}
......@@ -542,9 +542,17 @@
<source>form.password</source>
<target>Mot de passe</target>
</trans-unit>
<trans-unit id="form.location">
<source>form.location</source>
<target>Votre code postal ou votre adresse</target>
</trans-unit>
<trans-unit id="form.newsletterFrequency">
<source>form.newsletterFrequency</source>
<target>La fréquence à laquelle vous voulez être tenu informé</target>
</trans-unit>
<trans-unit id="form.password_confirmation">
<source>form.password_confirmation</source>
<target>Vérification</target>
<target>Retapez votre mot de passe</target>
</trans-unit>
<trans-unit id="title_user_edit_password">
<source>title_user_edit_password</source>
......@@ -563,6 +571,7 @@
<source>resetting.flash.success</source>
<target>Le mot de passe a été réinitialisé avec succès</target>
</trans-unit>
</body>
</file>
</xliff>
......@@ -3,43 +3,58 @@
{% set pageName = 'register' %}
{% set navTitle = "Créer un compte" %}
{% block fos_user_content %}
<div class="panel panel-success">
<div class="panel-heading">
<h1 class="panel-title">{{ 'title_user_registration'|trans({}, 'SonataUserBundle') }}</h1>
</div>
<div class="panel-body">
<form action="{{ path('fos_user_registration_register') }}" {{ form_enctype(form) }} method="POST" class="fos_user_registration_register form-horizontal">
{{ form_widget(form) }}
<!-- Input adresse element -->
{#<div class="input-field">
{{ form_widget(form.username, {'id': 'input-username', 'attr': {'class': 'validate required' } }) }}
<label for="input-username">Nom d'utilisateur</label>
</div>
<div class="input-field">
{{ form_widget(form.email, {'id': 'input-email', 'attr': {'class': 'validate required' } }) }}
<label for="input-email">Email</label>
</div>
<div class="input-field">
{{ form_widget(form.plainPassword.first, {'id': 'input-passworde', 'attr': {'class': 'validate required' } }) }}
<label for="input-password">Mot de passe</label>
</div>
<div class="input-field">
{{ form_widget(form.plainPassword.second, {'id': 'input-confirme', 'attr': {'class': 'validate required' } }) }}
<label for="input-confirm">Confirmez le mot de passe</label>
</div>
{{ form_rest(form) }}#}
<div class="form-actions">
<input type="submit" value="{{ 'registration.submit'|trans({}, 'FOSUserBundle') }}" class="btn btn-success pull-right" />
</div>
</form>
</div>
</div>
{% endblock fos_user_content %}
{% block body_content %}
<h1 class="panel-title">{{ 'title_user_registration'|trans({}, 'SonataUserBundle') }}</h1>
<form action="{{ path('fos_user_registration_register') }}" {{ form_enctype(form) }} method="POST" class="fos_user_registration_register simple-form form-horizontal">
<section>
<div class="input-field col s12">
<i class="material-icons gogo-icon-account-circle prefix"></i>
{{ form_widget(form.username, {'id': 'input-username', 'attr': {'class': 'validate required'}}) }}
<label for="input-username" class="gogo-form-label">Nom d'utilisateur</label>
</div>
<div class="input-field col s12">
<i class="material-icons gogo-icon-mail prefix"></i>
{{ form_widget(form.email, {'id': 'input-email', 'attr': {'class': 'validate required'}}) }}
<label for="input-email" class="gogo-form-label">Adresse e-mail</label>
</div>
{{ form_errors(form.plainPassword.first) }}
<div class="input-field col s12">
<i class="material-icons gogo-icon-lock prefix"></i>
{{ form_widget(form.plainPassword.first, {'id': 'input-plainPassword-1', 'attr': {'class': 'validate required'}}) }}
<label for="input-plainPassword-1" class="gogo-form-label">Mot de passe</label>
</div>
<div class="input-field col s12">
<i class="material-icons gogo-icon-lock prefix"></i>
{{ form_widget(form.plainPassword.second, {'id': 'input-plainPassword-2', 'attr': {'class': 'validate required'}}) }}
<label for="input-plainPassword-2" class="gogo-form-label">Retapez votre mot de passe</label>
</div>
</section>
<div class="divider"></div>
<section>
<h2 class="optional">Optionnel</h2>
<p class="newsletter-explanation gogo-neutral">Soyez informés des nouveaux points référencés près de chez vous</p>
{{ form_widget(form.newsletterFrequency) }}
<div class="input-field col s12">
<i class="material-icons gogo-icon-marker-symbol prefix"></i>
{{ form_widget(form.location, {'id': 'input-location', 'attr': {'class': 'validate'}}) }}
<label for="input-location" class="gogo-form-label">Votre code postal ou votre adresse</label>
</div>
{{ form_errors(form) }}
{{ form_rest(form) }}
<div class="form-actions">
<input type="submit" value="{{ 'registration.submit'|trans({}, 'FOSUserBundle') }}" class="btn btn-success pull-right" />
</div>
</section>
</form>
{% endblock body_content %}
......@@ -5,9 +5,12 @@
{% block fos_user_content %}
{% endblock fos_user_content %}
</section>
{% endblock body_content %}
{% block page_stylesheets %}
<link rel="stylesheet" href="{{ asset('assets/css/element-form.css?ver=' ~ version) }}">
{% endblock %}
{% block page_javascripts %}
<script src="{{ asset('js/libs.js') }}"></script>
{% endblock page_javascripts %}
\ No newline at end of file
<script src="{{ asset('js/element-form.js?ver=' ~ version) }}"></script>
{% endblock %}
\ No newline at end of file
......@@ -16,6 +16,12 @@ use Sonata\UserBundle\Document\BaseUser as BaseUser;
use Sonata\UserBundle\Model\UserInterface;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
abstract class NewsletterFrequencyOptions
{
const Never = 0;
const Weekly = 1;
const Monthly = 2;
}
/**
* @MongoDB\Document
* @MongoDB\HasLifecycleCallbacks
......@@ -25,7 +31,25 @@ class User extends BaseUser
/**
* @MongoDB\Id(strategy="auto")
*/
protected $id;
protected $id;
/**
* Address of the user. Can be a simple postalCode, or a more precise address
* @MongoDB\Field(type="string")
*/
protected $location;
/**
* Geolocalisation of the location attribute
* @MongoDB\EmbedOne(targetDocument="Biopen\GeoDirectoryBundle\Document\Coordinates")
*/
public $geo;
/**
* See NewsletterFrequencyOptions
* @MongoDB\Field(type="int")
*/
protected $newsletterFrequency;
/**
* @MongoDB\Field(type="int")
......@@ -462,4 +486,70 @@ class User extends BaseUser
{
return $this->votesCount;
}
/**
* Set location
*
* @param string $location
* @return $this
*/
public function setLocation($location)
{
$this->location = $location;
return $this;
}
/**
* Get location
*
* @return string $location
*/
public function getLocation()
{
return $this->location;
}
/**
* Set geo
*
* @param Biopen\GeoDirectoryBundle\Document\Coordinates $geo
* @return $this
*/
public function setGeo(\Biopen\GeoDirectoryBundle\Document\Coordinates $geo)
{
$this->geo = $geo;
return $this;
}
/**
* Get geo
*
* @return Biopen\GeoDirectoryBundle\Document\Coordinates $geo
*/
public function getGeo()
{
return $this->geo;
}
/**
* Set newsletterFrequency
*
* @param int $newsletterFrequency
* @return $this
*/
public function setNewsletterFrequency($newsletterFrequency)
{
$this->newsletterFrequency = $newsletterFrequency;
return $this;
}
/**
* Get newsletterFrequency
*
* @return int $newsletterFrequency
*/
public function getNewsletterFrequency()
{
return $this->newsletterFrequency;
}
}
......@@ -2,17 +2,49 @@
* @Author: Sebastian Castro
* @Date: 2017-04-03 13:36:02
* @Last Modified by: Sebastian Castro
* @Last Modified time: 2017-07-29 08:23:06
*/
label ~ ul { color: #b51717; }
h2 { text-align: center; }
.form-actions { text-align: center; margin-top: 1rem; }
* @Last Modified time: 2018-02-20 18:13:56
*/
#page-content.register
{
max-width: 500px !important;
padding-bottom: 1rem;
box-shadow: 6px 6px 9px 0px rgba(0, 0, 0, 0.35);
.panel { margin-top: -2rem; }
section { padding: 3.5rem 3.5rem 1rem 2.5rem; }
@media #{$small-only} { padding-top: 10px; }
max-width: 550px !important;
background-color: transparent !important;
section {
background-color: white;
border-radius: 5px;
}
.form-actions {
text-align: center;
margin-top: 1rem;
@media #{$medium-and-up} { margin-top: 2rem; }
.btn { width: 100%; }
}
h2.optional {
margin: 0;
}
.newsletter-explanation {
margin-top: 15px;
margin-bottom: 15px;
text-align: center;
font-style: italic;
}
#biopen_user_registration_newsletterFrequency {
padding-left: 3px;
label {
margin-right: 20px;
@media #{$small-only} {
display: block;
margin-bottom: 15px;
}
}
}
.divider {
border:none;
}
h2 { text-align: center !important; }
}
\ No newline at end of file
......@@ -5,7 +5,7 @@
*
* @copyright Copyright (c) 2016 Sebastian Castro - 90scastro@gmail.com
* @license MIT License
* @Last Modified time: 2018-01-19 13:04:59
* @Last Modified time: 2018-02-20 17:36:33
*/
jQuery(document).ready(function()
{
......@@ -38,7 +38,7 @@ jQuery(document).ready(function()
var inputDescription = $('#input-description');
var inputDescriptionMore = $('#input-description-more');
if (inputDescriptionMore.val().length > 0) inputDescriptionMore.parent('.input-field').show();
if (inputDescriptionMore.val() && inputDescriptionMore.val().length > 0) inputDescriptionMore.parent('.input-field').show();
inputDescription.on('input', function() {
if ($(this).hasClass('invalid')) inputDescriptionMore.parent('.input-field').slideDown(800);
......