Skip to content

Commit

Permalink
Merge pull request #540 from magento-firedrakes/backports-2.0.11
Browse files Browse the repository at this point in the history
[Firedrakes] Bugfixes to 2.0-develop
  • Loading branch information
Volodymyr Klymenko authored Oct 27, 2016
2 parents 8ccf7fa + c6595d1 commit f3dc998
Show file tree
Hide file tree
Showing 158 changed files with 13,728 additions and 751 deletions.
23 changes: 21 additions & 2 deletions app/code/Magento/CatalogImportExport/Model/Export/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ protected function collectRawData()
if (is_scalar($attrValue)) {
if (!in_array($fieldName, $this->_getExportMainAttrCodes())) {
$additionalAttributes[$fieldName] = $fieldName .
ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $attrValue;
ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $this->wrapValue($attrValue);
}
$data[$itemId][$storeId][$fieldName] = $attrValue;
}
Expand All @@ -904,7 +904,7 @@ protected function collectRawData()
$additionalAttributes[$code] = $fieldName .
ImportProduct::PAIR_NAME_VALUE_SEPARATOR . implode(
ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR,
$this->collectedMultiselectsData[$storeId][$itemId][$code]
$this->wrapValue($this->collectedMultiselectsData[$storeId][$itemId][$code])
);
}
}
Expand Down Expand Up @@ -933,6 +933,25 @@ protected function collectRawData()
return $data;
}

/**
* Wrap values with double quotes if "Fields Enclosure" option is enabled
*
* @param string|array $value
* @return string|array
*/
private function wrapValue($value)
{
if (!empty($this->_parameters[\Magento\ImportExport\Model\Export::FIELDS_ENCLOSURE])) {
$wrap = function ($value) {
return sprintf('"%s"', str_replace('"', '""', $value));
};

$value = is_array($value) ? array_map($wrap, $value) : $wrap($value);
}

return $value;
}

/**
* @return array
*/
Expand Down
119 changes: 108 additions & 11 deletions app/code/Magento/CatalogImportExport/Model/Import/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,13 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
/** @var array */
protected $rowNumbers = [];

/**
* Escaped separator value for regular expression.
* The value is based on PSEUDO_MULTI_LINE_SEPARATOR constant.
* @var string
*/
private $multiLineSeparatorForRegexp;

/**
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
* @param \Magento\ImportExport\Helper\Data $importExportData
Expand Down Expand Up @@ -2319,20 +2326,110 @@ private function _parseAdditionalAttributes($rowData)
if (empty($rowData['additional_attributes'])) {
return $rowData;
}
$rowData = array_merge($rowData, $this->parseAdditionalAttributes($rowData['additional_attributes']));
return $rowData;
}

$attributeNameValuePairs = explode($this->getMultipleValueSeparator(), $rowData['additional_attributes']);
foreach ($attributeNameValuePairs as $attributeNameValuePair) {
$separatorPosition = strpos($attributeNameValuePair, self::PAIR_NAME_VALUE_SEPARATOR);
if ($separatorPosition !== false) {
$key = substr($attributeNameValuePair, 0, $separatorPosition);
$value = substr(
$attributeNameValuePair,
$separatorPosition + strlen(self::PAIR_NAME_VALUE_SEPARATOR)
);
$rowData[$key] = $value === false ? '' : $value;
/**
* Retrieves additional attributes in format:
* [
* code1 => value1,
* code2 => value2,
* ...
* codeN => valueN
* ]
*
* @param string $additionalAttributes Attributes data that will be parsed
* @return array
*/
private function parseAdditionalAttributes($additionalAttributes)
{
return empty($this->_parameters[Import::FIELDS_ENCLOSURE])
? $this->parseAttributesWithoutWrappedValues($additionalAttributes)
: $this->parseAttributesWithWrappedValues($additionalAttributes);
}

/**
* Parses data and returns attributes in format:
* [
* code1 => value1,
* code2 => value2,
* ...
* codeN => valueN
* ]
*
* @param string $attributesData Attributes data that will be parsed. It keeps data in format:
* code=value,code2=value2...,codeN=valueN
* @return array
*/
private function parseAttributesWithoutWrappedValues($attributesData)
{
$attributeNameValuePairs = explode($this->getMultipleValueSeparator(), $attributesData);
$preparedAttributes = [];
$code = '';
foreach ($attributeNameValuePairs as $attributeData) {
//process case when attribute has ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR inside its value
if (strpos($attributeData, self::PAIR_NAME_VALUE_SEPARATOR) === false) {
if (!$code) {
continue;
}
$preparedAttributes[$code] .= $this->getMultipleValueSeparator() . $attributeData;
continue;
}
list($code, $value) = explode(self::PAIR_NAME_VALUE_SEPARATOR, $attributeData, 2);
$preparedAttributes[$code] = $value;
}
return $rowData;
return $preparedAttributes;
}

/**
* Parses data and returns attributes in format:
* [
* code1 => value1,
* code2 => value2,
* ...
* codeN => valueN
* ]
* All values have unescaped data except mupliselect attributes,
* they should be parsed in additional method - parseMultiselectValues()
*
* @param string $attributesData Attributes data that will be parsed. It keeps data in format:
* code="value",code2="value2"...,codeN="valueN"
* where every value is wrapped in double quotes. Double quotes as part of value should be duplicated.
* E.g. attribute with code 'attr_code' has value 'my"value'. This data should be stored as attr_code="my""value"
*
* @return array
*/
private function parseAttributesWithWrappedValues($attributesData)
{
$attributes = [];
preg_match_all('~((?:[a-z0-9_])+)="((?:[^"]|""|"' . $this->getMultiLineSeparatorForRegexp() . '")+)"+~',
$attributesData,
$matches
);
foreach ($matches[1] as $i => $attributeCode) {
$attribute = $this->retrieveAttributeByCode($attributeCode);
$value = 'multiselect' != $attribute->getFrontendInput()
? str_replace('""', '"', $matches[2][$i])
: '"' . $matches[2][$i] . '"';
$attributes[$attributeCode] = $value;
}
return $attributes;
}

/**
* Retrieves escaped PSEUDO_MULTI_LINE_SEPARATOR if it is metacharacter for regular expression
*
* @return string
*/
private function getMultiLineSeparatorForRegexp()
{
if (!$this->multiLineSeparatorForRegexp) {
$this->multiLineSeparatorForRegexp = in_array(self::PSEUDO_MULTI_LINE_SEPARATOR, str_split('[\^$.|?*+(){}'))
? '\\' . self::PSEUDO_MULTI_LINE_SEPARATOR
: self::PSEUDO_MULTI_LINE_SEPARATOR;
}
return $this->multiLineSeparatorForRegexp;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,23 +475,29 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe
$resultAttrs = [];

foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) {
if (!$attrParams['is_static']) {
if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) {
$resultAttrs[$attrCode] = in_array($attrParams['type'], ['select', 'boolean'])
? $attrParams['options'][strtolower($rowData[$attrCode])]
: $rowData[$attrCode];
if ('multiselect' == $attrParams['type']) {
$resultAttrs[$attrCode] = [];
foreach (explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]) as $value) {
$resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)];
}
$resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]);
if ($attrParams['is_static']) {
continue;
}
if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) {
if (in_array($attrParams['type'], ['select', 'boolean'])) {
$resultAttrs[$attrCode] = $attrParams['options'][strtolower($rowData[$attrCode])];
} elseif ('multiselect' == $attrParams['type']) {
$resultAttrs[$attrCode] = [];
$multiSelectValues = $this->parseMultiSelectValues(
$this->_entityModel->getParameters(),
$rowData[$attrCode]
);
foreach ($multiSelectValues as $value) {
$resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)];
}
} elseif (array_key_exists($attrCode, $rowData)) {
$resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]);
} else {
$resultAttrs[$attrCode] = $rowData[$attrCode];
} elseif ($withDefaultValue && null !== $attrParams['default_value']) {
$resultAttrs[$attrCode] = $attrParams['default_value'];
}
} elseif (array_key_exists($attrCode, $rowData)) {
$resultAttrs[$attrCode] = $rowData[$attrCode];
} elseif ($withDefaultValue && null !== $attrParams['default_value']) {
$resultAttrs[$attrCode] = $attrParams['default_value'];
}
}

Expand Down Expand Up @@ -523,4 +529,24 @@ public function saveData()
{
return $this;
}

/**
* Parse values of multiselect attributes depends on "Fields Enclosure" parameter
*
* @param array $parameters
* @param string $values
* @return array
*/
private function parseMultiSelectValues(array $parameters, $values)
{
if (empty($parameters[\Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE])) {
return explode(\Magento\CatalogImportExport\Model\Import\Product::PSEUDO_MULTI_LINE_SEPARATOR, $values);
}
if (preg_match_all('~"((?:[^"]|"")*)"~', $values, $matches)) {
return $values = array_map(function ($value) {
return str_replace('""', '"', $value);
}, $matches[1]);
}
return [$values];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,32 @@ protected function textValidation($attrCode, $type)
return $valid;
}

/**
* Check if value is valid attribute option
*
* @param string $attrCode
* @param array $possibleOptions
* @param string $value
* @return bool
*/
private function validateOption($attrCode, $possibleOptions, $value)
{
if (!isset($possibleOptions[strtolower($value)])) {
$this->_addMessages(
[
sprintf(
$this->context->retrieveMessageTemplate(
RowValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION
),
$attrCode
)
]
);
return false;
}
return true;
}

/**
* @param mixed $attrCode
* @param string $type
Expand Down Expand Up @@ -161,23 +187,15 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData)
break;
case 'select':
case 'boolean':
$valid = $this->validateOption($attrCode, $attrParams['options'], $rowData[$attrCode]);
break;
case 'multiselect':
$values = explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]);
$valid = true;
$values = $this->parseMultiSelectValues($this->context->getParameters(), $rowData[$attrCode]);
foreach ($values as $value) {
$valid = $valid && isset($attrParams['options'][strtolower($value)]);
}
if (!$valid) {
$this->_addMessages(
[
sprintf(
$this->context->retrieveMessageTemplate(
RowValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION
),
$attrCode
)
]
);
$valid = $this->validateOption($attrCode, $attrParams['options'], $value);
if (!$valid) {
break;
}
}
break;
case 'datetime':
Expand Down Expand Up @@ -271,4 +289,24 @@ public function init($context)
$validator->init($context);
}
}

/**
* Parse values of multiselect attributes depends on "Fields Enclosure" parameter
*
* @param array $parameters
* @param string $values
* @return array
*/
private function parseMultiSelectValues(array $parameters, $values)
{
if (empty($parameters[\Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE])) {
return explode(\Magento\CatalogImportExport\Model\Import\Product::PSEUDO_MULTI_LINE_SEPARATOR, $values);
}
if (preg_match_all('~"((?:[^"]|"")*)"~', $values, $matches)) {
return $values = array_map(function ($value) {
return str_replace('""', '"', $value);
}, $matches[1]);
}
return [$values];
}
}
3 changes: 3 additions & 0 deletions app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class AttributeMerger
'textarea' => 'Magento_Ui/js/form/element/textarea',
'multiline' => 'Magento_Ui/js/form/components/group',
'multiselect' => 'Magento_Ui/js/form/element/multiselect',
'image' => 'Magento_Ui/js/form/element/media',
'file' => 'Magento_Ui/js/form/element/media',
];

/**
Expand All @@ -32,6 +34,7 @@ class AttributeMerger
*/
protected $templateMap = [
'image' => 'media',
'file' => 'media',
];

/**
Expand Down
Loading

0 comments on commit f3dc998

Please sign in to comment.