feat: registration and password reset
This commit is contained in:
+45
-20
@@ -13,6 +13,7 @@ use App\BusProNet\Model\File;
|
||||
use App\BusProNet\Model\Notification;
|
||||
use App\BusProNet\Model\PersonalData;
|
||||
use App\Form\Model\BookingData;
|
||||
use App\Form\Model\RegistrationData;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Serializer\Encoder\XmlEncoder;
|
||||
@@ -33,10 +34,11 @@ class ApiClient
|
||||
public function __construct(
|
||||
private readonly HttpClientInterface $httpClient,
|
||||
private readonly SerializerInterface $serializer,
|
||||
private readonly ResponseParser $responseParser,
|
||||
private readonly LoggerInterface $logger,
|
||||
array $options
|
||||
) {
|
||||
private readonly ResponseParser $responseParser,
|
||||
private readonly LoggerInterface $logger,
|
||||
array $options
|
||||
)
|
||||
{
|
||||
$this->config = $this->resolveOptions($options);
|
||||
}
|
||||
|
||||
@@ -57,6 +59,45 @@ class ApiClient
|
||||
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiClientException
|
||||
*/
|
||||
public function register(RegistrationData $registrationData): Notification
|
||||
{
|
||||
$data = [
|
||||
'user' => $this->config['bpn_username'],
|
||||
'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
|
||||
'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
|
||||
'art' => 'Adresse_Neu',
|
||||
'adressdaten' => [
|
||||
'geschlecht' => $registrationData->gender,
|
||||
'vorname' => $registrationData->firstName,
|
||||
'name' => $registrationData->name,
|
||||
'kommunikation' => [
|
||||
'email' => $registrationData->email,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiClientException
|
||||
*/
|
||||
public function resetPassword(string $email): Notification
|
||||
{
|
||||
$data = [
|
||||
'user' => $this->config['bpn_username'],
|
||||
'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
|
||||
'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
|
||||
'art' => 'Passwort_Anfrage',
|
||||
'email' => $email,
|
||||
];
|
||||
|
||||
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiClientException
|
||||
*/
|
||||
@@ -180,22 +221,6 @@ class ApiClient
|
||||
return $this->sendRequest(static::TYPE_AVAILABILITY, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiClientException
|
||||
*/
|
||||
public function resetPassword(string $email): Notification
|
||||
{
|
||||
$data = [
|
||||
'user' => $this->config['bpn_username'],
|
||||
'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_CUSTOMER_DATA),
|
||||
'satz' => ['@typ' => static::TYPE_CUSTOMER_DATA],
|
||||
'art' => 'Passwort_Anfrage',
|
||||
'email' => $email,
|
||||
];
|
||||
|
||||
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ApiClientException
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\Exception\ApiClientException;
|
||||
use App\Form\Model\RegistrationData;
|
||||
use App\Form\RegistrationType;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class RegistrationController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly ApiClient $apiClient)
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/registration', name: 'app_registration')]
|
||||
#[IsGranted('PUBLIC_ACCESS')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$registrationData = new RegistrationData();
|
||||
$form = $this->createForm(RegistrationType::class, $registrationData,[
|
||||
'action' => $this->generateUrl('app_registration'),
|
||||
]);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
try {
|
||||
$response = $this->apiClient->register($registrationData);
|
||||
if ($response->isSuccessful()) {
|
||||
$this->addFlash('success', 'Du erhältst in Kürze eine E-Mail mit einem Link zum (Zurück)setzen deines Passworts.');
|
||||
} else {
|
||||
$this->addFlash('error', 'Möglicherweise bist du bereits registriert. Bitte setze dein Passwort zurück.');
|
||||
}
|
||||
} catch (ApiClientException $e) {
|
||||
$this->addFlash('error', $e->getMessage());
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('app_login');
|
||||
}
|
||||
|
||||
return $this->render('registration/index.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\Exception\ApiClientException;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use Symfony\Component\Validator\Constraints\Email;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
class ResetPasswordController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly ApiClient $apiClient)
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/reset-password', name: 'app_reset_password')]
|
||||
#[IsGranted('PUBLIC_ACCESS')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$form = $this
|
||||
->createFormBuilder(null, [
|
||||
'action' => $this->generateUrl('app_reset_password'),
|
||||
])
|
||||
->add('email', EmailType::class, [
|
||||
'label' => 'E-Mail',
|
||||
'constraints' => [
|
||||
new NotBlank([
|
||||
'message' => 'Bitte angeben',
|
||||
]),
|
||||
new Email([
|
||||
'mode' => 'strict',
|
||||
'message' => 'Bitte eine gültige E-Mail Adresse angeben',
|
||||
])
|
||||
],
|
||||
])
|
||||
->getForm()
|
||||
;
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
try {
|
||||
$this->apiClient->resetPassword($form->get('email')->getData());
|
||||
$this->addFlash('success', 'Du erhältst in Kürze eine E-Mail mit einem Link zum (Zurück)setzen deines Passworts.');
|
||||
} catch (ApiClientException $e) {
|
||||
$this->addFlash('error', $e->getMessage());
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('app_login');
|
||||
}
|
||||
|
||||
return $this->render('reset_password/index.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ class ParticipantData
|
||||
public ?\DateTimeImmutable $dateOfBirth = null;
|
||||
|
||||
#[Assert\NotBlank(message: 'Bitte angeben')]
|
||||
#[Assert\Email(message: 'Bitte eine gültige E-Mail Adresse angeben', mode: 'strict')]
|
||||
public ?string $email = null;
|
||||
|
||||
#[Assert\NotBlank(message: 'Bitte angeben')]
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form\Model;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class RegistrationData
|
||||
{
|
||||
#[Assert\NotBlank(message: 'Bitte angeben')]
|
||||
public ?string $firstName = null;
|
||||
|
||||
#[Assert\NotBlank(message: 'Bitte angeben')]
|
||||
public ?string $name = null;
|
||||
|
||||
#[Assert\NotBlank(message: 'Bitte angeben')]
|
||||
#[Assert\Choice(choices: ['M', 'W', 'd'])]
|
||||
public ?string $gender = null;
|
||||
|
||||
#[Assert\NotBlank(message: 'Bitte angeben')]
|
||||
#[Assert\Email(message: 'Bitte eine gültige E-Mail Adresse angeben', mode: 'strict')]
|
||||
public ?string $email = null;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Form\Model\RegistrationData;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class RegistrationType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('gender', ChoiceType::class, [
|
||||
'label' => 'Gender',
|
||||
'choices' => [
|
||||
'männlich' => 'M',
|
||||
'weiblich' => 'W',
|
||||
'divers' => 'D',
|
||||
],
|
||||
])
|
||||
->add('firstName', TextType::class, [
|
||||
'label' => 'Name',
|
||||
])
|
||||
->add('name', TextType::class, [
|
||||
'label' => 'Nachname',
|
||||
])
|
||||
->add('email', EmailType::class, [
|
||||
'label' => 'E-Mail',
|
||||
'property_path' => 'email',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => RegistrationData::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@
|
||||
hx-boost="true"
|
||||
hx-indicator="#loading-indicator">
|
||||
<div class="relative z-20 flex items-center justify-between pb-4">
|
||||
<img class="h-8 w-auto" src="{{ asset('build/images/logo.svg') }}" alt="My E&P Team">
|
||||
<div class="text-3xl font-medium">
|
||||
MyE&P
|
||||
</div>
|
||||
{% if is_granted('ROLE_USER') %}
|
||||
<div class="hidden lg:block">
|
||||
{{ knp_menu_render(knp_menu_get('main')) }}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="p-8 max-w-screen-sm border border-gray-300 rounded-md mx-auto">
|
||||
<h2 class="text-2xl font-semibold pb-4">
|
||||
Registrierung MyE&P
|
||||
</h2>
|
||||
{{ form_start(form) }}
|
||||
<div class="grid grid-cols-2 gap-x-8 gap-y-4 pb-4">
|
||||
{{ form_row(form.firstName) }}
|
||||
{{ form_row(form.name) }}
|
||||
{{ form_row(form.gender) }}
|
||||
{{ form_row(form.email) }}
|
||||
</div>
|
||||
<button type="submit" class="btn">
|
||||
Absenden
|
||||
</button>
|
||||
<div class="pt-4">
|
||||
<a href="{{ path('app_login') }}" class="underline">
|
||||
zurück
|
||||
</a>
|
||||
</div>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,23 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="p-8 max-w-screen-sm border border-gray-300 rounded-md mx-auto">
|
||||
<h2 class="text-2xl font-semibold pb-4">
|
||||
Passwort zurücksetzen
|
||||
</h2>
|
||||
{{ form_start(form) }}
|
||||
<div class="pb-4">
|
||||
{{ form_row(form.email) }}
|
||||
</div>
|
||||
<button type="submit" class="btn">
|
||||
Absenden
|
||||
</button>
|
||||
<div class="pt-4">
|
||||
<a href="{{ path('app_login') }}" class="underline">
|
||||
zurück
|
||||
</a>
|
||||
</div>
|
||||
{{ form_rest(form) }}
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -11,7 +11,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<h2 class="text-2xl font-semibold pb-4">
|
||||
Login MyE&P
|
||||
Login
|
||||
</h2>
|
||||
<form action="{{ path('app_login') }}" method="post">
|
||||
<div class="mb-6">
|
||||
@@ -47,6 +47,14 @@
|
||||
<button type="submit" class="btn w-full">
|
||||
Login
|
||||
</button>
|
||||
<div class="flex items-center space-x-2 pt-4">
|
||||
<a href="{{ path('app_reset_password') }}" class="underline">
|
||||
Passwort vergessen?
|
||||
</a>
|
||||
<a href="{{ path('app_registration') }}" class="underline">
|
||||
Registrierung
|
||||
</a>
|
||||
</div>
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user