Skip to content

Commit

Permalink
bug #15139 Fix "Show product in the shop page" button (mamazu, jakubt…
Browse files Browse the repository at this point in the history
…obiasz, GSadee)

This PR was merged into the 1.12 branch.

Discussion
----------

| Q               | A                                                            |
|-----------------|--------------------------------------------------------------|
| Branch?         | 1.12
| Bug fix?        | yes
| New feature?    | no
| BC breaks?      | no
| Deprecations?   | no
| Related tickets | replaces #14663
| License         | MIT


Commits
-------

d24183f Adding default if product has no slug
98d853e Adding try logic to get the shop's slug
9856b1e Refactor logic behind a button redirecting to the product's shop page
a394383 Add support for secure and unsecured URLs for generating product show page
60000de Remove unknown localeCode named paramter
bfb011d Allow passing a desired locale code to DefaultChannelFactory
cf86d24 Provide post-CR fixes
ae721b5 Refactor to provide a product translation instead of generate product shop url
  • Loading branch information
TheMilek committed Jul 7, 2023
2 parents f608775 + ae721b5 commit 00525c4
Show file tree
Hide file tree
Showing 21 changed files with 500 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ In order to check a product in shop in all channels it is available in
And the store has a product "Bugatti" available in "United States" channel
And I am logged in as an administrator

@ui
@ui @no-api
Scenario: Accessing product show page in shop from the product variant edit page where product is available in more than one channel
Given this product is also available in the "Europe" channel
And this product has "Red" variant priced at "$220000.00" in "Europe" channel
When I want to modify the "Bugatti" product variant
And I choose to show this product in the "Europe" channel
Then I should see this product in the "Europe" channel in the shop

@ui
@ui @no-api
Scenario: Accessing product show page in shop from the product variant edit page where product is available in one channel
Given this product has "Red" variant priced at "$220000.00" in "United States" channel
When I want to modify the "Bugatti" product variant
And I choose to show this product in this channel
Then I should see this product in the "United States" channel in the shop

@ui
@ui @no-api
Scenario: Being unable to access product show page in shop when the product is disabled
Given this product has been disabled
When I want to modify the "Bugatti" product variant
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@managing_products
Feature: Accessing a store's product page
In order to access a store's product page from the admin panel
As an Administrator
I want to have a way to be redirected to the store's product page from the admin panel

Background:
Given the store operates on a single channel in "German (Germany)" locale
And the store has a product "Französischer Bulldoggen T-Shirt"
And I am logged in as an administrator
And I am using "Polish (Poland)" locale for my panel

@ui @no-api
Scenario: Opening a product's page from the admin panel when the product has a translation with a defined slug in the administrator's chosen language
Given the locale "Polish (Poland)" is enabled
And this product is named "Bulldog francuski T-Shirt" in the "Polish (Poland)" locale
When I want to edit this product
Then the show product's page button should be enabled
And it should be leading to the product's page in the "Polish (Poland)" locale

@ui @no-api
Scenario: Opening a product's page from the admin panel when the product has a translation with a defined slug in the default channel's language
Given the locale "French (France)" is enabled
And this product is named "Tee-shirt bouledogue français" in the "French (France)" locale
When I want to edit this product
Then the show product's page button should be enabled
And it should be leading to the product's page in the "German (Germany)" locale

@ui @no-api
Scenario: Opening a product's page from the admin panel with using first available locale with slug and enabled in the channel
Given the locale "French (France)" is enabled
And the store also operates in "French (France)" locale
And this product has no slug in the "German (Germany)" locale
And this product is named "T-shirt bouledogue français" in the "French (France)" locale
When I want to edit this product
Then it should be leading to the product's page in the "French (France)" locale

@ui @no-api
Scenario: Not being able to open a product's page from the admin panel when the product has no translations meeting the criteria
Given this product has no translations with a defined slug
When I want to edit this product
Then the show product's page button should be disabled
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ Feature: Checking products in the shop while viewing them
And I am logged in as an administrator
And I am browsing products

@ui
@ui @no-api
Scenario: Accessing product show page in shop from the product show page where product is available in more than one channel
Given this product is available in the "Europe" channel
When I access "Bugatti" product page
And I show this product in the "Europe" channel
Then I should see this product in the "Europe" channel in the shop

@ui
@ui @no-api
Scenario: Accessing product show page in shop from the product show page where product is available in one channel
When I access "Bugatti" product page
And I show this product in this channel
Then I should see this product in the "Europe" channel in the shop

@ui
@ui @no-api
Scenario: Being unable to access product show page in shop when the product is disabled
Given this product has been disabled
When I access "Bugatti" product page
Expand Down
26 changes: 26 additions & 0 deletions src/Sylius/Behat/Context/Setup/ChannelContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
use Sylius\Component\Core\Model\ShopBillingData;
use Sylius\Component\Core\Model\TaxonInterface;
use Sylius\Component\Core\Test\Services\DefaultChannelFactoryInterface;
use Sylius\Component\Locale\Model\LocaleInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;

final class ChannelContext implements Context
{
Expand All @@ -35,6 +37,7 @@ public function __construct(
private DefaultChannelFactoryInterface $defaultChannelFactory,
private ChannelRepositoryInterface $channelRepository,
private ObjectManager $channelManager,
private RepositoryInterface $localeRepository,
) {
}

Expand Down Expand Up @@ -83,6 +86,17 @@ public function storeOperatesOnASingleChannel($currencyCode = null)
$this->sharedStorage->set('channel', $defaultData['channel']);
}

/**
* @Given the store operates on a single channel in :localeCode locale
*/
public function storeOperatesOnASingleChannelInLocale(string $localeCode): void
{
$defaultData = $this->defaultChannelFactory->create(localeCode: $localeCode);

$this->sharedStorage->setClipboard($defaultData);
$this->sharedStorage->set('channel', $defaultData['channel']);
}

/**
* @Given /^the store(?:| also) operates on (?:a|another) channel named "([^"]+)"$/
* @Given /^the store(?:| also) operates on (?:a|another) channel named "([^"]+)" in "([^"]+)" currency$/
Expand Down Expand Up @@ -263,6 +277,18 @@ public function itsRequiredAddressInTheCheckoutIs(string $type): void
$this->channelManager->flush();
}

/**
* @Given the store also operates in :locale locale
*/
public function theStoreAlsoOperatesInLocale(LocaleInterface $locale): void
{
/** @var ChannelInterface $channel */
$channel = $this->sharedStorage->get('channel');
$channel->addLocale($locale);

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

/**
* @param bool $state
*/
Expand Down
24 changes: 24 additions & 0 deletions src/Sylius/Behat/Context/Setup/ProductContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,30 @@ public function thisProductIsConfiguredWithTheOptionMatchingSelectionMethod(Prod
$this->saveProduct($product);
}

/**
* @Given /^(this product) has no slug in the ("[^"]+" locale)$/
*/
public function thisProductHasNoSlugInTheLocale(ProductInterface $product, string $localeCode): void
{
$productTranslation = $product->getTranslation($localeCode);
$productTranslation->setSlug('');

$this->saveProduct($product);
}

/**
* @Given /^(this product) has no translations with a defined slug$/
*/
public function thisProductHasNoTranslationsWithADefinedSlug(ProductInterface $product): void
{
/** @var ProductTranslationInterface $productTranslation */
foreach ($product->getTranslations() as $productTranslation) {
$productTranslation->setSlug('');
}

$this->saveProduct($product);
}

private function getPriceFromString(string $price): int
{
return (int) round((float) str_replace(['€', '£', '$'], '', $price) * 100, 2);
Expand Down
31 changes: 31 additions & 0 deletions src/Sylius/Behat/Context/Ui/Admin/ManagingProductsContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ public function productShouldExistInTheProductCatalog(ProductInterface $product)
/**
* @When I want to modify the :product product
* @When /^I want to modify (this product)$/
* @When /^I want to edit (this product)$/
* @When I modify the :product product
*/
public function iWantToModifyAProduct(ProductInterface $product): void
Expand Down Expand Up @@ -1188,6 +1189,36 @@ public function iShouldBeRedirectedToThePreviousFilteredPageWithFilterAndPage(in
Assert::eq($this->indexPage->getPageNumber(), $page);
}

/**
* @Then the show product's page button should be enabled
*/
public function theShowProductsPageButtonShouldBeEnabled(): void
{
Assert::false($this->updateSimpleProductPage->isShowInShopButtonDisabled());
}

/**
* @Then the show product's page button should be disabled
*/
public function theShowProductsPageButtonShouldBeDisabled(): void
{
Assert::true($this->updateSimpleProductPage->isShowInShopButtonDisabled());
}

/**
* @Then /^it should be leading to (the product)'s page in the ("[^"]+" locale)$/
*/
public function itShouldBeLeadingToTheProductPageInTheLocale(ProductInterface $product, string $localeCode): void
{
$productTranslation = $product->getTranslation($localeCode);
$showProductPageUrl = $this->updateSimpleProductPage->getShowProductInSingleChannelUrl();

Assert::contains(
$showProductPageUrl,
sprintf('/%s/products/%s', $localeCode, $productTranslation->getSlug()),
);
}

/**
* @param string $element
* @param string $value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,11 @@ public function hasInventoryTab(): bool
return null !== $this->getDocument()->find('css', '.tab > h3:contains("Inventory")');
}

public function getShowProductInSingleChannelUrl(): string
{
return $this->getElement('show_product_single_button')->getAttribute('href');
}

public function isShowInShopButtonDisabled(): bool
{
return $this->getElement('show_product_single_button')->hasClass('disabled');
Expand Down Expand Up @@ -471,7 +476,7 @@ protected function getDefinedElements(): array
'product_taxons' => '#sylius_product_productTaxons',
'shipping_required' => '#sylius_product_variant_shippingRequired',
'show_product_dropdown' => '.scrolling.menu',
'show_product_single_button' => 'a:contains("Show product in shop page")',
'show_product_single_button' => '[data-test-show-product-in-shop-page]',
'slug' => '#sylius_product_translations_%locale%_slug',
'tab' => '.menu [data-tab="%name%"]',
'taxonomy' => 'a[data-tab="taxonomy"]',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ public function goToVariantGeneration(): void;

public function hasInventoryTab(): bool;

public function getShowProductInSingleChannelUrl(): string;

public function isShowInShopButtonDisabled(): bool;

public function showProductInChannel(string $channel): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<argument type="service" id="sylius.behat.factory.default_channel" />
<argument type="service" id="sylius.repository.channel" />
<argument type="service" id="sylius.manager.channel" />
<argument type="service" id="sylius.repository.locale" />
</service>

<service id="sylius.behat.context.setup.currency" class="Sylius\Behat\Context\Setup\CurrencyContext">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,29 @@

{% if sylius_bundle_loaded_checker('SyliusShopBundle') %}
{% if not product.enabled or enabledChannels|length < 1 %}
<a class="ui labeled icon button disabled" href="#">
<a class="ui labeled icon button disabled" href="#" {{ sylius_test_html_attribute('show-product-in-shop-page') }}>
<i class="angle right icon"></i>
{{ 'sylius.ui.show_product_in_shop_page'|trans }}
</a>
{% elseif enabledChannels|length > 1 %}
<div class="ui floating dropdown labeled icon button">
<div class="ui floating dropdown labeled icon button" {{ sylius_test_html_attribute('show-product-in-shop-page') }}>
<i class="share alternate icon"></i>
<span class="text">
{{ 'sylius.ui.show_product_in_shop_page'|trans }}
</span>
<div class="menu">
<div class="scrolling menu">
{% for channel in enabledChannels %}
{% set url = sylius_channel_url(path('sylius_shop_product_show', {'slug': product.slug, '_locale': channel.defaultLocale.code}), channel) %}
<a href="{{ url|raw }}" class="item" target="_blank">
{% set product_translation = sylius_product_translation(product, channel) %}
{% if product_translation is not null %}
{% set url = sylius_channel_url(path('sylius_shop_product_show', {'slug': product_translation.slug, '_locale': product_translation.locale}), channel) %}
{% endif %}

<a
href="{{ (url is not defined) ? '#' : url|raw }}"
class="item {% if url is not defined %}disabled{% endif %}"
target="_blank"
>
<i class="angle right icon"></i>
{{ 'sylius.ui.show_in'|trans }}
{{ channel.name }} ({{ channel.code }})
Expand All @@ -27,8 +35,17 @@
</div>
{% else %}
{% for channel in enabledChannels %}
{% set url = sylius_channel_url(path('sylius_shop_product_show', {'slug': product.slug, '_locale': channel.defaultLocale.code}), channel) %}
<a class="ui labeled icon button" href="{{ url|raw }}" target="_blank">
{% set product_translation = sylius_product_translation(product, channel) %}
{% if product_translation is not null %}
{% set url = sylius_channel_url(path('sylius_shop_product_show', {'slug': product_translation.slug, '_locale': product_translation.locale}), channel) %}
{% endif %}

<a
class="ui labeled icon button {% if url is not defined %}disabled{% endif %}"
href="{{ (url is not defined) ? '#' : url|raw }}"
target="_blank"
{{ sylius_test_html_attribute('show-product-in-shop-page') }}
>
<i class="angle right icon"></i>
{{ 'sylius.ui.show_product_in_shop_page'|trans }}
</a>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?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\CoreBundle\Provider;

use Doctrine\Common\Collections\Collection;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Model\ProductTranslationInterface;
use Sylius\Component\Locale\Context\LocaleContextInterface;
use Sylius\Component\Locale\Model\LocaleInterface;

final class ChannelBasedProductTranslationProvider implements ChannelBasedProductTranslationProviderInterface
{
public function __construct(private LocaleContextInterface $localeContext)
{
}

public function provide(ProductInterface $product, ChannelInterface $channel): ?ProductTranslationInterface
{
/** @var Collection<array-key, ProductTranslationInterface> $productTranslations */
$productTranslations = $product->getTranslations();

$contextLocaleCode = $this->localeContext->getLocaleCode();
$productTranslation = $this->findTranslationWithSlugForLocales($productTranslations, [$contextLocaleCode]);

if (null !== $productTranslation) {
return $productTranslation;
}

/** @var string $channelDefaultLocaleCode */
$channelDefaultLocaleCode = $channel->getDefaultLocale()->getCode();
$productTranslation = $this->findTranslationWithSlugForLocales($productTranslations, [$channelDefaultLocaleCode]);

if (null !== $productTranslation) {
return $productTranslation;
}

$localesEnabledInChannel = $this->getLocalesCodesEnabledInChannel($channel);
$productTranslation = $this->findTranslationWithSlugForLocales($productTranslations, $localesEnabledInChannel);

return $productTranslation;
}

/**
* @param Collection<array-key, ProductTranslationInterface> $productTranslations
* @param array<string> $localeCodes
*/
private function findTranslationWithSlugForLocales(Collection $productTranslations, array $localeCodes): ?ProductTranslationInterface
{
foreach ($productTranslations as $productTranslation) {
$isLocaleCodeMatching = in_array($productTranslation->getLocale(), $localeCodes);
$isSlugPresent = '' !== $productTranslation->getSlug() && null !== $productTranslation->getSlug();

if ($isLocaleCodeMatching && $isSlugPresent) {
return $productTranslation;
}
}

return null;
}

/** @return array<array-key, string> */
private function getLocalesCodesEnabledInChannel(ChannelInterface $channel): array
{
return $channel->getLocales()->map(function (LocaleInterface $locale): string {
return $locale->getCode();
})->toArray();
}
}
Loading

0 comments on commit 00525c4

Please sign in to comment.