{{ 'Create an account'|trans }}
+
{{ 'Forgot your password?'|trans }}
{% endblock %}
diff --git a/templates/backoffice/registration/register.html.twig b/templates/backoffice/registration/register.html.twig
index d795784..3331c58 100644
--- a/templates/backoffice/registration/register.html.twig
+++ b/templates/backoffice/registration/register.html.twig
@@ -2,7 +2,7 @@
{% block title %}{{ 'Register'|trans }}{% endblock %}
{% block body %}
-
{{ 'Register'|trans }}
+
{{ 'Register'|trans }}
{{ form_errors(registrationForm) }}
diff --git a/templates/reset_password/check_email.html.twig b/templates/reset_password/check_email.html.twig
new file mode 100644
index 0000000..de0f3ca
--- /dev/null
+++ b/templates/reset_password/check_email.html.twig
@@ -0,0 +1,16 @@
+{% extends 'backoffice/base.html.twig' %}
+
+{% block title %}E-mail verstuurd{% endblock %}
+
+{% block body %}
+
{{ 'Check your email'|trans }}
+
+
+ {{ 'If an account matching your email exists, then an email was just sent that contains a link that you can use to reset your password.'|trans }}
+ {{ 'This link will expire in %count%.'|trans({'%count%': resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle')}) }}
+
+
+ {{ 'If you don\'t receive an email please check your spam folder or'|trans }}
+ {{ 'try again'|trans }}.
+
+{% endblock %}
diff --git a/templates/reset_password/email.html.twig b/templates/reset_password/email.html.twig
new file mode 100644
index 0000000..c714d22
--- /dev/null
+++ b/templates/reset_password/email.html.twig
@@ -0,0 +1,9 @@
+
Hi!
+
+
To reset your password, please visit the following link
+
+
{{ url('tvdt_reset_password', {token: resetToken.token}) }}
+
+
This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.
+
+
Cheers!
diff --git a/templates/reset_password/request.html.twig b/templates/reset_password/request.html.twig
new file mode 100644
index 0000000..3b539bc
--- /dev/null
+++ b/templates/reset_password/request.html.twig
@@ -0,0 +1,18 @@
+{% extends 'backoffice/base.html.twig' %}
+
+{% block title %}Wachtwoord vergeten{% endblock %}
+
+{% block body %}
+
{{ 'Reset your password'|trans }}
+
+ {{ form_start(requestForm) }}
+ {{ form_row(requestForm.email) }}
+
+
+ {{ 'Enter your email address, and we will send you a link to reset your password.'|trans }}
+
+
+
+
{{ 'Back to login'|trans }}
+ {{ form_end(requestForm) }}
+{% endblock %}
diff --git a/templates/reset_password/reset.html.twig b/templates/reset_password/reset.html.twig
new file mode 100644
index 0000000..cfe4873
--- /dev/null
+++ b/templates/reset_password/reset.html.twig
@@ -0,0 +1,12 @@
+{% extends 'backoffice/base.html.twig' %}
+
+{% block title %}Nieuw wachtwoord instellen{% endblock %}
+
+{% block body %}
+
{{ 'Reset your password'|trans }}
+
+ {{ form_start(resetForm) }}
+ {{ form_row(resetForm.plainPassword) }}
+
+ {{ form_end(resetForm) }}
+{% endblock %}
diff --git a/tests/Controller/ResetPasswordControllerTest.php b/tests/Controller/ResetPasswordControllerTest.php
new file mode 100644
index 0000000..dd49031
--- /dev/null
+++ b/tests/Controller/ResetPasswordControllerTest.php
@@ -0,0 +1,104 @@
+client = self::createClient();
+ $this->entityManager = self::getContainer()->get(EntityManagerInterface::class);
+ }
+
+ public function testRequestPageLoads(): void
+ {
+ $this->client->request(Request::METHOD_GET, '/reset-password');
+
+ $this->assertResponseIsSuccessful();
+ $this->assertSelectorExists('form');
+ }
+
+ public function testRequestWithUnknownEmailRedirectsToCheckEmail(): void
+ {
+ $this->client->request(Request::METHOD_GET, '/reset-password');
+ $form = $this->client->getCrawler()->filter('form')->form([
+ 'reset_password_request_form[email]' => 'unknown@example.org',
+ ]);
+ $this->client->submit($form);
+
+ $this->assertResponseRedirects('/reset-password/check-email');
+ }
+
+ public function testRequestWithKnownEmailRedirectsToCheckEmail(): void
+ {
+ $this->client->request(Request::METHOD_GET, '/reset-password');
+ $form = $this->client->getCrawler()->filter('form')->form([
+ 'reset_password_request_form[email]' => 'test@example.org',
+ ]);
+ $this->client->submit($form);
+
+ $this->assertResponseRedirects('/reset-password/check-email');
+ }
+
+ public function testCheckEmailPageLoads(): void
+ {
+ $this->client->request(Request::METHOD_GET, '/reset-password/check-email');
+
+ $this->assertResponseIsSuccessful();
+ }
+
+ public function testResetWithInvalidTokenRedirectsToRequest(): void
+ {
+ $this->client->request(Request::METHOD_GET, '/reset-password/reset/invalidtoken');
+ $this->client->followRedirect();
+
+ $this->assertResponseRedirects('/reset-password');
+ }
+
+ public function testFullResetFlow(): void
+ {
+ $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => 'test@example.org']);
+ $this->assertInstanceOf(User::class, $user);
+
+ /** @var ResetPasswordHelperInterface $helper */
+ $helper = self::getContainer()->get(ResetPasswordHelperInterface::class);
+ $resetToken = $helper->generateResetToken($user);
+
+ $this->client->request(Request::METHOD_GET, '/reset-password/reset/'.$resetToken->getToken());
+ $this->assertResponseRedirects('/reset-password/reset');
+
+ $this->client->followRedirect();
+ $this->assertResponseIsSuccessful();
+
+ $form = $this->client->getCrawler()->filter('form')->form([
+ 'change_password_form[plainPassword][first]' => 'NewPass123!',
+ 'change_password_form[plainPassword][second]' => 'NewPass123!',
+ ]);
+ $this->client->submit($form);
+ $this->assertResponseRedirects('/backoffice/');
+
+ $this->entityManager->clear();
+ $updatedUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => 'test@example.org']);
+ $this->assertInstanceOf(User::class, $updatedUser);
+
+ $hasher = self::getContainer()->get(UserPasswordHasherInterface::class);
+ $this->assertTrue($hasher->isPasswordValid($updatedUser, 'NewPass123!'));
+ }
+}
diff --git a/translations/ResetPasswordBundle.nl.xliff b/translations/ResetPasswordBundle.nl.xliff
new file mode 100644
index 0000000..ee3c386
--- /dev/null
+++ b/translations/ResetPasswordBundle.nl.xliff
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+ %count% day|%count% days
+ %count% dag|%count% dagen
+
+
+ %count% hour|%count% hours
+ %count% uur|%count% uren
+
+
+ %count% minute|%count% minutes
+ %count% minuut|%count% minuten
+
+
+ %count% month|%count% months
+ %count% maand|%count% maanden
+
+
+ %count% year|%count% years
+ %count% jaar|%count% jaar
+
+
+ Please update the request_password_repository configuration in config/packages/reset_password.yaml to point to your "request password repository" service.
+ Update de request_password_repository waarde in config/packages/reset_password.yaml met een verwijzing naar je "request password repository" service.
+
+
+ The link in your email is expired. Please try to reset your password again.
+ De link in je e-mail is verlopen. Probeer opnieuw je wachtwoord te herstellen.
+
+
+ The reset password link is invalid. Please try to reset your password again.
+ De link om je wachtwoord te herstellen is niet geldig. Probeer opnieuw je wachtwoord te herstellen.
+
+
+ There was a problem handling your password reset request
+ Er is een probleem opgetreden bij het afhandelen van het verzoek om je wachtwoord te herstellen
+
+
+ There was a problem validating your password reset request
+ Er is een probleem opgetreden bij het valideren van het verzoek om je wachtwoord te herstellen
+
+
+ You have already requested a reset password email. Please check your email or try again soon.
+ Je hebt al een verzoek ingediend voor een e-mail om je wachtwoord te herstellen. Controleer of je een e-mail hebt ontvangen of probeer het later nog eens.
+
+
+
+
diff --git a/translations/messages+intl-icu.nl.xliff b/translations/messages+intl-icu.nl.xliff
index e786c53..068365a 100644
--- a/translations/messages+intl-icu.nl.xliff
+++ b/translations/messages+intl-icu.nl.xliff
@@ -109,6 +109,10 @@
Back
Terug
+
+ Back to login
+ Terug naar inloggen
+
Backoffice
Backoffice
@@ -145,6 +149,10 @@
Candidates
Kandidaten
+
+ Check your email
+ Controleer je e-mail
+
Clear Quiz...
Test leegmaken...
@@ -249,6 +257,10 @@
Enter name
Voer een naam in
+
+ Enter your email address, and we will send you a link to reset your password.
+ Voer je e-mailadres in en we sturen je een link om je wachtwoord te herstellen.
+
Enter your name
Voer je naam in
@@ -273,6 +285,10 @@
Finalized
Afgerond
+
+ Forgot your password?
+ Wachtwoord vergeten?
+
Gray
Grijs
@@ -301,6 +317,14 @@
Home
Home
+
+ If an account matching your email exists, then an email was just sent that contains a link that you can use to reset your password.
+ Als er een account met dit e-mailadres bestaat, is er zojuist een e-mail verstuurd met een link om je wachtwoord te herstellen.
+
+
+ If you don't receive an email please check your spam folder or
+ Als je geen e-mail ontvangt, controleer dan je spammap of
+
Import
Importeren
@@ -373,6 +397,10 @@
New label
Nieuw label
+
+ New password
+ Nieuw wachtwoord
+
Next
Volgende
@@ -593,6 +621,14 @@
Repeat Password
Herhaal wachtwoord
+
+ Reset password
+ Wachtwoord herstellen
+
+
+ Reset your password
+ Wachtwoord herstellen
+
Results & Elimination
@@ -629,6 +665,10 @@
Seasons
Seizoenen
+
+ Send password reset email
+ Stuur herstel-e-mail
+
Settings
Instellingen
@@ -689,6 +729,10 @@
There is no active quiz
Er is geen test actief
+
+ This link will expire in %count%.
+ Deze link verloopt over %count%.
+
This question cannot be deleted because it is used in a locked or active quiz
Deze vraag kan niet verwijderd worden omdat die gebruik wordt in een vergrendelde of actieve test
@@ -773,6 +817,14 @@
Your email address has been verified.
Je e-mailadres is geverifieerd.
+
+ Your password reset request
+ Verzoek tot wachtwoordherstel
+
+
+ try again
+ probeer opnieuw
+