Skip to content

Commit 18ec01e

Browse files
author
Robert He
committed
Merge branch '2.0-develop' into MAGETWO-55682-backport_55514_2.0.x
2 parents 2020216 + f3dc998 commit 18ec01e

File tree

162 files changed

+13781
-795
lines changed

Some content is hidden

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

162 files changed

+13781
-795
lines changed

app/code/Magento/CatalogImportExport/Model/Export/Product.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ protected function collectRawData()
894894
if (is_scalar($attrValue)) {
895895
if (!in_array($fieldName, $this->_getExportMainAttrCodes())) {
896896
$additionalAttributes[$fieldName] = $fieldName .
897-
ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $attrValue;
897+
ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $this->wrapValue($attrValue);
898898
}
899899
$data[$itemId][$storeId][$fieldName] = $attrValue;
900900
}
@@ -904,7 +904,7 @@ protected function collectRawData()
904904
$additionalAttributes[$code] = $fieldName .
905905
ImportProduct::PAIR_NAME_VALUE_SEPARATOR . implode(
906906
ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR,
907-
$this->collectedMultiselectsData[$storeId][$itemId][$code]
907+
$this->wrapValue($this->collectedMultiselectsData[$storeId][$itemId][$code])
908908
);
909909
}
910910
}
@@ -933,6 +933,25 @@ protected function collectRawData()
933933
return $data;
934934
}
935935

936+
/**
937+
* Wrap values with double quotes if "Fields Enclosure" option is enabled
938+
*
939+
* @param string|array $value
940+
* @return string|array
941+
*/
942+
private function wrapValue($value)
943+
{
944+
if (!empty($this->_parameters[\Magento\ImportExport\Model\Export::FIELDS_ENCLOSURE])) {
945+
$wrap = function ($value) {
946+
return sprintf('"%s"', str_replace('"', '""', $value));
947+
};
948+
949+
$value = is_array($value) ? array_map($wrap, $value) : $wrap($value);
950+
}
951+
952+
return $value;
953+
}
954+
936955
/**
937956
* @return array
938957
*/

app/code/Magento/CatalogImportExport/Model/Import/Product.php

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,13 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
605605
/** @var array */
606606
protected $rowNumbers = [];
607607

608+
/**
609+
* Escaped separator value for regular expression.
610+
* The value is based on PSEUDO_MULTI_LINE_SEPARATOR constant.
611+
* @var string
612+
*/
613+
private $multiLineSeparatorForRegexp;
614+
608615
/**
609616
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
610617
* @param \Magento\ImportExport\Helper\Data $importExportData
@@ -2319,20 +2326,110 @@ private function _parseAdditionalAttributes($rowData)
23192326
if (empty($rowData['additional_attributes'])) {
23202327
return $rowData;
23212328
}
2329+
$rowData = array_merge($rowData, $this->parseAdditionalAttributes($rowData['additional_attributes']));
2330+
return $rowData;
2331+
}
23222332

2323-
$attributeNameValuePairs = explode($this->getMultipleValueSeparator(), $rowData['additional_attributes']);
2324-
foreach ($attributeNameValuePairs as $attributeNameValuePair) {
2325-
$separatorPosition = strpos($attributeNameValuePair, self::PAIR_NAME_VALUE_SEPARATOR);
2326-
if ($separatorPosition !== false) {
2327-
$key = substr($attributeNameValuePair, 0, $separatorPosition);
2328-
$value = substr(
2329-
$attributeNameValuePair,
2330-
$separatorPosition + strlen(self::PAIR_NAME_VALUE_SEPARATOR)
2331-
);
2332-
$rowData[$key] = $value === false ? '' : $value;
2333+
/**
2334+
* Retrieves additional attributes in format:
2335+
* [
2336+
* code1 => value1,
2337+
* code2 => value2,
2338+
* ...
2339+
* codeN => valueN
2340+
* ]
2341+
*
2342+
* @param string $additionalAttributes Attributes data that will be parsed
2343+
* @return array
2344+
*/
2345+
private function parseAdditionalAttributes($additionalAttributes)
2346+
{
2347+
return empty($this->_parameters[Import::FIELDS_ENCLOSURE])
2348+
? $this->parseAttributesWithoutWrappedValues($additionalAttributes)
2349+
: $this->parseAttributesWithWrappedValues($additionalAttributes);
2350+
}
2351+
2352+
/**
2353+
* Parses data and returns attributes in format:
2354+
* [
2355+
* code1 => value1,
2356+
* code2 => value2,
2357+
* ...
2358+
* codeN => valueN
2359+
* ]
2360+
*
2361+
* @param string $attributesData Attributes data that will be parsed. It keeps data in format:
2362+
* code=value,code2=value2...,codeN=valueN
2363+
* @return array
2364+
*/
2365+
private function parseAttributesWithoutWrappedValues($attributesData)
2366+
{
2367+
$attributeNameValuePairs = explode($this->getMultipleValueSeparator(), $attributesData);
2368+
$preparedAttributes = [];
2369+
$code = '';
2370+
foreach ($attributeNameValuePairs as $attributeData) {
2371+
//process case when attribute has ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR inside its value
2372+
if (strpos($attributeData, self::PAIR_NAME_VALUE_SEPARATOR) === false) {
2373+
if (!$code) {
2374+
continue;
2375+
}
2376+
$preparedAttributes[$code] .= $this->getMultipleValueSeparator() . $attributeData;
2377+
continue;
23332378
}
2379+
list($code, $value) = explode(self::PAIR_NAME_VALUE_SEPARATOR, $attributeData, 2);
2380+
$preparedAttributes[$code] = $value;
23342381
}
2335-
return $rowData;
2382+
return $preparedAttributes;
2383+
}
2384+
2385+
/**
2386+
* Parses data and returns attributes in format:
2387+
* [
2388+
* code1 => value1,
2389+
* code2 => value2,
2390+
* ...
2391+
* codeN => valueN
2392+
* ]
2393+
* All values have unescaped data except mupliselect attributes,
2394+
* they should be parsed in additional method - parseMultiselectValues()
2395+
*
2396+
* @param string $attributesData Attributes data that will be parsed. It keeps data in format:
2397+
* code="value",code2="value2"...,codeN="valueN"
2398+
* where every value is wrapped in double quotes. Double quotes as part of value should be duplicated.
2399+
* E.g. attribute with code 'attr_code' has value 'my"value'. This data should be stored as attr_code="my""value"
2400+
*
2401+
* @return array
2402+
*/
2403+
private function parseAttributesWithWrappedValues($attributesData)
2404+
{
2405+
$attributes = [];
2406+
preg_match_all('~((?:[a-z0-9_])+)="((?:[^"]|""|"' . $this->getMultiLineSeparatorForRegexp() . '")+)"+~',
2407+
$attributesData,
2408+
$matches
2409+
);
2410+
foreach ($matches[1] as $i => $attributeCode) {
2411+
$attribute = $this->retrieveAttributeByCode($attributeCode);
2412+
$value = 'multiselect' != $attribute->getFrontendInput()
2413+
? str_replace('""', '"', $matches[2][$i])
2414+
: '"' . $matches[2][$i] . '"';
2415+
$attributes[$attributeCode] = $value;
2416+
}
2417+
return $attributes;
2418+
}
2419+
2420+
/**
2421+
* Retrieves escaped PSEUDO_MULTI_LINE_SEPARATOR if it is metacharacter for regular expression
2422+
*
2423+
* @return string
2424+
*/
2425+
private function getMultiLineSeparatorForRegexp()
2426+
{
2427+
if (!$this->multiLineSeparatorForRegexp) {
2428+
$this->multiLineSeparatorForRegexp = in_array(self::PSEUDO_MULTI_LINE_SEPARATOR, str_split('[\^$.|?*+(){}'))
2429+
? '\\' . self::PSEUDO_MULTI_LINE_SEPARATOR
2430+
: self::PSEUDO_MULTI_LINE_SEPARATOR;
2431+
}
2432+
return $this->multiLineSeparatorForRegexp;
23362433
}
23372434

23382435
/**

app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -475,23 +475,29 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe
475475
$resultAttrs = [];
476476

477477
foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) {
478-
if (!$attrParams['is_static']) {
479-
if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) {
480-
$resultAttrs[$attrCode] = in_array($attrParams['type'], ['select', 'boolean'])
481-
? $attrParams['options'][strtolower($rowData[$attrCode])]
482-
: $rowData[$attrCode];
483-
if ('multiselect' == $attrParams['type']) {
484-
$resultAttrs[$attrCode] = [];
485-
foreach (explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]) as $value) {
486-
$resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)];
487-
}
488-
$resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]);
478+
if ($attrParams['is_static']) {
479+
continue;
480+
}
481+
if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) {
482+
if (in_array($attrParams['type'], ['select', 'boolean'])) {
483+
$resultAttrs[$attrCode] = $attrParams['options'][strtolower($rowData[$attrCode])];
484+
} elseif ('multiselect' == $attrParams['type']) {
485+
$resultAttrs[$attrCode] = [];
486+
$multiSelectValues = $this->parseMultiSelectValues(
487+
$this->_entityModel->getParameters(),
488+
$rowData[$attrCode]
489+
);
490+
foreach ($multiSelectValues as $value) {
491+
$resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)];
489492
}
490-
} elseif (array_key_exists($attrCode, $rowData)) {
493+
$resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]);
494+
} else {
491495
$resultAttrs[$attrCode] = $rowData[$attrCode];
492-
} elseif ($withDefaultValue && null !== $attrParams['default_value']) {
493-
$resultAttrs[$attrCode] = $attrParams['default_value'];
494496
}
497+
} elseif (array_key_exists($attrCode, $rowData)) {
498+
$resultAttrs[$attrCode] = $rowData[$attrCode];
499+
} elseif ($withDefaultValue && null !== $attrParams['default_value']) {
500+
$resultAttrs[$attrCode] = $attrParams['default_value'];
495501
}
496502
}
497503

@@ -523,4 +529,24 @@ public function saveData()
523529
{
524530
return $this;
525531
}
532+
533+
/**
534+
* Parse values of multiselect attributes depends on "Fields Enclosure" parameter
535+
*
536+
* @param array $parameters
537+
* @param string $values
538+
* @return array
539+
*/
540+
private function parseMultiSelectValues(array $parameters, $values)
541+
{
542+
if (empty($parameters[\Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE])) {
543+
return explode(\Magento\CatalogImportExport\Model\Import\Product::PSEUDO_MULTI_LINE_SEPARATOR, $values);
544+
}
545+
if (preg_match_all('~"((?:[^"]|"")*)"~', $values, $matches)) {
546+
return $values = array_map(function ($value) {
547+
return str_replace('""', '"', $value);
548+
}, $matches[1]);
549+
}
550+
return [$values];
551+
}
526552
}

app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,32 @@ protected function textValidation($attrCode, $type)
6666
return $valid;
6767
}
6868

69+
/**
70+
* Check if value is valid attribute option
71+
*
72+
* @param string $attrCode
73+
* @param array $possibleOptions
74+
* @param string $value
75+
* @return bool
76+
*/
77+
private function validateOption($attrCode, $possibleOptions, $value)
78+
{
79+
if (!isset($possibleOptions[strtolower($value)])) {
80+
$this->_addMessages(
81+
[
82+
sprintf(
83+
$this->context->retrieveMessageTemplate(
84+
RowValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION
85+
),
86+
$attrCode
87+
)
88+
]
89+
);
90+
return false;
91+
}
92+
return true;
93+
}
94+
6995
/**
7096
* @param mixed $attrCode
7197
* @param string $type
@@ -161,23 +187,15 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData)
161187
break;
162188
case 'select':
163189
case 'boolean':
190+
$valid = $this->validateOption($attrCode, $attrParams['options'], $rowData[$attrCode]);
191+
break;
164192
case 'multiselect':
165-
$values = explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]);
166-
$valid = true;
193+
$values = $this->parseMultiSelectValues($this->context->getParameters(), $rowData[$attrCode]);
167194
foreach ($values as $value) {
168-
$valid = $valid && isset($attrParams['options'][strtolower($value)]);
169-
}
170-
if (!$valid) {
171-
$this->_addMessages(
172-
[
173-
sprintf(
174-
$this->context->retrieveMessageTemplate(
175-
RowValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION
176-
),
177-
$attrCode
178-
)
179-
]
180-
);
195+
$valid = $this->validateOption($attrCode, $attrParams['options'], $value);
196+
if (!$valid) {
197+
break;
198+
}
181199
}
182200
break;
183201
case 'datetime':
@@ -271,4 +289,24 @@ public function init($context)
271289
$validator->init($context);
272290
}
273291
}
292+
293+
/**
294+
* Parse values of multiselect attributes depends on "Fields Enclosure" parameter
295+
*
296+
* @param array $parameters
297+
* @param string $values
298+
* @return array
299+
*/
300+
private function parseMultiSelectValues(array $parameters, $values)
301+
{
302+
if (empty($parameters[\Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE])) {
303+
return explode(\Magento\CatalogImportExport\Model\Import\Product::PSEUDO_MULTI_LINE_SEPARATOR, $values);
304+
}
305+
if (preg_match_all('~"((?:[^"]|"")*)"~', $values, $matches)) {
306+
return $values = array_map(function ($value) {
307+
return str_replace('""', '"', $value);
308+
}, $matches[1]);
309+
}
310+
return [$values];
311+
}
274312
}

app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class AttributeMerger
2323
'textarea' => 'Magento_Ui/js/form/element/textarea',
2424
'multiline' => 'Magento_Ui/js/form/components/group',
2525
'multiselect' => 'Magento_Ui/js/form/element/multiselect',
26+
'image' => 'Magento_Ui/js/form/element/media',
27+
'file' => 'Magento_Ui/js/form/element/media',
2628
];
2729

2830
/**
@@ -32,6 +34,7 @@ class AttributeMerger
3234
*/
3335
protected $templateMap = [
3436
'image' => 'media',
37+
'file' => 'media',
3538
];
3639

3740
/**

0 commit comments

Comments
 (0)