feat: registration and password reset
This commit is contained in:
+42
-17
@@ -13,6 +13,7 @@ use App\BusProNet\Model\File;
|
|||||||
use App\BusProNet\Model\Notification;
|
use App\BusProNet\Model\Notification;
|
||||||
use App\BusProNet\Model\PersonalData;
|
use App\BusProNet\Model\PersonalData;
|
||||||
use App\Form\Model\BookingData;
|
use App\Form\Model\BookingData;
|
||||||
|
use App\Form\Model\RegistrationData;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\Serializer\Encoder\XmlEncoder;
|
use Symfony\Component\Serializer\Encoder\XmlEncoder;
|
||||||
@@ -36,7 +37,8 @@ class ApiClient
|
|||||||
private readonly ResponseParser $responseParser,
|
private readonly ResponseParser $responseParser,
|
||||||
private readonly LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
array $options
|
array $options
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
$this->config = $this->resolveOptions($options);
|
$this->config = $this->resolveOptions($options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +59,45 @@ class ApiClient
|
|||||||
return $this->sendRequest(static::TYPE_CUSTOMER_DATA, $data);
|
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
|
* @throws ApiClientException
|
||||||
*/
|
*/
|
||||||
@@ -180,22 +221,6 @@ class ApiClient
|
|||||||
return $this->sendRequest(static::TYPE_AVAILABILITY, $data);
|
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
|
* @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;
|
public ?\DateTimeImmutable $dateOfBirth = null;
|
||||||
|
|
||||||
#[Assert\NotBlank(message: 'Bitte angeben')]
|
#[Assert\NotBlank(message: 'Bitte angeben')]
|
||||||
|
#[Assert\Email(message: 'Bitte eine gültige E-Mail Adresse angeben', mode: 'strict')]
|
||||||
public ?string $email = null;
|
public ?string $email = null;
|
||||||
|
|
||||||
#[Assert\NotBlank(message: 'Bitte angeben')]
|
#[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-boost="true"
|
||||||
hx-indicator="#loading-indicator">
|
hx-indicator="#loading-indicator">
|
||||||
<div class="relative z-20 flex items-center justify-between pb-4">
|
<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') %}
|
{% if is_granted('ROLE_USER') %}
|
||||||
<div class="hidden lg:block">
|
<div class="hidden lg:block">
|
||||||
{{ knp_menu_render(knp_menu_get('main')) }}
|
{{ 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>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h2 class="text-2xl font-semibold pb-4">
|
<h2 class="text-2xl font-semibold pb-4">
|
||||||
Login MyE&P
|
Login
|
||||||
</h2>
|
</h2>
|
||||||
<form action="{{ path('app_login') }}" method="post">
|
<form action="{{ path('app_login') }}" method="post">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
@@ -47,6 +47,14 @@
|
|||||||
<button type="submit" class="btn w-full">
|
<button type="submit" class="btn w-full">
|
||||||
Login
|
Login
|
||||||
</button>
|
</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') }}">
|
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user