diff --git a/README.md b/README.md index 1dd81a7eed27250fcebe0720c3a5ebdf3994ab8e..c72357db26d16847c5fff1cfb7c6c93af44eae29 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,13 @@ Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a cutting edge, feature-rich eCommerce solution that gets results. ## Magento system requirements -[Magento system requirements](http://devdocs.magento.com/magento-system-requirements.html) +[Magento system requirements](http://devdocs.magento.com/guides/v2.2/install-gde/system-requirements2.html) ## Install Magento To install Magento, see either: * [Magento DevBox](https://magento.com/tech-resources/download), the easiest way to get started with Magento. -* [Installation guide](http://devdocs.magento.com/guides/v2.0/install-gde/bk-install-guide.html) +* [Installation guide](http://devdocs.magento.com/guides/v2.2/install-gde/bk-install-guide.html) <h2>Contributing to the Magento 2 code base</h2> Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes, optimizations, or just good suggestions. @@ -22,8 +22,8 @@ To learn about issues, click [here][2]. To open an issue, click [here][3]. To suggest documentation improvements, click [here][4]. -[1]: <http://devdocs.magento.com/guides/v2.0/contributor-guide/contributing.html> -[2]: <http://devdocs.magento.com/guides/v2.0/contributor-guide/contributing.html#report> +[1]: <http://devdocs.magento.com/guides/v2.2/contributor-guide/contributing.html> +[2]: <http://devdocs.magento.com/guides/v2.2/contributor-guide/contributing.html#report> [3]: <https://github.com/magento/magento2/issues> [4]: <http://devdocs.magento.com> diff --git a/app/code/Magento/Backup/Model/Db.php b/app/code/Magento/Backup/Model/Db.php index 98b04149cc7ead396c968b09168a6a319402846b..8fbd5da1c98425a59fd9336e30a731f9786bce8e 100644 --- a/app/code/Magento/Backup/Model/Db.php +++ b/app/code/Magento/Backup/Model/Db.php @@ -154,7 +154,7 @@ class Db implements \Magento\Framework\Backup\Db\BackupDbInterface if ($tableStatus->getDataLength() > self::BUFFER_LENGTH) { if ($tableStatus->getAvgRowLength() < self::BUFFER_LENGTH) { - $limit = floor(self::BUFFER_LENGTH / $tableStatus->getAvgRowLength()); + $limit = floor(self::BUFFER_LENGTH / max($tableStatus->getAvgRowLength(), 1)); $multiRowsLength = ceil($tableStatus->getRows() / $limit); } else { $limit = 1; diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Option.php b/app/code/Magento/Bundle/Model/ResourceModel/Option.php index 2ad7e57f522d664dd79c621ec188c354688a008f..46fd8b910f6f1e1b57d36ea4959361145b233bca 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Option.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Option.php @@ -81,31 +81,39 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { parent::_afterSave($object); - $condition = [ + $conditions = [ 'option_id = ?' => $object->getId(), 'store_id = ? OR store_id = 0' => $object->getStoreId(), 'parent_product_id = ?' => $object->getParentId() ]; $connection = $this->getConnection(); - $connection->delete($this->getTable('catalog_product_bundle_option_value'), $condition); - $data = new \Magento\Framework\DataObject(); - $data->setOptionId($object->getId()) - ->setStoreId($object->getStoreId()) - ->setParentProductId($object->getParentId()) - ->setTitle($object->getTitle()); - - $connection->insert($this->getTable('catalog_product_bundle_option_value'), $data->getData()); - - /** - * also saving default value if this store view scope - */ + if ($this->isOptionPresent($conditions)) { + $connection->update( + $this->getTable('catalog_product_bundle_option_value'), + [ + 'title' => $object->getTitle() + ], + $conditions + ); + } else { + $data = new \Magento\Framework\DataObject(); + $data->setOptionId($object->getId()) + ->setStoreId($object->getStoreId()) + ->setParentProductId($object->getParentId()) + ->setTitle($object->getTitle()); - if ($object->getStoreId()) { - $data->setStoreId(0); - $data->setTitle($object->getDefaultTitle()); $connection->insert($this->getTable('catalog_product_bundle_option_value'), $data->getData()); + + /** + * also saving default value if this store view scope + */ + if ($object->getStoreId()) { + $data->setStoreId(0); + $data->setTitle($object->getDefaultTitle()); + $connection->insert($this->getTable('catalog_product_bundle_option_value'), $data->getData()); + } } return $this; @@ -210,4 +218,26 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb return $this; } + + /** + * Is Bundle option present in the database + * + * @param array $conditions + * + * @return bool + */ + private function isOptionPresent($conditions) + { + $connection = $this->getConnection(); + + $select = $connection->select()->from($this->getTable('catalog_product_bundle_option_value')); + foreach ($conditions as $condition => $conditionValue) { + $select->where($condition, $conditionValue); + } + $select->limit(1); + + $rowSelect = $connection->fetchRow($select); + + return (is_array($rowSelect) && !empty($rowSelect)); + } } diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php index 6f8a45c6ac7ed75a13026f6a5d9dc0696cf1cedd..a5b6b34d324f7ea0fe4d3648cec5521ec5e1d6e1 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php @@ -228,7 +228,7 @@ class Tree extends \Magento\Catalog\Block\Adminhtml\Category\AbstractCategory public function getLoadTreeUrl($expanded = null) { $params = ['_current' => true, 'id' => null, 'store' => null]; - if (is_null($expanded) && $this->_backendSession->getIsTreeWasExpanded() || $expanded == true) { + if ($expanded === null && $this->_backendSession->getIsTreeWasExpanded() || $expanded == true) { $params['expand_all'] = true; } return $this->getUrl('*/*/categoriesJson', $params); diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Options/Option.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Options/Option.php index 64856a5c69dc7392146dc72acc27b35942cd07e0..339239ea491e091504e9ba8604ecd5b0e9468af1 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Options/Option.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Options/Option.php @@ -313,9 +313,9 @@ class Option extends Widget $value['checkboxScopeTitle'] = $this->getCheckboxScopeHtml( $option->getOptionId(), 'title', - is_null($option->getStoreTitle()) + $option->getStoreTitle() === null ); - $value['scopeTitleDisabled'] = is_null($option->getStoreTitle()) ? 'disabled' : null; + $value['scopeTitleDisabled'] = $option->getStoreTitle() === null ? 'disabled' : null; } if ($option->getGroupByType() == ProductCustomOptionInterface::OPTION_GROUP_SELECT) { @@ -341,22 +341,22 @@ class Option extends Widget $value['optionValues'][$i]['checkboxScopeTitle'] = $this->getCheckboxScopeHtml( $_value->getOptionId(), 'title', - is_null($_value->getStoreTitle()), + $_value->getStoreTitle() === null, $_value->getOptionTypeId() ); - $value['optionValues'][$i]['scopeTitleDisabled'] = is_null( - $_value->getStoreTitle() + $value['optionValues'][$i]['scopeTitleDisabled'] = ( + $_value->getStoreTitle() === null ) ? 'disabled' : null; if ($scope == \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE) { $value['optionValues'][$i]['checkboxScopePrice'] = $this->getCheckboxScopeHtml( $_value->getOptionId(), 'price', - is_null($_value->getstorePrice()), + $_value->getstorePrice() === null, $_value->getOptionTypeId(), ['$(this).up(1).previous()'] ); - $value['optionValues'][$i]['scopePriceDisabled'] = is_null( - $_value->getStorePrice() + $value['optionValues'][$i]['scopePriceDisabled'] = ( + $_value->getStorePrice() === null ) ? 'disabled' : null; } } @@ -379,9 +379,9 @@ class Option extends Widget $value['checkboxScopePrice'] = $this->getCheckboxScopeHtml( $option->getOptionId(), 'price', - is_null($option->getStorePrice()) + $option->getStorePrice() === null ); - $value['scopePriceDisabled'] = is_null($option->getStorePrice()) ? 'disabled' : null; + $value['scopePriceDisabled'] = $option->getStorePrice() === null ? 'disabled' : null; } } $values[] = new \Magento\Framework\DataObject($value); diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php index 95d9b1ae612088146de159c28e769af4e75fbd17..3f9dac98033aa1a631cc63f279fb89df9cbbcab0 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php @@ -121,7 +121,7 @@ class Related extends \Magento\Catalog\Block\Product\AbstractProduct implements * getIdentities() depends on _itemCollection populated, but it can be empty if the block is hidden * @see https://github.com/magento/magento2/issues/5897 */ - if (is_null($this->_itemCollection)) { + if ($this->_itemCollection === null) { $this->_prepareData(); } return $this->_itemCollection; diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php index f97d1a788dafb8909804178af162ba81873d9395..40afd443052629290b523a085be3ca8b5896f005 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php @@ -140,7 +140,7 @@ class Upsell extends \Magento\Catalog\Block\Product\AbstractProduct implements \ * getIdentities() depends on _itemCollection populated, but it can be empty if the block is hidden * @see https://github.com/magento/magento2/issues/5897 */ - if (is_null($this->_itemCollection)) { + if ($this->_itemCollection === null) { $this->_prepareData(); } return $this->_itemCollection; @@ -151,7 +151,7 @@ class Upsell extends \Magento\Catalog\Block\Product\AbstractProduct implements \ */ public function getItems() { - if (is_null($this->_items)) { + if ($this->_items === null) { $this->_items = $this->getItemCollection()->getItems(); } return $this->_items; diff --git a/app/code/Magento/Catalog/Helper/Product/View.php b/app/code/Magento/Catalog/Helper/Product/View.php index 46ac05168715b9644add204d151e22b6b3559648..ae948a362ab5a92fa67034d905dbbdc4e4dffd57 100644 --- a/app/code/Magento/Catalog/Helper/Product/View.php +++ b/app/code/Magento/Catalog/Helper/Product/View.php @@ -122,18 +122,18 @@ class View extends \Magento\Framework\App\Helper\AbstractHelper // Load default page handles and page configurations if ($params && $params->getBeforeHandles()) { foreach ($params->getBeforeHandles() as $handle) { - $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle); $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false); + $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle); } } - - $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku]); + $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], null, false); + $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku]); if ($params && $params->getAfterHandles()) { foreach ($params->getAfterHandles() as $handle) { - $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle); $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false); + $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle); } } diff --git a/app/code/Magento/Catalog/Model/Config.php b/app/code/Magento/Catalog/Model/Config.php index 227821463b7f055e9541d00b91926135c3b0dd14..b3b5204887ea10fb2e4aaa08d517067e712a823b 100644 --- a/app/code/Magento/Catalog/Model/Config.php +++ b/app/code/Magento/Catalog/Model/Config.php @@ -407,7 +407,7 @@ class Config extends \Magento\Eav\Model\Config */ public function getProductAttributes() { - if (is_null($this->_productAttributes)) { + if ($this->_productAttributes === null) { $this->_productAttributes = array_keys($this->getAttributesUsedInProductListing()); } return $this->_productAttributes; @@ -430,7 +430,7 @@ class Config extends \Magento\Eav\Model\Config */ public function getAttributesUsedInProductListing() { - if (is_null($this->_usedInProductListing)) { + if ($this->_usedInProductListing === null) { $this->_usedInProductListing = []; $entityType = \Magento\Catalog\Model\Product::ENTITY; $attributesData = $this->_getResource()->setStoreId($this->getStoreId())->getAttributesUsedInListing(); @@ -453,7 +453,7 @@ class Config extends \Magento\Eav\Model\Config */ public function getAttributesUsedForSortBy() { - if (is_null($this->_usedForSortBy)) { + if ($this->_usedForSortBy === null) { $this->_usedForSortBy = []; $entityType = \Magento\Catalog\Model\Product::ENTITY; $attributesData = $this->_getResource()->getAttributesUsedForSortBy(); diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/Price.php b/app/code/Magento/Catalog/Model/Layer/Filter/Price.php index ec5e2bff81ab36e0fdb75e398f4bbc52b0745036..68ef96c0f36a1eed800128180de34ba22f963200 100644 --- a/app/code/Magento/Catalog/Model/Layer/Filter/Price.php +++ b/app/code/Magento/Catalog/Model/Layer/Filter/Price.php @@ -150,7 +150,7 @@ class Price extends \Magento\Catalog\Model\Layer\Filter\AbstractFilter public function getCustomerGroupId() { $customerGroupId = $this->_getData('customer_group_id'); - if (is_null($customerGroupId)) { + if ($customerGroupId === null) { $customerGroupId = $this->_customerSession->getCustomerGroupId(); } @@ -176,7 +176,7 @@ class Price extends \Magento\Catalog\Model\Layer\Filter\AbstractFilter public function getCurrencyRate() { $rate = $this->_getData('currency_rate'); - if (is_null($rate)) { + if ($rate === null) { $rate = $this->_storeManager->getStore($this->getStoreId()) ->getCurrentCurrencyRate(); } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php index cd686c05908ce724cb2c61f59cc3e0694e47dd83..84770a4a93ed41461eb55ad8b2358be1a4ae05dc 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php @@ -91,7 +91,7 @@ abstract class AbstractGroupPrice extends Price */ protected function _getWebsiteCurrencyRates() { - if (is_null($this->_rates)) { + if ($this->_rates === null) { $this->_rates = []; $baseCurrency = $this->_config->getValue( \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, diff --git a/app/code/Magento/Catalog/Model/Product/Option/Value.php b/app/code/Magento/Catalog/Model/Product/Option/Value.php index d92646769b13b2dfba439393f852e2540b14bcb9..10aae63ed349ce51bcbf94cd1185c4676c068d76 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Value.php @@ -179,7 +179,7 @@ class Value extends AbstractModel implements \Magento\Catalog\Api\Data\ProductCu */ public function getProduct() { - if (is_null($this->_product)) { + if ($this->_product === null) { $this->_product = $this->getOption()->getProduct(); } return $this->_product; diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 83feea903f99343dae5f75dc01516945eae1e3d4..f260b01c02ef41d6b652c39750ed795c2f36e01f 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -6,10 +6,8 @@ */ namespace Magento\Catalog\Model; -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap; use Magento\Catalog\Model\ResourceModel\Product\Collection; -use Magento\Framework\Api\Data\ImageContentInterface; use Magento\Framework\Api\Data\ImageContentInterfaceFactory; use Magento\Framework\Api\ImageContentValidatorInterface; use Magento\Framework\Api\ImageProcessorInterface; @@ -18,10 +16,8 @@ use Magento\Framework\DB\Adapter\ConnectionException; use Magento\Framework\DB\Adapter\DeadlockException; use Magento\Framework\DB\Adapter\LockWaitException; use Magento\Framework\Exception\CouldNotSaveException; -use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Exception\StateException; use Magento\Framework\Exception\ValidatorException; /** @@ -116,11 +112,15 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa protected $fileSystem; /** + * @deprecated + * @see \Magento\Catalog\Model\MediaGalleryProcessor * @var ImageContentInterfaceFactory */ protected $contentFactory; /** + * @deprecated + * @see \Magento\Catalog\Model\MediaGalleryProcessor * @var ImageProcessorInterface */ protected $imageProcessor; @@ -131,7 +131,7 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa protected $extensionAttributesJoinProcessor; /** - * @var \Magento\Catalog\Model\Product\Gallery\Processor + * @var ProductRepository\MediaGalleryProcessor */ protected $mediaGalleryProcessor; @@ -329,6 +329,9 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa unset($productData['media_gallery']); if ($createNew) { $product = $this->productFactory->create(); + if (isset($productData['price']) && !isset($productData['product_type'])) { + $product->setTypeId(Product\Type::TYPE_SIMPLE); + } if ($this->storeManager->hasSingleStore()) { $product->setWebsiteIds([$this->storeManager->getStore(true)->getWebsiteId()]); } @@ -375,53 +378,6 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa $product->setWebsiteIds($websiteIds); } - /** - * @param ProductInterface $product - * @param array $newEntry - * @return $this - * @throws InputException - * @throws StateException - * @throws \Magento\Framework\Exception\LocalizedException - */ - protected function processNewMediaGalleryEntry( - ProductInterface $product, - array $newEntry - ) { - /** @var ImageContentInterface $contentDataObject */ - $contentDataObject = $newEntry['content']; - - /** @var \Magento\Catalog\Model\Product\Media\Config $mediaConfig */ - $mediaConfig = $product->getMediaConfig(); - $mediaTmpPath = $mediaConfig->getBaseTmpMediaPath(); - - $relativeFilePath = $this->imageProcessor->processImageContent($mediaTmpPath, $contentDataObject); - $tmpFilePath = $mediaConfig->getTmpMediaShortUrl($relativeFilePath); - - if (!$product->hasGalleryAttribute()) { - throw new StateException(__('Requested product does not support images.')); - } - - $imageFileUri = $this->getMediaGalleryProcessor()->addImage( - $product, - $tmpFilePath, - isset($newEntry['types']) ? $newEntry['types'] : [], - true, - isset($newEntry['disabled']) ? $newEntry['disabled'] : true - ); - // Update additional fields that are still empty after addImage call - $this->getMediaGalleryProcessor()->updateImage( - $product, - $imageFileUri, - [ - 'label' => $newEntry['label'], - 'position' => $newEntry['position'], - 'disabled' => $newEntry['disabled'], - 'media_type' => $newEntry['media_type'], - ] - ); - return $this; - } - /** * Process product links, creating new links, updating and deleting existing links * @@ -480,67 +436,6 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa return $this; } - /** - * Process Media gallery data before save product. - * - * Compare Media Gallery Entries Data with existing Media Gallery - * * If Media entry has not value_id set it as new - * * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag - * * Merge Existing and new media gallery - * - * @param ProductInterface $product contains only existing media gallery items - * @param array $mediaGalleryEntries array which contains all media gallery items - * @return $this - * @throws InputException - * @throws StateException - * @throws LocalizedException - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - protected function processMediaGallery(ProductInterface $product, $mediaGalleryEntries) - { - $existingMediaGallery = $product->getMediaGallery('images'); - $newEntries = []; - $entriesById = []; - if (!empty($existingMediaGallery)) { - foreach ($mediaGalleryEntries as $entry) { - if (isset($entry['id'])) { - $entriesById[$entry['id']] = $entry; - } else { - $newEntries[] = $entry; - } - } - foreach ($existingMediaGallery as $key => &$existingEntry) { - if (isset($entriesById[$existingEntry['value_id']])) { - $updatedEntry = $entriesById[$existingEntry['value_id']]; - if (array_key_exists('file', $updatedEntry) && $updatedEntry['file'] === null) { - unset($updatedEntry['file']); - } - $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry); - } else { - //set the removed flag - $existingEntry['removed'] = true; - } - } - unset($existingEntry); - $product->setData('media_gallery', ["images" => $existingMediaGallery]); - } else { - $newEntries = $mediaGalleryEntries; - } - - $this->getMediaGalleryProcessor()->clearMediaAttribute($product, array_keys($product->getMediaAttributes())); - $images = $product->getMediaGallery('images'); - if ($images) { - foreach ($images as $image) { - if (!isset($image['removed']) && !empty($image['types'])) { - $this->getMediaGalleryProcessor()->setMediaAttribute($product, $image['types'], $image['file']); - } - } - } - $this->processEntries($product, $newEntries, $entriesById); - - return $this; - } - /** * {@inheritdoc} * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -577,7 +472,10 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa $this->processLinks($product, $productLinks); if (isset($productDataArray['media_gallery_entries'])) { - $this->processMediaGallery($product, $productDataArray['media_gallery_entries']); + $this->getMediaGalleryProcessor()->processMediaGallery( + $product, + $productDataArray['media_gallery_entries'] + ); } if (!$product->getOptionsReadonly()) { @@ -749,13 +647,13 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa } /** - * @return Product\Gallery\Processor + * @return ProductRepository\MediaGalleryProcessor */ private function getMediaGalleryProcessor() { if (null === $this->mediaGalleryProcessor) { $this->mediaGalleryProcessor = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Model\Product\Gallery\Processor::class); + ->get(ProductRepository\MediaGalleryProcessor::class); } return $this->mediaGalleryProcessor; } @@ -775,60 +673,4 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa } return $this->collectionProcessor; } - - /** - * Convert extension attribute for product media gallery. - * - * @param array $newEntry - * @param array $extensionAttributes - * @return void - */ - private function processExtensionAttributes(array &$newEntry, array $extensionAttributes) - { - foreach ($extensionAttributes as $code => $value) { - if (is_array($value)) { - $this->processExtensionAttributes($newEntry, $value); - } else { - $newEntry[$code] = $value; - } - } - unset($newEntry['extension_attributes']); - } - - /** - * Convert entries into product media gallery data and set to product. - * - * @param ProductInterface $product - * @param array $newEntries - * @param array $entriesById - * @throws InputException - * @throws LocalizedException - * @throws StateException - * @return void - */ - private function processEntries(ProductInterface $product, array $newEntries, array $entriesById) - { - foreach ($newEntries as $newEntry) { - if (!isset($newEntry['content'])) { - throw new InputException(__('The image content is not valid.')); - } - /** @var ImageContentInterface $contentDataObject */ - $contentDataObject = $this->contentFactory->create() - ->setName($newEntry['content'][ImageContentInterface::NAME]) - ->setBase64EncodedData($newEntry['content'][ImageContentInterface::BASE64_ENCODED_DATA]) - ->setType($newEntry['content'][ImageContentInterface::TYPE]); - $newEntry['content'] = $contentDataObject; - $this->processNewMediaGalleryEntry($product, $newEntry); - - $finalGallery = $product->getData('media_gallery'); - $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById)); - if (isset($newEntry['extension_attributes'])) { - $this->processExtensionAttributes($newEntry, $newEntry['extension_attributes']); - } - $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]); - $entriesById[$newEntryId] = $newEntry; - $finalGallery['images'][$newEntryId] = $newEntry; - $product->setData('media_gallery', $finalGallery); - } - } } diff --git a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php new file mode 100644 index 0000000000000000000000000000000000000000..4cc31d98fdfc2fd1d0d1ddc9487c0c5a1e301f6b --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php @@ -0,0 +1,218 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\ProductRepository; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Gallery\Processor; +use Magento\Framework\Api\Data\ImageContentInterface; +use Magento\Framework\Api\Data\ImageContentInterfaceFactory; +use Magento\Framework\Api\ImageProcessorInterface; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\StateException; + +/** + * Process Media gallery data for ProductRepository before save product. + */ +class MediaGalleryProcessor +{ + /** + * @var Processor + */ + private $processor; + + /** + * @var ImageContentInterfaceFactory + */ + private $contentFactory; + + /** + * @var ImageProcessorInterface + */ + private $imageProcessor; + + /** + * MediaGalleryProcessor constructor. + * + * @param Processor $processor + * @param ImageContentInterfaceFactory $contentFactory + * @param ImageProcessorInterface $imageProcessor + */ + public function __construct( + Processor $processor, + ImageContentInterfaceFactory $contentFactory, + ImageProcessorInterface $imageProcessor + ) { + $this->processor = $processor; + $this->contentFactory = $contentFactory; + $this->imageProcessor = $imageProcessor; + } + + /** + * Process Media gallery data before save product. + * + * Compare Media Gallery Entries Data with existing Media Gallery + * * If Media entry has not value_id set it as new + * * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag + * * Merge Existing and new media gallery + * + * @param ProductInterface $product contains only existing media gallery items. + * @param array $mediaGalleryEntries array which contains all media gallery items. + * @return void + * @throws InputException + * @throws StateException + * @throws LocalizedException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + public function processMediaGallery(ProductInterface $product, array $mediaGalleryEntries) + { + $existingMediaGallery = $product->getMediaGallery('images'); + $newEntries = []; + $entriesById = []; + if (!empty($existingMediaGallery)) { + foreach ($mediaGalleryEntries as $entry) { + if (isset($entry['id'])) { + $entriesById[$entry['id']] = $entry; + } else { + $newEntries[] = $entry; + } + } + foreach ($existingMediaGallery as $key => &$existingEntry) { + if (isset($entriesById[$existingEntry['value_id']])) { + $updatedEntry = $entriesById[$existingEntry['value_id']]; + if (array_key_exists('file', $updatedEntry) && $updatedEntry['file'] === null) { + unset($updatedEntry['file']); + } + $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry); + } else { + //set the removed flag. + $existingEntry['removed'] = true; + } + } + unset($existingEntry); + $product->setData('media_gallery', ["images" => $existingMediaGallery]); + } else { + $newEntries = $mediaGalleryEntries; + } + + $this->processor->clearMediaAttribute($product, array_keys($product->getMediaAttributes())); + $images = $product->getMediaGallery('images'); + if ($images) { + foreach ($images as $image) { + if (!isset($image['removed']) && !empty($image['types'])) { + $this->processor->setMediaAttribute($product, $image['types'], $image['file']); + } + } + } + $this->processEntries($product, $newEntries, $entriesById); + } + + /** + * Convert entries into product media gallery data and set to product. + * + * @param ProductInterface $product + * @param array $newEntries + * @param array $entriesById + * @throws InputException + * @throws LocalizedException + * @throws StateException + * @return void + */ + private function processEntries(ProductInterface $product, array $newEntries, array $entriesById) + { + foreach ($newEntries as $newEntry) { + if (!isset($newEntry['content'])) { + throw new InputException(__('The image content is not valid.')); + } + /** @var ImageContentInterface $contentDataObject */ + $contentDataObject = $this->contentFactory->create() + ->setName($newEntry['content'][ImageContentInterface::NAME]) + ->setBase64EncodedData($newEntry['content'][ImageContentInterface::BASE64_ENCODED_DATA]) + ->setType($newEntry['content'][ImageContentInterface::TYPE]); + $newEntry['content'] = $contentDataObject; + $this->processNewMediaGalleryEntry($product, $newEntry); + + $finalGallery = $product->getData('media_gallery'); + $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById)); + if (isset($newEntry['extension_attributes'])) { + $this->processExtensionAttributes($newEntry, $newEntry['extension_attributes']); + } + $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]); + $entriesById[$newEntryId] = $newEntry; + $finalGallery['images'][$newEntryId] = $newEntry; + $product->setData('media_gallery', $finalGallery); + } + } + + /** + * Save gallery entry as image. + * + * @param ProductInterface $product + * @param array $newEntry + * @return void + * @throws InputException + * @throws StateException + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function processNewMediaGalleryEntry( + ProductInterface $product, + array $newEntry + ) { + /** @var ImageContentInterface $contentDataObject */ + $contentDataObject = $newEntry['content']; + + /** @var \Magento\Catalog\Model\Product\Media\Config $mediaConfig */ + $mediaConfig = $product->getMediaConfig(); + $mediaTmpPath = $mediaConfig->getBaseTmpMediaPath(); + + $relativeFilePath = $this->imageProcessor->processImageContent($mediaTmpPath, $contentDataObject); + $tmpFilePath = $mediaConfig->getTmpMediaShortUrl($relativeFilePath); + + if (!$product->hasGalleryAttribute()) { + throw new StateException(__('Requested product does not support images.')); + } + + $imageFileUri = $this->processor->addImage( + $product, + $tmpFilePath, + isset($newEntry['types']) ? $newEntry['types'] : [], + true, + isset($newEntry['disabled']) ? $newEntry['disabled'] : true + ); + // Update additional fields that are still empty after addImage call. + $this->processor->updateImage( + $product, + $imageFileUri, + [ + 'label' => $newEntry['label'], + 'position' => $newEntry['position'], + 'disabled' => $newEntry['disabled'], + 'media_type' => $newEntry['media_type'], + ] + ); + } + + /** + * Convert extension attribute for product media gallery. + * + * @param array $newEntry + * @param array $extensionAttributes + * @return void + */ + private function processExtensionAttributes(array &$newEntry, array $extensionAttributes) + { + foreach ($extensionAttributes as $code => $value) { + if (is_array($value)) { + $this->processExtensionAttributes($newEntry, $value); + } else { + $newEntry[$code] = $value; + } + } + unset($newEntry['extension_attributes']); + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 58e8424663c83cf226ccc7d1a0c8fa5b200f25b3..925c9fe60855d247fef190d9b4bce3322eb0cafb 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1092,7 +1092,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac protected function _getSelectCountSql($select = null, $resetLeftJoins = true) { $this->_renderFilters(); - $countSelect = is_null($select) ? $this->_getClearSelect() : $this->_buildClearSelect($select); + $countSelect = $select === null ? $this->_getClearSelect() : $this->_buildClearSelect($select); $countSelect->columns('COUNT(DISTINCT e.entity_id)'); if ($resetLeftJoins) { $countSelect->resetJoinLeft(); @@ -1435,7 +1435,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac $ids = $this->_allIdsCache; } - if (is_null($ids)) { + if ($ids === null) { $ids = $this->getAllIds(); $this->setAllIdsCache($ids); } @@ -1466,17 +1466,17 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac { $this->_productLimitationFilters->setUsePriceIndex(true); - if (!isset($this->_productLimitationFilters['customer_group_id']) && is_null($customerGroupId)) { + if (!isset($this->_productLimitationFilters['customer_group_id']) && $customerGroupId === null) { $customerGroupId = $this->_customerSession->getCustomerGroupId(); } - if (!isset($this->_productLimitationFilters['website_id']) && is_null($websiteId)) { + if (!isset($this->_productLimitationFilters['website_id']) && $websiteId === null) { $websiteId = $this->_storeManager->getStore($this->getStoreId())->getWebsiteId(); } - if (!is_null($customerGroupId)) { + if ($customerGroupId !== null) { $this->_productLimitationFilters['customer_group_id'] = $customerGroupId; } - if (!is_null($websiteId)) { + if ($websiteId !== null) { $this->_productLimitationFilters['website_id'] = $websiteId; } @@ -2347,7 +2347,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac */ public function getMaxPrice() { - if (is_null($this->_maxPrice)) { + if ($this->_maxPrice === null) { $this->_prepareStatisticsData(); } @@ -2361,7 +2361,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac */ public function getMinPrice() { - if (is_null($this->_minPrice)) { + if ($this->_minPrice === null) { $this->_prepareStatisticsData(); } @@ -2375,7 +2375,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac */ public function getPriceStandardDeviation() { - if (is_null($this->_priceStandardDeviation)) { + if ($this->_priceStandardDeviation === null) { $this->_prepareStatisticsData(); } @@ -2389,7 +2389,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac */ public function getPricesCount() { - if (is_null($this->_pricesCount)) { + if ($this->_pricesCount === null) { $this->_prepareStatisticsData(); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepository/MediaGalleryProcessorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepository/MediaGalleryProcessorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..02773b2fb3d703e507101ca3ad899c53fdacc84f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepository/MediaGalleryProcessorTest.php @@ -0,0 +1,227 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\ProductRepository; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Gallery\Processor; +use Magento\Catalog\Model\ProductRepository\MediaGalleryProcessor; +use Magento\Framework\Api\Data\ImageContentInterface; +use Magento\Framework\Api\Data\ImageContentInterfaceFactory; +use Magento\Framework\Api\ImageProcessorInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Provide tests for ProductRepository/MediaGalleryProcessor. + */ +class MediaGalleryProcessorTest extends TestCase +{ + /** + * Test subject. + * + * @var MediaGalleryProcessor + */ + private $model; + + /** + * @var Processor|\PHPUnit_Framework_MockObject_MockObject + */ + private $processor; + + /** + * @var ImageContentInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $contentFactory; + + /** + * @var ImageProcessorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $imageProcessor; + + /** + * @var Product|\PHPUnit_Framework_MockObject_MockObject + */ + private $product; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->product = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + [ + 'hasGalleryAttribute', + 'getMediaConfig', + 'getMediaAttributes', + 'getMediaGalleryEntries', + ] + ); + $this->product->expects($this->any()) + ->method('hasGalleryAttribute') + ->willReturn(true); + $this->processor = $this->getMockBuilder(Processor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->contentFactory = $this->getMockBuilder(ImageContentInterfaceFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->imageProcessor = $this->getMockBuilder(ImageProcessorInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + MediaGalleryProcessor::class, + [ + 'processor' => $this->processor, + 'contentFactory' => $this->contentFactory, + 'imageProcessor' => $this->imageProcessor, + ] + ); + } + + /** + * Test add image. + * + * @return void + */ + public function testProcessWithNewMediaEntry() + { + $mediaGalleryEntries = [ + [ + 'value_id' => null, + 'label' => 'label_text', + 'position' => 10, + 'disabled' => false, + 'types' => ['image', 'small_image'], + 'content' => [ + ImageContentInterface::NAME => 'filename', + ImageContentInterface::TYPE => 'image/jpeg', + ImageContentInterface::BASE64_ENCODED_DATA => 'encoded_content', + ], + 'media_type' => 'media_type', + ], + ]; + + //setup media attribute backend. + $mediaTmpPath = '/tmp'; + $absolutePath = '/a/b/filename.jpg'; + $mediaConfigMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Media\Config::class) + ->disableOriginalConstructor() + ->getMock(); + $mediaConfigMock->expects($this->once()) + ->method('getTmpMediaShortUrl') + ->with($absolutePath) + ->willReturn($mediaTmpPath . $absolutePath); + $this->product->setData('media_gallery', ['images' => $mediaGalleryEntries]); + $this->product->expects($this->any()) + ->method('getMediaAttributes') + ->willReturn(['image' => 'imageAttribute', 'small_image' => 'small_image_attribute']); + $this->product->expects($this->once()) + ->method('getMediaConfig') + ->willReturn($mediaConfigMock); + $this->processor->expects($this->once())->method('clearMediaAttribute') + ->with($this->product, ['image', 'small_image']); + + //verify new entries. + $contentDataObject = $this->getMockBuilder(\Magento\Framework\Api\ImageContent::class) + ->disableOriginalConstructor() + ->setMethods(null) + ->getMock(); + $this->contentFactory->expects($this->once()) + ->method('create') + ->willReturn($contentDataObject); + + $this->imageProcessor->expects($this->once()) + ->method('processImageContent') + ->willReturn($absolutePath); + + $imageFileUri = 'imageFileUri'; + $this->processor->expects($this->once())->method('addImage') + ->with($this->product, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false) + ->willReturn($imageFileUri); + $this->processor->expects($this->once())->method('updateImage') + ->with( + $this->product, + $imageFileUri, + [ + 'label' => 'label_text', + 'position' => 10, + 'disabled' => false, + 'media_type' => 'media_type', + ] + ); + + $this->model->processMediaGallery($this->product, $mediaGalleryEntries); + } + + /** + * Test update(delete) images. + */ + public function testProcessExistingWithMediaGalleryEntries() + { + //update one entry, delete one entry. + $newEntries = [ + [ + 'id' => 5, + 'label' => 'new_label_text', + 'file' => 'filename1', + 'position' => 10, + 'disabled' => false, + 'types' => ['image', 'small_image'], + ], + ]; + + $existingMediaGallery = [ + 'images' => [ + [ + 'value_id' => 5, + 'label' => 'label_text', + 'file' => 'filename1', + 'position' => 10, + 'disabled' => true, + ], + [ + 'value_id' => 6, //will be deleted. + 'file' => 'filename2', + ], + ], + ]; + + $expectedResult = [ + [ + 'value_id' => 5, + 'id' => 5, + 'label' => 'new_label_text', + 'file' => 'filename1', + 'position' => 10, + 'disabled' => false, + 'types' => ['image', 'small_image'], + ], + [ + 'value_id' => 6, //will be deleted. + 'file' => 'filename2', + 'removed' => true, + ], + ]; + + $this->product->setData('media_gallery', $existingMediaGallery); + $this->product->expects($this->any()) + ->method('getMediaAttributes') + ->willReturn(['image' => 'filename1', 'small_image' => 'filename2']); + + $this->processor->expects($this->once())->method('clearMediaAttribute') + ->with($this->product, ['image', 'small_image']); + $this->processor->expects($this->once()) + ->method('setMediaAttribute') + ->with($this->product, ['image', 'small_image'], 'filename1'); + $this->model->processMediaGallery($this->product, $newEntries); + $this->assertEquals($expectedResult, $this->product->getMediaGallery('images')); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index a220b9a5768fed1b0d043cb7d1629439e657fc7d..14c84f4781a3aa962dbbf04f2ec60d1e11954915 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -9,6 +9,7 @@ namespace Magento\Catalog\Test\Unit\Model; +use Magento\Catalog\Model\ProductRepository\MediaGalleryProcessor; use Magento\Framework\Api\Data\ImageContentInterface; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\DB\Adapter\ConnectionException; @@ -139,7 +140,7 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase protected $storeManagerMock; /** - * @var \Magento\Catalog\Model\Product\Gallery\Processor|\PHPUnit_Framework_MockObject_MockObject + * @var MediaGalleryProcessor|\PHPUnit_Framework_MockObject_MockObject */ protected $mediaGalleryProcessor; @@ -234,7 +235,7 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase $storeMock->expects($this->any())->method('getCode')->willReturn(\Magento\Store\Model\Store::ADMIN_CODE); $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); - $this->mediaGalleryProcessor = $this->createMock(\Magento\Catalog\Model\Product\Gallery\Processor::class); + $this->mediaGalleryProcessor = $this->createMock(MediaGalleryProcessor::class); $this->collectionProcessorMock = $this->getMockBuilder(CollectionProcessorInterface::class) ->getMock(); @@ -1174,7 +1175,21 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase ] ] ]; - + $expectedEntriesData = [ + [ + 'id' => null, + 'label' => "label_text", + 'position' => 10, + 'disabled' => false, + 'types' => ['image', 'small_image'], + 'content' => [ + ImageContentInterface::NAME => 'filename', + ImageContentInterface::TYPE => 'image/jpeg', + ImageContentInterface::BASE64_ENCODED_DATA => 'encoded_content', + ], + 'media_type' => 'media_type', + ], + ]; $this->setupProductMocksForSave(); //media gallery data $this->productData['media_gallery_entries'] = [ @@ -1198,56 +1213,8 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase ->will($this->returnValue($this->productData)); $this->initializedProductMock->setData('media_gallery', $newEntriesData); - $this->initializedProductMock->expects($this->any()) - ->method('getMediaAttributes') - ->willReturn(["image" => "imageAttribute", "small_image" => "small_image_attribute"]); - - //setup media attribute backend - $mediaTmpPath = '/tmp'; - $absolutePath = '/a/b/filename.jpg'; - - $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute') - ->with($this->initializedProductMock, ['image', 'small_image']); - - $mediaConfigMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Media\Config::class) - ->disableOriginalConstructor() - ->getMock(); - $mediaConfigMock->expects($this->once()) - ->method('getTmpMediaShortUrl') - ->with($absolutePath) - ->willReturn($mediaTmpPath . $absolutePath); - $this->initializedProductMock->expects($this->once()) - ->method('getMediaConfig') - ->willReturn($mediaConfigMock); - - //verify new entries - $contentDataObject = $this->getMockBuilder(\Magento\Framework\Api\ImageContent::class) - ->disableOriginalConstructor() - ->setMethods(null) - ->getMock(); - $this->contentFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($contentDataObject); - - $this->imageProcessorMock->expects($this->once()) - ->method('processImageContent') - ->willReturn($absolutePath); - - $imageFileUri = "imageFileUri"; - $this->mediaGalleryProcessor->expects($this->once())->method('addImage') - ->with($this->initializedProductMock, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false) - ->willReturn($imageFileUri); - $this->mediaGalleryProcessor->expects($this->once())->method('updateImage') - ->with( - $this->initializedProductMock, - $imageFileUri, - [ - 'label' => 'label_text', - 'position' => 10, - 'disabled' => false, - 'media_type' => 'media_type', - ] - ); + $this->mediaGalleryProcessor->expects($this->once())->method('processMediaGallery') + ->with($this->initializedProductMock, $expectedEntriesData); $this->initializedProductMock->expects($this->once())->method('getWebsiteIds')->willReturn([]); $this->initializedProductMock->expects($this->atLeastOnce()) ->method('getSku')->willReturn($this->productData['sku']); @@ -1325,24 +1292,6 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase ], ], ]; - - $expectedResult = [ - [ - 'value_id' => 5, - 'id' => 5, - "label" => "new_label_text", - 'file' => 'filename1', - 'position' => 10, - 'disabled' => false, - 'types' => ['image', 'small_image'], - ], - [ - 'value_id' => 6, //will be deleted - 'file' => 'filename2', - 'removed' => true, - ], - ]; - $this->setupProductMocksForSave(); //media gallery data $this->productData['media_gallery_entries'] = $newEntries; @@ -1352,21 +1301,15 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase ->will($this->returnValue($this->productData)); $this->initializedProductMock->setData('media_gallery', $existingMediaGallery); - $this->initializedProductMock->expects($this->any()) - ->method('getMediaAttributes') - ->willReturn(["image" => "filename1", "small_image" => "filename2"]); - $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute') - ->with($this->initializedProductMock, ['image', 'small_image']); $this->mediaGalleryProcessor->expects($this->once()) - ->method('setMediaAttribute') - ->with($this->initializedProductMock, ['image', 'small_image'], 'filename1'); + ->method('processMediaGallery') + ->with($this->initializedProductMock, $newEntries); $this->initializedProductMock->expects($this->once())->method('getWebsiteIds')->willReturn([]); $this->initializedProductMock->expects($this->atLeastOnce()) ->method('getSku')->willReturn($this->productData['sku']); $this->productMock->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']); $this->productMock->expects($this->any())->method('getMediaGalleryEntries')->willReturn(null); $this->model->save($this->productMock); - $this->assertEquals($expectedResult, $this->initializedProductMock->getMediaGallery('images')); } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml index fc0967ca60d2d0c0701a74ecd421585bc09f38fc..862375503691caba52f6c33432f9210ffcf2e06e 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml @@ -146,8 +146,8 @@ switch ($type = $block->getType()) { } break; - case 'other': - break; + default: + $exist = null; } ?> diff --git a/app/code/Magento/CatalogInventory/etc/events.xml b/app/code/Magento/CatalogInventory/etc/events.xml index 0a9f3c2d40dcad6495d77ef2269f20b72d48da4e..3197501e9b70b1f410f4765c2282276dfceed924 100644 --- a/app/code/Magento/CatalogInventory/etc/events.xml +++ b/app/code/Magento/CatalogInventory/etc/events.xml @@ -27,9 +27,6 @@ <event name="sales_model_service_quote_submit_failure"> <observer name="inventory" instance="Magento\CatalogInventory\Observer\RevertQuoteInventoryObserver"/> </event> - <event name="restore_quote"> - <observer name="inventory" instance="Magento\CatalogInventory\Observer\RevertQuoteInventoryObserver"/> - </event> <event name="sales_order_item_cancel"> <observer name="inventory" instance="Magento\CatalogInventory\Observer\CancelOrderItemObserver"/> </event> diff --git a/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml b/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml index 201d6ffe4c68344608f274589dd647f69a336d9f..574cbe1107e883b110fff30fcc384b8415d0b205 100644 --- a/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml +++ b/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml @@ -35,8 +35,7 @@ <ol class="product-items <?= /* @noEscape */ $type ?>"> <?php $iterator = 1; ?> <?php foreach ($items as $_item): ?> - <?php if ($iterator++ != 1): ?></li><?php endif ?> - <li class="product-item"> + <?= /* @noEscape */ ($iterator++ == 1) ? '<li class="product-item">' : '</li><li class="product-item">' ?> <div class="product-item-info"> <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product-item-photo"> <?= $block->getImage($_item, $image)->toHtml() ?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/coupon.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/coupon.phtml index b224c96f07e9bb42c1c24a4bc8a752821e15b3ec..1d67b325e01c5d07e709144cdc0fec7745f94e1d 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/coupon.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/coupon.phtml @@ -24,7 +24,7 @@ <div class="field"> <label for="coupon_code" class="label"><span><?= /* @escapeNotVerified */ __('Enter discount code') ?></span></label> <div class="control"> - <input type="text" class="input-text" id="coupon_code" name="coupon_code" value="<?= $block->escapeHtml($block->getCouponCode()) ?>" placeholder="<?= $block->escapeHtml(__('Enter discount code')) ?>" /> + <input type="text" class="input-text" id="coupon_code" name="coupon_code" value="<?= $block->escapeHtml($block->getCouponCode()) ?>" placeholder="<?= $block->escapeHtml(__('Enter discount code')) ?>" <?php if (strlen($block->getCouponCode())): ?> disabled="disabled" <?php endif; ?> /> </div> </div> <div class="actions-toolbar"> diff --git a/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php b/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php index dceb5767edae91ce12db919370c0ea0a38c33d0d..42d7d91fb90e85785acdb3f6a53f1d37f6e680a6 100644 --- a/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php +++ b/app/code/Magento/ConfigurableProductSales/Model/Order/Reorder/OrderedProductAvailabilityChecker.php @@ -45,7 +45,7 @@ class OrderedProductAvailabilityChecker implements OrderedProductAvailabilityChe public function isAvailable(Item $item) { $buyRequest = $item->getBuyRequest(); - $superAttribute = $buyRequest->getData()['super_attribute']; + $superAttribute = $buyRequest->getData()['super_attribute'] ?? []; $connection = $this->getConnection(); $select = $connection->select(); $orderItemParentId = $item->getParentItem()->getProductId(); diff --git a/app/code/Magento/Eav/Model/Attribute.php b/app/code/Magento/Eav/Model/Attribute.php index 64504b59fe9c96ac318cd23a5b83413477cc1998..e53f3ccc82a755eb71272bfd600a987da58b54e7 100644 --- a/app/code/Magento/Eav/Model/Attribute.php +++ b/app/code/Magento/Eav/Model/Attribute.php @@ -62,7 +62,7 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute */ public function getWebsite() { - if (is_null($this->_website)) { + if ($this->_website === null) { $this->_website = $this->_storeManager->getWebsite(); } @@ -88,7 +88,7 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute public function getUsedInForms() { $forms = $this->getData('used_in_forms'); - if (is_null($forms)) { + if ($forms === null) { $forms = $this->_getResource()->getUsedInForms($this); $this->setData('used_in_forms', $forms); } diff --git a/app/code/Magento/Eav/Model/Attribute/Data/AbstractData.php b/app/code/Magento/Eav/Model/Attribute/Data/AbstractData.php index ed052cb71e2fa094f41225f18ace2f84ec4285fd..12023acc3b33b37bacbb2f63e7e5e3f32adccd96 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/AbstractData.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/AbstractData.php @@ -205,7 +205,7 @@ abstract class AbstractData */ public function getExtractedData($index = null) { - if (!is_null($index)) { + if ($index !== null) { if (isset($this->_extractedData[$index])) { return $this->_extractedData[$index]; } @@ -262,9 +262,9 @@ abstract class AbstractData */ protected function _dateFilterFormat($format = null) { - if (is_null($format)) { + if ($format === null) { // get format - if (is_null($this->_dateFilterFormat)) { + if ($this->_dateFilterFormat === null) { $this->_dateFilterFormat = \IntlDateFormatter::SHORT; } return $this->_localeDate->getDateFormat($this->_dateFilterFormat); diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index b0d186705026f86b681c9997535f43ebe8869530..feb2c4d7d3f9faed766a36414d1f9a1d1ffca2cf 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -1294,7 +1294,7 @@ abstract class AbstractEntity extends AbstractResource implements EntityInterfac $origData = $this->_getOrigObject($newObject)->getOrigData(); } - if (is_null($origData)) { + if ($origData === null) { $origData = []; } diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/Datetime.php b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/Datetime.php index 786060ea7b312cec5da89d4325bb309375816044..1a5f0a7811de34d440e5a18ca7a2943f0f56cc06 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/Datetime.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/Datetime.php @@ -49,7 +49,7 @@ class Datetime extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBacke throw new \Magento\Framework\Exception\LocalizedException(__('Invalid date')); } - if (is_null($value)) { + if ($value === null) { $value = $object->getData($attributeName); } diff --git a/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js b/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js index 324881cdc502821ea4251d43bc0ef24abce95d1c..cd6292b39e98921e649b3afc4bb3965063129725 100644 --- a/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js +++ b/app/code/Magento/GoogleAnalytics/view/frontend/web/js/google-analytics.js @@ -51,10 +51,9 @@ define([ if (config.pageTrackingData.isAnonymizedIpActive) { ga('set', 'anonymizeIp', true); } - ga('send', 'pageview' + config.pageTrackingData.optPageUrl); // Process orders data - if (config.ordersTrackingData) { + if (config.ordersTrackingData.length) { ga('require', 'ec', 'ec.js'); //Set currency code @@ -75,6 +74,9 @@ define([ } ga('send', 'pageview'); + }else{ + // Process Data if not orders + ga('send', 'pageview' + config.pageTrackingData.optPageUrl); } } } diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index b8d7ccf83af7ccc29a6e322e769cd39b267030d5..8f29798472f19c11178f4480c5272d2b4638b55d 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -604,14 +604,20 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel $this->save(); $sendSubscription = $sendInformationEmail; - if ($sendSubscription === null xor $sendSubscription) { + if ($sendSubscription === null xor $sendSubscription && $this->isStatusChanged()) { try { - if ($isConfirmNeed) { - $this->sendConfirmationRequestEmail(); - } elseif ($this->isStatusChanged() && $status == self::STATUS_UNSUBSCRIBED) { - $this->sendUnsubscriptionEmail(); - } elseif ($this->isStatusChanged() && $status == self::STATUS_SUBSCRIBED) { - $this->sendConfirmationSuccessEmail(); + switch ($status) { + case self::STATUS_UNSUBSCRIBED: + $this->sendUnsubscriptionEmail(); + break; + case self::STATUS_SUBSCRIBED: + $this->sendConfirmationSuccessEmail(); + break; + case self::STATUS_NOT_ACTIVE: + if ($isConfirmNeed) { + $this->sendConfirmationRequestEmail(); + } + break; } } catch (MailException $e) { // If we are not able to send a new account email, this should be ignored diff --git a/app/code/Magento/Newsletter/Setup/UpgradeSchema.php b/app/code/Magento/Newsletter/Setup/UpgradeSchema.php new file mode 100644 index 0000000000000000000000000000000000000000..e7ce898de83a3e04fa3369b963b22137b480243d --- /dev/null +++ b/app/code/Magento/Newsletter/Setup/UpgradeSchema.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Newsletter\Setup; + +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\SchemaSetupInterface; +use Magento\Framework\Setup\UpgradeSchemaInterface; + +/** + * Upgrade the Newsletter module DB scheme + */ +class UpgradeSchema implements UpgradeSchemaInterface +{ + /** + * {@inheritdoc} + */ + public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context) + { + $setup->startSetup(); + + if (version_compare($context->getVersion(), '2.0.1', '<')) { + $connection = $setup->getConnection(); + + $connection->addIndex( + $setup->getTable('newsletter_subscriber'), + $setup->getIdxName('newsletter_subscriber', ['subscriber_email']), + ['subscriber_email'] + ); + } + + $setup->endSetup(); + } +} diff --git a/app/code/Magento/Newsletter/etc/module.xml b/app/code/Magento/Newsletter/etc/module.xml index f338445225222a56ab82e2baebee390ccabefe36..5da16a9a3e9ba7b76c73cbeb9236dcd15a71a3b2 100644 --- a/app/code/Magento/Newsletter/etc/module.xml +++ b/app/code/Magento/Newsletter/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Newsletter" setup_version="2.0.0"> + <module name="Magento_Newsletter" setup_version="2.0.1"> <sequence> <module name="Magento_Store"/> <module name="Magento_Customer"/> diff --git a/app/code/Magento/ProductAlert/Controller/Add/TestObserver.php b/app/code/Magento/ProductAlert/Controller/Add/TestObserver.php deleted file mode 100644 index 74f03220e59d381e0f8af2e788e6484e0d7fdbc9..0000000000000000000000000000000000000000 --- a/app/code/Magento/ProductAlert/Controller/Add/TestObserver.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\ProductAlert\Controller\Add; - -use Magento\ProductAlert\Controller\Add as AddController; -use Magento\Framework\DataObject; - -class TestObserver extends AddController -{ - /** - * @return void - */ - public function execute() - { - $object = new DataObject(); - /** @var \Magento\ProductAlert\Model\Observer $observer */ - $observer = $this->_objectManager->get(\Magento\ProductAlert\Model\Observer::class); - $observer->process($object); - } -} diff --git a/app/code/Magento/Quote/Model/Quote/Item.php b/app/code/Magento/Quote/Model/Quote/Item.php index d8177ddfe5236500cb5d2099bfab2b6d47ee9391..fe6d712500bcd7e6d61a782fe757dc78f67c96e5 100644 --- a/app/code/Magento/Quote/Model/Quote/Item.php +++ b/app/code/Magento/Quote/Model/Quote/Item.php @@ -745,6 +745,9 @@ class Item extends \Magento\Quote\Model\Quote\Item\AbstractItem implements \Mage unset($this->_options[$index]); unset($this->_optionsByCode[$option->getCode()]); } else { + if (!$option->getItem() || !$option->getItem()->getId()) { + $option->setItem($this); + } $option->save(); } } diff --git a/app/code/Magento/Quote/Model/Quote/Validator/MinimumOrderAmount/ValidationMessage.php b/app/code/Magento/Quote/Model/Quote/Validator/MinimumOrderAmount/ValidationMessage.php index 3113721f8a59781406107f23a54f832b11fb976b..38bfcbf1d30ca445785faee62117cd4dfa57957f 100644 --- a/app/code/Magento/Quote/Model/Quote/Validator/MinimumOrderAmount/ValidationMessage.php +++ b/app/code/Magento/Quote/Model/Quote/Validator/MinimumOrderAmount/ValidationMessage.php @@ -19,22 +19,32 @@ class ValidationMessage /** * @var \Magento\Framework\Locale\CurrencyInterface + * @deprecated since 101.0.0 */ private $currency; + /** + * @var \Magento\Framework\Pricing\Helper\Data + */ + private $priceHelper; + /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Framework\Locale\CurrencyInterface $currency + * @param \Magento\Framework\Pricing\Helper\Data $priceHelper */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Locale\CurrencyInterface $currency + \Magento\Framework\Locale\CurrencyInterface $currency, + \Magento\Framework\Pricing\Helper\Data $priceHelper = null ) { $this->scopeConfig = $scopeConfig; $this->storeManager = $storeManager; $this->currency = $currency; + $this->priceHelper = $priceHelper ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Pricing\Helper\Data::class); } /** @@ -50,13 +60,11 @@ class ValidationMessage \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); if (!$message) { - $currencyCode = $this->storeManager->getStore()->getCurrentCurrencyCode(); - $minimumAmount = $this->currency->getCurrency($currencyCode)->toCurrency( - $this->scopeConfig->getValue( - 'sales/minimum_order/amount', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - ); + $minimumAmount = $this->priceHelper->currency($this->scopeConfig->getValue( + 'sales/minimum_order/amount', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ), true, false); + $message = __('Minimum order amount is %1', $minimumAmount); } else { //Added in order to address the issue: https://github.com/magento/magento2/issues/8287 diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Validator/MinimumOrderAmount/ValidationMessageTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Validator/MinimumOrderAmount/ValidationMessageTest.php index 64204ea1fb93dbeb35cf0b5f66bbd85632c787eb..272a4e3a4ba49206273330c8d864b262ea402bdf 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Validator/MinimumOrderAmount/ValidationMessageTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Validator/MinimumOrderAmount/ValidationMessageTest.php @@ -26,19 +26,27 @@ class ValidationMessageTest extends \PHPUnit\Framework\TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject + * @deprecated since 101.0.0 */ private $currencyMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $priceHelperMock; + protected function setUp() { $this->scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); $this->currencyMock = $this->createMock(\Magento\Framework\Locale\CurrencyInterface::class); + $this->priceHelperMock = $this->createMock(\Magento\Framework\Pricing\Helper\Data::class); $this->model = new \Magento\Quote\Model\Quote\Validator\MinimumOrderAmount\ValidationMessage( $this->scopeConfigMock, $this->storeManagerMock, - $this->currencyMock + $this->currencyMock, + $this->priceHelperMock ); } @@ -46,8 +54,6 @@ class ValidationMessageTest extends \PHPUnit\Framework\TestCase { $minimumAmount = 20; $minimumAmountCurrency = '$20'; - $currencyCode = 'currency_code'; - $this->scopeConfigMock->expects($this->at(0)) ->method('getValue') ->with('sales/minimum_order/description', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) @@ -58,27 +64,13 @@ class ValidationMessageTest extends \PHPUnit\Framework\TestCase ->with('sales/minimum_order/amount', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) ->willReturn($minimumAmount); - $storeMock = $this->createPartialMock(\Magento\Store\Model\Store::class, ['getCurrentCurrencyCode']); - $storeMock->expects($this->once())->method('getCurrentCurrencyCode')->willReturn($currencyCode); - $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($storeMock); + $this->priceHelperMock->expects($this->once()) + ->method('currency') + ->with($minimumAmount, true, false) + ->will($this->returnValue($minimumAmountCurrency)); - $currencyMock = $this->createMock(\Magento\Framework\Currency::class); - $this->currencyMock->expects($this->once()) - ->method('getCurrency') - ->with($currencyCode) - ->willReturn($currencyMock); - - $currencyMock->expects($this->once()) - ->method('toCurrency') - ->with($minimumAmount) - ->willReturn($minimumAmountCurrency); - - $this->assertEquals( - __('Minimum order amount is %1', $minimumAmountCurrency), - $this->model->getMessage() - ); + $this->assertEquals(__('Minimum order amount is %1', $minimumAmountCurrency), $this->model->getMessage()); } - public function testGetConfigMessage() { $configMessage = 'config_message'; diff --git a/app/code/Magento/Review/Controller/Product/ListAction.php b/app/code/Magento/Review/Controller/Product/ListAction.php index dd8b272867c551aefd5690f51ae65c9a58dad7af..26344d125172a86fe32582230eee2033a0139d30 100644 --- a/app/code/Magento/Review/Controller/Product/ListAction.php +++ b/app/code/Magento/Review/Controller/Product/ListAction.php @@ -26,8 +26,8 @@ class ListAction extends ProductController $resultPage->getConfig()->setPageLayout($product->getPageLayout()); } $urlSafeSku = rawurlencode($product->getSku()); - $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku]); $resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], null, false); + $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku]); $resultPage->addUpdate($product->getCustomLayoutUpdate()); return $resultPage; } diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 69f4d19e4dd630a45242df8e507de199525602d5..8ea09e6fb3d42de9e8ffc9b6c2f061200d37e2a3 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -681,7 +681,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ */ public function getCustomerWishlist($cacheReload = false) { - if (!is_null($this->_wishlist) && !$cacheReload) { + if (($this->_wishlist !== null) && !$cacheReload) { return $this->_wishlist; } @@ -708,7 +708,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ */ public function getCustomerCart() { - if (!is_null($this->_cart)) { + if ($this->_cart !== null) { return $this->_cart; } @@ -736,7 +736,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ */ public function getCustomerCompareList() { - if (!is_null($this->_compareList)) { + if ($this->_compareList !== null) { return $this->_compareList; } $customerId = (int)$this->getSession()->getCustomerId(); @@ -807,7 +807,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ break; case 'cart': $cart = $this->getCustomerCart(); - if ($cart && is_null($item->getOptionByCode('additional_options'))) { + if ($cart && ($item->getOptionByCode('additional_options') === null)) { //options and info buy request $product = $this->_objectManager->create( \Magento\Catalog\Model\Product::class @@ -1727,7 +1727,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ } $data = $form->restoreData($data); foreach ($data as $key => $value) { - if (!is_null($value)) { + if ($value !== null) { unset($data[$key]); } } @@ -1851,12 +1851,12 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ switch ($addressType) { case \Magento\Quote\Model\Quote\Address::ADDRESS_TYPE_BILLING: - if (is_null($customer->getDefaultBilling())) { + if ($customer->getDefaultBilling() === null) { $customerAddress->setIsDefaultBilling(true); } break; case \Magento\Quote\Model\Quote\Address::ADDRESS_TYPE_SHIPPING: - if (is_null($customer->getDefaultShipping())) { + if ($customer->getDefaultShipping() === null) { $customerAddress->setIsDefaultShipping(true); } break; @@ -1948,7 +1948,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ protected function _validate() { $customerId = $this->getSession()->getCustomerId(); - if (is_null($customerId)) { + if ($customerId === null) { throw new \Magento\Framework\Exception\LocalizedException(__('Please select a customer')); } diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo.php b/app/code/Magento/Sales/Model/Order/Creditmemo.php index 64b903fe5b5c1eacca4eb2b0241f71102f523176..0d0e0d23496b75ba8781c500d36463c0aff59229 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo.php @@ -431,7 +431,7 @@ class Creditmemo extends AbstractModel implements EntityInterface, CreditmemoInt /** * If we not retrieve negative answer from payment yet */ - if (is_null($canVoid)) { + if ($canVoid === null) { $canVoid = $this->getOrder()->getPayment()->canVoid(); if ($canVoid === false) { $this->setCanVoidFlag(false); @@ -451,7 +451,7 @@ class Creditmemo extends AbstractModel implements EntityInterface, CreditmemoInt */ public static function getStates() { - if (is_null(static::$_states)) { + if (static::$_states === null) { static::$_states = [ self::STATE_OPEN => __('Pending'), self::STATE_REFUNDED => __('Refunded'), @@ -469,11 +469,11 @@ class Creditmemo extends AbstractModel implements EntityInterface, CreditmemoInt */ public function getStateName($stateId = null) { - if (is_null($stateId)) { + if ($stateId === null) { $stateId = $this->getState(); } - if (is_null(static::$_states)) { + if (static::$_states === null) { static::getStates(); } if (isset(static::$_states[$stateId])) { diff --git a/app/code/Magento/Sales/Model/Order/CustomerManagement.php b/app/code/Magento/Sales/Model/Order/CustomerManagement.php index bf54e65d0ce1058ccac59429ba6ad8021f113339..466f3ff8adddb61b8ff3fd129a54a49b5c9e9408 100644 --- a/app/code/Magento/Sales/Model/Order/CustomerManagement.php +++ b/app/code/Magento/Sales/Model/Order/CustomerManagement.php @@ -131,6 +131,7 @@ class CustomerManagement implements \Magento\Sales\Api\OrderCustomerManagementIn $customer = $this->customerFactory->create(['data' => $customerData]); $account = $this->accountManagement->createAccount($customer); $order->setCustomerId($account->getId()); + $order->setCustomerIsGuest(0); $this->orderRepository->save($order); return $account; diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php index b9b7a142095d9de0750949dbfa68d5914c3a4d1b..7540ee1902b572e72fab886c4db2e9292126bdac 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php @@ -733,7 +733,7 @@ class Transaction extends AbstractModel implements TransactionInterface */ public function getOrderWebsiteId() { - if (is_null($this->_orderWebsiteId)) { + if ($this->_orderWebsiteId === null) { $this->_orderWebsiteId = (int)$this->getResource()->getOrderWebsiteId($this->getOrderId()); } return $this->_orderWebsiteId; diff --git a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php index 1a25ff7bfdb80ffd079669adcdb3ea421e9ee6b1..1b80d08e68cda046cbdc887e76a45649f57d4408 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php @@ -804,7 +804,7 @@ abstract class AbstractPdf extends \Magento\Framework\DataObject throw new \Magento\Framework\Exception\LocalizedException(__('We found an invalid renderer model.')); } - if (is_null($this->_renderers[$type]['renderer'])) { + if ($this->_renderers[$type]['renderer'] === null) { $this->_renderers[$type]['renderer'] = $this->_pdfItemsFactory->get($this->_renderers[$type]['model']); } diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Invoice.php b/app/code/Magento/Sales/Model/Order/Pdf/Invoice.php index f294128a72f9f2df99360c201f0d1ce8f5fe8e81..ba99ed083e952dbc9714a4cf2b3059297523cffd 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Invoice.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Invoice.php @@ -96,7 +96,7 @@ class Invoice extends AbstractPdf $lines[0][] = ['text' => __('Qty'), 'feed' => 435, 'align' => 'right']; - $lines[0][] = ['text' => __('Price'), 'feed' => 360, 'align' => 'right']; + $lines[0][] = ['text' => __('Price'), 'feed' => 375, 'align' => 'right']; $lines[0][] = ['text' => __('Tax'), 'feed' => 495, 'align' => 'right']; diff --git a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php index bb6078e425900a94adc2cfb358cbf8a6eac439eb..7d62e839ad92463472421faf721206a8d341b234 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/Items/Invoice/DefaultInvoice.php @@ -81,8 +81,8 @@ class DefaultInvoice extends \Magento\Sales\Model\Order\Pdf\Items\AbstractItems // draw item Prices $i = 0; $prices = $this->getItemPricesForDisplay(); - $feedPrice = 395; - $feedSubtotal = $feedPrice + 170; + $feedPrice = 375; + $feedSubtotal = $feedPrice + 190; foreach ($prices as $priceData) { if (isset($priceData['label'])) { // draw Price label diff --git a/app/code/Magento/Sales/Model/ResourceModel/Report/Bestsellers/Collection.php b/app/code/Magento/Sales/Model/ResourceModel/Report/Bestsellers/Collection.php index 7f0aaff02d1040403b4cf384fc75b46694f54860..fa4fccb1b17e768ed0906d4f04345bc50e4d78c9 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Report/Bestsellers/Collection.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Report/Bestsellers/Collection.php @@ -255,8 +255,8 @@ class Collection extends \Magento\Sales\Model\ResourceModel\Report\Collection\Ab $selectUnions = []; // apply date boundaries (before calling $this->_applyDateRangeFilter()) - $periodFrom = !is_null($this->_from) ? new \DateTime($this->_from) : null; - $periodTo = !is_null($this->_to) ? new \DateTime($this->_to) : null; + $periodFrom = ($this->_from !== null) ? new \DateTime($this->_from) : null; + $periodTo = ($this->_to !== null) ? new \DateTime($this->_to) : null; if ('year' == $this->_period) { if ($periodFrom) { // not the first day of the year diff --git a/app/code/Magento/SalesRule/view/frontend/web/template/payment/discount.html b/app/code/Magento/SalesRule/view/frontend/web/template/payment/discount.html index 7246460382fa75e371257ee8d0646cf007d17f82..d622b5ea5762d7fa796f3a8999c0e16b844c461d 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/template/payment/discount.html +++ b/app/code/Magento/SalesRule/view/frontend/web/template/payment/discount.html @@ -27,7 +27,7 @@ id="discount-code" name="discount_code" data-validate="{'required-entry':true}" - data-bind="value: couponCode, attr:{placeholder: $t('Enter discount code')} " /> + data-bind="value: couponCode, attr:{disabled:isApplied() , placeholder: $t('Enter discount code')} " /> </div> </div> </div> diff --git a/app/code/Magento/Swatches/Block/Adminhtml/Attribute/Edit/Options/Visual.php b/app/code/Magento/Swatches/Block/Adminhtml/Attribute/Edit/Options/Visual.php index 306f01af17e71f93a8487e5b5e1ed702a261a6aa..66ecd06c646e7980be68eb94de32eab394aac435 100644 --- a/app/code/Magento/Swatches/Block/Adminhtml/Attribute/Edit/Options/Visual.php +++ b/app/code/Magento/Swatches/Block/Adminhtml/Attribute/Edit/Options/Visual.php @@ -84,15 +84,15 @@ class Visual extends AbstractSwatch * Parse swatch labels for template * * @codeCoverageIgnore - * @param null $swatchStoreValue - * @return string + * @param null|array $swatchStoreValue + * @return null|array */ protected function reformatSwatchLabels($swatchStoreValue = null) { if ($swatchStoreValue === null) { return; } - $newSwatch = ''; + $newSwatch = []; foreach ($swatchStoreValue as $key => $value) { if ($value[0] == '#') { $newSwatch[$key] = 'background: '.$value; diff --git a/app/code/Magento/Swatches/Model/Plugin/EavAttribute.php b/app/code/Magento/Swatches/Model/Plugin/EavAttribute.php index 599406f4552816ac0a3e5c2eb074a37156a3940f..d3904f058dc2db8bd4001c7b6eb8b4b9f47523c1 100644 --- a/app/code/Magento/Swatches/Model/Plugin/EavAttribute.php +++ b/app/code/Magento/Swatches/Model/Plugin/EavAttribute.php @@ -432,7 +432,7 @@ class EavAttribute $options = $attribute->getData('optiontext'); } if ($options && !$this->isOptionsValid($options, $attribute)) { - throw new InputException(__('Admin is a required field in the each row')); + throw new InputException(__('Admin is a required field in each row')); } return true; } diff --git a/app/code/Magento/Swatches/Test/Unit/Model/Plugin/EavAttributeTest.php b/app/code/Magento/Swatches/Test/Unit/Model/Plugin/EavAttributeTest.php index 258347887ff08ac7ada538e18aea89234cd958c8..31a45ddb2847cd72c1f61fe94183fbec979ad2c4 100644 --- a/app/code/Magento/Swatches/Test/Unit/Model/Plugin/EavAttributeTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Model/Plugin/EavAttributeTest.php @@ -191,7 +191,7 @@ class EavAttributeTest extends \PHPUnit\Framework\TestCase /** * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage Admin is a required field in the each row + * @expectedExceptionMessage Admin is a required field in each row */ public function testBeforeSaveWithFailedValidation() { diff --git a/app/code/Magento/Swatches/i18n/en_US.csv b/app/code/Magento/Swatches/i18n/en_US.csv index 3ea4977a75348e621707de5952093eff09446d54..00874d7611169530b54227ae7d2cd215a7458d4b 100644 --- a/app/code/Magento/Swatches/i18n/en_US.csv +++ b/app/code/Magento/Swatches/i18n/en_US.csv @@ -1,4 +1,4 @@ -"Admin is a required field in the each row","Admin is a required field in the each row" +"Admin is a required field in each row","Admin is a required field in each row" "Update Product Preview Image","Update Product Preview Image" "Filtering by this attribute will update the product image on catalog page","Filtering by this attribute will update the product image on catalog page" "Use Product Image for Swatch if Possible","Use Product Image for Swatch if Possible" diff --git a/app/code/Magento/Tax/Model/Calculation/Rate.php b/app/code/Magento/Tax/Model/Calculation/Rate.php index f639d6680a7fc039b95e3617c63f3664b1134e1d..9e44cd113ddeaa7d4b0ff1704c70edcb12a76df6 100644 --- a/app/code/Magento/Tax/Model/Calculation/Rate.php +++ b/app/code/Magento/Tax/Model/Calculation/Rate.php @@ -227,7 +227,7 @@ class Rate extends \Magento\Framework\Model\AbstractExtensibleModel implements T */ public function saveTitles($titles = null) { - if (is_null($titles)) { + if ($titles === null) { $titles = $this->getTitle(); } @@ -256,7 +256,7 @@ class Rate extends \Magento\Framework\Model\AbstractExtensibleModel implements T */ public function getTitleModel() { - if (is_null($this->_titleModel)) { + if ($this->_titleModel === null) { $this->_titleModel = $this->_titleFactory->create(); } return $this->_titleModel; @@ -270,7 +270,7 @@ class Rate extends \Magento\Framework\Model\AbstractExtensibleModel implements T if ($this->getData(self::KEY_TITLES)) { return $this->getData(self::KEY_TITLES); } - if (is_null($this->_titles)) { + if ($this->_titles === null) { $this->_titles = $this->getTitleModel()->getCollection()->loadByRateId($this->getId())->getItems(); } return $this->_titles; diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index 1fcaf0e413cd5e6f7ba077422f9a59e998956f27..7752911ceb22f59bb0523623cbe20a913d3041db 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -771,7 +771,7 @@ class CommonTaxCollector extends AbstractTotal $previouslyAppliedTaxes[$row['id']] = $row; } - if (!is_null($row['percent'])) { + if ($row['percent'] !== null) { $row['percent'] = $row['percent'] ? $row['percent'] : 1; $rate = $rate ? $rate : 1; diff --git a/app/code/Magento/Ui/i18n/en_US.csv b/app/code/Magento/Ui/i18n/en_US.csv index 225d83387563be037b4615c232535d2480af5fd5..cff52a3fd6fedf836d9ed37863312b8d005f3125 100644 --- a/app/code/Magento/Ui/i18n/en_US.csv +++ b/app/code/Magento/Ui/i18n/en_US.csv @@ -192,7 +192,7 @@ CSV,CSV "Please enter a valid value from list","Please enter a valid value from list" "Please enter valid SKU key.","Please enter valid SKU key." "Please enter a valid number.","Please enter a valid number." -"Admin is a required field in the each row.","Admin is a required field in the each row." +"Admin is a required field in each row.","Admin is a required field in each row." "Please fix this field.","Please fix this field." "Please enter a valid date (ISO).","Please enter a valid date (ISO)." "Please enter only digits.","Please enter only digits." diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/html.js b/app/code/Magento/Ui/view/base/web/js/form/components/html.js index 82e51aff402872c5abffce21db6a94599c437ce4..466b6840e9b195301d1db28e072a9c8db877d7e8 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/html.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/html.js @@ -19,6 +19,7 @@ define([ showSpinner: false, loading: false, visible: true, + error: false, template: 'ui/content/content', additionalClasses: {} }, diff --git a/app/code/Magento/Ui/view/base/web/templates/content/content.html b/app/code/Magento/Ui/view/base/web/templates/content/content.html index 8cf47120865dcbc172acd113fc634f9cd4a17b4e..62e5959a02559be8b3ed975b193ce793f1ba93d6 100644 --- a/app/code/Magento/Ui/view/base/web/templates/content/content.html +++ b/app/code/Magento/Ui/view/base/web/templates/content/content.html @@ -17,3 +17,5 @@ <span repeat="8"/> </div> </div> + +<label class="admin__field-error" if="error" attr="for: uid" text="error"/> diff --git a/app/code/Magento/Vault/Setup/InstallSchema.php b/app/code/Magento/Vault/Setup/InstallSchema.php index 32e1d62754439f5324c474f848517d48fce84b10..dd65f7a8c9772883318ce2f91d559c06a68f94f2 100644 --- a/app/code/Magento/Vault/Setup/InstallSchema.php +++ b/app/code/Magento/Vault/Setup/InstallSchema.php @@ -90,13 +90,13 @@ class InstallSchema implements InstallSchemaInterface 'is_active', Table::TYPE_BOOLEAN, null, - ['nullable' => false, 'dafault' => true], + ['nullable' => false, 'default' => true], 'Is active flag' )->addColumn( 'is_visible', Table::TYPE_BOOLEAN, null, - ['nullable' => false, 'dafault' => true], + ['nullable' => false, 'default' => true], 'Is visible flag' )->addIndex( $setup->getIdxName( diff --git a/app/code/Magento/Vault/Setup/UpgradeSchema.php b/app/code/Magento/Vault/Setup/UpgradeSchema.php new file mode 100644 index 0000000000000000000000000000000000000000..643bd1751f6689a7b41ba4caffea7c4f11318bab --- /dev/null +++ b/app/code/Magento/Vault/Setup/UpgradeSchema.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Vault\Setup; + +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\SchemaSetupInterface; +use Magento\Framework\Setup\UpgradeSchemaInterface; +use Magento\Framework\DB\Ddl\Table; + +/** + * Upgrade the Vault module DB scheme + */ +class UpgradeSchema implements UpgradeSchemaInterface +{ + /** + * @inheritdoc + */ + public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context) + { + $setup->startSetup(); + if (version_compare($context->getVersion(), '2.0.3', '<')) { + $this->upgradeTokenTableDefaultValues($setup); + } + $setup->endSetup(); + } + + /** + * @param SchemaSetupInterface $setup + * @return void + */ + private function upgradeTokenTableDefaultValues(SchemaSetupInterface $setup) + { + $columns = ['is_active', 'is_visible']; + + foreach ($columns as $columnName) { + $setup->getConnection()->modifyColumn( + $setup->getTable(InstallSchema::PAYMENT_TOKEN_TABLE), + $columnName, + [ + 'type' => Table::TYPE_BOOLEAN, + 'nullable' => false, + 'default' => '1' + ] + ); + } + } +} diff --git a/app/code/Magento/Vault/etc/module.xml b/app/code/Magento/Vault/etc/module.xml index 1a7d1fe7d09fd17c7d6109c81f83b7ac83e1fbf2..253e7f13aaadc4171ae26e4a877c231d69bac9d3 100644 --- a/app/code/Magento/Vault/etc/module.xml +++ b/app/code/Magento/Vault/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Vault" setup_version="2.0.2"> + <module name="Magento_Vault" setup_version="2.0.3"> <sequence> <module name="Magento_Sales"/> <module name="Magento_Store"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Customer/Cest/AdminCreateCustomerCest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Customer/Cest/AdminCreateCustomerCest.xml index b0a4cb06c0842fd7c178c6877294123fd430f36e..4e446a838d327c44490731aff20fe4ff4b681513 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Customer/Cest/AdminCreateCustomerCest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Customer/Cest/AdminCreateCustomerCest.xml @@ -21,6 +21,7 @@ <testCaseId value="MAGETWO-72095"/> <group value="customer"/> <group value="create"/> + <group value="skip"/> </annotations> <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> <fillField userInput="{{_ENV.MAGENTO_ADMIN_USERNAME}}" selector="{{AdminLoginFormSection.username}}" stepKey="fillUsername"/> diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 09f6362c833d4de773bc6702fc0f5a28c614b473..9dab97621f2f54de8da46ab592a3f79fb765b2d0 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -326,6 +326,32 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract } } + /** + * Test that Product Repository can correctly create simple product, if product type not specified in request. + * + * @return void + */ + public function testCreateWithoutSpecifiedType() + { + $price = 3.62; + $weight = 12.2; + $sku = 'simple_product_without_specified_type'; + $product = [ + 'sku' => $sku, + 'name' => 'Simple Product Without Specified Type', + 'price' => $price, + 'weight' => $weight, + 'attribute_set_id' => 4, + ]; + $response = $this->saveProduct($product); + $this->assertSame($sku, $response[ProductInterface::SKU]); + $this->assertSame(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE, $response[ProductInterface::TYPE_ID]); + $this->assertSame($price, $response[ProductInterface::PRICE]); + $this->assertSame($weight, $response[ProductInterface::WEIGHT]); + //Clean up. + $this->deleteProduct($product[ProductInterface::SKU]); + } + /** * @param array $fixtureProduct * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php index 9518e9c0cdf4fbf476e2cf554ce566afc3547714..330487b757f617bec675e0a87ddba0a9c99049e1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php @@ -49,4 +49,25 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase $updatedProduct->load($productId); self::assertSame($newSku, $updatedProduct->getSku()); } + + /** + * Check Product Repository able to correctly create product without specified type. + * + * @magentoDbIsolation enabled + */ + public function testCreateWithoutSpecifiedType() + { + /** @var Product $product */ + $product = Bootstrap::getObjectManager()->get(ProductFactory::class)->create(); + $product->setName('Simple without specified type'); + $product->setSku('simple_without_specified_type'); + $product->setPrice(1.12); + $product->setWeight(1.23); + $product->setAttributeSetId(4); + $product = $this->productRepository->save($product); + + self::assertSame('1.1200', $product->getPrice()); + self::assertSame('1.2300', $product->getWeight()); + self::assertSame('simple', $product->getTypeId()); + } } diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index 49265774814d964d4897ede372c636c8b04eb1ec..61e36c908eeff3931473163f13c1da11d0c03919 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -34,7 +34,8 @@ "zendframework/zend-uri": "^2.5.1", "zendframework/zend-validator": "^2.6.0", "zendframework/zend-stdlib": "^2.7.7", - "zendframework/zend-http": "^2.6.0" + "zendframework/zend-http": "^2.6.0", + "magento/zendframework1": "~1.13.0" }, "suggest": { "ext-imagick": "Use Image Magick >=3.0.0 as an optional alternative image processing library" diff --git a/lib/web/i18n/en_US.csv b/lib/web/i18n/en_US.csv index 5c63a191420a4d2daeebffef9dcab9e886edbd93..4acc62aa6dc81edd1f2b4c6397d577bdd9f3621c 100644 --- a/lib/web/i18n/en_US.csv +++ b/lib/web/i18n/en_US.csv @@ -95,7 +95,7 @@ Submit,Submit "Please enter valid SKU key.","Please enter valid SKU key." "Please enter a valid number.","Please enter a valid number." "This is required field","This is required field" -"Admin is a required field in the each row.","Admin is a required field in the each row." +"Admin is a required field in each row.","Admin is a required field in each row." "Password cannot be the same as email address.","Password cannot be the same as email address." "Please fix this field.","Please fix this field." "Please enter a valid email address.","Please enter a valid email address." diff --git a/lib/web/mage/apply/main.js b/lib/web/mage/apply/main.js index 60b737e59e11089fd269f963720f8954ce3275ed..489e467f9b1100aa128d33fefe95324a75b7f36b 100644 --- a/lib/web/mage/apply/main.js +++ b/lib/web/mage/apply/main.js @@ -32,6 +32,12 @@ define([ } else if ($(el)[component]) { $(el)[component](config); } + }, function (error) { + if ('console' in window && typeof window.console.error === 'function') { + console.error(error); + } + + return true; }); } diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index fee88826be7eb76adaa906667f48279c64f5452a..2b5aaff8385025d2caf3a5fa505034feee5ca6c5 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -1550,15 +1550,15 @@ ], 'required-text-swatch-entry': [ tableSingleValidation, - $.mage.__('Admin is a required field in the each row.') + $.mage.__('Admin is a required field in each row.') ], 'required-visual-swatch-entry': [ tableSingleValidation, - $.mage.__('Admin is a required field in the each row.') + $.mage.__('Admin is a required field in each row.') ], 'required-dropdown-attribute-entry': [ tableSingleValidation, - $.mage.__('Admin is a required field in the each row.') + $.mage.__('Admin is a required field in each row.') ], 'validate-item-quantity': [ function (value, element, params) { diff --git a/lib/web/magnifier/magnifier.js b/lib/web/magnifier/magnifier.js index 958af0e96641bed61c90bb161170f4ffa935b26e..c47536436892235cb90eb7315d360970857ab080 100644 --- a/lib/web/magnifier/magnifier.js +++ b/lib/web/magnifier/magnifier.js @@ -588,7 +588,7 @@ _init($box, gOptions); }); - $(document).on('mousemove', onMousemove); + $box.on('mousemove', onMousemove); _init($box, gOptions); }