diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index 73153aa5a64e5..6de6ea6c6c6bc 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -7,6 +7,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\LocalizedException; /** * @api @@ -561,6 +562,7 @@ public function getSpecifyOptionMessage() * @param \Magento\Catalog\Model\Product $product * @param string $processMode * @return array + * @throws LocalizedException */ protected function _prepareOptions(\Magento\Framework\DataObject $buyRequest, $product, $processMode) { @@ -571,20 +573,29 @@ protected function _prepareOptions(\Magento\Framework\DataObject $buyRequest, $p $options = $product->getOptions(); } if ($options !== null) { + $results = []; foreach ($options as $option) { /* @var $option \Magento\Catalog\Model\Product\Option */ - $group = $option->groupFactory($option->getType()) - ->setOption($option) - ->setProduct($product) - ->setRequest($buyRequest) - ->setProcessMode($processMode) - ->validateUserValue($buyRequest->getOptions()); + try { + $group = $option->groupFactory($option->getType()) + ->setOption($option) + ->setProduct($product) + ->setRequest($buyRequest) + ->setProcessMode($processMode) + ->validateUserValue($buyRequest->getOptions()); + } catch (LocalizedException $e) { + $results[] = $e->getMessage(); + continue; + } $preparedValue = $group->prepareForCart(); if ($preparedValue !== null) { $transport->options[$option->getId()] = $preparedValue; } } + if (count($results) > 0) { + throw new LocalizedException(__(implode("\n", $results))); + } } $eventName = sprintf('catalog_product_type_prepare_%s_options', $processMode); diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index ec9b0a87ff671..0da521c07d8ba 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -44,10 +44,20 @@ define([ return this.options.processStart && this.options.processStop; }, - submitForm: function(form) { - var self = this; + /** + * Handler for the form 'submit' event + * + * @param {Object} form + */ + submitForm: function (form) { + var addToCartButton, self = this; + if (form.has('input[type="file"]').length && form.find('input[type="file"]').val() !== '') { self.element.off('submit'); + // disable 'Add to Cart' button + addToCartButton = $(form).find(this.options.addToCartButtonSelector); + addToCartButton.prop('disabled', true); + addToCartButton.addClass(this.options.addToCartButtonDisabledClass); form.submit(); } else { self.ajaxSubmit(form); diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php index ed4ab88fb4a9d..634120fe43a6a 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php @@ -593,15 +593,13 @@ protected function _getDefaultSourceModel() */ public function isValueEmpty($value) { - $attrType = $this->getBackend()->getType(); - $isEmpty = (is_array($value) && count($value) == 0) || - $value === null || - $value === false && $attrType != 'int' || - $value === '' && ($attrType == 'int' || - $attrType == 'decimal' || - $attrType == 'datetime'); - - return $isEmpty; + /** @var array $emptyStringTypes list of attribute types that treat empty string as a possible value */ + $emptyStringTypes = ['int', 'decimal', 'datetime', 'varchar', 'text']; + $attributeType = $this->getBackend()->getType(); + return (is_array($value) && count($value) == 0) + || $value === null + || ($value === false && $attributeType != 'int') + || ($value === '' && in_array($attributeType, $emptyStringTypes)); } /** diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php index 873b4680a0dff..f083839929170 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php @@ -33,7 +33,6 @@ public function testGetOptionWhenOptionsAreSet() ->with(['options']) ->willReturn('expected value'); - $this->assertEquals('expected value', $model->getOptions()); } @@ -175,4 +174,59 @@ public function testGetValidationRulesWhenRuleIsEmpty() $this->assertEquals([], $model->getValidationRules()); } + + /** + * @param bool $isEmpty + * @param mixed $value + * @param string $attributeType + * @dataProvider attributeValueDataProvider + */ + public function testIsValueEmpty($isEmpty, $value, $attributeType) + { + /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $model */ + $model = $this->getMockForAbstractClass( + '\Magento\Eav\Model\Entity\Attribute\AbstractAttribute', + [], + '', + false, + false, + true, + [ + 'getBackend' + ] + ); + $backendModelMock = $this->getMockForAbstractClass( + '\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend', + [], + '', + false, + false, + true, + [ + 'getType' + ] + ); + $backendModelMock->expects($this->any())->method('getType')->willReturn($attributeType); + $model->expects($this->once())->method('getBackend')->willReturn($backendModelMock); + $this->assertEquals($isEmpty, $model->isValueEmpty($value)); + } + + /** + * @return array + */ + public function attributeValueDataProvider() + { + return [ + [true, '', 'int'], + [true, '', 'decimal'], + [true, '', 'datetime'], + [true, '', 'varchar'], + [true, '', 'text'], + [true, null, 'varchar'], + [true, [], 'varchar'], + [true, false, 'varchar'], + [false, 'not empty value', 'varchar'], + [false, false, 'int'], + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index 534a39ad58c9f..f660eda22d845 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -233,7 +233,7 @@ public function saveActionDataProvider() 'display_mode' => true, 'meta_title' => true, 'custom_design' => true, - 'page_layout' => true, + 'page_layout' => false, 'is_active' => true, 'include_in_menu' => true, 'landing_page' => true, @@ -242,7 +242,7 @@ public function saveActionDataProvider() 'description' => true, 'meta_keywords' => true, 'meta_description' => true, - 'custom_layout_update' => true, + 'custom_layout_update' => false, 'custom_design_from' => true, 'custom_design_to' => true, 'filter_price_range' => false diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/AbstractTypeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/AbstractTypeTest.php index fd074a3eb17ae..7dd3177f30bfc 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/AbstractTypeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/AbstractTypeTest.php @@ -190,7 +190,8 @@ public function testPrepareForCartOptionsException() ); $product = $repository->get('simple'); // fixture - $this->assertEquals( + + $this->assertContains( 'Please specify product\'s required option(s).', $this->_model->prepareForCart(new \Magento\Framework\DataObject(), $product) );