Skip to content

Commit f430b14

Browse files
author
Dmytro Voskoboinikov
committed
Merge branch '2.2-develop' into 2.2.6-develop
2 parents 9585f44 + 4052cf6 commit f430b14

File tree

42 files changed

+1230
-48
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1230
-48
lines changed

app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public function execute()
115115
{
116116
$data = $this->getRequest()->getPostValue();
117117
if ($data) {
118+
$this->preprocessOptionsData($data);
118119
$setId = $this->getRequest()->getParam('set');
119120

120121
$attributeSet = null;
@@ -210,7 +211,7 @@ public function execute()
210211

211212
$data['attribute_code'] = $model->getAttributeCode();
212213
$data['is_user_defined'] = $model->getIsUserDefined();
213-
$data['frontend_input'] = $model->getFrontendInput();
214+
$data['frontend_input'] = $data['frontend_input'] ?? $model->getFrontendInput();
214215
} else {
215216
/**
216217
* @todo add to helper and specify all relations for properties
@@ -311,6 +312,28 @@ public function execute()
311312
return $this->returnResult('catalog/*/', [], ['error' => true]);
312313
}
313314

315+
/**
316+
* Extract options data from serialized options field and append to data array.
317+
*
318+
* This logic is required to overcome max_input_vars php limit
319+
* that may vary and/or be inaccessible to change on different instances.
320+
*
321+
* @param array $data
322+
* @return void
323+
*/
324+
private function preprocessOptionsData(&$data)
325+
{
326+
if (isset($data['serialized_options'])) {
327+
$serializedOptions = json_decode($data['serialized_options'], JSON_OBJECT_AS_ARRAY);
328+
foreach ($serializedOptions as $serializedOption) {
329+
$option = [];
330+
parse_str($serializedOption, $option);
331+
$data = array_replace_recursive($data, $option);
332+
}
333+
}
334+
unset($data['serialized_options']);
335+
}
336+
314337
/**
315338
* @param string $path
316339
* @param array $params

app/code/Magento/Catalog/Model/ResourceModel/Category.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
*/
1212
namespace Magento\Catalog\Model\ResourceModel;
1313

14+
use Magento\Catalog\Model\Indexer\Category\Product\Processor;
15+
use Magento\Framework\DataObject;
1416
use Magento\Framework\EntityManager\EntityManager;
1517

1618
/**
@@ -82,6 +84,11 @@ class Category extends AbstractResource
8284
*/
8385
protected $aggregateCount;
8486

87+
/**
88+
* @var Processor
89+
*/
90+
private $indexerProcessor;
91+
8592
/**
8693
* Category constructor.
8794
* @param \Magento\Eav\Model\Entity\Context $context
@@ -92,6 +99,7 @@ class Category extends AbstractResource
9299
* @param Category\CollectionFactory $categoryCollectionFactory
93100
* @param array $data
94101
* @param \Magento\Framework\Serialize\Serializer\Json|null $serializer
102+
* @param Processor|null $indexerProcessor
95103
*/
96104
public function __construct(
97105
\Magento\Eav\Model\Entity\Context $context,
@@ -101,7 +109,8 @@ public function __construct(
101109
\Magento\Catalog\Model\ResourceModel\Category\TreeFactory $categoryTreeFactory,
102110
\Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory,
103111
$data = [],
104-
\Magento\Framework\Serialize\Serializer\Json $serializer = null
112+
\Magento\Framework\Serialize\Serializer\Json $serializer = null,
113+
Processor $indexerProcessor = null
105114
) {
106115
parent::__construct(
107116
$context,
@@ -115,6 +124,8 @@ public function __construct(
115124
$this->connectionName = 'catalog';
116125
$this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
117126
->get(\Magento\Framework\Serialize\Serializer\Json::class);
127+
$this->indexerProcessor = $indexerProcessor ?: \Magento\Framework\App\ObjectManager::getInstance()
128+
->get(Processor::class);
118129
}
119130

120131
/**
@@ -197,6 +208,19 @@ protected function _beforeDelete(\Magento\Framework\DataObject $object)
197208
$this->deleteChildren($object);
198209
}
199210

211+
/**
212+
* Mark Category indexer as invalid to be picked up by cron.
213+
*
214+
* @param DataObject $object
215+
* @return $this
216+
*/
217+
protected function _afterDelete(DataObject $object): Category
218+
{
219+
$this->indexerProcessor->markIndexerAsInvalid();
220+
221+
return parent::_afterDelete($object);
222+
}
223+
200224
/**
201225
* Delete children categories of specific category
202226
*

app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/CategoryTest.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
namespace Magento\Catalog\Test\Unit\Model\ResourceModel;
88

99
use Magento\Catalog\Model\Factory;
10+
use Magento\Catalog\Model\Indexer\Category\Product\Processor;
1011
use Magento\Catalog\Model\ResourceModel\Category;
1112
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
1213
use Magento\Eav\Model\Config;
@@ -91,6 +92,11 @@ class CategoryTest extends \PHPUnit\Framework\TestCase
9192
*/
9293
private $serializerMock;
9394

95+
/**
96+
* @var Processor|\PHPUnit_Framework_MockObject_MockObject
97+
*/
98+
private $indexerProcessorMock;
99+
94100
/**
95101
* {@inheritDoc}
96102
*/
@@ -121,6 +127,9 @@ protected function setUp()
121127
$this->collectionFactoryMock = $this->getMockBuilder(CollectionFactory::class)
122128
->disableOriginalConstructor()
123129
->getMock();
130+
$this->indexerProcessorMock = $this->getMockBuilder(Processor::class)
131+
->disableOriginalConstructor()
132+
->getMock();
124133

125134
$this->serializerMock = $this->getMockBuilder(Json::class)->getMock();
126135

@@ -132,7 +141,8 @@ protected function setUp()
132141
$this->treeFactoryMock,
133142
$this->collectionFactoryMock,
134143
[],
135-
$this->serializerMock
144+
$this->serializerMock,
145+
$this->indexerProcessorMock
136146
);
137147
}
138148

app/code/Magento/Catalog/i18n/en_US.csv

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,9 @@ Groups,Groups
516516
"Maximum image width","Maximum image width"
517517
"Maximum image height","Maximum image height"
518518
"Maximum number of characters:","Maximum number of characters:"
519+
"Maximum %1 characters", "Maximum %1 characters"
520+
"too many", "too many"
521+
"remaining", "remaining"
519522
"start typing to search template","start typing to search template"
520523
"Product online","Product online"
521524
"Product offline","Product offline"

app/code/Magento/Catalog/view/adminhtml/web/js/options.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@ define([
1313
'jquery/ui',
1414
'prototype',
1515
'form',
16-
'validation'
16+
'validation',
17+
'mage/translate'
1718
], function (jQuery, mageTemplate, rg) {
1819
'use strict';
1920

2021
return function (config) {
21-
var attributeOption = {
22+
var optionPanel = jQuery('#manage-options-panel'),
23+
optionsValues = [],
24+
editForm = jQuery('#edit_form'),
25+
attributeOption = {
2226
table: $('attribute-options-table'),
2327
itemCount: 0,
2428
totalItems: 0,
@@ -150,7 +154,7 @@ define([
150154
attributeOption.remove(event);
151155
});
152156

153-
jQuery('#manage-options-panel').on('render', function () {
157+
optionPanel.on('render', function () {
154158
attributeOption.ignoreValidate();
155159

156160
if (attributeOption.rendered) {
@@ -176,7 +180,31 @@ define([
176180
});
177181
});
178182
}
183+
editForm.on('submit', function () {
184+
optionPanel.find('input')
185+
.each(function () {
186+
if (this.disabled) {
187+
return;
188+
}
179189

190+
if (this.type === 'checkbox' || this.type === 'radio') {
191+
if (this.checked) {
192+
optionsValues.push(this.name + '=' + jQuery(this).val());
193+
}
194+
} else {
195+
optionsValues.push(this.name + '=' + jQuery(this).val());
196+
}
197+
});
198+
jQuery('<input>')
199+
.attr({
200+
type: 'hidden',
201+
name: 'serialized_options'
202+
})
203+
.val(JSON.stringify(optionsValues))
204+
.prependTo(editForm);
205+
optionPanel.find('table')
206+
.replaceWith(jQuery('<div>').text(jQuery.mage.__('Sending attribute values as package.')));
207+
});
180208
window.attributeOption = attributeOption;
181209
window.optionDefaultInputType = attributeOption.getOptionInputType();
182210

app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/text.phtml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,23 @@ $class = ($_option->getIsRequire()) ? ' required' : '';
6161
cols="25"><?= $block->escapeHtml($block->getDefaultValue()) ?></textarea>
6262
<?php endif; ?>
6363
<?php if ($_option->getMaxCharacters()): ?>
64-
<p class="note"><?= /* @escapeNotVerified */ __('Maximum number of characters:') ?>
65-
<strong><?= /* @escapeNotVerified */ $_option->getMaxCharacters() ?></strong></p>
64+
<p class="note note_<?= /* @escapeNotVerified */ $_option->getId() ?>">
65+
<?= /* @escapeNotVerified */ __('Maximum %1 characters', $_option->getMaxCharacters()) ?>
66+
<span class="character-counter no-display"></span>
67+
</p>
6668
<?php endif; ?>
6769
</div>
70+
<?php if ($_option->getMaxCharacters()): ?>
71+
<script type="text/x-magento-init">
72+
{
73+
"[data-selector='options[<?= /* @escapeNotVerified */ $_option->getId() ?>]']": {
74+
"Magento_Catalog/js/product/remaining-characters": {
75+
"maxLength": "<?= /* @escapeNotVerified */ $_option->getMaxCharacters() ?>",
76+
"noteSelector": ".note_<?= /* @escapeNotVerified */ $_option->getId() ?>",
77+
"counterSelector": ".note_<?= /* @escapeNotVerified */ $_option->getId() ?> .character-counter"
78+
}
79+
}
80+
}
81+
</script>
82+
<?php endif; ?>
6883
</div>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
define([
7+
'jquery',
8+
'mage/translate',
9+
'jquery/ui'
10+
], function ($, $t) {
11+
'use strict';
12+
13+
$.widget('mage.remainingCharacters', {
14+
options: {
15+
remainingText: $t('remaining'),
16+
tooManyText: $t('too many'),
17+
errorClass: 'mage-error',
18+
noDisplayClass: 'no-display'
19+
},
20+
21+
/**
22+
* Initializes custom option component
23+
*
24+
* @private
25+
*/
26+
_create: function () {
27+
this.note = $(this.options.noteSelector);
28+
this.counter = $(this.options.counterSelector);
29+
30+
this.updateCharacterCount();
31+
this.element.on('change keyup paste', this.updateCharacterCount.bind(this));
32+
},
33+
34+
/**
35+
* Updates counter message
36+
*/
37+
updateCharacterCount: function () {
38+
var length = this.element.val().length,
39+
diff = this.options.maxLength - length;
40+
41+
this.counter.text(this._formatMessage(diff));
42+
this.counter.toggleClass(this.options.noDisplayClass, length === 0);
43+
this.note.toggleClass(this.options.errorClass, diff < 0);
44+
},
45+
46+
/**
47+
* Format remaining characters message
48+
*
49+
* @param {int} diff
50+
* @returns {String}
51+
* @private
52+
*/
53+
_formatMessage: function (diff) {
54+
var count = Math.abs(diff),
55+
qualifier = diff < 0 ? this.options.tooManyText : this.options.remainingText;
56+
57+
return '(' + count + ' ' + qualifier + ')';
58+
}
59+
});
60+
61+
return $.mage.remainingCharacters;
62+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Model\Plugin;
9+
10+
use Magento\Catalog\Model\ResourceModel\Category as Resource;
11+
use Magento\CatalogSearch\Model\Indexer\Fulltext\Processor;
12+
13+
/**
14+
* Perform indexer invalidation after a category delete.
15+
*/
16+
class Category
17+
{
18+
/**
19+
* @var Processor
20+
*/
21+
private $fulltextIndexerProcessor;
22+
23+
/**
24+
* @param Processor $fulltextIndexerProcessor
25+
*/
26+
public function __construct(Processor $fulltextIndexerProcessor)
27+
{
28+
$this->fulltextIndexerProcessor = $fulltextIndexerProcessor;
29+
}
30+
31+
/**
32+
* Mark fulltext indexer as invalid post-deletion of category.
33+
*
34+
* @param Resource $subjectCategory
35+
* @param Resource $resultCategory
36+
* @return Resource
37+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
38+
*/
39+
public function afterDelete(Resource $subjectCategory, Resource $resultCategory): Resource
40+
{
41+
$this->fulltextIndexerProcessor->markIndexerAsInvalid();
42+
43+
return $resultCategory;
44+
}
45+
}

app/code/Magento/CatalogSearch/etc/adminhtml/di.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,12 @@
2626
</argument>
2727
</arguments>
2828
</type>
29+
<type name="Magento\Catalog\Model\ProductLink\Search">
30+
<arguments>
31+
<argument name="filter" xsi:type="object">Magento\CatalogSearch\Ui\DataProvider\Product\AddFulltextFilterToCollection</argument>
32+
</arguments>
33+
</type>
34+
<type name="Magento\Catalog\Model\ResourceModel\Category">
35+
<plugin name="fulltext_search_indexer" type="Magento\CatalogSearch\Model\Indexer\Fulltext\Model\Plugin\Category"/>
36+
</type>
2937
</config>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
9+
<type name="Magento\Catalog\Model\ResourceModel\Category">
10+
<plugin name="fulltext_search_indexer" type="Magento\CatalogSearch\Model\Indexer\Fulltext\Model\Plugin\Category"/>
11+
</type>
12+
</config>

0 commit comments

Comments
 (0)