Skip to content

Commit 3f4f56c

Browse files
authored
Merge pull request #3672 from magento-tsg/2.2-develop-pr72
[TSG] Backporting for 2.2 (pr72) (2.2)
2 parents eb6946b + 08ced37 commit 3f4f56c

File tree

33 files changed

+952
-36
lines changed

33 files changed

+952
-36
lines changed

app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,14 @@
5151
<actionGroup name="CreateBundleProductForTwoSimpleProductsWithRadioTypeOptions" extends="CreateBundleProductForTwoSimpleProducts">
5252
<selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="Radio Buttons" after="fillOptionTitle" stepKey="selectInputType"/>
5353
</actionGroup>
54+
55+
<actionGroup name="CreateBundleProductForOneSimpleProductsWithRadioTypeOption" extends="CreateBundleProductForTwoSimpleProducts">
56+
<remove keyForRemoval="clickOnFiltersButton2"/>
57+
<remove keyForRemoval="clearFilters2"/>
58+
<remove keyForRemoval="fillNameFilter2"/>
59+
<remove keyForRemoval="applyFilters2"/>
60+
<remove keyForRemoval="selectSecondSimple"/>
61+
<remove keyForRemoval="fillDefaultQuantityForSecondProduct"/>
62+
<selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="Radio Buttons" after="fillOptionTitle" stepKey="selectInputType"/>
63+
</actionGroup>
5464
</actionGroups>

app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,8 @@
1616
<element name="addProductsToOption" type="button" selector="[data-index='modal_set']" timeout="30"/>
1717
<element name="bundleOptionXProductYQuantity" type="input" selector="[name='bundle_options[bundle_options][{{x}}][bundle_selections][{{y}}][selection_qty]']" parameterized="true"/>
1818
<element name="bundledItems" type="block" selector="[data-index=bundle-items]"/>
19+
<element name="priceTypeSwitcher" type="button" selector="div[data-index='price_type'] div[data-role='switcher']" timeout="30"/>
20+
<element name="bundleSelectionPriceType" type="select" selector="bundle_options[bundle_options][0][bundle_selections][0][selection_price_type]"/>
21+
<element name="bundleSelectionPriceValue" type="input" selector="bundle_options[bundle_options][0][bundle_selections][0][selection_price_value]"/>
1922
</section>
2023
</sections>

app/code/Magento/CacheInvalidate/Model/PurgeCache.php

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
use Magento\Framework\Cache\InvalidateLogger;
99

10+
/**
11+
* Purge cache action.
12+
*/
1013
class PurgeCache
1114
{
1215
const HEADER_X_MAGENTO_TAGS_PATTERN = 'X-Magento-Tags-Pattern';
@@ -26,6 +29,18 @@ class PurgeCache
2629
*/
2730
private $logger;
2831

32+
/**
33+
* Batch size of the purge request.
34+
*
35+
* Based on default Varnish 4 http_req_hdr_len size minus a 512 bytes margin for method,
36+
* header name, line feeds etc.
37+
*
38+
* @see https://varnish-cache.org/docs/4.1/reference/varnishd.html
39+
*
40+
* @var int
41+
*/
42+
private $requestSize = 7680;
43+
2944
/**
3045
* Constructor
3146
*
@@ -44,18 +59,68 @@ public function __construct(
4459
}
4560

4661
/**
47-
* Send curl purge request
48-
* to invalidate cache by tags pattern
62+
* Send curl purge request to invalidate cache by tags pattern.
4963
*
5064
* @param string $tagsPattern
5165
* @return bool Return true if successful; otherwise return false
5266
*/
5367
public function sendPurgeRequest($tagsPattern)
5468
{
69+
$successful = true;
5570
$socketAdapter = $this->socketAdapterFactory->create();
5671
$servers = $this->cacheServer->getUris();
57-
$headers = [self::HEADER_X_MAGENTO_TAGS_PATTERN => $tagsPattern];
5872
$socketAdapter->setOptions(['timeout' => 10]);
73+
74+
$formattedTagsChunks = $this->splitTags($tagsPattern);
75+
foreach ($formattedTagsChunks as $formattedTagsChunk) {
76+
if (!$this->sendPurgeRequestToServers($socketAdapter, $servers, $formattedTagsChunk)) {
77+
$successful = false;
78+
}
79+
}
80+
81+
return $successful;
82+
}
83+
84+
/**
85+
* Split tags by batches
86+
*
87+
* @param string $tagsPattern
88+
* @return \Generator
89+
*/
90+
private function splitTags(string $tagsPattern) : \Generator
91+
{
92+
$tagsBatchSize = 0;
93+
$formattedTagsChunk = [];
94+
$formattedTags = explode('|', $tagsPattern);
95+
foreach ($formattedTags as $formattedTag) {
96+
if ($tagsBatchSize + strlen($formattedTag) > $this->requestSize - count($formattedTagsChunk) - 1) {
97+
yield implode('|', $formattedTagsChunk);
98+
$formattedTagsChunk = [];
99+
$tagsBatchSize = 0;
100+
}
101+
102+
$tagsBatchSize += strlen($formattedTag);
103+
$formattedTagsChunk[] = $formattedTag;
104+
}
105+
if (!empty($formattedTagsChunk)) {
106+
yield implode('|', $formattedTagsChunk);
107+
}
108+
}
109+
110+
/**
111+
* Send curl purge request to servers to invalidate cache by tags pattern.
112+
*
113+
* @param \Zend\Http\Client\Adapter\Socket $socketAdapter
114+
* @param \Zend\Uri\Uri[] $servers
115+
* @param string $formattedTagsChunk
116+
* @return bool Return true if successful; otherwise return false
117+
*/
118+
private function sendPurgeRequestToServers(
119+
\Zend\Http\Client\Adapter\Socket $socketAdapter,
120+
array $servers,
121+
string $formattedTagsChunk
122+
): bool {
123+
$headers = [self::HEADER_X_MAGENTO_TAGS_PATTERN => $formattedTagsChunk];
59124
foreach ($servers as $server) {
60125
$headers['Host'] = $server->getHost();
61126
try {
@@ -69,12 +134,13 @@ public function sendPurgeRequest($tagsPattern)
69134
$socketAdapter->read();
70135
$socketAdapter->close();
71136
} catch (\Exception $e) {
72-
$this->logger->critical($e->getMessage(), compact('server', 'tagsPattern'));
137+
$this->logger->critical($e->getMessage(), compact('server', 'formattedTagsChunk'));
138+
73139
return false;
74140
}
75141
}
142+
$this->logger->execute(compact('servers', 'formattedTagsChunk'));
76143

77-
$this->logger->execute(compact('servers', 'tagsPattern'));
78144
return true;
79145
}
80146
}

app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
-->
88

99
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10-
xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd">
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
1111
<!--Filter the product grid by new from date filter-->
1212
<actionGroup name="filterProductGridBySetNewFromDate">
1313
<conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/>
@@ -33,6 +33,7 @@
3333
<actionGroup name="deleteProductUsingProductGrid">
3434
<arguments>
3535
<argument name="product"/>
36+
<argument name="productCount" type="string" defaultValue="1"/>
3637
</arguments>
3738
<!--TODO use other action group for filtering grid when MQE-539 is implemented -->
3839
<amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/>
@@ -48,7 +49,7 @@
4849
<click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/>
4950
<waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/>
5051
<click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/>
51-
<see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMessage"/>
52+
<see selector="{{AdminMessagesSection.success}}" userInput="A total of {{productCount}} record(s) have been deleted." stepKey="seeSuccessMessage"/>
5253
<conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/>
5354
</actionGroup>
5455

app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
-->
88

99
<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10-
xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd">
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/dataProfileSchema.xsd">
1111
<entity name="colorProductAttribute" type="product_attribute">
1212
<data key="default_label" unique="suffix">Color</data>
1313
<data key="attribute_quantity">1</data>
14+
<data key="input_type">Dropdown</data>
1415
</entity>
1516
<entity name="colorProductAttribute1" type="product_attribute">
1617
<data key="name" unique="suffix">White</data>

app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,5 +188,6 @@
188188
</section>
189189
<section name="AdminChooseAffectedAttributeSetPopup">
190190
<element name="confirm" type="button" selector="button[data-index='confirm_button']" timeout="30"/>
191+
<element name="closePopUp" type="button" selector=".modal-popup._show [data-role='closeBtn']" timeout="30"/>
191192
</section>
192193
</sections>

app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,19 @@ public function execute(\Magento\Framework\Event\Observer $observer)
100100
}
101101

102102
$mapsGenerated = false;
103-
if ($category->dataHasChangedFor('url_key')
104-
|| $category->dataHasChangedFor('is_anchor')
105-
|| $category->getChangedProductIds()
106-
) {
103+
if ($this->isCategoryHasChanged($category)) {
107104
if ($category->dataHasChangedFor('url_key')) {
108105
$categoryUrlRewriteResult = $this->categoryUrlRewriteGenerator->generate($category);
109106
$this->urlRewriteBunchReplacer->doBunchReplace($categoryUrlRewriteResult);
110107
}
111-
$productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category);
112-
$this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult);
108+
if ($this->isChangedOnlyProduct($category)) {
109+
$productUrlRewriteResult =
110+
$this->urlRewriteHandler->updateProductUrlRewritesForChangedProduct($category);
111+
$this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult);
112+
} else {
113+
$productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category);
114+
$this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult);
115+
}
113116
$mapsGenerated = true;
114117
}
115118

@@ -120,8 +123,42 @@ public function execute(\Magento\Framework\Event\Observer $observer)
120123
}
121124

122125
/**
123-
* in case store_id is not set for category then we can assume that it was passed through product import.
124-
* store group must have only one root category, so receiving category's path and checking if one of it parts
126+
* Check is category changed changed.
127+
*
128+
* @param Category $category
129+
* @return bool
130+
*/
131+
private function isCategoryHasChanged(Category $category): bool
132+
{
133+
if ($category->dataHasChangedFor('url_key')
134+
|| $category->dataHasChangedFor('is_anchor')
135+
|| !empty($category->getChangedProductIds())) {
136+
return true;
137+
}
138+
139+
return false;
140+
}
141+
142+
/**
143+
* Check is only product changed.
144+
*
145+
* @param Category $category
146+
* @return bool
147+
*/
148+
private function isChangedOnlyProduct(Category $category): bool
149+
{
150+
if (!empty($category->getChangedProductIds())
151+
&& !$category->dataHasChangedFor('is_anchor')
152+
&& !$category->dataHasChangedFor('url_key')) {
153+
return true;
154+
}
155+
156+
return false;
157+
}
158+
159+
/**
160+
* In case store_id is not set for category then we can assume that it was passed through product import.
161+
* Store group must have only one root category, so receiving category's path and checking if one of it parts
125162
* is the root category for store group, we can set default_store_id value from it to category.
126163
* it prevents urls duplication for different stores
127164
* ("Default Category/category/sub" and "Default Category2/category/sub")

app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
2525

2626
/**
27+
* Class for management url rewrites.
28+
*
2729
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2830
*/
2931
class UrlRewriteHandler
@@ -125,7 +127,7 @@ public function generateProductUrlRewrites(Category $category): array
125127
{
126128
$mergeDataProvider = clone $this->mergeDataProviderPrototype;
127129
$this->isSkippedProduct[$category->getEntityId()] = [];
128-
$saveRewriteHistory = $category->getData('save_rewrites_history');
130+
$saveRewriteHistory = (bool)$category->getData('save_rewrites_history');
129131
$storeId = (int)$category->getStoreId();
130132

131133
if ($category->getChangedProductIds()) {
@@ -156,6 +158,30 @@ public function generateProductUrlRewrites(Category $category): array
156158
}
157159

158160
/**
161+
* Update product url rewrites for changed product.
162+
*
163+
* @param Category $category
164+
* @return array
165+
*/
166+
public function updateProductUrlRewritesForChangedProduct(Category $category): array
167+
{
168+
$mergeDataProvider = clone $this->mergeDataProviderPrototype;
169+
$this->isSkippedProduct[$category->getEntityId()] = [];
170+
$saveRewriteHistory = (bool)$category->getData('save_rewrites_history');
171+
$storeIds = $this->getCategoryStoreIds($category);
172+
173+
if ($category->getChangedProductIds()) {
174+
foreach ($storeIds as $storeId) {
175+
$this->generateChangedProductUrls($mergeDataProvider, $category, (int)$storeId, $saveRewriteHistory);
176+
}
177+
}
178+
179+
return $mergeDataProvider->getData();
180+
}
181+
182+
/**
183+
* Delete category rewrites for children.
184+
*
159185
* @param Category $category
160186
* @return void
161187
*/
@@ -184,6 +210,8 @@ public function deleteCategoryRewritesForChildren(Category $category)
184210
}
185211

186212
/**
213+
* Get category products url rewrites.
214+
*
187215
* @param Category $category
188216
* @param int $storeId
189217
* @param bool $saveRewriteHistory
@@ -230,15 +258,15 @@ private function getCategoryProductsUrlRewrites(
230258
*
231259
* @param MergeDataProvider $mergeDataProvider
232260
* @param Category $category
233-
* @param Product $product
234261
* @param int $storeId
235-
* @param $saveRewriteHistory
262+
* @param bool $saveRewriteHistory
263+
* @return void
236264
*/
237265
private function generateChangedProductUrls(
238266
MergeDataProvider $mergeDataProvider,
239267
Category $category,
240268
int $storeId,
241-
$saveRewriteHistory
269+
bool $saveRewriteHistory
242270
) {
243271
$this->isSkippedProduct[$category->getEntityId()] = $category->getAffectedProductIds();
244272

0 commit comments

Comments
 (0)