diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php
index 39ed11b1806cd..853cc65270306 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php
@@ -195,25 +195,6 @@ public function execute()
? $model->getAttributeCode()
: $this->getRequest()->getParam('attribute_code');
$attributeCode = $attributeCode ?: $this->generateCode($this->getRequest()->getParam('frontend_label')[0]);
- if (strlen($attributeCode) > 0) {
- $validatorAttrCode = new \Zend_Validate_Regex(
- ['pattern' => '/^[a-zA-Z\x{600}-\x{6FF}][a-zA-Z\x{600}-\x{6FF}_0-9]{0,30}$/u']
- );
- if (!$validatorAttrCode->isValid($attributeCode)) {
- $this->messageManager->addErrorMessage(
- __(
- 'Attribute code "%1" is invalid. Please use only letters (a-z or A-Z), ' .
- 'numbers (0-9) or underscore(_) in this field, first character should be a letter.',
- $attributeCode
- )
- );
- return $this->returnResult(
- 'catalog/*/edit',
- ['attribute_id' => $attributeId, '_current' => true],
- ['error' => true]
- );
- }
- }
$data['attribute_code'] = $attributeCode;
//validate frontend_input
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php
index 124ee1abb078e..c74a382724a00 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php
@@ -7,12 +7,13 @@
namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute;
-use Magento\Framework\Serialize\Serializer\FormData;
+use Magento\Catalog\Controller\Adminhtml\Product\Attribute as AttributeAction;
+use Magento\Eav\Model\Validator\Attribute\Code as AttributeCodeValidator;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\DataObject;
-use Magento\Catalog\Controller\Adminhtml\Product\Attribute as AttributeAction;
+use Magento\Framework\Serialize\Serializer\FormData;
/**
* Product attribute validate controller.
@@ -43,6 +44,11 @@ class Validate extends AttributeAction implements HttpGetActionInterface, HttpPo
*/
private $formDataSerializer;
+ /**
+ * @var AttributeCodeValidator
+ */
+ private $attributeCodeValidator;
+
/**
* Constructor
*
@@ -54,6 +60,7 @@ class Validate extends AttributeAction implements HttpGetActionInterface, HttpPo
* @param \Magento\Framework\View\LayoutFactory $layoutFactory
* @param array $multipleAttributeList
* @param FormData|null $formDataSerializer
+ * @param AttributeCodeValidator|null $attributeCodeValidator
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
@@ -63,7 +70,8 @@ public function __construct(
\Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
\Magento\Framework\View\LayoutFactory $layoutFactory,
array $multipleAttributeList = [],
- FormData $formDataSerializer = null
+ FormData $formDataSerializer = null,
+ AttributeCodeValidator $attributeCodeValidator = null
) {
parent::__construct($context, $attributeLabelCache, $coreRegistry, $resultPageFactory);
$this->resultJsonFactory = $resultJsonFactory;
@@ -71,6 +79,9 @@ public function __construct(
$this->multipleAttributeList = $multipleAttributeList;
$this->formDataSerializer = $formDataSerializer ?: ObjectManager::getInstance()
->get(FormData::class);
+ $this->attributeCodeValidator = $attributeCodeValidator ?: ObjectManager::getInstance()->get(
+ AttributeCodeValidator::class
+ );
}
/**
@@ -115,6 +126,12 @@ public function execute()
$response->setError(true);
$response->setProductAttribute($attribute->toArray());
}
+
+ if (!$this->attributeCodeValidator->isValid($attributeCode)) {
+ $this->setMessageToResponse($response, $this->attributeCodeValidator->getMessages());
+ $response->setError(true);
+ }
+
if ($this->getRequest()->has('new_attribute_set_name')) {
$setName = $this->getRequest()->getParam('new_attribute_set_name');
/** @var $attributeSet \Magento\Eav\Model\Entity\Attribute\Set */
diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php
index ced65b2d2e15d..30d3503e4640e 100644
--- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php
@@ -7,6 +7,7 @@
use Magento\Catalog\Api\Data\ProductAttributeInterface;
use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save;
+use Magento\Eav\Model\Validator\Attribute\Code as AttributeCodeValidator;
use Magento\Framework\Serialize\Serializer\FormData;
use Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\AttributeTest;
use Magento\Catalog\Model\Product\AttributeSet\BuildFactory;
@@ -94,6 +95,11 @@ class SaveTest extends AttributeTest
*/
private $productAttributeMock;
+ /**
+ * @var AttributeCodeValidator|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $attributeCodeValidatorMock;
+
protected function setUp()
{
parent::setUp();
@@ -138,6 +144,9 @@ protected function setUp()
$this->formDataSerializerMock = $this->getMockBuilder(FormData::class)
->disableOriginalConstructor()
->getMock();
+ $this->attributeCodeValidatorMock = $this->getMockBuilder(AttributeCodeValidator::class)
+ ->disableOriginalConstructor()
+ ->getMock();
$this->productAttributeMock = $this->getMockBuilder(ProductAttributeInterface::class)
->setMethods(['getId', 'get'])
->getMockForAbstractClass();
@@ -171,6 +180,7 @@ protected function getModel()
'groupCollectionFactory' => $this->groupCollectionFactoryMock,
'layoutFactory' => $this->layoutFactoryMock,
'formDataSerializer' => $this->formDataSerializerMock,
+ 'attributeCodeValidator' => $this->attributeCodeValidatorMock
]);
}
@@ -224,6 +234,10 @@ public function testExecute()
$this->productAttributeMock
->method('getAttributeCode')
->willReturn('test_code');
+ $this->attributeCodeValidatorMock
+ ->method('isValid')
+ ->with('test_code')
+ ->willReturn(true);
$this->requestMock->expects($this->once())
->method('getPostValue')
->willReturn($data);
diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php
index c6210f93e1290..742148b1bf7f1 100644
--- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php
@@ -6,6 +6,7 @@
namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\Attribute;
use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Validate;
+use Magento\Eav\Model\Validator\Attribute\Code as AttributeCodeValidator;
use Magento\Framework\Serialize\Serializer\FormData;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
use Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\AttributeTest;
@@ -67,6 +68,11 @@ class ValidateTest extends AttributeTest
*/
private $formDataSerializerMock;
+ /**
+ * @var AttributeCodeValidator|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $attributeCodeValidatorMock;
+
protected function setUp()
{
parent::setUp();
@@ -95,6 +101,9 @@ protected function setUp()
$this->formDataSerializerMock = $this->getMockBuilder(FormData::class)
->disableOriginalConstructor()
->getMock();
+ $this->attributeCodeValidatorMock = $this->getMockBuilder(AttributeCodeValidator::class)
+ ->disableOriginalConstructor()
+ ->getMock();
$this->contextMock->expects($this->any())
->method('getObjectManager')
@@ -117,6 +126,7 @@ protected function getModel()
'layoutFactory' => $this->layoutFactoryMock,
'multipleAttributeList' => ['select' => 'option'],
'formDataSerializer' => $this->formDataSerializerMock,
+ 'attributeCodeValidator' => $this->attributeCodeValidatorMock,
]
);
}
@@ -141,6 +151,12 @@ public function testExecute()
$this->attributeMock->expects($this->once())
->method('loadByCode')
->willReturnSelf();
+
+ $this->attributeCodeValidatorMock->expects($this->once())
+ ->method('isValid')
+ ->with('test_attribute_code')
+ ->willReturn(true);
+
$this->requestMock->expects($this->once())
->method('has')
->with('new_attribute_set_name')
@@ -190,6 +206,11 @@ public function testUniqueValidation(array $options, $isError)
->with($serializedOptions)
->willReturn($options);
+ $this->attributeCodeValidatorMock->expects($this->once())
+ ->method('isValid')
+ ->with('test_attribute_code')
+ ->willReturn(true);
+
$this->objectManagerMock->expects($this->once())
->method('create')
->willReturn($this->attributeMock);
@@ -333,6 +354,11 @@ public function testEmptyOption(array $options, $result)
->method('loadByCode')
->willReturnSelf();
+ $this->attributeCodeValidatorMock->expects($this->once())
+ ->method('isValid')
+ ->with('test_attribute_code')
+ ->willReturn(true);
+
$this->resultJsonFactoryMock->expects($this->once())
->method('create')
->willReturn($this->resultJson);
@@ -444,6 +470,10 @@ public function testExecuteWithOptionsDataError()
[\Magento\Eav\Model\Entity\Attribute\Set::class, [], $this->attributeSetMock]
]);
+ $this->attributeCodeValidatorMock
+ ->method('isValid')
+ ->willReturn(true);
+
$this->attributeMock
->method('loadByCode')
->willReturnSelf();
@@ -463,4 +493,81 @@ public function testExecuteWithOptionsDataError()
$this->getModel()->execute();
}
+
+ /**
+ * Test execute with an invalid attribute code
+ *
+ * @dataProvider provideInvalidAttributeCodes
+ * @param string $attributeCode
+ * @param $result
+ * @throws \Magento\Framework\Exception\NotFoundException
+ */
+ public function testExecuteWithInvalidAttributeCode($attributeCode, $result)
+ {
+ $serializedOptions = '{"key":"value"}';
+ $this->requestMock->expects($this->any())
+ ->method('getParam')
+ ->willReturnMap([
+ ['frontend_label', null, null],
+ ['frontend_input', 'select', 'multipleselect'],
+ ['attribute_code', null, $attributeCode],
+ ['new_attribute_set_name', null, 'test_attribute_set_name'],
+ ['message_key', Validate::DEFAULT_MESSAGE_KEY, 'message'],
+ ['serialized_options', '[]', $serializedOptions],
+ ]);
+
+ $this->formDataSerializerMock
+ ->expects($this->once())
+ ->method('unserialize')
+ ->with($serializedOptions)
+ ->willReturn(["key" => "value"]);
+
+ $this->objectManagerMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->attributeMock);
+
+ $this->attributeMock->expects($this->once())
+ ->method('loadByCode')
+ ->willReturnSelf();
+
+ $this->attributeCodeValidatorMock->expects($this->once())
+ ->method('isValid')
+ ->with($attributeCode)
+ ->willReturn(false);
+
+ $this->attributeCodeValidatorMock->expects($this->once())
+ ->method('getMessages')
+ ->willReturn(['Invalid Attribute Code.']);
+
+ $this->resultJsonFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->resultJson);
+
+ $this->resultJson->expects($this->once())
+ ->method('setJsonData')
+ ->willReturnArgument(0);
+
+ $response = $this->getModel()->execute();
+ $responseObject = json_decode($response);
+
+ $this->assertEquals($responseObject, $result);
+ }
+
+ /**
+ * Providing invalid attribute codes
+ *
+ * @return array
+ */
+ public function provideInvalidAttributeCodes()
+ {
+ return [
+ 'invalid attribute code' => [
+ '.attribute_code',
+ (object) [
+ 'error' => true,
+ 'message' => 'Invalid Attribute Code.',
+ ]
+ ]
+ ];
+ }
}
diff --git a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php
index a20c146d68d92..5dedf2c7e7eba 100644
--- a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php
+++ b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php
@@ -278,6 +278,7 @@ protected function getMultilineFieldConfig($attributeCode, array $attributeConfi
for ($lineIndex = 0; $lineIndex < (int)$attributeConfig['size']; $lineIndex++) {
$isFirstLine = $lineIndex === 0;
$line = [
+ 'label' => __("%1: Line %2", $attributeConfig['label'], $lineIndex + 1),
'component' => 'Magento_Ui/js/form/element/abstract',
'config' => [
// customScope is used to group elements within a single form e.g. they can be validated separately
diff --git a/app/code/Magento/Config/Block/System/Config/Form.php b/app/code/Magento/Config/Block/System/Config/Form.php
index 2a29fa33feb74..8378c058c1955 100644
--- a/app/code/Magento/Config/Block/System/Config/Form.php
+++ b/app/code/Magento/Config/Block/System/Config/Form.php
@@ -424,6 +424,10 @@ private function getFieldData(\Magento\Config\Model\Config\Structure\Element\Fie
$backendModel = $field->getBackendModel();
// Backend models which implement ProcessorInterface are processed by ScopeConfigInterface
if (!$backendModel instanceof ProcessorInterface) {
+ if (array_key_exists($path, $this->_configData)) {
+ $data = $this->_configData[$path];
+ }
+
$backendModel->setPath($path)
->setValue($data)
->setWebsite($this->getWebsiteCode())
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php
index 06a4abb985802..23054ad613c21 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute.php
@@ -5,7 +5,9 @@
*/
namespace Magento\Eav\Model\Entity;
+use Magento\Eav\Model\Validator\Attribute\Code as AttributeCodeValidator;
use Magento\Framework\Api\AttributeValueFactory;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Stdlib\DateTime;
use Magento\Framework\Stdlib\DateTime\DateTimeFormatterInterface;
@@ -80,6 +82,11 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute\AbstractAttribute im
*/
protected $dateTimeFormatter;
+ /**
+ * @var AttributeCodeValidator|null
+ */
+ private $attributeCodeValidator;
+
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
@@ -100,6 +107,7 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute\AbstractAttribute im
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
* @param array $data
+ * @param AttributeCodeValidator|null $attributeCodeValidator
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
* @codeCoverageIgnore
*/
@@ -122,7 +130,8 @@ public function __construct(
DateTimeFormatterInterface $dateTimeFormatter,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
- array $data = []
+ array $data = [],
+ AttributeCodeValidator $attributeCodeValidator = null
) {
parent::__construct(
$context,
@@ -145,6 +154,9 @@ public function __construct(
$this->_localeResolver = $localeResolver;
$this->reservedAttributeList = $reservedAttributeList;
$this->dateTimeFormatter = $dateTimeFormatter;
+ $this->attributeCodeValidator = $attributeCodeValidator ?: ObjectManager::getInstance()->get(
+ AttributeCodeValidator::class
+ );
}
/**
@@ -230,6 +242,13 @@ public function loadEntityAttributeIdBySet()
*/
public function beforeSave()
{
+ if (isset($this->_data['attribute_code'])
+ && !$this->attributeCodeValidator->isValid($this->_data['attribute_code'])
+ ) {
+ $errorMessages = implode("\n", $this->attributeCodeValidator->getMessages());
+ throw new LocalizedException(__($errorMessages));
+ }
+
// prevent overriding product data
if (isset($this->_data['attribute_code']) && $this->reservedAttributeList->isReservedAttribute($this)) {
throw new LocalizedException(
@@ -240,25 +259,6 @@ public function beforeSave()
);
}
- /**
- * Check for maximum attribute_code length
- */
- if (isset(
- $this->_data['attribute_code']
- ) && !\Zend_Validate::is(
- $this->_data['attribute_code'],
- 'StringLength',
- ['max' => self::ATTRIBUTE_CODE_MAX_LENGTH]
- )
- ) {
- throw new LocalizedException(
- __(
- 'The attribute code needs to be %1 characters or fewer. Re-enter the code and try again.',
- self::ATTRIBUTE_CODE_MAX_LENGTH
- )
- );
- }
-
$defaultValue = $this->getDefaultValue();
$hasDefaultValue = (string)$defaultValue != '';
@@ -513,7 +513,7 @@ public function __sleep()
public function __wakeup()
{
parent::__wakeup();
- $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+ $objectManager = ObjectManager::getInstance();
$this->_localeDate = $objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class);
$this->_localeResolver = $objectManager->get(\Magento\Framework\Locale\ResolverInterface::class);
$this->reservedAttributeList = $objectManager->get(\Magento\Catalog\Model\Product\ReservedAttributeList::class);
diff --git a/app/code/Magento/Eav/Model/Form.php b/app/code/Magento/Eav/Model/Form.php
index c8c50521f5509..a34b53eede354 100644
--- a/app/code/Magento/Eav/Model/Form.php
+++ b/app/code/Magento/Eav/Model/Form.php
@@ -286,7 +286,8 @@ public function getFormCode()
}
/**
- * Return entity type instance
+ * Return entity type instance.
+ *
* Return EAV entity type if entity type is not defined
*
* @return \Magento\Eav\Model\Entity\Type
@@ -323,6 +324,8 @@ public function getAttributes()
if ($this->_attributes === null) {
$this->_attributes = [];
$this->_userAttributes = [];
+ $this->_systemAttributes = [];
+ $this->_allowedAttributes = [];
/** @var $attribute \Magento\Eav\Model\Attribute */
foreach ($this->_getFilteredFormAttributeCollection() as $attribute) {
$this->_attributes[$attribute->getAttributeCode()] = $attribute;
diff --git a/app/code/Magento/Eav/Model/Validator/Attribute/Code.php b/app/code/Magento/Eav/Model/Validator/Attribute/Code.php
new file mode 100644
index 0000000000000..f3ee37721b8ce
--- /dev/null
+++ b/app/code/Magento/Eav/Model/Validator/Attribute/Code.php
@@ -0,0 +1,72 @@
+ $minLength, 'max' => $maxLength]
+ );
+ if (!$isAllowedLength) {
+ $errorMessages[] = __(
+ 'An attribute code must not be less than %1 and more than %2 characters.',
+ $minLength,
+ $maxLength
+ );
+ }
+
+ $this->_addMessages($errorMessages);
+
+ return !$this->hasMessages();
+ }
+}
diff --git a/app/code/Magento/Eav/Setup/EavSetup.php b/app/code/Magento/Eav/Setup/EavSetup.php
index 29f9163a6e91d..de285e81b1d03 100644
--- a/app/code/Magento/Eav/Setup/EavSetup.php
+++ b/app/code/Magento/Eav/Setup/EavSetup.php
@@ -9,7 +9,9 @@
use Magento\Eav\Model\Entity\Setup\Context;
use Magento\Eav\Model\Entity\Setup\PropertyMapperInterface;
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory;
+use Magento\Eav\Model\Validator\Attribute\Code;
use Magento\Framework\App\CacheInterface;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Setup\ModuleDataSetupInterface;
@@ -80,6 +82,11 @@ class EavSetup
*/
private $_defaultAttributeSetName = 'Default';
+ /**
+ * @var Code
+ */
+ private $attributeCodeValidator;
+
/**
* Init
*
@@ -87,17 +94,22 @@ class EavSetup
* @param Context $context
* @param CacheInterface $cache
* @param CollectionFactory $attrGroupCollectionFactory
+ * @param Code|null $attributeCodeValidator
*/
public function __construct(
ModuleDataSetupInterface $setup,
Context $context,
CacheInterface $cache,
- CollectionFactory $attrGroupCollectionFactory
+ CollectionFactory $attrGroupCollectionFactory,
+ Code $attributeCodeValidator = null
) {
$this->cache = $cache;
$this->attrGroupCollectionFactory = $attrGroupCollectionFactory;
$this->attributeMapper = $context->getAttributeMapper();
$this->setup = $setup;
+ $this->attributeCodeValidator = $attributeCodeValidator ?: ObjectManager::getInstance()->get(
+ Code::class
+ );
}
/**
@@ -777,38 +789,6 @@ private function _getValue($array, $key, $default = null)
return isset($array[$key]) ? $array[$key] : $default;
}
- /**
- * Validate attribute data before insert into table
- *
- * @param array $data
- * @return true
- * @throws LocalizedException
- */
- private function _validateAttributeData($data)
- {
- $minLength = \Magento\Eav\Model\Entity\Attribute::ATTRIBUTE_CODE_MIN_LENGTH;
- $maxLength = \Magento\Eav\Model\Entity\Attribute::ATTRIBUTE_CODE_MAX_LENGTH;
- $attributeCode = isset($data['attribute_code']) ? $data['attribute_code'] : '';
-
- $isAllowedLength = \Zend_Validate::is(
- trim($attributeCode),
- 'StringLength',
- ['min' => $minLength, 'max' => $maxLength]
- );
-
- if (!$isAllowedLength) {
- $errorMessage = __(
- 'An attribute code must not be less than %1 and more than %2 characters.',
- $minLength,
- $maxLength
- );
-
- throw new LocalizedException($errorMessage);
- }
-
- return true;
- }
-
/**
* Add attribute to an entity type
*
@@ -818,6 +798,8 @@ private function _validateAttributeData($data)
* @param string $code
* @param array $attr
* @return $this
+ * @throws LocalizedException
+ * @throws \Zend_Validate_Exception
*/
public function addAttribute($entityTypeId, $code, array $attr)
{
@@ -828,7 +810,7 @@ public function addAttribute($entityTypeId, $code, array $attr)
$this->attributeMapper->map($attr, $entityTypeId)
);
- $this->_validateAttributeData($data);
+ $this->validateAttributeCode($data);
$sortOrder = isset($attr['sort_order']) ? $attr['sort_order'] : null;
$attributeId = $this->getAttribute($entityTypeId, $code, 'attribute_id');
@@ -1549,4 +1531,21 @@ private function _insertAttributeAdditionalData($entityTypeId, array $data)
return $this;
}
+
+ /**
+ * Validate attribute code.
+ *
+ * @param array $data
+ * @throws LocalizedException
+ * @throws \Zend_Validate_Exception
+ */
+ private function validateAttributeCode(array $data): void
+ {
+ $attributeCode = $data['attribute_code'] ?? '';
+ if (!$this->attributeCodeValidator->isValid($attributeCode)) {
+ $errorMessage = implode('\n', $this->attributeCodeValidator->getMessages());
+
+ throw new LocalizedException(__($errorMessage));
+ }
+ }
}
diff --git a/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php
new file mode 100644
index 0000000000000..9db290bcba3e1
--- /dev/null
+++ b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php
@@ -0,0 +1,67 @@
+assertEquals($expected, $validator->isValid($attributeCode));
+ }
+
+ /**
+ * Data provider for testIsValid
+ *
+ * @return array
+ */
+ public function isValidDataProvider(): array
+ {
+ return [
+ [
+ 'Attribute_code',
+ true
+ ], [
+ 'attribute_1',
+ true
+ ],[
+ 'Attribute_1',
+ true
+ ], [
+ '_attribute_code',
+ false
+ ], [
+ 'attribute.code',
+ false
+ ], [
+ '1attribute_code',
+ false
+ ], [
+ 'more_than_60_chars_more_than_60_chars_more_than_60_chars_more',
+ false
+ ]
+ ];
+ }
+}
diff --git a/app/code/Magento/Quote/etc/db_schema.xml b/app/code/Magento/Quote/etc/db_schema.xml
index 6f9f81ba6b3fa..48954f1af90fc 100644
--- a/app/code/Magento/Quote/etc/db_schema.xml
+++ b/app/code/Magento/Quote/etc/db_schema.xml
@@ -202,6 +202,8 @@
+
+
diff --git a/app/code/Magento/Quote/i18n/en_US.csv b/app/code/Magento/Quote/i18n/en_US.csv
index ae7453aa0d0cc..b24179297493a 100644
--- a/app/code/Magento/Quote/i18n/en_US.csv
+++ b/app/code/Magento/Quote/i18n/en_US.csv
@@ -65,3 +65,5 @@ error345,error345
Carts,Carts
"Manage carts","Manage carts"
"Invalid state change requested","Invalid state change requested"
+"Validated Country Code","Validated Country Code"
+"Validated Vat Number","Validated Vat Number"
diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php
index f9030ee75630b..76555ce8a6d8c 100644
--- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php
+++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php
@@ -160,7 +160,8 @@ public function getConfigFlag($field)
}
/**
- * Do request to shipment
+ * Do request to shipment.
+ *
* Implementation must be in overridden method
*
* @param Request $request
@@ -173,7 +174,8 @@ public function requestToShipment($request)
}
/**
- * Do return of shipment
+ * Do return of shipment.
+ *
* Implementation must be in overridden method
*
* @param Request $request
@@ -275,6 +277,8 @@ public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $para
}
/**
+ * Validate request for available ship countries.
+ *
* @param \Magento\Framework\DataObject $request
* @return $this|bool|false|\Magento\Framework\Model\AbstractModel
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
@@ -400,6 +404,8 @@ public function getSortOrder()
}
/**
+ * Allows free shipping when all product items have free shipping.
+ *
* @param \Magento\Quote\Model\Quote\Address\RateRequest $request
* @return void
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
@@ -531,10 +537,10 @@ protected function _getPerorderPrice($cost, $handlingType, $handlingFee)
}
/**
- * Sets the number of boxes for shipping
+ * Gets the average weight of each box available for shipping
*
- * @param int $weight in some measure
- * @return int
+ * @param float $weight in some measure
+ * @return float
*/
public function getTotalNumOfBoxes($weight)
{
@@ -545,7 +551,7 @@ public function getTotalNumOfBoxes($weight)
$maxPackageWeight = $this->getConfigData('max_package_weight');
if ($weight > $maxPackageWeight && $maxPackageWeight != 0) {
$this->_numBoxes = ceil($weight / $maxPackageWeight);
- $weight = $weight / $this->_numBoxes;
+ $weight = (float)$weight / $this->_numBoxes;
}
return $weight;
@@ -671,7 +677,8 @@ protected function filterDebugData($data)
}
/**
- * Recursive replace sensitive xml nodes values by specified mask
+ * Recursive replace sensitive xml nodes values by specified mask.
+ *
* @param \SimpleXMLElement $xml
* @return void
*/
diff --git a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php
index 4d678f5c1cb94..1419fa375a117 100644
--- a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php
+++ b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php
@@ -5,12 +5,12 @@
*/
namespace Magento\Sitemap\Model\ResourceModel\Catalog;
+use Magento\Catalog\Helper\Product as HelperProduct;
use Magento\Catalog\Model\Product\Image\UrlBuilder;
use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator;
-use Magento\Store\Model\Store;
use Magento\Framework\App\ObjectManager;
use Magento\Store\Model\ScopeInterface;
-use Magento\Catalog\Helper\Product as HelperProduct;
+use Magento\Store\Model\Store;
/**
* Sitemap resource product collection model
@@ -259,7 +259,7 @@ protected function _joinAttribute($storeId, $attributeCode, $column = null)
// Add attribute value to result set if needed
if (isset($column)) {
$this->_select->columns([
- $column => $columnValue
+ $column => $columnValue
]);
}
}
@@ -282,7 +282,7 @@ protected function _getAttribute($attributeCode)
'attribute_id' => $attribute->getId(),
'table' => $attribute->getBackend()->getTable(),
'is_global' => $attribute->getIsGlobal() ==
- \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
+ \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
'backend_type' => $attribute->getBackendType(),
];
}
@@ -324,7 +324,8 @@ public function getCollection($storeId)
[]
)->joinLeft(
['url_rewrite' => $this->getTable('url_rewrite')],
- 'e.entity_id = url_rewrite.entity_id AND url_rewrite.is_autogenerated = 1 AND url_rewrite.metadata IS '
+ 'e.entity_id = url_rewrite.entity_id AND url_rewrite.is_autogenerated = 1 '
+ . 'AND NULLIF(url_rewrite.metadata,"") IS '
. $urlsConfigCondition . 'NULL'
. $connection->quoteInto(' AND url_rewrite.store_id = ?', $store->getId())
. $connection->quoteInto(' AND url_rewrite.entity_type = ?', ProductUrlRewriteGenerator::ENTITY_TYPE),
diff --git a/app/code/Magento/Ui/Component/Form.php b/app/code/Magento/Ui/Component/Form.php
index 4033abba820e0..dc6e7b5ca06ab 100644
--- a/app/code/Magento/Ui/Component/Form.php
+++ b/app/code/Magento/Ui/Component/Form.php
@@ -10,6 +10,7 @@
use Magento\Framework\View\Element\UiComponentInterface;
/**
+ * Ui component Form
* @api
* @since 100.0.2
*/
@@ -53,14 +54,15 @@ public function getComponentName()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getDataSourceData()
{
$dataSource = [];
$id = $this->getContext()->getRequestParam($this->getContext()->getDataProvider()->getRequestFieldName(), null);
- $filter = $this->filterBuilder->setField($this->getContext()->getDataProvider()->getPrimaryFieldName())
+ $idFieldName = $this->getContext()->getDataProvider()->getPrimaryFieldName();
+ $filter = $this->filterBuilder->setField($idFieldName)
->setValue($id)
->create();
$this->getContext()->getDataProvider()
@@ -74,7 +76,7 @@ public function getDataSourceData()
];
} elseif (isset($data['items'])) {
foreach ($data['items'] as $item) {
- if ($item[$item['id_field_name']] == $id) {
+ if ($item[$idFieldName] == $id) {
$dataSource = ['data' => ['general' => $item]];
}
}
diff --git a/app/code/Magento/Ui/Test/Unit/Component/FormTest.php b/app/code/Magento/Ui/Test/Unit/Component/FormTest.php
index 6951583291df9..6df69c7d0e48d 100644
--- a/app/code/Magento/Ui/Test/Unit/Component/FormTest.php
+++ b/app/code/Magento/Ui/Test/Unit/Component/FormTest.php
@@ -9,7 +9,6 @@
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface;
-use Magento\Framework\View\Element\UiComponent\Processor;
use Magento\Ui\Component\Form;
class FormTest extends \PHPUnit\Framework\TestCase
@@ -215,4 +214,65 @@ public function testGetDataSourceDataWithoutId()
$this->assertEquals($dataSource, $this->model->getDataSourceData());
}
+
+ public function testGetDataSourceDataWithAbstractDataProvider()
+ {
+ $requestFieldName = 'request_id';
+ $primaryFieldName = 'primary_id';
+ $fieldId = 44;
+ $row = ['key' => 'value', $primaryFieldName => $fieldId];
+ $data = [
+ 'items' => [$row],
+ ];
+ $dataSource = [
+ 'data' => [
+ 'general' => $row
+ ],
+ ];
+
+ /** @var DataProviderInterface|\PHPUnit_Framework_MockObject_MockObject $dataProviderMock */
+ $dataProviderMock =
+ $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface::class)
+ ->getMock();
+ $dataProviderMock->expects($this->once())
+ ->method('getRequestFieldName')
+ ->willReturn($requestFieldName);
+ $dataProviderMock->expects($this->once())
+ ->method('getPrimaryFieldName')
+ ->willReturn($primaryFieldName);
+
+ $this->contextMock->expects($this->any())
+ ->method('getDataProvider')
+ ->willReturn($dataProviderMock);
+ $this->contextMock->expects($this->once())
+ ->method('getRequestParam')
+ ->with($requestFieldName)
+ ->willReturn($fieldId);
+
+ /** @var Filter|\PHPUnit_Framework_MockObject_MockObject $filterMock */
+ $filterMock = $this->getMockBuilder(\Magento\Framework\Api\Filter::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->filterBuilderMock->expects($this->once())
+ ->method('setField')
+ ->with($primaryFieldName)
+ ->willReturnSelf();
+ $this->filterBuilderMock->expects($this->once())
+ ->method('setValue')
+ ->with($fieldId)
+ ->willReturnSelf();
+ $this->filterBuilderMock->expects($this->once())
+ ->method('create')
+ ->willReturn($filterMock);
+
+ $dataProviderMock->expects($this->once())
+ ->method('addFilter')
+ ->with($filterMock);
+ $dataProviderMock->expects($this->once())
+ ->method('getData')
+ ->willReturn($data);
+
+ $this->assertEquals($dataSource, $this->model->getDataSourceData());
+ }
}
diff --git a/app/code/Magento/Ui/i18n/en_US.csv b/app/code/Magento/Ui/i18n/en_US.csv
index 039e28f318176..1ce2692886ae3 100644
--- a/app/code/Magento/Ui/i18n/en_US.csv
+++ b/app/code/Magento/Ui/i18n/en_US.csv
@@ -78,6 +78,7 @@ Keyword,Keyword
"Empty Value.","Empty Value."
"Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field.","Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field."
"Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.","Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter."
+"Attribute code ""%1"" is invalid. Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.","Attribute code ""%1"" is invalid. Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter."
"Please use only letters (a-z or A-Z), numbers (0-9), spaces and ""#"" in this field.","Please use only letters (a-z or A-Z), numbers (0-9), spaces and ""#"" in this field."
"Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.","Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890."
"Please enter a valid fax number (Ex: 123-456-7890).","Please enter a valid fax number (Ex: 123-456-7890)."
diff --git a/app/code/Magento/Ups/Model/Carrier.php b/app/code/Magento/Ups/Model/Carrier.php
index 8c60f5a53a2d9..9cb1fe615aa42 100644
--- a/app/code/Magento/Ups/Model/Carrier.php
+++ b/app/code/Magento/Ups/Model/Carrier.php
@@ -77,7 +77,7 @@ class Carrier extends AbstractCarrierOnline implements CarrierInterface
*
* @var string
*/
- protected $_defaultCgiGatewayUrl = 'http://www.ups.com:80/using/services/rave/qcostcgi.cgi';
+ protected $_defaultCgiGatewayUrl = 'https://www.ups.com/using/services/rave/qcostcgi.cgi';
/**
* Test urls for shipment
diff --git a/app/code/Magento/Ups/etc/config.xml b/app/code/Magento/Ups/etc/config.xml
index e2ac1c6d6c443..791b325c65e3f 100644
--- a/app/code/Magento/Ups/etc/config.xml
+++ b/app/code/Magento/Ups/etc/config.xml
@@ -19,7 +19,7 @@
RES
GND
- http://www.ups.com/using/services/rave/qcostcgi.cgi
+ https://www.ups.com/using/services/rave/qcostcgi.cgi
https://onlinetools.ups.com/ups.app/xml/Rate
0
Magento\Ups\Model\Carrier
diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml
index 7d8151d270308..de8575178d06d 100644
--- a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml
+++ b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml
@@ -14,6 +14,8 @@
urlrewriteGrid
Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection
url_rewrite_id
+
+ 1
diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less
index 54ba530092cc0..213b8131815b3 100644
--- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less
+++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less
@@ -165,7 +165,7 @@
// Checkout address (create shipping address)
.field.street {
- .field.additional {
+ .field {
.label {
&:extend(.abs-visually-hidden all);
}
diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less
index bd8ddde98c506..6adf4b5b2f86b 100755
--- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less
@@ -200,7 +200,7 @@
// Checkout address (create shipping address)
.field.street {
- .field.additional {
+ .field {
.label {
&:extend(.abs-visually-hidden all);
}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixture.php
index f0ee8c7b3c2bb..ddebbf37b16d1 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixture.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixture.php
@@ -158,14 +158,9 @@ private function isModuleAnnotation(string $fixture)
*/
private function getModulePath(string $fixture)
{
- $fixturePathParts = explode('::', $fixture, 2);
- $moduleName = $fixturePathParts[0];
- $fixtureFile = $fixturePathParts[1];
+ [$moduleName, $fixtureFile] = explode('::', $fixture, 2);
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- /** @var ComponentRegistrar $componentRegistrar */
- $componentRegistrar = $objectManager->get(ComponentRegistrar::class);
- $modulePath = $componentRegistrar->getPath(ComponentRegistrar::MODULE, $moduleName);
+ $modulePath = (new ComponentRegistrar())->getPath(ComponentRegistrar::MODULE, $moduleName);
if ($modulePath === null) {
throw new \Magento\Framework\Exception\LocalizedException(
@@ -173,7 +168,7 @@ private function getModulePath(string $fixture)
);
}
- return $modulePath . '/' . $fixtureFile;
+ return $modulePath . '/' . ltrim($fixtureFile, '/');
}
/**
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureBeforeTransaction.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureBeforeTransaction.php
index db7f57362d807..ba23ecd13b6a7 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureBeforeTransaction.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureBeforeTransaction.php
@@ -4,13 +4,14 @@
* See COPYING.txt for license details.
*/
-/**
- * Implementation of the @magentoDataFixture DocBlock annotation
- */
namespace Magento\TestFramework\Annotation;
+use Magento\Framework\Component\ComponentRegistrar;
use PHPUnit\Framework\Exception;
+/**
+ * Implementation of the @magentoDataFixtureBeforeTransaction DocBlock annotation
+ */
class DataFixtureBeforeTransaction
{
/**
@@ -93,6 +94,8 @@ protected function _getFixtures(\PHPUnit\Framework\TestCase $test, $scope = null
$fixtureMethod = [get_class($test), $fixture];
if (is_callable($fixtureMethod)) {
$result[] = $fixtureMethod;
+ } elseif ($this->isModuleAnnotation($fixture)) {
+ $result[] = $this->getModulePath($fixture);
} else {
$result[] = $this->_fixtureBaseDir . '/' . $fixture;
}
@@ -102,6 +105,42 @@ protected function _getFixtures(\PHPUnit\Framework\TestCase $test, $scope = null
}
/**
+ * Check is the Annotation like Magento_InventoryApi::Test/_files/products.php
+ *
+ * @param string $fixture
+ * @return bool
+ */
+ private function isModuleAnnotation(string $fixture)
+ {
+ return (strpos($fixture, '::') !== false);
+ }
+
+ /**
+ * Resolve the Fixture
+ *
+ * @param string $fixture
+ * @return string
+ * @throws \Magento\Framework\Exception\LocalizedException
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ */
+ private function getModulePath(string $fixture)
+ {
+ [$moduleName, $fixtureFile] = explode('::', $fixture, 2);
+
+ $modulePath = (new ComponentRegistrar())->getPath(ComponentRegistrar::MODULE, $moduleName);
+
+ if ($modulePath === null) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ new \Magento\Framework\Phrase('Can\'t find registered Module with name %1 .', [$moduleName])
+ );
+ }
+
+ return $modulePath . '/' . ltrim($fixtureFile, '/');
+ }
+
+ /**
+ * Get annotations for test.
+ *
* @param \PHPUnit\Framework\TestCase $test
* @return array
*/
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php
index 22240ad8e1fe9..00af4419e1142 100644
--- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Test\Annotation;
+use Magento\Framework\Component\ComponentRegistrar;
+
/**
* Test class for \Magento\TestFramework\Annotation\DataFixture.
*
@@ -178,4 +180,16 @@ public function testRollbackTransactionRevertFixtureFile()
);
$this->_object->rollbackTransaction();
}
+
+ /**
+ * @magentoDataFixture Foo_DataFixtureDummy::Test/Integration/foo.php
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ */
+ public function testModuleDataFixture()
+ {
+ ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Foo_DataFixtureDummy', __DIR__);
+ $this->_object->expects($this->once())->method('_applyOneFixture')
+ ->with(__DIR__ . '/Test/Integration/foo.php');
+ $this->_object->startTransaction($this);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php
index fe08ec01a9715..e1d3e960593a9 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php
@@ -157,7 +157,7 @@ public function testWrongAttributeCode()
$message = $messages->getItemsByType('error')[0];
$this->assertEquals(
'Attribute code "_()&&&?" is invalid. Please use only letters (a-z or A-Z),'
- . ' numbers (0-9) or underscore(_) in this field, first character should be a letter.',
+ . ' numbers (0-9) or underscore (_) in this field, and the first character should be a letter.',
$message->getText()
);
}
diff --git a/dev/tests/integration/testsuite/Magento/Eav/Setup/EavSetupTest.php b/dev/tests/integration/testsuite/Magento/Eav/Setup/EavSetupTest.php
index 5d7a72c65597d..a5843f20ad98a 100644
--- a/dev/tests/integration/testsuite/Magento/Eav/Setup/EavSetupTest.php
+++ b/dev/tests/integration/testsuite/Magento/Eav/Setup/EavSetupTest.php
@@ -55,7 +55,7 @@ public function addAttributeDataProvider()
{
return [
['eav_setup_test'],
- ['_59_characters_59_characters_59_characters_59_characters_59'],
+ ['characters_59_characters_59_characters_59_characters_59_59_'],
];
}
@@ -90,6 +90,33 @@ public function addAttributeThrowExceptionDataProvider()
];
}
+ /**
+ * Verify that add attribute throw exception if attribute_code is not valid.
+ *
+ * @param string|null $attributeCode
+ *
+ * @dataProvider addInvalidAttributeThrowExceptionDataProvider
+ * @expectedException \Magento\Framework\Exception\LocalizedException
+ * @expectedExceptionMessage Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field,
+ */
+ public function testAddInvalidAttributeThrowException($attributeCode)
+ {
+ $attributeData = $this->getAttributeData();
+ $this->eavSetup->addAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode, $attributeData);
+ }
+ /**
+ * Data provider for testAddInvalidAttributeThrowException().
+ *
+ * @return array
+ */
+ public function addInvalidAttributeThrowExceptionDataProvider()
+ {
+ return [
+ ['1first_character_is_not_letter'],
+ ['attribute.with.dots'],
+ ];
+ }
+
/**
* Get simple attribute data.
*/
diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
index 173064b472217..89a37429c47c9 100644
--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php
@@ -27,6 +27,8 @@ class Cache implements ConfigOptionsListInterface
const INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE = 'cache-backend-redis-db';
const INPUT_KEY_CACHE_BACKEND_REDIS_PORT = 'cache-backend-redis-port';
const INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD = 'cache-backend-redis-password';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_COMPRESS_DATA = 'cache-backend-redis-compress-data';
+ const INPUT_KEY_CACHE_BACKEND_REDIS_COMPRESSION_LIB = 'cache-backend-redis-compression-lib';
const INPUT_KEY_CACHE_ID_PREFIX = 'cache-id-prefix';
const CONFIG_PATH_CACHE_BACKEND = 'cache/frontend/default/backend';
@@ -34,6 +36,8 @@ class Cache implements ConfigOptionsListInterface
const CONFIG_PATH_CACHE_BACKEND_DATABASE = 'cache/frontend/default/backend_options/database';
const CONFIG_PATH_CACHE_BACKEND_PORT = 'cache/frontend/default/backend_options/port';
const CONFIG_PATH_CACHE_BACKEND_PASSWORD = 'cache/frontend/default/backend_options/password';
+ const CONFIG_PATH_CACHE_BACKEND_COMPRESS_DATA = 'cache/frontend/default/backend_options/compress_data';
+ const CONFIG_PATH_CACHE_BACKEND_COMPRESSION_LIB = 'cache/frontend/default/backend_options/compression_lib';
const CONFIG_PATH_CACHE_ID_PREFIX = 'cache/frontend/default/id_prefix';
/**
@@ -43,7 +47,9 @@ class Cache implements ConfigOptionsListInterface
self::INPUT_KEY_CACHE_BACKEND_REDIS_SERVER => '127.0.0.1',
self::INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE => '0',
self::INPUT_KEY_CACHE_BACKEND_REDIS_PORT => '6379',
- self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD => ''
+ self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD => '',
+ self::INPUT_KEY_CACHE_BACKEND_REDIS_COMPRESS_DATA => '1',
+ self::INPUT_KEY_CACHE_BACKEND_REDIS_COMPRESSION_LIB => '',
];
/**
@@ -60,7 +66,9 @@ class Cache implements ConfigOptionsListInterface
self::INPUT_KEY_CACHE_BACKEND_REDIS_SERVER => self::CONFIG_PATH_CACHE_BACKEND_SERVER,
self::INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE => self::CONFIG_PATH_CACHE_BACKEND_DATABASE,
self::INPUT_KEY_CACHE_BACKEND_REDIS_PORT => self::CONFIG_PATH_CACHE_BACKEND_PORT,
- self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD => self::CONFIG_PATH_CACHE_BACKEND_PASSWORD
+ self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD => self::CONFIG_PATH_CACHE_BACKEND_PASSWORD,
+ self::INPUT_KEY_CACHE_BACKEND_REDIS_COMPRESS_DATA => self::CONFIG_PATH_CACHE_BACKEND_COMPRESS_DATA,
+ self::INPUT_KEY_CACHE_BACKEND_REDIS_COMPRESSION_LIB => self::CONFIG_PATH_CACHE_BACKEND_COMPRESSION_LIB,
];
/**
@@ -115,12 +123,24 @@ public function getOptions()
self::CONFIG_PATH_CACHE_BACKEND_PASSWORD,
'Redis server password'
),
+ new TextConfigOption(
+ self::INPUT_KEY_CACHE_BACKEND_REDIS_COMPRESS_DATA,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_CACHE_BACKEND_COMPRESS_DATA,
+ 'Set to 0 to disable compression (default is 1, enabled)'
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_CACHE_BACKEND_REDIS_COMPRESSION_LIB,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_CACHE_BACKEND_COMPRESSION_LIB,
+ 'Compression lib to use [snappy,lzf,l4z,zstd,gzip] (leave blank to determine automatically)'
+ ),
new TextConfigOption(
self::INPUT_KEY_CACHE_ID_PREFIX,
TextConfigOption::FRONTEND_WIZARD_TEXT,
self::CONFIG_PATH_CACHE_ID_PREFIX,
'ID prefix for cache keys'
- )
+ ),
];
}
diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
index 7451b59356828..65bfc650c0206 100644
--- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php
@@ -6,10 +6,10 @@
namespace Magento\Setup\Model\ConfigOptionsList;
-use Magento\Framework\Setup\ConfigOptionsListInterface;
-use Magento\Framework\Config\File\ConfigFilePool;
use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\Config\Data\ConfigData;
+use Magento\Framework\Config\File\ConfigFilePool;
+use Magento\Framework\Setup\ConfigOptionsListInterface;
use Magento\Framework\Setup\Option\SelectConfigOption;
use Magento\Framework\Setup\Option\TextConfigOption;
use Magento\Setup\Validator\RedisConnectionValidator;
@@ -26,16 +26,18 @@ class PageCache implements ConfigOptionsListInterface
const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER = 'page-cache-redis-server';
const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE = 'page-cache-redis-db';
const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT = 'page-cache-redis-port';
- const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA = 'page-cache-redis-compress-data';
const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD = 'page-cache-redis-password';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA = 'page-cache-redis-compress-data';
+ const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESSION_LIB = 'page-cache-redis-compression-lib';
const INPUT_KEY_PAGE_CACHE_ID_PREFIX = 'page-cache-id-prefix';
const CONFIG_PATH_PAGE_CACHE_BACKEND = 'cache/frontend/page_cache/backend';
const CONFIG_PATH_PAGE_CACHE_BACKEND_SERVER = 'cache/frontend/page_cache/backend_options/server';
const CONFIG_PATH_PAGE_CACHE_BACKEND_DATABASE = 'cache/frontend/page_cache/backend_options/database';
const CONFIG_PATH_PAGE_CACHE_BACKEND_PORT = 'cache/frontend/page_cache/backend_options/port';
- const CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA = 'cache/frontend/page_cache/backend_options/compress_data';
const CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD = 'cache/frontend/page_cache/backend_options/password';
+ const CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA = 'cache/frontend/page_cache/backend_options/compress_data';
+ const CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESSION_LIB = 'cache/frontend/page_cache/backend_options/compression_lib';
const CONFIG_PATH_PAGE_CACHE_ID_PREFIX = 'cache/frontend/page_cache/id_prefix';
/**
@@ -45,8 +47,9 @@ class PageCache implements ConfigOptionsListInterface
self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER => '127.0.0.1',
self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE => '1',
self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT => '6379',
+ self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD => '',
self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => '0',
- self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD => ''
+ self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESSION_LIB => '',
];
/**
@@ -63,8 +66,10 @@ class PageCache implements ConfigOptionsListInterface
self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER => self::CONFIG_PATH_PAGE_CACHE_BACKEND_SERVER,
self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE => self::CONFIG_PATH_PAGE_CACHE_BACKEND_DATABASE,
self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT => self::CONFIG_PATH_PAGE_CACHE_BACKEND_PORT,
+ self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD => self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD,
self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => self::CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA,
- self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD => self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD
+ self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESSION_LIB =>
+ self::CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESSION_LIB,
];
/**
@@ -113,6 +118,12 @@ public function getOptions()
self::CONFIG_PATH_PAGE_CACHE_BACKEND_PORT,
'Redis server listen port'
),
+ new TextConfigOption(
+ self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD,
+ 'Redis server password'
+ ),
new TextConfigOption(
self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA,
TextConfigOption::FRONTEND_WIZARD_TEXT,
@@ -120,17 +131,17 @@ public function getOptions()
'Set to 1 to compress the full page cache (use 0 to disable)'
),
new TextConfigOption(
- self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD,
+ self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESSION_LIB,
TextConfigOption::FRONTEND_WIZARD_TEXT,
- self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD,
- 'Redis server password'
+ self::CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESSION_LIB,
+ 'Compression library to use [snappy,lzf,l4z,zstd,gzip] (leave blank to determine automatically)'
),
new TextConfigOption(
self::INPUT_KEY_PAGE_CACHE_ID_PREFIX,
TextConfigOption::FRONTEND_WIZARD_TEXT,
self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX,
'ID prefix for cache keys'
- )
+ ),
];
}
@@ -224,7 +235,7 @@ private function validateRedisConfig(array $options, DeploymentConfig $deploymen
self::CONFIG_PATH_PAGE_CACHE_BACKEND_DATABASE,
$this->getDefaultConfigValue(self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE)
);
-
+
$config['password'] = isset($options[self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD])
? $options[self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD]
: $deploymentConfig->get(
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php
index 9c123fcb330dd..783c11e941eed 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php
@@ -45,7 +45,7 @@ protected function setUp()
public function testGetOptions()
{
$options = $this->configOptionsList->getOptions();
- $this->assertCount(6, $options);
+ $this->assertCount(8, $options);
$this->assertArrayHasKey(0, $options);
$this->assertInstanceOf(SelectConfigOption::class, $options[0]);
@@ -69,7 +69,15 @@ public function testGetOptions()
$this->assertArrayHasKey(5, $options);
$this->assertInstanceOf(TextConfigOption::class, $options[5]);
- $this->assertEquals('cache-id-prefix', $options[5]->getName());
+ $this->assertEquals('cache-backend-redis-compress-data', $options[5]->getName());
+
+ $this->assertArrayHasKey(6, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[6]);
+ $this->assertEquals('cache-backend-redis-compression-lib', $options[6]->getName());
+
+ $this->assertArrayHasKey(7, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[7]);
+ $this->assertEquals('cache-id-prefix', $options[7]->getName());
}
/**
@@ -88,7 +96,9 @@ public function testCreateConfigCacheRedis()
'server' => '',
'port' => '',
'database' => '',
- 'password' => ''
+ 'password' => '',
+ 'compress_data' => '',
+ 'compression_lib' => '',
],
'id_prefix' => $this->expectedIdPrefix(),
]
@@ -115,18 +125,23 @@ public function testCreateConfigWithRedisConfig()
'server' => 'localhost',
'port' => '1234',
'database' => '5',
- 'password' => ''
+ 'password' => '',
+ 'compress_data' => '1',
+ 'compression_lib' => 'gzip',
],
'id_prefix' => $this->expectedIdPrefix(),
]
]
]
];
+
$options = [
'cache-backend' => 'redis',
'cache-backend-redis-server' => 'localhost',
'cache-backend-redis-port' => '1234',
- 'cache-backend-redis-db' => '5'
+ 'cache-backend-redis-db' => '5',
+ 'cache-backend-redis-compress-data' => '1',
+ 'cache-backend-redis-compression-lib' => 'gzip'
];
$configData = $this->configOptionsList->createConfig($options, $this->deploymentConfigMock);
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php
index 1cf3937f98684..673168fe2fffd 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php
@@ -45,7 +45,7 @@ protected function setUp()
public function testGetOptions()
{
$options = $this->configList->getOptions();
- $this->assertCount(7, $options);
+ $this->assertCount(8, $options);
$this->assertArrayHasKey(0, $options);
$this->assertInstanceOf(SelectConfigOption::class, $options[0]);
@@ -65,15 +65,19 @@ public function testGetOptions()
$this->assertArrayHasKey(4, $options);
$this->assertInstanceOf(TextConfigOption::class, $options[4]);
- $this->assertEquals('page-cache-redis-compress-data', $options[4]->getName());
+ $this->assertEquals('page-cache-redis-password', $options[4]->getName());
$this->assertArrayHasKey(5, $options);
$this->assertInstanceOf(TextConfigOption::class, $options[5]);
- $this->assertEquals('page-cache-redis-password', $options[5]->getName());
+ $this->assertEquals('page-cache-redis-compress-data', $options[5]->getName());
$this->assertArrayHasKey(6, $options);
$this->assertInstanceOf(TextConfigOption::class, $options[6]);
- $this->assertEquals('page-cache-id-prefix', $options[6]->getName());
+ $this->assertEquals('page-cache-redis-compression-lib', $options[6]->getName());
+
+ $this->assertArrayHasKey(7, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[7]);
+ $this->assertEquals('page-cache-id-prefix', $options[7]->getName());
}
/**
@@ -93,7 +97,8 @@ public function testCreateConfigWithRedis()
'port' => '',
'database' => '',
'compress_data' => '',
- 'password' => ''
+ 'password' => '',
+ 'compression_lib' => '',
],
'id_prefix' => $this->expectedIdPrefix(),
]
@@ -120,8 +125,9 @@ public function testCreateConfigWithRedisConfiguration()
'server' => 'foo.bar',
'port' => '9000',
'database' => '6',
+ 'password' => '',
'compress_data' => '1',
- 'password' => ''
+ 'compression_lib' => 'gzip',
],
'id_prefix' => $this->expectedIdPrefix(),
]
@@ -134,7 +140,8 @@ public function testCreateConfigWithRedisConfiguration()
'page-cache-redis-server' => 'foo.bar',
'page-cache-redis-port' => '9000',
'page-cache-redis-db' => '6',
- 'page-cache-redis-compress-data' => '1'
+ 'page-cache-redis-compress-data' => '1',
+ 'page-cache-redis-compression-lib' => 'gzip',
];
$configData = $this->configList->createConfig($options, $this->deploymentConfigMock);