diff --git a/src/Controller/Backoffice/SeasonController.php b/src/Controller/Backoffice/SeasonController.php index fd1dbbc..22fc715 100644 --- a/src/Controller/Backoffice/SeasonController.php +++ b/src/Controller/Backoffice/SeasonController.php @@ -10,10 +10,12 @@ use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormError; use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsCsrfTokenValid; use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; @@ -97,6 +99,24 @@ class SeasonController extends AbstractController ]); } + #[IsCsrfTokenValid('regenerate_season_code')] + #[IsGranted(SeasonVoter::EDIT, subject: 'season')] + #[Route( + '/backoffice/season/{seasonCode:season}/settings/regenerate-code', + name: 'tvdt_backoffice_season_regenerate_code', + requirements: ['seasonCode' => self::SEASON_CODE_REGEX], + methods: ['POST'], + )] + public function regenerateSeasonCode(Season $season): RedirectResponse + { + $season->generateSeasonCode(); + $this->em->flush(); + + $this->addFlash(FlashType::Success, $this->translator->trans('Season code regenerated')); + + return $this->redirectToRoute('tvdt_backoffice_season_settings', ['seasonCode' => $season->seasonCode]); + } + #[IsGranted(SeasonVoter::EDIT, subject: 'season')] #[Route( '/backoffice/season/{seasonCode:season}/add-candidate', diff --git a/templates/backoffice/help/nl/season_settings.html.twig b/templates/backoffice/help/nl/season_settings.html.twig index 9b86a80..856655a 100644 --- a/templates/backoffice/help/nl/season_settings.html.twig +++ b/templates/backoffice/help/nl/season_settings.html.twig @@ -1,3 +1,4 @@
Seizoensinstellingen

Pas hier de weergave-instellingen van dit seizoen aan.

Nummers tonen: toont vraagnummers tijdens de test. Antwoord bevestigen: vraagt kandidaten om hun antwoord te bevestigen voordat ze doorgaan.

+

Seizoenscode: de code waarmee kandidaten dit seizoen kunnen vinden. Genereer een nieuwe code als de huidige per ongeluk gedeeld is, de oude code werkt daarna niet meer.

diff --git a/templates/backoffice/season/tab_settings.html.twig b/templates/backoffice/season/tab_settings.html.twig index 160e858..f3c58c9 100644 --- a/templates/backoffice/season/tab_settings.html.twig +++ b/templates/backoffice/season/tab_settings.html.twig @@ -1,8 +1,40 @@
{{ form(form) }} + +
+ +

{{ 'Season code'|trans }}

+

{{ 'The season code is used by candidates to join this season. Regenerating it invalidates the current code, so make sure to share the new one.'|trans }}

+

{{ 'Current code:'|trans }} {{ season.seasonCode }}

+
{{ include('backoffice/help/season_settings.html.twig') }}
+ + diff --git a/tests/Controller/Backoffice/SeasonControllerTest.php b/tests/Controller/Backoffice/SeasonControllerTest.php new file mode 100644 index 0000000..12922c3 --- /dev/null +++ b/tests/Controller/Backoffice/SeasonControllerTest.php @@ -0,0 +1,70 @@ +client = self::createClient(); + $this->entityManager = self::getContainer()->get(EntityManagerInterface::class); + + $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => 'krtek-admin@example.org']); + $this->assertInstanceOf(User::class, $user); + $this->client->loginUser($user); + } + + public function testRegenerateSeasonCodeChangesTheCode(): void + { + $oldCode = 'krtek'; + $crawler = $this->client->request(Request::METHOD_GET, \sprintf('/backoffice/season/%s/settings', $oldCode)); + self::assertResponseIsSuccessful(); + + $token = (string) $crawler->filter('form[action*="/regenerate-code"] input[name="_token"]')->first()->attr('value'); + + $this->client->request(Request::METHOD_POST, \sprintf('/backoffice/season/%s/settings/regenerate-code', $oldCode), [ + '_token' => $token, + ]); + + self::assertResponseRedirects(); + $this->entityManager->clear(); + + $this->assertNotInstanceOf(Season::class, $this->entityManager->getRepository(Season::class)->findOneBy(['seasonCode' => $oldCode])); + + $location = (string) $this->client->getResponse()->headers->get('Location'); + $this->assertMatchesRegularExpression('#^/backoffice/season/[a-z]{5}/settings$#', $location); + } + + public function testRegenerateSeasonCodeIsDeniedForNonOwner(): void + { + $crawler = $this->client->request(Request::METHOD_GET, '/backoffice/season/krtek/settings'); + self::assertResponseIsSuccessful(); + $token = (string) $crawler->filter('form[action*="/regenerate-code"] input[name="_token"]')->first()->attr('value'); + + $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => 'test@example.org']); + $this->assertInstanceOf(User::class, $user); + $this->client->loginUser($user); + + $this->client->request(Request::METHOD_POST, '/backoffice/season/krtek/settings/regenerate-code', [ + '_token' => $token, + ]); + + self::assertResponseStatusCodeSame(403); + } +} diff --git a/translations/messages+intl-icu.nl.xliff b/translations/messages+intl-icu.nl.xliff index b077c92..0628e58 100644 --- a/translations/messages+intl-icu.nl.xliff +++ b/translations/messages+intl-icu.nl.xliff @@ -221,6 +221,10 @@ Create an empty quiz and add questions from the question bank. Maak een lege quiz aan en voeg vragen toe vanuit de vragenbank. + + Current code: + Huidige code: + Current email address: Huidig e-mailadres: @@ -669,6 +673,14 @@ Refresh the page to try again. Ververs de pagina om het opnieuw te proberen. + + Regenerate season code + Seizoenscode opnieuw genereren + + + Regenerate season code... + Seizoenscode opnieuw genereren... + Register Registreren @@ -729,6 +741,14 @@ Season Name Seizoennaam + + Season code + Seizoenscode + + + Season code regenerated + Seizoenscode opnieuw gegenereerd + Seasons Seizoenen @@ -769,6 +789,14 @@ Sync latest changes to this quiz Laatste wijzigingen synchroniseren naar deze quiz + + The confirmation email could not be sent. Please try again later. + De bevestigingsmail kon niet worden verzonden. Probeer het later opnieuw. + + + The confirmation email could not be sent. Please use the resend button to try again. + De bevestigingsmail kon niet worden verzonden. Gebruik de knop om het opnieuw te proberen. + The password fields must match. De wachtwoorden moeten overeen komen. @@ -793,6 +821,10 @@ The quiz must be finalized before it can be activated De test moet afgerond zijn voordat deze geactiveerd kan worden + + The season code is used by candidates to join this season. Regenerating it invalidates the current code, so make sure to share the new one. + De seizoenscode wordt door kandidaten gebruikt om dit seizoen te vinden. Als je een nieuwe code genereert, werkt de oude niet meer, dus deel de nieuwe code opnieuw. + There are no answers for this question Er zijn geen antwoorden voor deze vraag @@ -809,6 +841,10 @@ This deletes your account and every season you are the only owner of. Enter your password to confirm. Dit verwijdert je account en alle seizoenen waarvan jij de enige eigenaar bent. Vul je wachtwoord in om te bevestigen. + + This invalidates the current season code. Anyone using the old code will no longer be able to join this season. + Hiermee wordt de huidige seizoenscode ongeldig. Iedereen die de oude code gebruikt, kan dit seizoen niet meer vinden. + This link will expire in %count%. Deze link verloopt over %count%. @@ -901,18 +937,14 @@ Your data Je gegevens + + Your email address has been changed. + Je e-mailadres is gewijzigd. + Your email address has been changed. Please check your inbox to confirm it. Je e-mailadres is gewijzigd. Check je inbox om het te bevestigen. - - The confirmation email could not be sent. Please use the resend button to try again. - De bevestigingsmail kon niet worden verzonden. Gebruik de knop om het opnieuw te proberen. - - - The confirmation email could not be sent. Please try again later. - De bevestigingsmail kon niet worden verzonden. Probeer het later opnieuw. - Your email address has been verified. Je e-mailadres is geverifieerd.