diff --git a/src/Application/Organization/Logo/Command/DeleteOrganizationLogoCommand.php b/src/Application/Organization/Logo/Command/DeleteOrganizationLogoCommand.php new file mode 100644 index 000000000..01489b097 --- /dev/null +++ b/src/Application/Organization/Logo/Command/DeleteOrganizationLogoCommand.php @@ -0,0 +1,16 @@ +organization; + $logo = $organization->getLogo(); + + if (!$logo) { + return; + } + + $this->storage->delete($logo); + $organization->setLogo(null); + } +} diff --git a/src/Domain/User/Organization.php b/src/Domain/User/Organization.php index 002e0866d..c254394fd 100644 --- a/src/Domain/User/Organization.php +++ b/src/Domain/User/Organization.php @@ -44,7 +44,7 @@ public function setSiret(string $siret): self return $this; } - public function setLogo(string $logo): self + public function setLogo(?string $logo): self { $this->logo = $logo; diff --git a/src/Infrastructure/Controller/MyArea/Organization/DeleteOrganizationLogoController.php b/src/Infrastructure/Controller/MyArea/Organization/DeleteOrganizationLogoController.php new file mode 100644 index 000000000..5202accf9 --- /dev/null +++ b/src/Infrastructure/Controller/MyArea/Organization/DeleteOrganizationLogoController.php @@ -0,0 +1,53 @@ + Requirement::UUID], + methods: ['DELETE'], + )] + #[IsCsrfTokenValid('delete-logo')] + public function __invoke(string $uuid): RedirectResponse + { + $organization = $this->getOrganization($uuid); + + if (!$this->security->isGranted(OrganizationVoter::EDIT, $organization)) { + throw new AccessDeniedHttpException(); + } + + $this->commandBus->handle(new DeleteOrganizationLogoCommand($organization)); + + return new RedirectResponse( + url: $this->router->generate('app_config_organization_edit_logo', ['uuid' => $uuid]), + status: Response::HTTP_SEE_OTHER, + ); + } +} diff --git a/templates/my_area/organization/logo.html.twig b/templates/my_area/organization/logo.html.twig index 3ebea6bbc..691532ce3 100644 --- a/templates/my_area/organization/logo.html.twig +++ b/templates/my_area/organization/logo.html.twig @@ -1,5 +1,8 @@ {% extends 'layouts/layout.html.twig' %} + {% set metaTitle = 'organization.logo'|trans %} +{% set deleteCsrfToken = csrf_token('delete-logo') %} + {% block title %} {{ metaTitle }} - {{ parent() }} {% endblock %} @@ -33,6 +36,14 @@ file_button_icon: 'fr-icon-upload-fill', }) }} {{ form_end(form) }} + {% if logo %} +
+ + +
+ {% endif %}
diff --git a/tests/Integration/Infrastructure/Controller/MyArea/Organization/DeleteOrganizationLogoControllerTest.php b/tests/Integration/Infrastructure/Controller/MyArea/Organization/DeleteOrganizationLogoControllerTest.php new file mode 100644 index 000000000..239a99619 --- /dev/null +++ b/tests/Integration/Infrastructure/Controller/MyArea/Organization/DeleteOrganizationLogoControllerTest.php @@ -0,0 +1,55 @@ +login('florimond.manca@beta.gouv.fr'); + $client->request('DELETE', '/mon-espace/organizations/' . OrganizationFixture::OTHER_ORG_ID_2 . '/logo/delete', [ + '_token' => $this->generateCsrfToken($client, 'delete-logo'), + ]); + + $this->assertResponseStatusCodeSame(303); + $client->followRedirect(); + + $this->assertResponseStatusCodeSame(200); + $this->assertRouteSame('app_config_organization_edit_logo'); + } + + public function testNotAdministrator(): void + { + $client = $this->login(); + $client->request('DELETE', '/mon-espace/organizations/' . OrganizationFixture::MAIN_ORG_ID . '/logo/delete', [ + '_token' => $this->generateCsrfToken($client, 'delete-logo'), + ]); + $this->assertResponseStatusCodeSame(403); + } + + public function testOrganizationOrUserNotFound(): void + { + $client = $this->login(); + $client->request('DELETE', '/mon-espace/organizations/f5c1cea8-a61d-43a7-9b5d-4b8c9557c673/logo/delete', [ + '_token' => $this->generateCsrfToken($client, 'delete-logo'), + ]); + $this->assertResponseStatusCodeSame(404); + } + + public function testWithoutAuthenticatedUser(): void + { + $client = static::createClient(); + $client->request('DELETE', '/mon-espace/organizations/' . OrganizationFixture::MAIN_ORG_ID . '/logo/delete', [ + '_token' => $this->generateCsrfToken($client, 'delete-logo'), + ]); + $this->assertResponseRedirects('http://localhost/login', 302); + } +} diff --git a/tests/Unit/Application/Organization/Logo/Command/DeleteOrganizationLogoCommandHandlerTest.php b/tests/Unit/Application/Organization/Logo/Command/DeleteOrganizationLogoCommandHandlerTest.php new file mode 100644 index 000000000..154283bf8 --- /dev/null +++ b/tests/Unit/Application/Organization/Logo/Command/DeleteOrganizationLogoCommandHandlerTest.php @@ -0,0 +1,72 @@ +storage = $this->createMock(StorageInterface::class); + } + + public function testDelete(): void + { + $organization = $this->createMock(Organization::class); + $organization + ->expects(self::once()) + ->method('getLogo') + ->willReturn('/path/to/logo.png'); + + $this->storage + ->expects(self::once()) + ->method('delete') + ->with('/path/to/logo.png'); + + $organization + ->expects(self::once()) + ->method('setLogo') + ->with(null); + + $handler = new DeleteOrganizationLogoCommandHandler( + $this->storage, + ); + $command = new DeleteOrganizationLogoCommand($organization); + + $handler($command); + } + + public function testWithoutFile(): void + { + $organization = $this->createMock(Organization::class); + $organization + ->expects(self::once()) + ->method('getLogo') + ->willReturn(null); + + $this->storage + ->expects(self::never()) + ->method('delete'); + + $organization + ->expects(self::never()) + ->method('setLogo'); + + $handler = new DeleteOrganizationLogoCommandHandler( + $this->storage, + ); + $command = new DeleteOrganizationLogoCommand($organization); + + $handler($command); + } +} diff --git a/translations/messages.fr.xlf b/translations/messages.fr.xlf index 5e74c2d5d..a326a50a5 100644 --- a/translations/messages.fr.xlf +++ b/translations/messages.fr.xlf @@ -2124,6 +2124,10 @@ organization.logo.update Modifier le logo + + organization.logo.delete + Supprimer le logo + organization.siret SIRET