Merge pull request #26 from Apes-HDF/chore/sync-les-tilleuls

Chore/sync les tilleuls
This commit is contained in:
Paul Andrieux 2025-11-05 11:50:53 +01:00 committed by GitHub
commit 462b59f41c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 176 additions and 147 deletions

View file

@ -1 +1,6 @@
CVE-2025-30204 CVE-2025-30204
GHSA-c2pc-g5qf-rfrf
CVE-2024-8176
CVE-2024-55549
CVE-2025-24855
CVE-2025-59530

View file

@ -88,7 +88,6 @@ App\Entity\User:
name: 'APES compte lieu' name: 'APES compte lieu'
address: '@address_region_hauts_de_france' address: '@address_region_hauts_de_france'
schedule: '9h30 - 17h30' schedule: '9h30 - 17h30'
phoneNumber: null
createdAt: <date_create_immutable('+1 month')> createdAt: <date_create_immutable('+1 month')>
# —— Users ————————————————————————————————————————————————————————————————— # —— Users —————————————————————————————————————————————————————————————————
@ -170,6 +169,7 @@ App\Entity\User:
firstname: <firstname()> firstname: <firstname()>
lastname: <lastname()> lastname: <lastname()>
address: null address: null
phoneNumber: '+33600000000'
avatar: 'a9a9bf49-24e4-4b3e-bdbd-86808c32939e.jpg' avatar: 'a9a9bf49-24e4-4b3e-bdbd-86808c32939e.jpg'
# user with an address and a preferred category set # user with an address and a preferred category set

View file

@ -342,7 +342,7 @@ abstract class AbstractUserCrudController extends AbstractCrudController impleme
->setFormType(PhoneNumberType::class) ->setFormType(PhoneNumberType::class)
->setFormTypeOptions([ ->setFormTypeOptions([
'format' => PhoneNumberFormat::INTERNATIONAL, 'format' => PhoneNumberFormat::INTERNATIONAL,
'required' => false, 'required' => true,
]) ])
->setHelp($i18prefix.'.field.phone.help') ->setHelp($i18prefix.'.field.phone.help')
; ;

View file

@ -8,6 +8,7 @@ use App\Controller\FlashTrait;
use App\Controller\i18nTrait; use App\Controller\i18nTrait;
use App\Controller\User\MyAccountAction; use App\Controller\User\MyAccountAction;
use App\Entity\User; use App\Entity\User;
use App\Enum\User\UserType;
use App\Exception\UserConfirmationTokenExpiredException; use App\Exception\UserConfirmationTokenExpiredException;
use App\Exception\UserNotFoundException; use App\Exception\UserNotFoundException;
use App\Form\Type\Security\AccountCreateStep1FormType; use App\Form\Type\Security\AccountCreateStep1FormType;
@ -20,12 +21,15 @@ use App\MessageBus\CommandBus;
use App\MessageBus\QueryBus; use App\MessageBus\QueryBus;
use App\MessageHandler\Command\Security\AccountCreateStep1CommandHandler; use App\MessageHandler\Command\Security\AccountCreateStep1CommandHandler;
use App\Repository\ConfigurationRepository; use App\Repository\ConfigurationRepository;
use libphonenumber\PhoneNumber;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\Exception\HandlerFailedException; use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
/** /**
* @see AccountCreateActionStep1Test * @see AccountCreateActionStep1Test
@ -43,6 +47,7 @@ final class AccountCreateController extends AbstractController
private readonly CommandBus $commandBus, private readonly CommandBus $commandBus,
private readonly Security $security, private readonly Security $security,
private readonly ConfigurationRepository $configurationRepository, private readonly ConfigurationRepository $configurationRepository,
private readonly TranslatorInterface $translator,
) { ) {
$this->i18nPrefix = $this->getI18nPrefix(); $this->i18nPrefix = $this->getI18nPrefix();
} }
@ -102,39 +107,55 @@ final class AccountCreateController extends AbstractController
$configuration = $this->configurationRepository->getInstanceConfigurationOrCreate(); $configuration = $this->configurationRepository->getInstanceConfigurationOrCreate();
// nominal case: user found and token not expired // nominal case: user found and token not expired
$form = $this->createForm(AccountCreateStep2FormType::class, $user->setStep2Defaults())->handleRequest($request); $form = $this->createForm(AccountCreateStep2FormType::class, $user->setStep2Defaults())->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted()) {
/** @var User $user */ $phone = $form->get('phone')->getData() ?? '';
$user = $form->getData();
$this->commandBus->dispatch(new AccountCreateStep2Command($user));
$this->security->login($user); // auto-log the user
// If user has pending invitations then redirect them to the first group if (!$phone instanceof PhoneNumber || $phone->getNationalNumber() === '') {
// found without doing the confirmation stuff, it must be done on the $form->get('phone')->addError(
// page group. new FormError($this->translator->trans('account_create.phone.empty.error', [], 'validators'))
$group = $user->getMyGroupsAsInvited()->first(); );
if ($group !== false) { }
// If platform needs payment, redirect to payment
if ($configuration->getPaidMembership()) { if ($phone instanceof PhoneNumber && \strlen($phone->getNationalNumber() ?? '') < 8) {
$successMessage = $this->i18nPrefix.'.step2.with_invitation.global_paid_membership.flash.success'; $form->get('phone')->addError(
new FormError($this->translator->trans('account_create.phone.short.error', [], 'validators'))
);
}
if ($form->isValid()) {
/** @var User $user */
$user = $form->getData();
$user->setType(UserType::USER);
$this->commandBus->dispatch(new AccountCreateStep2Command($user));
$this->security->login($user); // auto-log the user
// If user has pending invitations then redirect them to the first group
// found without doing the confirmation stuff, it must be done on the
// page group.
$group = $user->getMyGroupsAsInvited()->first();
if ($group !== false) {
// If platform needs payment, redirect to payment
if ($configuration->getPaidMembership()) {
$successMessage = $this->i18nPrefix.'.step2.with_invitation.global_paid_membership.flash.success';
$this->addFlashSuccess($successMessage);
return $this->redirectToRoute('redirect_to_payment');
}
$successMessage = $this->i18nPrefix.'.step2.with_invitation.flash.success';
$this->addFlashSuccess($successMessage); $this->addFlashSuccess($successMessage);
return $this->redirectToRoute('redirect_to_payment'); return $this->redirectToRoute('app_group_show_logged', $group->getRoutingParameters());
} }
$successMessage = $this->i18nPrefix.'.step2.with_invitation.flash.success';
if ($configuration->getPaidMembership()) {
$successMessage = $this->i18nPrefix.'.step2.global_paid_membership.flash.success';
} else {
$successMessage = $this->i18nPrefix.'.step2.flash.success';
}
// otherwise go to the address form
$this->addFlashSuccess($successMessage); $this->addFlashSuccess($successMessage);
return $this->redirectToRoute('app_group_show_logged', $group->getRoutingParameters()); return $this->redirectToRoute(MyAccountAction::ROUTE);
} }
if ($configuration->getPaidMembership()) {
$successMessage = $this->i18nPrefix.'.step2.global_paid_membership.flash.success';
} else {
$successMessage = $this->i18nPrefix.'.step2.flash.success';
}
// otherwise go to the address form
$this->addFlashSuccess($successMessage);
return $this->redirectToRoute(MyAccountAction::ROUTE);
} }
return $this->render('pages/register/step2.html.twig', compact('form', 'user')); return $this->render('pages/register/step2.html.twig', compact('form', 'user'));

View file

@ -13,7 +13,9 @@ use App\Form\Type\User\EditProfileFormType;
use App\Repository\UserRepository; use App\Repository\UserRepository;
use App\Tests\Functional\Controller\User\Account\EditProfileActionTest; use App\Tests\Functional\Controller\User\Account\EditProfileActionTest;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use libphonenumber\PhoneNumber;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -21,6 +23,7 @@ use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter; use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
use Symfony\Component\Security\Http\Attribute\CurrentUser; use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Translation\TranslatorInterface;
/** /**
* @see EditProfileActionTest * @see EditProfileActionTest
@ -34,6 +37,7 @@ final class EditProfileAction extends AbstractController
private readonly UserRepository $userRepository, private readonly UserRepository $userRepository,
private readonly UserManager $userManager, private readonly UserManager $userManager,
private readonly EntityManagerInterface $entityManager, private readonly EntityManagerInterface $entityManager,
private readonly TranslatorInterface $translator,
) { ) {
} }
@ -45,14 +49,30 @@ final class EditProfileAction extends AbstractController
public function __invoke(Request $request, #[CurrentUser] User $user): Response public function __invoke(Request $request, #[CurrentUser] User $user): Response
{ {
$form = $this->createForm(EditProfileFormType::class, $user)->handleRequest($request); $form = $this->createForm(EditProfileFormType::class, $user)->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted()) {
/** @var UploadedFile|null $avatar */ $phone = $form->get('phone')->getData() ?? '';
$avatar = $form->get('avatar')->getData();
$this->userManager->upload($avatar, $user);
$this->userRepository->save($user, true);
$this->addFlashSuccess($this->getI18nPrefix().'.flash.success');
return $this->redirectToRoute(MyAccountAction::ROUTE); if (!$phone instanceof PhoneNumber || $phone->getNationalNumber() === '') {
$form->get('phone')->addError(
new FormError($this->translator->trans('account_create.phone.empty.error', [], 'validators'))
);
}
if ($phone instanceof PhoneNumber && \strlen($phone->getNationalNumber() ?? '') < 8) {
$form->get('phone')->addError(
new FormError($this->translator->trans('account_create.phone.short.error', [], 'validators'))
);
}
if ($form->isValid()) {
/** @var UploadedFile|null $avatar */
$avatar = $form->get('avatar')->getData();
$this->userManager->upload($avatar, $user);
$this->userRepository->save($user, true);
$this->addFlashSuccess($this->getI18nPrefix().'.flash.success');
return $this->redirectToRoute(MyAccountAction::ROUTE);
}
} }
// In case of error, we must reload the original firstname (to display it in navbar) // In case of error, we must reload the original firstname (to display it in navbar)

View file

@ -5,11 +5,10 @@ declare(strict_types=1);
namespace App\Form\Type\Security; namespace App\Form\Type\Security;
use App\Entity\User; use App\Entity\User;
use App\Enum\User\UserType; use libphonenumber\PhoneNumberFormat;
use Misd\PhoneNumberBundle\Form\Type\PhoneNumberType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType; use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType; use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@ -27,19 +26,6 @@ final class AccountCreateStep2FormType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$builder $builder
->add('type', ChoiceType::class, [
'label' => 'account_create_action.account_type',
'label_attr' => ['class' => 'text-black fw-light'],
'choices' => UserType::getForFront(),
'choice_attr' => function () {
return [
'data-controller' => 'account',
'data-action' => 'click->account#choosenType',
];
},
'expanded' => true,
])
->add('firstname', TextType::class, [ ->add('firstname', TextType::class, [
'label' => 'account_create_action.firsname', 'label' => 'account_create_action.firsname',
'label_attr' => ['class' => 'text-black fw-light required'], 'label_attr' => ['class' => 'text-black fw-light required'],
@ -60,14 +46,14 @@ final class AccountCreateStep2FormType extends AbstractType
'required' => false, 'required' => false,
]) ])
->add('name', TextType::class, [ ->add('phone', PhoneNumberType::class, [
'label' => 'account_create_action.name', 'label' => 'account_create_action.phone',
'label_attr' => ['class' => 'text-black fw-light required'], 'label_attr' => ['class' => 'text-black fs-6 fw-normal'],
'attr' => [ 'widget' => PhoneNumberType::WIDGET_COUNTRY_CHOICE,
'class' => 'form-control-sm input-name', 'format' => PhoneNumberFormat::INTERNATIONAL,
'placeholder' => 'account_create_action.name.placeholder', 'country_display_emoji_flag' => true,
], 'preferred_country_choices' => ['FR'],
'required' => false, 'required' => true,
]) ])
->add('plainPassword', RepeatedType::class, [ ->add('plainPassword', RepeatedType::class, [
@ -118,15 +104,6 @@ final class AccountCreateStep2FormType extends AbstractType
'attr' => ['class' => 'btn btn-primary btn-sm'], 'attr' => ['class' => 'btn btn-primary btn-sm'],
]) ])
; ;
$builder->get('type')->addModelTransformer(new CallbackTransformer(
function (?UserType $enumToString) {
return $enumToString === null ? '' : $enumToString->value;
},
function (string $stringToEnum) {
return UserType::from($stringToEnum);
}
));
} }
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver): void

View file

@ -50,8 +50,9 @@ final class EditProfileFormType extends AbstractType
'label_attr' => ['class' => 'text-black fs-6 fw-normal'], 'label_attr' => ['class' => 'text-black fs-6 fw-normal'],
'format' => PhoneNumberFormat::INTERNATIONAL, 'format' => PhoneNumberFormat::INTERNATIONAL,
'widget' => PhoneNumberType::WIDGET_COUNTRY_CHOICE, 'widget' => PhoneNumberType::WIDGET_COUNTRY_CHOICE,
'country_display_emoji_flag' => true,
'preferred_country_choices' => ['FR'], 'preferred_country_choices' => ['FR'],
'required' => false, 'required' => true,
]) ])
->add('smsNotifications', CheckboxType::class, [ ->add('smsNotifications', CheckboxType::class, [

View file

@ -7,6 +7,7 @@ namespace App\Message\Command\Security;
use App\Entity\User; use App\Entity\User;
use App\Enum\User\UserType; use App\Enum\User\UserType;
use App\MessageHandler\Command\Security\AccountCreateStep2CommandHandler; use App\MessageHandler\Command\Security\AccountCreateStep2CommandHandler;
use libphonenumber\PhoneNumber;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
@ -22,6 +23,7 @@ final class AccountCreateStep2Command
public ?string $firstname = null; public ?string $firstname = null;
public ?string $name = null; public ?string $name = null;
public string $plainPassword; public string $plainPassword;
public PhoneNumber $phone;
public function __construct(User $user) public function __construct(User $user)
{ {
@ -33,5 +35,7 @@ final class AccountCreateStep2Command
$this->name = $user->getName(); $this->name = $user->getName();
Assert::stringNotEmpty($user->getPlainPassword()); Assert::stringNotEmpty($user->getPlainPassword());
$this->plainPassword = $user->getPlainPassword(); $this->plainPassword = $user->getPlainPassword();
Assert::notNull($user->phone);
$this->phone = $user->phone;
} }
} }

View file

@ -9,6 +9,8 @@ use App\Entity\User;
use App\Enum\User\UserType; use App\Enum\User\UserType;
use App\Message\Command\Security\AccountCreateStep2Command; use App\Message\Command\Security\AccountCreateStep2Command;
use App\Repository\UserRepository; use App\Repository\UserRepository;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
@ -47,6 +49,11 @@ final class AccountCreateStep2CommandHandler
throw new \UnexpectedValueException('This hanlder can only create users or places.'); throw new \UnexpectedValueException('This hanlder can only create users or places.');
} }
$phoneObjectToString = PhoneNumberUtil::getInstance()->format(
$message->phone,
PhoneNumberFormat::E164
);
$user->setPhoneNumber($phoneObjectToString);
$this->userManager->updatePassword($user->setPlainPassword($message->plainPassword)); $this->userManager->updatePassword($user->setPlainPassword($message->plainPassword));
$this->userManager->finalizeAccountCreateStep2($user); $this->userManager->finalizeAccountCreateStep2($user);
$this->userManager->save($user, true); $this->userManager->save($user, true);

View file

@ -1,6 +1,7 @@
{% set is_product_owner = product.owner == app.user %} {% set is_product_owner = product.owner == app.user %}
<div class="bg-light rounded-2 p-3" data-controller="calendar" data-calendar-unavailabilities-value="{{ product.getUnavailabilities()|join(',') }}"> <div class="bg-light rounded-2 p-3" data-controller="calendar"
data-calendar-unavailabilities-value="{{ product.getUnavailabilities()|join(',') }}">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h5>{{ title }}</h5> <h5>{{ title }}</h5>
@ -71,22 +72,36 @@
</div> </div>
{% endif %} {% endif %}
<div class="d-grid col mt-3"> <div class="d-grid col mt-3">
<button class="btn border border-0 text-primary text-decoration-underline" data-action="click->calendar#resetDates" type="button"> <button class="btn border border-0 text-primary text-decoration-underline"
data-action="click->calendar#resetDates" type="button">
{{ 'templates.components.product.calendar.reset'|trans }} {{ 'templates.components.product.calendar.reset'|trans }}
</button> </button>
</div> </div>
{% if actionNeeded %} {% if actionNeeded %}
<div class="d-grid col-12 mt-3"> <div class="d-grid col-12 mt-3">
{% if app.user is null or is_granted('borrow', product) %} {% if app.user is null or is_granted('borrow', product) %}
<button {% if app.user and app.user.address is null %}
id="service-request" {% include 'components/product/_modal.html.twig' with {
class="btn btn-sm btn-primary" menu_action: false,
data-path="{{ path('app_user_service_request_new', {id: product.id}) }}" page_type: 'article',
data-action="click->calendar#serviceRequest" button: 'templates.components.product.calendar.service_request'|trans,
disabled title: 'templates.pages.account.index.no-address-title'|trans,
> message: 'templates.pages.account.index.no-address-message'|trans({
{{ 'templates.components.product.calendar.service_request'|trans }} '%product%': product.type.isObject ? 'objet' : 'service'
</button> }),
action: 'templates.pages.account.product.list.no-address-add'|trans
} %}
{% else %}
<button
id="service-request"
class="btn btn-sm btn-primary"
data-path="{{ path('app_user_service_request_new', {id: product.id}) }}"
data-action="click->calendar#serviceRequest"
disabled
>
{{ 'templates.components.product.calendar.service_request'|trans }}
</button>
{% endif %}
{% else %} {% else %}
{{ form_widget(form.submit) }} {{ form_widget(form.submit) }}
{% endif %} {% endif %}

View file

@ -1,7 +1,14 @@
{% if menu_action is defined and menu_action == true %} {% if menu_action is defined and menu_action == true %}
<span class="text-decoration-none text-primary ms-2 position-relative pe-1 cursor-pointer" <span class="text-decoration-none text-primary ms-2 position-relative pe-1 cursor-pointer"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#modalAddAddress">{{ button }}</span> data-bs-target="#modalAddAddress">{{ button }}</span>
{% elseif menu_action is defined and menu_action == false and page_type == 'article' %}
<button type="button"
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#modalAddAddress">
{{ button }}
</button>
{% else %} {% else %}
<div class="d-grid col-12 col-lg-4 mx-auto my-3 order-last"> <div class="d-grid col-12 col-lg-4 mx-auto my-3 order-last">
<button type="button" <button type="button"
@ -32,7 +39,7 @@
<button type="button" <button type="button"
class="btn btn-outline-secondary" class="btn btn-outline-secondary"
data-bs-dismiss="modal"> data-bs-dismiss="modal">
{{ (i18n_prefix ~ '.no-address-cancel')|trans }} {{ 'templates.pages.account.product.list.no-address-cancel'|trans }}
</button> </button>
<a href="{{ path('user_address_step1') }}" <a href="{{ path('user_address_step1') }}"
class="btn btn-secondary"> class="btn btn-secondary">

View file

@ -31,7 +31,6 @@
{{ form_start(form, {attr: {novalidate: true}}) }} {{ form_start(form, {attr: {novalidate: true}}) }}
{{ form_row(form.type) }}
<div class="user-input mt-2"> <div class="user-input mt-2">
{{ form_label(form.firstname) }} {{ form_label(form.firstname) }}
{{ form_widget(form.firstname) }} {{ form_widget(form.firstname) }}
@ -40,10 +39,10 @@
{{ form_widget(form.lastname) }} {{ form_widget(form.lastname) }}
{{ form_errors(form.lastname) }} {{ form_errors(form.lastname) }}
</div> </div>
<div class="place-input mt-2"> <div class="phone-input mt-2">
{{ form_label(form.name) }} {{ form_label(form.phone) }}
{{ form_widget(form.name) }} {{ form_widget(form.phone) }}
{{ form_errors(form.name) }} {{ form_errors(form.phone) }}
</div> </div>
{% include 'components/form/_password_visibility.html.twig' with { {% include 'components/form/_password_visibility.html.twig' with {

View file

@ -1,53 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Tests\Functional\Controller\Security;
use App\Test\ContainerRepositoryTrait;
use App\Test\KernelTrait;
use App\Tests\TestReference;
use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\String\ByteString;
/**
* @see AccountCreateController
*/
final class AccountCreateActionStep2PlaceTest extends WebTestCase
{
use ContainerRepositoryTrait;
use RefreshDatabaseTrait;
use KernelTrait;
private const ROUTE = '/fr/compte/creer-mon-compte-etape-2/';
public function testUserConfirmationTokenExpiredException(): void
{
$client = self::createClient();
$client->request('GET', self::ROUTE.TestReference::USER_13_CONFIRMATION_TOKEN);
self::assertResponseRedirects();
$client->followRedirect();
self::assertResponseIsSuccessful();
self::assertSelectorTextContains('body', 'app.controller.security.account_create_controller.step2.user_confirmation_token_expired.warning');
}
public function testFormSubmitPlaceSuccess(): void
{
$client = self::createClient();
$crawler = $client->request('GET', self::ROUTE.TestReference::USER_12_CONFIRMATION_TOKEN);
$form = $crawler->selectButton('account_create_step2_form_submit')->form();
$password = ByteString::fromRandom(13);
$client->submit($form, [
$form->getName().'[type]' => 'place',
$form->getName().'[name]' => 'My Association',
$form->getName().'[plainPassword][first]' => $password,
$form->getName().'[plainPassword][second]' => $password,
$form->getName().'[gdpr]' => 1,
]);
self::assertResponseRedirects();
$client->followRedirect();
self::assertResponseIsSuccessful();
}
}

View file

@ -33,11 +33,12 @@ final class AccountCreateActionStep2UserInvitationTest extends WebTestCase
$password = ByteString::fromRandom(13); $password = ByteString::fromRandom(13);
$client->submit($form, [ $client->submit($form, [
$form->getName().'[type]' => 'user',
$form->getName().'[firstname]' => 'Foo', $form->getName().'[firstname]' => 'Foo',
$form->getName().'[lastname]' => 'Bar', $form->getName().'[lastname]' => 'Bar',
$form->getName().'[plainPassword][first]' => $password, $form->getName().'[plainPassword][first]' => $password,
$form->getName().'[plainPassword][second]' => $password, $form->getName().'[plainPassword][second]' => $password,
$form->getName().'[phone][country]' => 'FR',
$form->getName().'[phone][number]' => '602030405',
$form->getName().'[gdpr]' => 1, $form->getName().'[gdpr]' => 1,
]); ]);
self::assertResponseRedirects(); self::assertResponseRedirects();

View file

@ -40,11 +40,12 @@ final class AccountCreateActionStep2UserTest extends WebTestCase
$password = ByteString::fromRandom(13); $password = ByteString::fromRandom(13);
$client->submit($form, [ $client->submit($form, [
$form->getName().'[type]' => 'user',
$form->getName().'[firstname]' => 'Foo', $form->getName().'[firstname]' => 'Foo',
$form->getName().'[lastname]' => 'Bar', $form->getName().'[lastname]' => 'Bar',
$form->getName().'[plainPassword][first]' => $password, $form->getName().'[plainPassword][first]' => $password,
$form->getName().'[plainPassword][second]' => $password, $form->getName().'[plainPassword][second]' => $password,
$form->getName().'[phone][country]' => 'FR',
$form->getName().'[phone][number]' => '602030405',
$form->getName().'[gdpr]' => 1, $form->getName().'[gdpr]' => 1,
]); ]);
self::assertResponseRedirects(); self::assertResponseRedirects();

View file

@ -44,7 +44,7 @@ final class EditProfileActionTest extends WebTestCase
$form->getName().'[category]' => TestReference::CATEGORY_OBJECT_1, $form->getName().'[category]' => TestReference::CATEGORY_OBJECT_1,
$form->getName().'[description]' => 'description test', $form->getName().'[description]' => 'description test',
$form->getName().'[phone][country]' => 'FR', $form->getName().'[phone][country]' => 'FR',
$form->getName().'[phone][number]' => '', $form->getName().'[phone][number]' => '634563424',
$form->getName().'[smsNotifications]' => false, $form->getName().'[smsNotifications]' => false,
]); ]);
@ -53,7 +53,7 @@ final class EditProfileActionTest extends WebTestCase
/** @var User $editedUser */ /** @var User $editedUser */
$editedUser = $repo->find(TestReference::USER_16); $editedUser = $repo->find(TestReference::USER_16);
self::assertNull($editedUser->getPhoneNumber()); self::assertNotNull($editedUser->getPhoneNumber());
self::assertResponseRedirects(); self::assertResponseRedirects();
$client->followRedirect(); $client->followRedirect();

View file

@ -26,6 +26,7 @@ final class UserManagerTest extends KernelTestCase
$user = new User(); $user = new User();
$user->setEmail(ByteString::fromRandom(6)->toString().'@example.com'); $user->setEmail(ByteString::fromRandom(6)->toString().'@example.com');
$user->setPhoneNumber('+33600000000');
$user->setPassword('foo'); $user->setPassword('foo');
$userManager->save($user, true); $userManager->save($user, true);

View file

@ -10,6 +10,7 @@ use App\Message\Command\Security\AccountCreateStep2Command;
use App\MessageHandler\Command\Security\AccountCreateStep2CommandHandler; use App\MessageHandler\Command\Security\AccountCreateStep2CommandHandler;
use App\Tests\TestReference; use App\Tests\TestReference;
use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait; use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;
use libphonenumber\PhoneNumberUtil;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
@ -25,11 +26,17 @@ final class AccountCreateStep2CommandHandlerTest extends KernelTestCase
$this->expectException(\UnexpectedValueException::class); $this->expectException(\UnexpectedValueException::class);
$this->expectExceptionMessage('This hanlder can only create users or places'); $this->expectExceptionMessage('This hanlder can only create users or places');
$user = (new User()) $user = (new User())
->setId(Uuid::fromString(TestReference::USER_17)) ->setId(Uuid::fromString(TestReference::USER_17))
->setType(UserType::ADMIN) ->setType(UserType::ADMIN)
->setPlainPassword('foo') ->setPlainPassword('foo')
; ;
$phoneUtil = PhoneNumberUtil::getInstance();
$phone = $phoneUtil->parse('+33602030405', 'FR');
$user->phone = $phone;
$message = new AccountCreateStep2Command($user); $message = new AccountCreateStep2Command($user);
$handler($message); $handler($message);
} }

View file

@ -30,6 +30,7 @@ final class UserRepositoryTest extends KernelTestCase
$user = new User(); $user = new User();
$user->setEmail(ByteString::fromRandom(6)->toString().'@example.com'); $user->setEmail(ByteString::fromRandom(6)->toString().'@example.com');
$user->setPhoneNumber('+33600000000');
$user->setPassword('foo'); $user->setPassword('foo');
$repo->save($user, true); $repo->save($user, true);
$count = $repo->count([]); $count = $repo->count([]);

View file

@ -75,6 +75,11 @@
<target>8 caractères minimun</target> <target>8 caractères minimun</target>
</trans-unit> </trans-unit>
<trans-unit id="3Wd6Kp0" resname="account_create_action.phone">
<source>account_create_action.phone</source>
<target>Numéro de téléphone</target>
</trans-unit>
<trans-unit id="m3UMrJf" resname="account_create_action.gdpr"> <trans-unit id="m3UMrJf" resname="account_create_action.gdpr">
<source>account_create_action.gdpr</source> <source>account_create_action.gdpr</source>
<target>Jai lu et jaccepte les <![CDATA[<a href="%link%" class="text-primary">Conditions Générales d'Utilisation</a>]]></target> <target>Jai lu et jaccepte les <![CDATA[<a href="%link%" class="text-primary">Conditions Générales d'Utilisation</a>]]></target>

View file

@ -26,6 +26,16 @@
<target>Le nom du lieu est obligatoire</target> <target>Le nom du lieu est obligatoire</target>
</trans-unit> </trans-unit>
<trans-unit id="tY0WDru" resname="account_create.phone.empty.error">
<source>account_create.phone.empty.error</source>
<target>Le numéro de téléphone est obligatoire</target>
</trans-unit>
<trans-unit id="tY0WDzu" resname="account_create.phone.short.error">
<source>account_create.phone.short.error</source>
<target>Veuillez indiquer un numéro de téléphone valide</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -74,7 +74,7 @@
<trans-unit id="ftBMsiP" resname="templates.pages.account.index.no-address-message"> <trans-unit id="ftBMsiP" resname="templates.pages.account.index.no-address-message">
<source>templates.pages.account.index.no-address-message</source> <source>templates.pages.account.index.no-address-message</source>
<target>Pour pouvoir créer un %product%, commencez par remplir votre adresse.</target> <target>Pour pouvoir créer ou emprunter un %product%, commencez par remplir votre adresse.</target>
</trans-unit> </trans-unit>
<trans-unit id="e8xvcp6" resname="templates.pages.account.index.no-address-cancel"> <trans-unit id="e8xvcp6" resname="templates.pages.account.index.no-address-cancel">