Skip to content

Commit

Permalink
Delete logo (#1086)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmarchois authored Nov 25, 2024
1 parent e083497 commit f2cc008
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Application\Organization\Logo\Command;

use App\Application\CommandInterface;
use App\Domain\User\Organization;

final class DeleteOrganizationLogoCommand implements CommandInterface
{
public function __construct(
public readonly Organization $organization,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace App\Application\Organization\Logo\Command;

use App\Application\StorageInterface;

final class DeleteOrganizationLogoCommandHandler
{
public function __construct(
private StorageInterface $storage,
) {
}

public function __invoke(DeleteOrganizationLogoCommand $command): void
{
$organization = $command->organization;
$logo = $organization->getLogo();

if (!$logo) {
return;
}

$this->storage->delete($logo);
$organization->setLogo(null);
}
}
2 changes: 1 addition & 1 deletion src/Domain/User/Organization.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace App\Infrastructure\Controller\MyArea\Organization;

use App\Application\CommandBusInterface;
use App\Application\Organization\Logo\Command\DeleteOrganizationLogoCommand;
use App\Application\QueryBusInterface;
use App\Infrastructure\Security\Voter\OrganizationVoter;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Requirement\Requirement;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Http\Attribute\IsCsrfTokenValid;

final class DeleteOrganizationLogoController extends AbstractOrganizationController
{
public function __construct(
private RouterInterface $router,
private CommandBusInterface $commandBus,
QueryBusInterface $queryBus,
Security $security,
) {
parent::__construct($queryBus, $security);
}

#[Route(
'/organizations/{uuid}/logo/delete',
name: 'app_config_organization_delete_logo',
requirements: ['uuid' => 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,
);
}
}
11 changes: 11 additions & 0 deletions templates/my_area/organization/logo.html.twig
Original file line number Diff line number Diff line change
@@ -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 %}
Expand Down Expand Up @@ -33,6 +36,14 @@
file_button_icon: 'fr-icon-upload-fill',
}) }}
{{ form_end(form) }}
{% if logo %}
<form method="delete" action="{{ path('app_config_organization_delete_logo', { uuid: organization.uuid }) }}">
<button class="fr-mt-2w fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-delete-bin-line">
{{ 'organization.logo.delete'|trans }}
</button>
<input type="hidden" name="_token" value="{{ deleteCsrfToken }}" />
</form>
{% endif %}
</div>
<div class="fr-col-12 fr-col-md-6 fr-pl-md-3w">
<figure>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace App\Tests\Integration\Infrastructure\Controller\MyArea\Organization;

use App\Infrastructure\Persistence\Doctrine\Fixtures\OrganizationFixture;
use App\Tests\Integration\Infrastructure\Controller\AbstractWebTestCase;
use App\Tests\SessionHelper;

final class DeleteOrganizationLogoControllerTest extends AbstractWebTestCase
{
use SessionHelper;

public function testDeleteLogo(): void
{
$client = $this->login('[email protected]');
$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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace App\Tests\Unit\Application\Organization\Logo\Command;

use App\Application\Organization\Logo\Command\DeleteOrganizationLogoCommand;
use App\Application\Organization\Logo\Command\DeleteOrganizationLogoCommandHandler;
use App\Application\StorageInterface;
use App\Domain\User\Organization;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

final class DeleteOrganizationLogoCommandHandlerTest extends TestCase
{
private MockObject $storage;

public function setUp(): void
{
$this->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);
}
}
4 changes: 4 additions & 0 deletions translations/messages.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -2124,6 +2124,10 @@
<source>organization.logo.update</source>
<target>Modifier le logo</target>
</trans-unit>
<trans-unit id="organization.logo.delete">
<source>organization.logo.delete</source>
<target>Supprimer le logo</target>
</trans-unit>
<trans-unit id="organization.siret">
<source>organization.siret</source>
<target>SIRET</target>
Expand Down

0 comments on commit f2cc008

Please sign in to comment.