Skip to content

Commit

Permalink
feature #15026 Allow removing locales (jakubtobiasz)
Browse files Browse the repository at this point in the history
This PR was merged into the 1.13 branch.

Discussion
----------

| Q               | A                                                            |
|-----------------|--------------------------------------------------------------|
| Branch?         | 1.13
| Bug fix?        | no
| New feature?    | yes
| BC breaks?      | no
| Deprecations?   | no
| Related tickets | fixes #10183
| License         | MIT

![CleanShot 2023-06-01 at 15 26 39@2x](https://github.com/Sylius/Sylius/assets/80641364/87961cad-e9af-467c-8a22-40063e925be8)
<img width="560" alt="CleanShot 2023-06-01 at 15 27 21@2x" src="https://github.com/Sylius/Sylius/assets/80641364/3a5ad0ba-cdc0-4799-9904-89bc8badf8d0">

Only unused locales can be removed. If a locale appears in any translation, automatically, it cannot be removed.

Commits
-------

aad14e7 Create LocaleUsageChecker service
1cbe3a9 Add Behat scenario covering removing a locale
1c4b55e Implement RemoveLocale command
f71029f Fix CI
343c2ed Provide improvements after code review
7b83097 Add a delete locale API endpoint
2af0ee8 Remove RemoveLocale CLI command
3533434 Cover removing a locale via API scenarios
388d0ee Add contract tests for removing a locale
4251f06 Implement DeleteLocaleAction in the SyliusAdmin
383220b Create LocaleRemover service
da96a6a Refactor the DeleteLocale action to use the LocaleRemover
7988cbe Refactor the ApiBundle's DeleteLocale action to use the LocaleRemover
11311e3 Implement the UI part of removing the locales
e458531 Adjust Behat scenario for removing a locale
05bf744 Replace custom Action for Locale removing in AP with an event listener
d63d179 Replace custom Action for Locale removing via API with a data persister
3d5cb02 Delete an unused LocaleRemover service
956f861 Apply ECS fixes
fb9da05 Provide post-CR fixes
  • Loading branch information
TheMilek committed Jun 28, 2023
2 parents 23dce64 + fb9da05 commit b600c40
Show file tree
Hide file tree
Showing 24 changed files with 681 additions and 0 deletions.
29 changes: 29 additions & 0 deletions features/locale/managing_locales/removing_locale.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@managing_locales
Feature: Removing locales
In order to delete accidentally created locales
As an Administrator
I want to be able to delete locales

Background:
Given the store operates on a channel named "Web" with hostname "web"
And that channel allows to shop using "English (United States)" and "Polish (Poland)" locales
And it uses the "English (United States)" locale by default
And I am logged in as an administrator

@ui @api
Scenario: Deleting unused locale
Given the store has a product "T-Shirt banana"
And this product is named "Banana T-Shirt with Minions" in the "English (United States)" locale
And this product has no translation in the "Polish (Poland)" locale
When I remove "Polish (Poland)" locale
Then I should be informed that locale "Polish (Poland)" has been deleted
And only the "English (United States)" locale should be present in the system

@ui @api
Scenario: Deleting a locale in use
Given the store has a product "T-Shirt banana"
And this product is named "Banana T-Shirt with Minions" in the "English (United States)" locale
And this product is named "Koszulka Banan z Minionami" in the "Polish (Poland)" locale
When I remove "Polish (Poland)" locale
Then I should be informed that locale "Polish (Poland)" is in use and cannot be deleted
And the "Polish (Poland)" locale should be still present in the system
51 changes: 51 additions & 0 deletions src/Sylius/Behat/Context/Api/Admin/ManagingLocalesContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Sylius\Behat\Client\ApiClientInterface;
use Sylius\Behat\Client\ResponseCheckerInterface;
use Sylius\Behat\Context\Api\Resources;
use Symfony\Component\HttpFoundation\Response;
use Webmozart\Assert\Assert;

final class ManagingLocalesContext implements Context
Expand Down Expand Up @@ -53,6 +54,14 @@ public function iAddIt(): void
$this->client->create();
}

/**
* @When I remove :localeCode locale
*/
public function iRemoveLocale(string $localeCode): void
{
$this->client->delete(Resources::LOCALES, $localeCode);
}

/**
* @Then I should be notified that it has been successfully created
*/
Expand Down Expand Up @@ -115,4 +124,46 @@ public function iShouldBeNotifiedThatTheCodeIsInvalid(): void
);
Assert::same($this->responseChecker->getError($response), 'code: This value is not a valid locale code.');
}

/**
* @Then I should be informed that locale :localeCode has been deleted
*/
public function iShouldBeInformedThatLocaleHasBeenDeleted(string $localeCode): void
{
Assert::same($this->client->getLastResponse()->getStatusCode(), Response::HTTP_NO_CONTENT);
}

/**
* @Then only the :localeCode locale should be present in the system
*/
public function onlyTheLocaleShouldBePresentInTheSystem(string $localeCode): void
{
$response = $this->client->index(Resources::LOCALES);
Assert::true($this->responseChecker->countCollectionItems($response) === 1);
Assert::true(
$this->responseChecker->hasItemWithValue($response, 'code', $localeCode),
sprintf('There is no locale with code "%s"', $localeCode),
);
}

/**
* @Then I should be informed that locale :localeCode is in use and cannot be deleted
*/
public function iShouldBeInformedThatLocaleIsInUseAndCannotBeDeleted(string $localeCode): void
{
Assert::same($this->client->getLastResponse()->getStatusCode(), Response::HTTP_UNPROCESSABLE_ENTITY);
Assert::same($this->responseChecker->getError($this->client->getLastResponse()), sprintf('Locale "%s" is used.', $localeCode));
}

/**
* @Then the :localeCode locale should be still present in the system
*/
public function theLocaleShouldBeStillPresentInTheSystem(string $localeCode): void
{
$response = $this->client->index(Resources::LOCALES);
Assert::true(
$this->responseChecker->hasItemWithValue($response, 'code', $localeCode),
sprintf('There is no locale with code "%s"', $localeCode),
);
}
}
18 changes: 18 additions & 0 deletions src/Sylius/Behat/Context/Setup/ProductContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,16 @@ public function thisProductIsNamedIn(ProductInterface $product, $name, $locale)
$this->objectManager->flush();
}

/**
* @Given /^(this product) has no translation in the "([^"]+)" locale$/
*/
public function thisProductHasNoTranslationIn(ProductInterface $product, $locale): void
{
$product->removeTranslation($product->getTranslation($locale));

$this->objectManager->flush();
}

/**
* @Given /^the store has a product named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
*/
Expand Down Expand Up @@ -1468,6 +1478,14 @@ private function addProductTranslation(ProductInterface $product, $name, $locale
$product->addTranslation($translation);
}

private function removeProductTranslation(ProductInterface $product, $locale): void
{
/** @var ProductTranslationInterface $translation */
$translation = $product->getTranslation($locale);

$product->removeTranslation($translation);
}

/**
* @param string $name
* @param string $locale
Expand Down
53 changes: 53 additions & 0 deletions src/Sylius/Behat/Context/Ui/Admin/ManagingLocalesContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@
namespace Sylius\Behat\Context\Ui\Admin;

use Behat\Behat\Context\Context;
use Sylius\Behat\NotificationType;
use Sylius\Behat\Page\Admin\Crud\IndexPageInterface;
use Sylius\Behat\Page\Admin\Locale\CreatePageInterface;
use Sylius\Behat\Service\NotificationCheckerInterface;
use Sylius\Component\Locale\Converter\LocaleConverterInterface;
use Webmozart\Assert\Assert;

final class ManagingLocalesContext implements Context
{
public function __construct(
private CreatePageInterface $createPage,
private IndexPageInterface $indexPage,
private NotificationCheckerInterface $notificationChecker,
private LocaleConverterInterface $localeConverter,
) {
}

Expand Down Expand Up @@ -51,6 +56,15 @@ public function iAdd()
$this->createPage->create();
}

/**
* @When I remove :localeCode locale
*/
public function iRemoveLocale(string $localeCode): void
{
$this->indexPage->open();
$this->indexPage->deleteResourceOnPage(['code' => $localeCode]);
}

/**
* @Then the store should be available in the :name language
*/
Expand All @@ -68,4 +82,43 @@ public function iShouldNotBeAbleToChoose($name)
{
Assert::false($this->createPage->isOptionAvailable($name));
}

/**
* @Then I should be informed that locale :localeCode has been deleted
*/
public function iShouldBeInformedThatLocaleHasBeenDeleted(string $localeCode): void
{
$this->notificationChecker->checkNotification(
'Locale has been successfully deleted.',
NotificationType::success(),
);
}

/**
* @Then only the :localeCode locale should be present in the system
*/
public function onlyTheLocaleShouldBePresentInTheSystem(string $localeCode): void
{
Assert::true($this->indexPage->isSingleResourceOnPage(['code' => $localeCode]));
Assert::true($this->indexPage->countItems() === 1);
}

/**
* @Then I should be informed that locale :localeCode is in use and cannot be deleted
*/
public function iShouldBeInformedThatLocaleIsInUseAndCannotBeDeleted(string $localeCode): void
{
$this->notificationChecker->checkNotification(
'Cannot delete the locale, as it is used by at least one translation.',
NotificationType::failure(),
);
}

/**
* @Then the :localeCode locale should be still present in the system
*/
public function theLocaleShouldBeStillPresentInTheSystem(string $localeCode): void
{
Assert::true($this->indexPage->isSingleResourceOnPage(['code' => $localeCode]));
}
}
2 changes: 2 additions & 0 deletions src/Sylius/Behat/Resources/config/services/contexts/ui.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@
<service id="sylius.behat.context.ui.admin.managing_locales" class="Sylius\Behat\Context\Ui\Admin\ManagingLocalesContext">
<argument type="service" id="sylius.behat.page.admin.locale.create" />
<argument type="service" id="sylius.behat.page.admin.locale.index" />
<argument type="service" id="sylius.behat.notification_checker" />
<argument type="service" id="sylius.locale_converter" />
</service>

<service id="sylius.behat.context.ui.admin.managing_orders" class="Sylius\Behat\Context\Ui\Admin\ManagingOrdersContext">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ default:
- sylius.behat.context.setup.admin_api_security
- sylius.behat.context.setup.channel
- sylius.behat.context.setup.locale
- sylius.behat.context.setup.product

- sylius.behat.context.api.admin.managing_locales
filters:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ default:
- sylius.behat.context.setup.channel
- sylius.behat.context.setup.locale
- sylius.behat.context.setup.admin_security
- sylius.behat.context.setup.product

- sylius.behat.context.ui.admin.managing_locales
- sylius.behat.context.ui.admin.managing_translatable_entities
Expand Down
38 changes: 38 additions & 0 deletions src/Sylius/Bundle/AdminBundle/EventListener/LocaleListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\AdminBundle\EventListener;

use Sylius\Bundle\LocaleBundle\Checker\LocaleUsageCheckerInterface;
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
use Sylius\Component\Locale\Model\LocaleInterface;
use Symfony\Component\HttpFoundation\Response;

final class LocaleListener
{
public function __construct(private LocaleUsageCheckerInterface $localeUsageChecker)
{
}

public function preDelete(ResourceControllerEvent $event): void
{
/** @var LocaleInterface $locale */
$locale = $event->getSubject();

if (!$this->localeUsageChecker->isUsed($locale->getCode())) {
return;
}

$event->stop('sylius.locale.delete.is_used', errorCode: Response::HTTP_UNPROCESSABLE_ENTITY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ sylius_grid:
item:
update:
type: update
delete:
type: delete
10 changes: 10 additions & 0 deletions src/Sylius/Bundle/AdminBundle/Resources/config/routing/locale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ sylius_admin_locale:
index:
icon: translate
type: sylius.resource

sylius_admin_locale_delete:
path: /locales/{id}
methods: [DELETE]
defaults:
_controller: sylius.controller.locale::deleteAction
_sylius:
section: admin
redirect: referer
permission: true
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
<tag name="kernel.event_listener" event="sylius.shipment.post_ship" method="sendConfirmationEmail" />
</service>

<service id="sylius.listener.locale" class="Sylius\Bundle\AdminBundle\EventListener\LocaleListener">
<argument type="service" id="Sylius\Bundle\LocaleBundle\Checker\LocaleUsageCheckerInterface" />

<tag name="kernel.event_listener" event="sylius.locale.pre_delete" method="preDelete" />
</service>

<service id="sylius.event_subscriber.resource_delete" class="Sylius\Bundle\AdminBundle\EventListener\ResourceDeleteSubscriber">
<argument type="service" id="router" />
<argument type="service" id="request_stack" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ sylius:
shipment_confirmation_resent: 'Shipment confirmation has been successfully resent to the customer.'
product_variant:
cannot_generate_variants: 'Cannot generate variants for a product without options values.'
locale:
delete:
is_used: 'Cannot delete the locale, as it is used by at least one translation.'
success: 'Locale has been successfully deleted.'
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace spec\Sylius\Bundle\AdminBundle\EventListener;

use PhpSpec\ObjectBehavior;
use Sylius\Bundle\LocaleBundle\Checker\LocaleUsageCheckerInterface;
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
use Sylius\Component\Locale\Model\LocaleInterface;
use Symfony\Component\HttpFoundation\Response;

final class LocaleListenerSpec extends ObjectBehavior
{
function let(LocaleUsageCheckerInterface $localeUsageChecker): void
{
$this->beConstructedWith($localeUsageChecker);
}

function it_does_nothing_if_locale_is_not_used(
LocaleUsageCheckerInterface $localeUsageChecker,
LocaleInterface $locale,
ResourceControllerEvent $event,
): void {
$localeUsageChecker->isUsed('en_US')->willReturn(false);

$locale->getCode()->willReturn('en_US');

$event->getSubject()->willReturn($locale);
$event->stop()->shouldNotBeCalled();

$this->preDelete($event);
}

function it_stops_event_if_locale_is_used(
LocaleUsageCheckerInterface $localeUsageChecker,
LocaleInterface $locale,
ResourceControllerEvent $event,
): void {
$localeUsageChecker->isUsed('en_US')->willReturn(true);

$locale->getCode()->willReturn('en_US');

$event->getSubject()->willReturn($locale);
$event->stop('sylius.locale.delete.is_used', 'error', [], Response::HTTP_UNPROCESSABLE_ENTITY)->shouldBeCalled();

$this->preDelete($event);
}
}

0 comments on commit b600c40

Please sign in to comment.