getUiId('content-header') ?>>
+
diff --git a/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html b/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html
index fe30ca7e83f19..0033f4c071e42 100644
--- a/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html
+++ b/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html
@@ -69,7 +69,7 @@
+
-
diff --git a/app/code/Magento/Braintree/Controller/Paypal/Review.php b/app/code/Magento/Braintree/Controller/Paypal/Review.php
index 14ec829d98024..eb2de7c7b6e39 100644
--- a/app/code/Magento/Braintree/Controller/Paypal/Review.php
+++ b/app/code/Magento/Braintree/Controller/Paypal/Review.php
@@ -13,11 +13,12 @@
use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\App\Action\HttpPostActionInterface;
+use Magento\Framework\App\Action\HttpGetActionInterface;
/**
* Class Review
*/
-class Review extends AbstractAction implements HttpPostActionInterface
+class Review extends AbstractAction implements HttpPostActionInterface, HttpGetActionInterface
{
/**
* @var QuoteUpdater
diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js
index e56cc6f32d804..f8d2f8bc11116 100644
--- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js
+++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js
@@ -374,8 +374,17 @@ define([
function applyTierPrice(oneItemPrice, qty, optionConfig) {
var tiers = optionConfig.tierPrice,
magicKey = _.keys(oneItemPrice)[0],
+ tiersFirstKey = _.keys(optionConfig)[0],
lowest = false;
+ if (!tiers) {//tiers is undefined when options has only one option
+ tiers = optionConfig[tiersFirstKey].tierPrice;
+ }
+
+ tiers.sort(function (a, b) {//sorting based on "price_qty"
+ return a['price_qty'] - b['price_qty'];
+ });
+
_.each(tiers, function (tier, index) {
if (tier['price_qty'] > qty) {
return;
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php
index dd09e40ac5b35..1b6756968662f 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php
@@ -4,11 +4,6 @@
* See COPYING.txt for license details.
*/
-/**
- * Product attribute add/edit form main tab
- *
- * @author Magento Core Team
- */
namespace Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab;
use Magento\Backend\Block\Widget\Form\Generic;
@@ -18,6 +13,8 @@
use Magento\Framework\App\ObjectManager;
/**
+ * Product attribute add/edit form main tab
+ *
* @api
* @since 100.0.2
*/
@@ -73,6 +70,7 @@ public function __construct(
* Adding product form elements for editing attribute
*
* @return $this
+ * @throws \Magento\Framework\Exception\LocalizedException
* @SuppressWarnings(PHPMD)
*/
protected function _prepareForm()
@@ -255,7 +253,7 @@ protected function _prepareForm()
}
/**
- * Initialize form fileds values
+ * Initialize form fields values
*
* @return $this
*/
diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php
index 0730e7a7c5dc1..342bbc388f872 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php
@@ -6,8 +6,10 @@
*/
namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute;
-use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
+use Magento\AsynchronousOperations\Api\Data\OperationInterface;
+use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Backend\App\Action;
+use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
/**
* Class Save
@@ -16,75 +18,68 @@
class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute implements HttpPostActionInterface
{
/**
- * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor
+ * @var \Magento\Framework\Bulk\BulkManagementInterface
*/
- protected $_productFlatIndexerProcessor;
+ private $bulkManagement;
/**
- * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor
+ * @var \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory
*/
- protected $_productPriceIndexerProcessor;
+ private $operationFactory;
/**
- * Catalog product
- *
- * @var \Magento\Catalog\Helper\Product
+ * @var \Magento\Framework\DataObject\IdentityGeneratorInterface
*/
- protected $_catalogProduct;
+ private $identityService;
/**
- * @var \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory
+ * @var \Magento\Framework\Serialize\SerializerInterface
*/
- protected $stockItemFactory;
+ private $serializer;
/**
- * Stock Indexer
- *
- * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor
+ * @var \Magento\Authorization\Model\UserContextInterface
*/
- protected $_stockIndexerProcessor;
+ private $userContext;
/**
- * @var \Magento\Framework\Api\DataObjectHelper
+ * @var int
*/
- protected $dataObjectHelper;
+ private $bulkSize;
/**
* @param Action\Context $context
* @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper
- * @param \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor
- * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor
- * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor
- * @param \Magento\Catalog\Helper\Product $catalogProduct
- * @param \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory
- * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
+ * @param \Magento\Framework\Bulk\BulkManagementInterface $bulkManagement
+ * @param \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory $operartionFactory
+ * @param \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService
+ * @param \Magento\Framework\Serialize\SerializerInterface $serializer
+ * @param \Magento\Authorization\Model\UserContextInterface $userContext
+ * @param int $bulkSize
*/
public function __construct(
Action\Context $context,
\Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper,
- \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor,
- \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor,
- \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor,
- \Magento\Catalog\Helper\Product $catalogProduct,
- \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory,
- \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
+ \Magento\Framework\Bulk\BulkManagementInterface $bulkManagement,
+ \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory $operartionFactory,
+ \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService,
+ \Magento\Framework\Serialize\SerializerInterface $serializer,
+ \Magento\Authorization\Model\UserContextInterface $userContext,
+ int $bulkSize = 100
) {
- $this->_productFlatIndexerProcessor = $productFlatIndexerProcessor;
- $this->_productPriceIndexerProcessor = $productPriceIndexerProcessor;
- $this->_stockIndexerProcessor = $stockIndexerProcessor;
- $this->_catalogProduct = $catalogProduct;
- $this->stockItemFactory = $stockItemFactory;
parent::__construct($context, $attributeHelper);
- $this->dataObjectHelper = $dataObjectHelper;
+ $this->bulkManagement = $bulkManagement;
+ $this->operationFactory = $operartionFactory;
+ $this->identityService = $identityService;
+ $this->serializer = $serializer;
+ $this->userContext = $userContext;
+ $this->bulkSize = $bulkSize;
}
/**
* Update product attributes
*
- * @return \Magento\Backend\Model\View\Result\Redirect
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ * @return \Magento\Framework\Controller\Result\Redirect
*/
public function execute()
{
@@ -93,128 +88,184 @@ public function execute()
}
/* Collect Data */
- $inventoryData = $this->getRequest()->getParam('inventory', []);
$attributesData = $this->getRequest()->getParam('attributes', []);
$websiteRemoveData = $this->getRequest()->getParam('remove_website_ids', []);
$websiteAddData = $this->getRequest()->getParam('add_website_ids', []);
- /* Prepare inventory data item options (use config settings) */
- $options = $this->_objectManager->get(\Magento\CatalogInventory\Api\StockConfigurationInterface::class)
- ->getConfigItemOptions();
- foreach ($options as $option) {
- if (isset($inventoryData[$option]) && !isset($inventoryData['use_config_' . $option])) {
- $inventoryData['use_config_' . $option] = 0;
- }
- }
+ $storeId = $this->attributeHelper->getSelectedStoreId();
+ $websiteId = $this->attributeHelper->getStoreWebsiteId($storeId);
+ $productIds = $this->attributeHelper->getProductIds();
+
+ $attributesData = $this->sanitizeProductAttributes($attributesData);
try {
- $storeId = $this->attributeHelper->getSelectedStoreId();
- if ($attributesData) {
- $dateFormat = $this->_objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class)
- ->getDateFormat(\IntlDateFormatter::SHORT);
-
- foreach ($attributesData as $attributeCode => $value) {
- $attribute = $this->_objectManager->get(\Magento\Eav\Model\Config::class)
- ->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode);
- if (!$attribute->getAttributeId()) {
- unset($attributesData[$attributeCode]);
- continue;
- }
- if ($attribute->getBackendType() == 'datetime') {
- if (!empty($value)) {
- $filterInput = new \Zend_Filter_LocalizedToNormalized(['date_format' => $dateFormat]);
- $filterInternal = new \Zend_Filter_NormalizedToLocalized(
- ['date_format' => \Magento\Framework\Stdlib\DateTime::DATE_INTERNAL_FORMAT]
- );
- $value = $filterInternal->filter($filterInput->filter($value));
- } else {
- $value = null;
- }
- $attributesData[$attributeCode] = $value;
- } elseif ($attribute->getFrontendInput() == 'multiselect') {
- // Check if 'Change' checkbox has been checked by admin for this attribute
- $isChanged = (bool)$this->getRequest()->getPost('toggle_' . $attributeCode);
- if (!$isChanged) {
- unset($attributesData[$attributeCode]);
- continue;
- }
- if (is_array($value)) {
- $value = implode(',', $value);
- }
- $attributesData[$attributeCode] = $value;
- }
- }
+ $this->publish($attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds);
+ $this->messageManager->addSuccessMessage(__('Message is added to queue'));
+ } catch (\Magento\Framework\Exception\LocalizedException $e) {
+ $this->messageManager->addErrorMessage($e->getMessage());
+ } catch (\Exception $e) {
+ $this->messageManager->addExceptionMessage(
+ $e,
+ __('Something went wrong while updating the product(s) attributes.')
+ );
+ }
- $this->_objectManager->get(\Magento\Catalog\Model\Product\Action::class)
- ->updateAttributes($this->attributeHelper->getProductIds(), $attributesData, $storeId);
- }
+ return $this->resultRedirectFactory->create()->setPath('catalog/product/', ['store' => $storeId]);
+ }
- if ($inventoryData) {
- // TODO why use ObjectManager?
- /** @var \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry */
- $stockRegistry = $this->_objectManager
- ->create(\Magento\CatalogInventory\Api\StockRegistryInterface::class);
- /** @var \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository */
- $stockItemRepository = $this->_objectManager
- ->create(\Magento\CatalogInventory\Api\StockItemRepositoryInterface::class);
- foreach ($this->attributeHelper->getProductIds() as $productId) {
- $stockItemDo = $stockRegistry->getStockItem(
- $productId,
- $this->attributeHelper->getStoreWebsiteId($storeId)
- );
- if (!$stockItemDo->getProductId()) {
- $inventoryData['product_id'] = $productId;
- }
-
- $stockItemId = $stockItemDo->getId();
- $this->dataObjectHelper->populateWithArray(
- $stockItemDo,
- $inventoryData,
- \Magento\CatalogInventory\Api\Data\StockItemInterface::class
+ /**
+ * Sanitize product attributes
+ *
+ * @param array $attributesData
+ *
+ * @return array
+ */
+ private function sanitizeProductAttributes($attributesData)
+ {
+ $dateFormat = $this->_objectManager->get(TimezoneInterface::class)->getDateFormat(\IntlDateFormatter::SHORT);
+ $config = $this->_objectManager->get(\Magento\Eav\Model\Config::class);
+
+ foreach ($attributesData as $attributeCode => $value) {
+ $attribute = $config->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode);
+ if (!$attribute->getAttributeId()) {
+ unset($attributesData[$attributeCode]);
+ continue;
+ }
+ if ($attribute->getBackendType() === 'datetime') {
+ if (!empty($value)) {
+ $filterInput = new \Zend_Filter_LocalizedToNormalized(['date_format' => $dateFormat]);
+ $filterInternal = new \Zend_Filter_NormalizedToLocalized(
+ ['date_format' => \Magento\Framework\Stdlib\DateTime::DATE_INTERNAL_FORMAT]
);
- $stockItemDo->setItemId($stockItemId);
- $stockItemRepository->save($stockItemDo);
+ $value = $filterInternal->filter($filterInput->filter($value));
+ } else {
+ $value = null;
}
- $this->_stockIndexerProcessor->reindexList($this->attributeHelper->getProductIds());
- }
-
- if ($websiteAddData || $websiteRemoveData) {
- /* @var $actionModel \Magento\Catalog\Model\Product\Action */
- $actionModel = $this->_objectManager->get(\Magento\Catalog\Model\Product\Action::class);
- $productIds = $this->attributeHelper->getProductIds();
-
- if ($websiteRemoveData) {
- $actionModel->updateWebsites($productIds, $websiteRemoveData, 'remove');
+ $attributesData[$attributeCode] = $value;
+ } elseif ($attribute->getFrontendInput() === 'multiselect') {
+ // Check if 'Change' checkbox has been checked by admin for this attribute
+ $isChanged = (bool)$this->getRequest()->getPost('toggle_' . $attributeCode);
+ if (!$isChanged) {
+ unset($attributesData[$attributeCode]);
+ continue;
}
- if ($websiteAddData) {
- $actionModel->updateWebsites($productIds, $websiteAddData, 'add');
+ if (is_array($value)) {
+ $value = implode(',', $value);
}
-
- $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]);
+ $attributesData[$attributeCode] = $value;
}
+ }
+ return $attributesData;
+ }
- $this->messageManager->addSuccessMessage(
- __('A total of %1 record(s) were updated.', count($this->attributeHelper->getProductIds()))
- );
-
- $this->_productFlatIndexerProcessor->reindexList($this->attributeHelper->getProductIds());
+ /**
+ * Schedule new bulk
+ *
+ * @param array $attributesData
+ * @param array $websiteRemoveData
+ * @param array $websiteAddData
+ * @param int $storeId
+ * @param int $websiteId
+ * @param array $productIds
+ * @throws \Magento\Framework\Exception\LocalizedException
+ *
+ * @return void
+ */
+ private function publish(
+ $attributesData,
+ $websiteRemoveData,
+ $websiteAddData,
+ $storeId,
+ $websiteId,
+ $productIds
+ ):void {
+ $productIdsChunks = array_chunk($productIds, $this->bulkSize);
+ $bulkUuid = $this->identityService->generateId();
+ $bulkDescription = __('Update attributes for ' . count($productIds) . ' selected products');
+ $operations = [];
+ foreach ($productIdsChunks as $productIdsChunk) {
+ if ($websiteRemoveData || $websiteAddData) {
+ $dataToUpdate = [
+ 'website_assign' => $websiteAddData,
+ 'website_detach' => $websiteRemoveData
+ ];
+ $operations[] = $this->makeOperation(
+ 'Update website assign',
+ 'product_action_attribute.website.update',
+ $dataToUpdate,
+ $storeId,
+ $websiteId,
+ $productIdsChunk,
+ $bulkUuid
+ );
+ }
- if ($this->_catalogProduct->isDataForPriceIndexerWasChanged($attributesData)
- || !empty($websiteRemoveData)
- || !empty($websiteAddData)
- ) {
- $this->_productPriceIndexerProcessor->reindexList($this->attributeHelper->getProductIds());
+ if ($attributesData) {
+ $operations[] = $this->makeOperation(
+ 'Update product attributes',
+ 'product_action_attribute.update',
+ $attributesData,
+ $storeId,
+ $websiteId,
+ $productIdsChunk,
+ $bulkUuid
+ );
}
- } catch (\Magento\Framework\Exception\LocalizedException $e) {
- $this->messageManager->addErrorMessage($e->getMessage());
- } catch (\Exception $e) {
- $this->messageManager->addExceptionMessage(
- $e,
- __('Something went wrong while updating the product(s) attributes.')
+ }
+
+ if (!empty($operations)) {
+ $result = $this->bulkManagement->scheduleBulk(
+ $bulkUuid,
+ $operations,
+ $bulkDescription,
+ $this->userContext->getUserId()
);
+ if (!$result) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __('Something went wrong while processing the request.')
+ );
+ }
}
+ }
+
+ /**
+ * Make asynchronous operation
+ *
+ * @param string $meta
+ * @param string $queue
+ * @param array $dataToUpdate
+ * @param int $storeId
+ * @param int $websiteId
+ * @param array $productIds
+ * @param int $bulkUuid
+ *
+ * @return OperationInterface
+ */
+ private function makeOperation(
+ $meta,
+ $queue,
+ $dataToUpdate,
+ $storeId,
+ $websiteId,
+ $productIds,
+ $bulkUuid
+ ): OperationInterface {
+ $dataToEncode = [
+ 'meta_information' => $meta,
+ 'product_ids' => $productIds,
+ 'store_id' => $storeId,
+ 'website_id' => $websiteId,
+ 'attributes' => $dataToUpdate
+ ];
+ $data = [
+ 'data' => [
+ 'bulk_uuid' => $bulkUuid,
+ 'topic_name' => $queue,
+ 'serialized_data' => $this->serializer->serialize($dataToEncode),
+ 'status' => \Magento\Framework\Bulk\OperationInterface::STATUS_TYPE_OPEN,
+ ]
+ ];
- return $this->resultRedirectFactory->create()
- ->setPath('catalog/product/', ['store' => $this->attributeHelper->getSelectedStoreId()]);
+ return $this->operationFactory->create($data);
}
}
diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php
new file mode 100644
index 0000000000000..dc24a3090481e
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php
@@ -0,0 +1,163 @@
+catalogProduct = $catalogProduct;
+ $this->productFlatIndexerProcessor = $productFlatIndexerProcessor;
+ $this->productPriceIndexerProcessor = $productPriceIndexerProcessor;
+ $this->productAction = $action;
+ $this->logger = $logger;
+ $this->serializer = $serializer;
+ $this->operationManagement = $operationManagement;
+ $this->entityManager = $entityManager;
+ }
+
+ /**
+ * Process
+ *
+ * @param \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation
+ * @throws \Exception
+ *
+ * @return void
+ */
+ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterface $operation)
+ {
+ try {
+ $serializedData = $operation->getSerializedData();
+ $data = $this->serializer->unserialize($serializedData);
+ $this->execute($data);
+ } catch (\Zend_Db_Adapter_Exception $e) {
+ $this->logger->critical($e->getMessage());
+ if ($e instanceof \Magento\Framework\DB\Adapter\LockWaitException
+ || $e instanceof \Magento\Framework\DB\Adapter\DeadlockException
+ || $e instanceof \Magento\Framework\DB\Adapter\ConnectionException
+ ) {
+ $status = OperationInterface::STATUS_TYPE_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = $e->getMessage();
+ } else {
+ $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = __(
+ 'Sorry, something went wrong during product attributes update. Please see log for details.'
+ );
+ }
+ } catch (NoSuchEntityException $e) {
+ $this->logger->critical($e->getMessage());
+ $status = ($e instanceof TemporaryStateExceptionInterface)
+ ? OperationInterface::STATUS_TYPE_RETRIABLY_FAILED
+ : OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = $e->getMessage();
+ } catch (LocalizedException $e) {
+ $this->logger->critical($e->getMessage());
+ $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = $e->getMessage();
+ } catch (\Exception $e) {
+ $this->logger->critical($e->getMessage());
+ $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = __('Sorry, something went wrong during product attributes update. Please see log for details.');
+ }
+
+ $operation->setStatus($status ?? OperationInterface::STATUS_TYPE_COMPLETE)
+ ->setErrorCode($errorCode ?? null)
+ ->setResultMessage($message ?? null);
+
+ $this->entityManager->save($operation);
+ }
+
+ /**
+ * Execute
+ *
+ * @param array $data
+ *
+ * @return void
+ */
+ private function execute($data): void
+ {
+ $this->productAction->updateAttributes($data['product_ids'], $data['attributes'], $data['store_id']);
+ if ($this->catalogProduct->isDataForPriceIndexerWasChanged($data['attributes'])) {
+ $this->productPriceIndexerProcessor->reindexList($data['product_ids']);
+ }
+
+ $this->productFlatIndexerProcessor->reindexList($data['product_ids']);
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php
new file mode 100644
index 0000000000000..32ba39d9afd98
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php
@@ -0,0 +1,168 @@
+productFlatIndexerProcessor = $productFlatIndexerProcessor;
+ $this->productAction = $action;
+ $this->logger = $logger;
+ $this->serializer = $serializer;
+ $this->productPriceIndexerProcessor = $productPriceIndexerProcessor;
+ $this->entityManager = $entityManager;
+ }
+
+ /**
+ * Process
+ *
+ * @param \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation
+ * @throws \Exception
+ *
+ * @return void
+ */
+ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterface $operation)
+ {
+ try {
+ $serializedData = $operation->getSerializedData();
+ $data = $this->serializer->unserialize($serializedData);
+ $this->execute($data);
+ } catch (\Zend_Db_Adapter_Exception $e) {
+ $this->logger->critical($e->getMessage());
+ if ($e instanceof \Magento\Framework\DB\Adapter\LockWaitException
+ || $e instanceof \Magento\Framework\DB\Adapter\DeadlockException
+ || $e instanceof \Magento\Framework\DB\Adapter\ConnectionException
+ ) {
+ $status = OperationInterface::STATUS_TYPE_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = __($e->getMessage());
+ } else {
+ $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = __(
+ 'Sorry, something went wrong during product attributes update. Please see log for details.'
+ );
+ }
+ } catch (NoSuchEntityException $e) {
+ $this->logger->critical($e->getMessage());
+ $status = ($e instanceof TemporaryStateExceptionInterface)
+ ? OperationInterface::STATUS_TYPE_RETRIABLY_FAILED
+ : OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = $e->getMessage();
+ } catch (LocalizedException $e) {
+ $this->logger->critical($e->getMessage());
+ $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = $e->getMessage();
+ } catch (\Exception $e) {
+ $this->logger->critical($e->getMessage());
+ $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED;
+ $errorCode = $e->getCode();
+ $message = __('Sorry, something went wrong during product attributes update. Please see log for details.');
+ }
+
+ $operation->setStatus($status ?? OperationInterface::STATUS_TYPE_COMPLETE)
+ ->setErrorCode($errorCode ?? null)
+ ->setResultMessage($message ?? null);
+
+ $this->entityManager->save($operation);
+ }
+
+ /**
+ * Update website in products
+ *
+ * @param array $productIds
+ * @param array $websiteRemoveData
+ * @param array $websiteAddData
+ *
+ * @return void
+ */
+ private function updateWebsiteInProducts($productIds, $websiteRemoveData, $websiteAddData): void
+ {
+ if ($websiteRemoveData) {
+ $this->productAction->updateWebsites($productIds, $websiteRemoveData, 'remove');
+ }
+ if ($websiteAddData) {
+ $this->productAction->updateWebsites($productIds, $websiteAddData, 'add');
+ }
+ }
+
+ /**
+ * Execute
+ *
+ * @param array $data
+ *
+ * @return void
+ */
+ private function execute($data): void
+ {
+ $this->updateWebsiteInProducts(
+ $data['product_ids'],
+ $data['attributes']['website_detach'],
+ $data['attributes']['website_assign']
+ );
+ $this->productPriceIndexerProcessor->reindexList($data['product_ids']);
+ $this->productFlatIndexerProcessor->reindexList($data['product_ids']);
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Action.php b/app/code/Magento/Catalog/Model/Product/Action.php
index f78048424b42c..3863cf2457247 100644
--- a/app/code/Magento/Catalog/Model/Product/Action.php
+++ b/app/code/Magento/Catalog/Model/Product/Action.php
@@ -168,5 +168,7 @@ public function updateWebsites($productIds, $websiteIds, $type)
if (!$categoryIndexer->isScheduled()) {
$categoryIndexer->reindexList(array_unique($productIds));
}
+
+ $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]);
}
}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php
index e346c912dccaa..db967052cb7a5 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php
@@ -165,19 +165,6 @@ protected function modifyPriceData($object, $data)
/** @var \Magento\Catalog\Model\Product $object */
$data = parent::modifyPriceData($object, $data);
$price = $object->getPrice();
-
- $specialPrice = $object->getSpecialPrice();
- $specialPriceFromDate = $object->getSpecialFromDate();
- $specialPriceToDate = $object->getSpecialToDate();
- $today = time();
-
- if ($specialPrice && ($object->getPrice() > $object->getFinalPrice())) {
- if ($today >= strtotime($specialPriceFromDate) && $today <= strtotime($specialPriceToDate) ||
- $today >= strtotime($specialPriceFromDate) && $specialPriceToDate === null) {
- $price = $specialPrice;
- }
- }
-
foreach ($data as $key => $tierPrice) {
$percentageValue = $this->getPercentage($tierPrice);
if ($percentageValue) {
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
index d786795bc3090..90d732c9654e1 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml
@@ -302,4 +302,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
index 20ea93cd272f1..da570f9ed99b0 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml
@@ -20,7 +20,7 @@
-
+
@@ -34,6 +34,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -77,6 +96,11 @@
+
+
+
+
+
@@ -389,6 +413,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInfoOnEditPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInfoOnEditPageActionGroup.xml
new file mode 100644
index 0000000000000..7917fe68aaebc
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInfoOnEditPageActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml
new file mode 100644
index 0000000000000..963c9d9f1c911
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml
new file mode 100644
index 0000000000000..1bb7c179dfca8
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductNameOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductNameOnProductPageActionGroup.xml
new file mode 100644
index 0000000000000..6cb156723b286
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductNameOnProductPageActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnProductPageActionGroup.xml
new file mode 100644
index 0000000000000..3c62ef89e584b
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnProductPageActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSkuOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSkuOnProductPageActionGroup.xml
new file mode 100644
index 0000000000000..85d3927a6d6d0
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSkuOnProductPageActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml
new file mode 100644
index 0000000000000..f5fabae5fc4ce
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogSpecialPriceData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogSpecialPriceData.xml
index 4c6b0749a0f9e..31783526932b6 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogSpecialPriceData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogSpecialPriceData.xml
@@ -12,4 +12,9 @@
0
+
+ 55.55
+ 0
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
index 0b3a31194ea36..383797933074e 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml
@@ -60,6 +60,48 @@
EavStockItem
CustomAttributeCategoryIds
+
+ SimpleProductForTest1
+ simple
+ 4
+ SimpleProductAfterImport1
+ 250.00
+ 4
+ 1
+ 100
+ simple-product-for-test-1
+ 1
+ EavStockItem
+ CustomAttributeCategoryIds
+
+
+ SimpleProductForTest2
+ simple
+ 4
+ SimpleProductAfterImport2
+ 300.00
+ 4
+ 1
+ 100
+ simple-product-for-test-2
+ 1
+ EavStockItem
+ CustomAttributeCategoryIds
+
+
+ SimpleProductForTest3
+ simple
+ 4
+ SimpleProductAfterImport3
+ 350.00
+ 4
+ 1
+ 100
+ simple-product-for-test-3
+ 1
+ EavStockItem
+ CustomAttributeCategoryIds
+
SimpleProduct
simple
@@ -386,6 +428,11 @@
ProductOptionDropDownWithLongValuesTitle
+
+
+ ProductOptionField
+ ProductOptionArea
+
api-virtual-product
virtual
@@ -906,4 +953,20 @@
EavStockItem
CustomAttributeCategoryIds
+
+ sku_simple_product_
+ simple
+ 4
+ 4
+ SimpleProduct
+ 560
+ simple-product-
+ 1
+ 25
+ 1
+ 1
+ 1
+ 2
+ EavStockItem
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml
index fee86ca1caa29..ea4f4bf53eb71 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml
@@ -10,5 +10,6 @@
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml
index e218f5ae74fc0..2de7bf19fd378 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml
index 4dcda8dcd41ae..c58479a7b73e5 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml
@@ -11,6 +11,6 @@
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml
index 8504683648bce..8393cee57996f 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml
@@ -13,6 +13,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml
index 45e0b03e8d995..ea10e12fb73f5 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml
@@ -9,6 +9,11 @@
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml
new file mode 100644
index 0000000000000..37ec4e0d32528
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml
new file mode 100644
index 0000000000000..575bb56912b25
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml
index 70edb0ce3ea7d..17769c79677f7 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml
@@ -139,7 +139,7 @@
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml
index d7607b4b269e8..4d581bae700d7 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml
@@ -54,7 +54,13 @@
-
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml
index c0eebd1512d6d..8a44c8093ca5e 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml
@@ -58,7 +58,13 @@
-
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml
index 845c47c0e4c20..bee13bec370da 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml
@@ -52,7 +52,13 @@
-
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml
new file mode 100644
index 0000000000000..234a7c69913c9
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml
index c9a37ec40e8fa..318ab6555235e 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml
@@ -135,7 +135,7 @@
-
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml
index d67d5b36109e6..34d85e7b46850 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml
@@ -229,7 +229,7 @@
productPriceAmount
-
+
diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php
deleted file mode 100644
index de44af7f58afc..0000000000000
--- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php
+++ /dev/null
@@ -1,258 +0,0 @@
-attributeHelper = $this->createPartialMock(
- \Magento\Catalog\Helper\Product\Edit\Action\Attribute::class,
- ['getProductIds', 'getSelectedStoreId', 'getStoreWebsiteId']
- );
-
- $this->dataObjectHelperMock = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->stockIndexerProcessor = $this->createPartialMock(
- \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class,
- ['reindexList']
- );
-
- $resultRedirect = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->resultRedirectFactory = $this->getMockBuilder(\Magento\Backend\Model\View\Result\RedirectFactory::class)
- ->disableOriginalConstructor()
- ->setMethods(['create'])
- ->getMock();
- $this->resultRedirectFactory->expects($this->atLeastOnce())
- ->method('create')
- ->willReturn($resultRedirect);
-
- $this->prepareContext();
-
- $this->object = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
- \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute\Save::class,
- [
- 'context' => $this->context,
- 'attributeHelper' => $this->attributeHelper,
- 'stockIndexerProcessor' => $this->stockIndexerProcessor,
- 'dataObjectHelper' => $this->dataObjectHelperMock,
- ]
- );
- }
-
- /**
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
- */
- protected function prepareContext()
- {
- $this->stockItemRepository = $this->getMockBuilder(
- \Magento\CatalogInventory\Api\StockItemRepositoryInterface::class
- )->disableOriginalConstructor()->getMock();
-
- $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class)
- ->disableOriginalConstructor()->getMock();
- $this->response = $this->createMock(\Magento\Framework\App\Response\Http::class);
- $this->objectManager = $this->createMock(\Magento\Framework\ObjectManagerInterface::class);
- $this->eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class);
- $this->url = $this->createMock(\Magento\Framework\UrlInterface::class);
- $this->redirect = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class);
- $this->actionFlag = $this->createMock(\Magento\Framework\App\ActionFlag::class);
- $this->view = $this->createMock(\Magento\Framework\App\ViewInterface::class);
- $this->messageManager = $this->createMock(\Magento\Framework\Message\ManagerInterface::class);
- $this->session = $this->createMock(\Magento\Backend\Model\Session::class);
- $this->authorization = $this->createMock(\Magento\Framework\AuthorizationInterface::class);
- $this->auth = $this->createMock(\Magento\Backend\Model\Auth::class);
- $this->helper = $this->createMock(\Magento\Backend\Helper\Data::class);
- $this->backendUrl = $this->createMock(\Magento\Backend\Model\UrlInterface::class);
- $this->formKeyValidator = $this->createMock(\Magento\Framework\Data\Form\FormKey\Validator::class);
- $this->localeResolver = $this->createMock(\Magento\Framework\Locale\ResolverInterface::class);
-
- $this->context = $this->context = $this->createPartialMock(\Magento\Backend\App\Action\Context::class, [
- 'getRequest',
- 'getResponse',
- 'getObjectManager',
- 'getEventManager',
- 'getUrl',
- 'getRedirect',
- 'getActionFlag',
- 'getView',
- 'getMessageManager',
- 'getSession',
- 'getAuthorization',
- 'getAuth',
- 'getHelper',
- 'getBackendUrl',
- 'getFormKeyValidator',
- 'getLocaleResolver',
- 'getResultRedirectFactory'
- ]);
- $this->context->expects($this->any())->method('getRequest')->willReturn($this->request);
- $this->context->expects($this->any())->method('getResponse')->willReturn($this->response);
- $this->context->expects($this->any())->method('getObjectManager')->willReturn($this->objectManager);
- $this->context->expects($this->any())->method('getEventManager')->willReturn($this->eventManager);
- $this->context->expects($this->any())->method('getUrl')->willReturn($this->url);
- $this->context->expects($this->any())->method('getRedirect')->willReturn($this->redirect);
- $this->context->expects($this->any())->method('getActionFlag')->willReturn($this->actionFlag);
- $this->context->expects($this->any())->method('getView')->willReturn($this->view);
- $this->context->expects($this->any())->method('getMessageManager')->willReturn($this->messageManager);
- $this->context->expects($this->any())->method('getSession')->willReturn($this->session);
- $this->context->expects($this->any())->method('getAuthorization')->willReturn($this->authorization);
- $this->context->expects($this->any())->method('getAuth')->willReturn($this->auth);
- $this->context->expects($this->any())->method('getHelper')->willReturn($this->helper);
- $this->context->expects($this->any())->method('getBackendUrl')->willReturn($this->backendUrl);
- $this->context->expects($this->any())->method('getFormKeyValidator')->willReturn($this->formKeyValidator);
- $this->context->expects($this->any())->method('getLocaleResolver')->willReturn($this->localeResolver);
- $this->context->expects($this->any())
- ->method('getResultRedirectFactory')
- ->willReturn($this->resultRedirectFactory);
-
- $this->product = $this->createPartialMock(
- \Magento\Catalog\Model\Product::class,
- ['isProductsHasSku', '__wakeup']
- );
-
- $this->stockItemService = $this->getMockBuilder(\Magento\CatalogInventory\Api\StockRegistryInterface::class)
- ->disableOriginalConstructor()
- ->setMethods(['getStockItem', 'saveStockItem'])
- ->getMockForAbstractClass();
- $this->stockItem = $this->getMockBuilder(\Magento\CatalogInventory\Api\Data\StockItemInterface::class)
- ->setMethods(['getId', 'getProductId'])
- ->disableOriginalConstructor()
- ->getMockForAbstractClass();
-
- $this->stockConfig = $this->getMockBuilder(\Magento\CatalogInventory\Api\StockConfigurationInterface::class)
- ->disableOriginalConstructor()
- ->getMockForAbstractClass();
-
- $this->objectManager->expects($this->any())->method('create')->will($this->returnValueMap([
- [\Magento\Catalog\Model\Product::class, [], $this->product],
- [\Magento\CatalogInventory\Api\StockRegistryInterface::class, [], $this->stockItemService],
- [\Magento\CatalogInventory\Api\StockItemRepositoryInterface::class, [], $this->stockItemRepository],
- ]));
-
- $this->objectManager->expects($this->any())->method('get')->will($this->returnValueMap([
- [\Magento\CatalogInventory\Api\StockConfigurationInterface::class, $this->stockConfig],
- ]));
- }
-
- public function testExecuteThatProductIdsAreObtainedFromAttributeHelper()
- {
- $this->attributeHelper->expects($this->any())->method('getProductIds')->will($this->returnValue([5]));
- $this->attributeHelper->expects($this->any())->method('getSelectedStoreId')->will($this->returnValue([1]));
- $this->attributeHelper->expects($this->any())->method('getStoreWebsiteId')->will($this->returnValue(1));
- $this->stockConfig->expects($this->any())->method('getConfigItemOptions')->will($this->returnValue([]));
- $this->dataObjectHelperMock->expects($this->any())
- ->method('populateWithArray')
- ->with($this->stockItem, $this->anything(), \Magento\CatalogInventory\Api\Data\StockItemInterface::class)
- ->willReturnSelf();
- $this->product->expects($this->any())->method('isProductsHasSku')->with([5])->will($this->returnValue(true));
- $this->stockItemService->expects($this->any())->method('getStockItem')->with(5, 1)
- ->will($this->returnValue($this->stockItem));
- $this->stockIndexerProcessor->expects($this->any())->method('reindexList')->with([5]);
-
- $this->request->expects($this->any())->method('getParam')->will($this->returnValueMap([
- ['inventory', [], [7]],
- ]));
-
- $this->messageManager->expects($this->never())->method('addErrorMessage');
- $this->messageManager->expects($this->never())->method('addExceptionMessage');
-
- $this->object->execute();
- }
-}
diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php
index 5af0d71dc246c..494b77724e5b7 100644
--- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php
+++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php
@@ -3,13 +3,19 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
+declare(strict_types=1);
+
namespace Magento\Catalog\Ui\Component\Listing\Columns;
-use Magento\Framework\View\Element\UiComponentFactory;
+use Magento\Framework\DB\Helper;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
+use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Store\Model\StoreManagerInterface;
/**
+ * Websites listing column component.
+ *
* @api
* @since 100.0.2
*/
@@ -20,6 +26,11 @@ class Websites extends \Magento\Ui\Component\Listing\Columns\Column
*/
const NAME = 'websites';
+ /**
+ * Data for concatenated website names value.
+ */
+ private $websiteNames = 'website_names';
+
/**
* Store manager
*
@@ -27,26 +38,36 @@ class Websites extends \Magento\Ui\Component\Listing\Columns\Column
*/
protected $storeManager;
+ /**
+ * @var \Magento\Framework\DB\Helper
+ */
+ private $resourceHelper;
+
/**
* @param ContextInterface $context
* @param UiComponentFactory $uiComponentFactory
* @param StoreManagerInterface $storeManager
* @param array $components
* @param array $data
+ * @param Helper $resourceHelper
*/
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
StoreManagerInterface $storeManager,
array $components = [],
- array $data = []
+ array $data = [],
+ Helper $resourceHelper = null
) {
parent::__construct($context, $uiComponentFactory, $components, $data);
+ $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->storeManager = $storeManager;
+ $this->resourceHelper = $resourceHelper ?: $objectManager->get(Helper::class);
}
/**
- * {@inheritdoc}
+ * @inheritdoc
+ *
* @deprecated 101.0.0
*/
public function prepareDataSource(array $dataSource)
@@ -71,9 +92,10 @@ public function prepareDataSource(array $dataSource)
return $dataSource;
}
-
+
/**
- * Prepare component configuration
+ * Prepare component configuration.
+ *
* @return void
*/
public function prepare()
@@ -83,4 +105,46 @@ public function prepare()
$this->_data['config']['componentDisabled'] = true;
}
}
+
+ /**
+ * Apply sorting.
+ *
+ * @return void
+ */
+ protected function applySorting()
+ {
+ $sorting = $this->getContext()->getRequestParam('sorting');
+ $isSortable = $this->getData('config/sortable');
+ if ($isSortable !== false
+ && !empty($sorting['field'])
+ && !empty($sorting['direction'])
+ && $sorting['field'] === $this->getName()
+ ) {
+ $collection = $this->getContext()->getDataProvider()->getCollection();
+ $collection
+ ->joinField(
+ 'websites_ids',
+ 'catalog_product_website',
+ 'website_id',
+ 'product_id=entity_id',
+ null,
+ 'left'
+ )
+ ->joinTable(
+ 'store_website',
+ 'website_id = websites_ids',
+ ['name'],
+ null,
+ 'left'
+ )
+ ->groupByAttribute('entity_id');
+ $this->resourceHelper->addGroupConcatColumn(
+ $collection->getSelect(),
+ $this->websiteNames,
+ 'name'
+ );
+
+ $collection->getSelect()->order($this->websiteNames . ' ' . $sorting['direction']);
+ }
+ }
}
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php
index f8f82511cc12f..af43c84501f65 100755
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php
@@ -871,6 +871,7 @@ protected function getPositionFieldConfig($sortOrder)
'formElement' => Hidden::NAME,
'dataScope' => static::FIELD_SORT_ORDER_NAME,
'dataType' => Number::NAME,
+ 'visible' => false,
'sortOrder' => $sortOrder,
],
],
diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json
index 44d051933909b..5c3ee3da8ca81 100644
--- a/app/code/Magento/Catalog/composer.json
+++ b/app/code/Magento/Catalog/composer.json
@@ -7,6 +7,8 @@
"require": {
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
+ "magento/module-authorization": "*",
+ "magento/module-asynchronous-operations": "*",
"magento/module-backend": "*",
"magento/module-catalog-inventory": "*",
"magento/module-catalog-rule": "*",
diff --git a/app/code/Magento/Catalog/etc/communication.xml b/app/code/Magento/Catalog/etc/communication.xml
new file mode 100644
index 0000000000000..1a957f6ac9fe5
--- /dev/null
+++ b/app/code/Magento/Catalog/etc/communication.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml
index 7d2c3699ee2c2..49447447622f9 100644
--- a/app/code/Magento/Catalog/etc/di.xml
+++ b/app/code/Magento/Catalog/etc/di.xml
@@ -72,6 +72,7 @@
+
diff --git a/app/code/Magento/Catalog/etc/queue.xml b/app/code/Magento/Catalog/etc/queue.xml
new file mode 100644
index 0000000000000..137f34a5c1e25
--- /dev/null
+++ b/app/code/Magento/Catalog/etc/queue.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/etc/queue_consumer.xml b/app/code/Magento/Catalog/etc/queue_consumer.xml
new file mode 100644
index 0000000000000..d9e66ae69c10c
--- /dev/null
+++ b/app/code/Magento/Catalog/etc/queue_consumer.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/etc/queue_publisher.xml b/app/code/Magento/Catalog/etc/queue_publisher.xml
new file mode 100644
index 0000000000000..1606ea42ec0b3
--- /dev/null
+++ b/app/code/Magento/Catalog/etc/queue_publisher.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/etc/queue_topology.xml b/app/code/Magento/Catalog/etc/queue_topology.xml
new file mode 100644
index 0000000000000..bdac891afbdb8
--- /dev/null
+++ b/app/code/Magento/Catalog/etc/queue_topology.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml
index c930d2195a01b..1c4a37fedebe3 100644
--- a/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml
+++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml
@@ -23,8 +23,8 @@
- = $block->escapeHtml(__($_data['label'])) ?>
- = /* @escapeNotVerified */ $_helper->productAttribute($_product, $_data['value'], $_data['code']) ?>
+ = $block->escapeHtml($_data['label']) ?>
+ = /* @escapeNotVerified */ $_helper->productAttribute($_product, $_data['value'], $_data['code']) ?>
diff --git a/app/code/Magento/CatalogAnalytics/composer.json b/app/code/Magento/CatalogAnalytics/composer.json
index 5c97261d483d8..805be8a17765f 100644
--- a/app/code/Magento/CatalogAnalytics/composer.json
+++ b/app/code/Magento/CatalogAnalytics/composer.json
@@ -4,7 +4,8 @@
"require": {
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
- "magento/module-catalog": "*"
+ "magento/module-catalog": "*",
+ "magento/module-analytics": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml
new file mode 100644
index 0000000000000..b9eea2b114634
--- /dev/null
+++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml
new file mode 100644
index 0000000000000..1f5ae6b6905bc
--- /dev/null
+++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml
new file mode 100644
index 0000000000000..a587d71ba0e68
--- /dev/null
+++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml
new file mode 100644
index 0000000000000..6f64da4693692
--- /dev/null
+++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml
new file mode 100644
index 0000000000000..993f1c9cd9da2
--- /dev/null
+++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml
new file mode 100644
index 0000000000000..491d20604a08b
--- /dev/null
+++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml
new file mode 100644
index 0000000000000..f671b54803e35
--- /dev/null
+++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php b/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php
index f711268bc7930..0fa4b919c40fa 100644
--- a/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php
+++ b/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php
@@ -7,16 +7,24 @@
/**
* Interface StockRegistryProviderInterface
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockRegistryProviderInterface
{
/**
+ * Get stock.
+ *
* @param int $scopeId
* @return \Magento\CatalogInventory\Api\Data\StockInterface
*/
public function getStock($scopeId);
/**
+ * Get stock item.
+ *
* @param int $productId
* @param int $scopeId
* @return \Magento\CatalogInventory\Api\Data\StockItemInterface
@@ -24,6 +32,8 @@ public function getStock($scopeId);
public function getStockItem($productId, $scopeId);
/**
+ * Get stock status.
+ *
* @param int $productId
* @param int $scopeId
* @return \Magento\CatalogInventory\Api\Data\StockStatusInterface
diff --git a/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php b/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php
index 89fb54e7e496b..30f703b5b928f 100644
--- a/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php
+++ b/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php
@@ -9,22 +9,32 @@
/**
* Interface StockStateProviderInterface
+ *
+ * @deprecated 2.3.0 Replaced with Multi Source Inventory
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html
+ * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html
*/
interface StockStateProviderInterface
{
/**
+ * Verify stock.
+ *
* @param StockItemInterface $stockItem
* @return bool
*/
public function verifyStock(StockItemInterface $stockItem);
/**
+ * Verify notification.
+ *
* @param StockItemInterface $stockItem
* @return bool
*/
public function verifyNotification(StockItemInterface $stockItem);
/**
+ * Validate quote qty.
+ *
* @param StockItemInterface $stockItem
* @param int|float $itemQty
* @param int|float $qtyToCheck
@@ -44,8 +54,9 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $itemQty, $qtyT
public function checkQty(StockItemInterface $stockItem, $qty);
/**
- * Returns suggested qty that satisfies qty increments and minQty/maxQty/minSaleQty/maxSaleQty conditions
- * or original qty if such value does not exist
+ * Returns suggested qty or original qty if such value does not exist.
+ *
+ * Suggested qty satisfies qty increments and minQty/maxQty/minSaleQty/maxSaleQty conditions.
*
* @param StockItemInterface $stockItem
* @param int|float $qty
@@ -54,6 +65,8 @@ public function checkQty(StockItemInterface $stockItem, $qty);
public function suggestQty(StockItemInterface $stockItem, $qty);
/**
+ * Check qty increments.
+ *
* @param StockItemInterface $stockItem
* @param int|float $qty
* @return \Magento\Framework\DataObject
diff --git a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php
new file mode 100644
index 0000000000000..334d2b22edbfa
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php
@@ -0,0 +1,159 @@
+stockIndexerProcessor = $stockIndexerProcessor;
+ $this->dataObjectHelper = $dataObjectHelper;
+ $this->stockRegistry = $stockRegistry;
+ $this->stockItemRepository = $stockItemRepository;
+ $this->stockConfiguration = $stockConfiguration;
+ $this->attributeHelper = $attributeHelper;
+ $this->messageManager = $messageManager;
+ }
+
+ /**
+ * Around execute plugin
+ *
+ * @param Save $subject
+ * @param callable $proceed
+ *
+ * @return \Magento\Framework\Controller\ResultInterface
+ */
+ public function aroundExecute(Save $subject, callable $proceed)
+ {
+ try {
+ /** @var \Magento\Framework\App\RequestInterface $request */
+ $request = $subject->getRequest();
+ $inventoryData = $request->getParam('inventory', []);
+ $inventoryData = $this->addConfigSettings($inventoryData);
+
+ $storeId = $this->attributeHelper->getSelectedStoreId();
+ $websiteId = $this->attributeHelper->getStoreWebsiteId($storeId);
+ $productIds = $this->attributeHelper->getProductIds();
+
+ if (!empty($inventoryData)) {
+ $this->updateInventoryInProducts($productIds, $websiteId, $inventoryData);
+ }
+
+ return $proceed();
+ } catch (\Magento\Framework\Exception\LocalizedException $e) {
+ $this->messageManager->addErrorMessage($e->getMessage());
+ return $proceed();
+ } catch (\Exception $e) {
+ $this->messageManager->addExceptionMessage(
+ $e,
+ __('Something went wrong while updating the product(s) attributes.')
+ );
+ return $proceed();
+ }
+ }
+
+ /**
+ * Add config settings
+ *
+ * @param array $inventoryData
+ *
+ * @return array
+ */
+ private function addConfigSettings($inventoryData)
+ {
+ $options = $this->stockConfiguration->getConfigItemOptions();
+ foreach ($options as $option) {
+ $useConfig = 'use_config_' . $option;
+ if (isset($inventoryData[$option]) && !isset($inventoryData[$useConfig])) {
+ $inventoryData[$useConfig] = 0;
+ }
+ }
+ return $inventoryData;
+ }
+
+ /**
+ * Update inventory in products
+ *
+ * @param array $productIds
+ * @param int $websiteId
+ * @param array $inventoryData
+ *
+ * @return void
+ */
+ private function updateInventoryInProducts($productIds, $websiteId, $inventoryData): void
+ {
+ foreach ($productIds as $productId) {
+ $stockItemDo = $this->stockRegistry->getStockItem($productId, $websiteId);
+ if (!$stockItemDo->getProductId()) {
+ $inventoryData['product_id'] = $productId;
+ }
+ $stockItemId = $stockItemDo->getId();
+ $this->dataObjectHelper->populateWithArray($stockItemDo, $inventoryData, StockItemInterface::class);
+ $stockItemDo->setItemId($stockItemId);
+ $this->stockItemRepository->save($stockItemDo);
+ }
+ $this->stockIndexerProcessor->reindexList($productIds);
+ }
+}
diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml
index 8d57fab843f4c..e7d79c593b8c7 100644
--- a/app/code/Magento/CatalogInventory/etc/di.xml
+++ b/app/code/Magento/CatalogInventory/etc/di.xml
@@ -44,7 +44,7 @@
- Magento\CatalogInventory\Model\ResourceModel\Stock\Proxy
+ Magento\CatalogInventory\Model\ResourceModel\Stock\Item\Proxy
@@ -135,4 +135,7 @@
+
+
+
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml
new file mode 100644
index 0000000000000..33ffa4fe1b296
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml
index 6b913e5b458e6..4b52b2c669edf 100644
--- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml
@@ -11,9 +11,9 @@
-
+
-
+
@@ -116,4 +116,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml
new file mode 100644
index 0000000000000..83e4ac50a74e6
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/SearchTermData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/SearchTermData.xml
new file mode 100644
index 0000000000000..995b860d107ca
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/SearchTermData.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Query text
+ Default Store View
+ http://example.com/
+ No
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermIndexPage.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermIndexPage.xml
new file mode 100644
index 0000000000000..bbafff8ad7739
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermIndexPage.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermNewPage.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermNewPage.xml
new file mode 100644
index 0000000000000..de7491471741c
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermNewPage.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml
new file mode 100644
index 0000000000000..ac316d060f6e9
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermMessagesSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermMessagesSection.xml
new file mode 100644
index 0000000000000..5d19198a1b94c
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermMessagesSection.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml
new file mode 100644
index 0000000000000..a7d577a7508c0
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml
new file mode 100644
index 0000000000000..2b425f34f8a5b
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml
new file mode 100644
index 0000000000000..c72ed424ef307
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml
index 593df1c5bc6e1..30a4290d882fb 100644
--- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml
@@ -67,7 +67,13 @@
-
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniShoppingCartSubTotalActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniShoppingCartSubTotalActionGroup.xml
new file mode 100644
index 0000000000000..8c5c6f41fffa7
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniShoppingCartSubTotalActionGroup.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+ $grabMiniCartTotal
+ {{dataQuote.subtotal}}
+
+
+ $grabMiniCartTotal
+ {{dataQuote.currency}}
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontUpdateProductQtyMiniShoppingCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontUpdateProductQtyMiniShoppingCartActionGroup.xml
new file mode 100644
index 0000000000000..ee8b761a452d4
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontUpdateProductQtyMiniShoppingCartActionGroup.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml
index 530157851191f..e7a5992ad8943 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml
@@ -15,4 +15,26 @@
495.00
Flat Rate - Fixed
+
+ 560.00
+ 2
+ 1,120.00
+ 10.00
+ 1,130.00
+ Flat Rate - Fixed
+ $
+
+
+ 123.00
+ 3
+ 369.00
+ $
+
+
+ 100.00
+ 20
+ 11
+ 1,320.00
+ $
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml
index bdb02835c6276..38c88bf4f80bb 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml
@@ -25,6 +25,8 @@
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml
new file mode 100644
index 0000000000000..423f4049f6722
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ grabProductQtyInCart
+ {{quoteQty3Price123.qty}}
+
+
+
+
+
+
+
+
+
+ grabProductQtyInMinicart
+ {{quoteQty3Price123.qty}}
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml
new file mode 100644
index 0000000000000..84080b04c80ee
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{quoteQty11Subtotal1320.qty}}
+ grabProductQtyInCart
+
+
+
+
+
+
+
+ {{quoteQty11Subtotal1320.qty}}
+ grabProductQtyInMinicart
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml
new file mode 100644
index 0000000000000..7318f865a0dc1
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/etc/di.xml b/app/code/Magento/Checkout/etc/di.xml
index 71dfd12bb4779..4ebd594a28562 100644
--- a/app/code/Magento/Checkout/etc/di.xml
+++ b/app/code/Magento/Checkout/etc/di.xml
@@ -49,7 +49,4 @@
-
-
-
diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml
index 00bcd2a27005a..8f35fe9f37abf 100644
--- a/app/code/Magento/Checkout/etc/frontend/di.xml
+++ b/app/code/Magento/Checkout/etc/frontend/di.xml
@@ -96,4 +96,7 @@
+
+
+
diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html
index a448537d64e83..4b1a68624e547 100644
--- a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html
+++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html
@@ -5,17 +5,17 @@
*/
-->
-
+
-
+
-
+
-
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml
index b4bcdaadf9a09..e6ab1c130606b 100644
--- a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml
+++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml
@@ -17,7 +17,6 @@
-
diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml
index 9f886f6f1345e..793fc7d26cb4a 100644
--- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml
+++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml
@@ -146,7 +146,6 @@
true
- true
text
diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php
index 2c4b8a8dc48d2..c63ccae871657 100644
--- a/app/code/Magento/Config/App/Config/Type/System.php
+++ b/app/code/Magento/Config/App/Config/Type/System.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Config\App\Config\Type;
use Magento\Framework\App\Config\ConfigSourceInterface;
@@ -13,11 +14,12 @@
use Magento\Config\App\Config\Type\System\Reader;
use Magento\Framework\App\ScopeInterface;
use Magento\Framework\Cache\FrontendInterface;
+use Magento\Framework\Cache\LockGuardedCacheLoader;
use Magento\Framework\Lock\LockManagerInterface;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Store\Model\Config\Processor\Fallback;
-use Magento\Store\Model\ScopeInterface as StoreScope;
use Magento\Framework\Encryption\Encryptor;
+use Magento\Store\Model\ScopeInterface as StoreScope;
/**
* System configuration type
@@ -25,6 +27,7 @@
* @api
* @since 100.1.2
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
*/
class System implements ConfigTypeInterface
{
@@ -42,22 +45,6 @@ class System implements ConfigTypeInterface
* @var string
*/
private static $lockName = 'SYSTEM_CONFIG';
- /**
- * Timeout between retrieves to load the configuration from the cache.
- *
- * Value of the variable in microseconds.
- *
- * @var int
- */
- private static $delayTimeout = 100000;
- /**
- * Lifetime of the lock for write in cache.
- *
- * Value of the variable in seconds.
- *
- * @var int
- */
- private static $lockTimeout = 42;
/**
* @var array
@@ -106,9 +93,9 @@ class System implements ConfigTypeInterface
private $encryptor;
/**
- * @var LockManagerInterface
+ * @var LockGuardedCacheLoader
*/
- private $locker;
+ private $lockQuery;
/**
* @param ConfigSourceInterface $source
@@ -122,6 +109,7 @@ class System implements ConfigTypeInterface
* @param Reader|null $reader
* @param Encryptor|null $encryptor
* @param LockManagerInterface|null $locker
+ * @param LockGuardedCacheLoader|null $lockQuery
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
@@ -136,7 +124,8 @@ public function __construct(
$configType = self::CONFIG_TYPE,
Reader $reader = null,
Encryptor $encryptor = null,
- LockManagerInterface $locker = null
+ LockManagerInterface $locker = null,
+ LockGuardedCacheLoader $lockQuery = null
) {
$this->postProcessor = $postProcessor;
$this->cache = $cache;
@@ -145,8 +134,8 @@ public function __construct(
$this->reader = $reader ?: ObjectManager::getInstance()->get(Reader::class);
$this->encryptor = $encryptor
?: ObjectManager::getInstance()->get(Encryptor::class);
- $this->locker = $locker
- ?: ObjectManager::getInstance()->get(LockManagerInterface::class);
+ $this->lockQuery = $lockQuery
+ ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
}
/**
@@ -225,83 +214,56 @@ private function getWithParts($path)
}
/**
- * Make lock on data load.
- *
- * @param callable $dataLoader
- * @param bool $flush
- * @return array
- */
- private function lockedLoadData(callable $dataLoader, bool $flush = false): array
- {
- $cachedData = $dataLoader(); //optimistic read
-
- while ($cachedData === false && $this->locker->isLocked(self::$lockName)) {
- usleep(self::$delayTimeout);
- $cachedData = $dataLoader();
- }
-
- while ($cachedData === false) {
- try {
- if ($this->locker->lock(self::$lockName, self::$lockTimeout)) {
- if (!$flush) {
- $data = $this->readData();
- $this->cacheData($data);
- $cachedData = $data;
- } else {
- $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
- $cachedData = [];
- }
- }
- } finally {
- $this->locker->unlock(self::$lockName);
- }
-
- if ($cachedData === false) {
- usleep(self::$delayTimeout);
- $cachedData = $dataLoader();
- }
- }
-
- return $cachedData;
- }
-
- /**
- * Load configuration data for all scopes
+ * Load configuration data for all scopes.
*
* @return array
*/
private function loadAllData()
{
- return $this->lockedLoadData(function () {
+ $loadAction = function () {
$cachedData = $this->cache->load($this->configType);
$data = false;
if ($cachedData !== false) {
$data = $this->serializer->unserialize($this->encryptor->decrypt($cachedData));
}
return $data;
- });
+ };
+
+ return $this->lockQuery->lockedLoadData(
+ self::$lockName,
+ $loadAction,
+ \Closure::fromCallable([$this, 'readData']),
+ \Closure::fromCallable([$this, 'cacheData'])
+ );
}
/**
- * Load configuration data for default scope
+ * Load configuration data for default scope.
*
* @param string $scopeType
* @return array
*/
private function loadDefaultScopeData($scopeType)
{
- return $this->lockedLoadData(function () use ($scopeType) {
+ $loadAction = function () use ($scopeType) {
$cachedData = $this->cache->load($this->configType . '_' . $scopeType);
$scopeData = false;
if ($cachedData !== false) {
$scopeData = [$scopeType => $this->serializer->unserialize($this->encryptor->decrypt($cachedData))];
}
return $scopeData;
- });
+ };
+
+ return $this->lockQuery->lockedLoadData(
+ self::$lockName,
+ $loadAction,
+ \Closure::fromCallable([$this, 'readData']),
+ \Closure::fromCallable([$this, 'cacheData'])
+ );
}
/**
- * Load configuration data for a specified scope
+ * Load configuration data for a specified scope.
*
* @param string $scopeType
* @param string $scopeId
@@ -309,7 +271,7 @@ private function loadDefaultScopeData($scopeType)
*/
private function loadScopeData($scopeType, $scopeId)
{
- return $this->lockedLoadData(function () use ($scopeType, $scopeId) {
+ $loadAction = function () use ($scopeType, $scopeId) {
$cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId);
$scopeData = false;
if ($cachedData === false) {
@@ -329,7 +291,14 @@ private function loadScopeData($scopeType, $scopeId)
}
return $scopeData;
- });
+ };
+
+ return $this->lockQuery->lockedLoadData(
+ self::$lockName,
+ $loadAction,
+ \Closure::fromCallable([$this, 'readData']),
+ \Closure::fromCallable([$this, 'cacheData'])
+ );
}
/**
@@ -371,7 +340,7 @@ private function cacheData(array $data)
}
/**
- * Walk nested hash map by keys from $pathParts
+ * Walk nested hash map by keys from $pathParts.
*
* @param array $data to walk in
* @param array $pathParts keys path
@@ -408,7 +377,7 @@ private function readData(): array
}
/**
- * Clean cache and global variables cache
+ * Clean cache and global variables cache.
*
* Next items cleared:
* - Internal property intended to store already loaded configuration data
@@ -420,11 +389,13 @@ private function readData(): array
public function clean()
{
$this->data = [];
- $this->lockedLoadData(
- function () {
- return false;
- },
- true
+ $cleanAction = function () {
+ $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]);
+ };
+
+ $this->lockQuery->lockedCleanData(
+ self::$lockName,
+ $cleanAction
);
}
}
diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php
index 6bf191c20a844..bd38d1451e1b6 100644
--- a/app/code/Magento/Config/Model/Config.php
+++ b/app/code/Magento/Config/Model/Config.php
@@ -114,6 +114,11 @@ class Config extends \Magento\Framework\DataObject
*/
private $scopeTypeNormalizer;
+ /**
+ * @var \Magento\MessageQueue\Api\PoisonPillPutInterface
+ */
+ private $pillPut;
+
/**
* @param \Magento\Framework\App\Config\ReinitableConfigInterface $config
* @param \Magento\Framework\Event\ManagerInterface $eventManager
@@ -126,6 +131,7 @@ class Config extends \Magento\Framework\DataObject
* @param array $data
* @param ScopeResolverPool|null $scopeResolverPool
* @param ScopeTypeNormalizer|null $scopeTypeNormalizer
+ * @param \Magento\MessageQueue\Api\PoisonPillPutInterface|null $pillPut
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -139,7 +145,8 @@ public function __construct(
SettingChecker $settingChecker = null,
array $data = [],
ScopeResolverPool $scopeResolverPool = null,
- ScopeTypeNormalizer $scopeTypeNormalizer = null
+ ScopeTypeNormalizer $scopeTypeNormalizer = null,
+ \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut = null
) {
parent::__construct($data);
$this->_eventManager = $eventManager;
@@ -155,6 +162,8 @@ public function __construct(
?? ObjectManager::getInstance()->get(ScopeResolverPool::class);
$this->scopeTypeNormalizer = $scopeTypeNormalizer
?? ObjectManager::getInstance()->get(ScopeTypeNormalizer::class);
+ $this->pillPut = $pillPut ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\MessageQueue\Api\PoisonPillPutInterface::class);
}
/**
@@ -224,6 +233,8 @@ public function save()
throw $e;
}
+ $this->pillPut->put();
+
return $this;
}
diff --git a/app/code/Magento/Config/composer.json b/app/code/Magento/Config/composer.json
index 57c067d2cae27..3312fb630ccda 100644
--- a/app/code/Magento/Config/composer.json
+++ b/app/code/Magento/Config/composer.json
@@ -7,6 +7,7 @@
"require": {
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
+ "magento/module-message-queue": "*",
"magento/module-backend": "*",
"magento/module-cron": "*",
"magento/module-deploy": "*",
diff --git a/app/code/Magento/Config/etc/di.xml b/app/code/Magento/Config/etc/di.xml
index 87a0e666d2d7b..920cac382fcbf 100644
--- a/app/code/Magento/Config/etc/di.xml
+++ b/app/code/Magento/Config/etc/di.xml
@@ -90,9 +90,18 @@
Magento\Framework\App\Config\PreProcessorComposite
Magento\Framework\Serialize\Serializer\Serialize
Magento\Config\App\Config\Type\System\Reader\Proxy
- Magento\Framework\Lock\Backend\Cache
+ systemConfigQueryLocker
+
+
+
+ Magento\Framework\Lock\Backend\Cache
+ 42000
+ 100
+
+
+
systemConfigSourceAggregated
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml
index 95b86ec3a8587..43dae2d70d416 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml
@@ -145,8 +145,8 @@
-
-
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml
index af12f49bf86ea..39aa516077c56 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml
@@ -57,7 +57,13 @@
-
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml
index 981985b3f4ea8..1db9b3e5b79b2 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml
@@ -144,7 +144,7 @@
-
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml
index e278018330aa6..934a410d58a8a 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml
@@ -126,7 +126,7 @@
-
+
diff --git a/app/code/Magento/Customer/Block/Address/Grid.php b/app/code/Magento/Customer/Block/Address/Grid.php
index de6767a0ef92a..963efc648d94b 100644
--- a/app/code/Magento/Customer/Block/Address/Grid.php
+++ b/app/code/Magento/Customer/Block/Address/Grid.php
@@ -1,9 +1,10 @@
addressCollectionFactory->create();
- $collection->setOrder('entity_id', 'desc')
- ->setCustomerFilter([$this->getCustomer()->getId()]);
+ $collection->setOrder('entity_id', 'desc');
+ $collection->addFieldToFilter(
+ 'entity_id',
+ ['nin' => [$this->getDefaultBilling(), $this->getDefaultShipping()]]
+ );
+ $collection->setCustomerFilter([$this->getCustomer()->getId()]);
$this->addressCollection = $collection;
}
return $this->addressCollection;
diff --git a/app/code/Magento/Customer/Model/Visitor.php b/app/code/Magento/Customer/Model/Visitor.php
index 9caa2988c5a94..4f129f05aa82c 100644
--- a/app/code/Magento/Customer/Model/Visitor.php
+++ b/app/code/Magento/Customer/Model/Visitor.php
@@ -14,6 +14,7 @@
*
* @package Magento\Customer\Model
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
*/
class Visitor extends \Magento\Framework\Model\AbstractModel
{
@@ -168,10 +169,6 @@ public function initByRequest($observer)
$this->setLastVisitAt((new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT));
- // prevent saving Visitor for safe methods, e.g. GET request
- if ($this->requestSafety->isSafeMethod()) {
- return $this;
- }
if (!$this->getId()) {
$this->setSessionId($this->session->getSessionId());
$this->save();
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerAccountPageTitleActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerAccountPageTitleActionGroup.xml
new file mode 100644
index 0000000000000..132b5ca81886f
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerAccountPageTitleActionGroup.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml
index 90544dc2673a3..703b9f542f81a 100644
--- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml
@@ -17,5 +17,6 @@
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateThroughCustomerTabsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateThroughCustomerTabsActionGroup.xml
new file mode 100644
index 0000000000000..5591bee529690
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateThroughCustomerTabsActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenMyAccountPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenMyAccountPageActionGroup.xml
new file mode 100644
index 0000000000000..6ca0f612deeaa
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenMyAccountPageActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml
index 54644819e852b..06c23a2864984 100644
--- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml
@@ -46,6 +46,19 @@
0
US_Address_TX
+
+ 1
+ true
+ true
+ John.Doe@example.com
+ LoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsum
+ Doe
+ John Doe
+ pwdTest123!
+ 0
+ 0
+ US_Address_TX
+
1
John.Doe@example.com
@@ -190,4 +203,4 @@
0
0
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml
index 9b6155e982013..28305d37cf77b 100644
--- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml
@@ -8,6 +8,12 @@
+
+ 0
+ NOT LOGGED IN
+ 3
+ Retail Customer
+
General
3
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml
index 1fdb15f189ace..4cb7f5e3f628e 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml
@@ -15,5 +15,6 @@
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/LoggedInCustomerHeaderLinksSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/LoggedInCustomerHeaderLinksSection.xml
new file mode 100644
index 0000000000000..907551e932fcf
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Section/LoggedInCustomerHeaderLinksSection.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml
new file mode 100644
index 0000000000000..3a4329969ae2b
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml
index bed0c71ae7fad..407c6480e9dde 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml
@@ -9,7 +9,7 @@
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml
new file mode 100644
index 0000000000000..648c30b1ca0bb
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php
index 31bcc37612302..47f96b132b3db 100644
--- a/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php
@@ -81,7 +81,7 @@ protected function setUp()
public function testGetChildHtml()
{
$customerId = 1;
-
+ $outputString = 'OutputString';
/** @var \Magento\Framework\View\Element\BlockInterface|\PHPUnit_Framework_MockObject_MockObject $block */
$block = $this->getMockBuilder(\Magento\Framework\View\Element\BlockInterface::class)
->setMethods(['setCollection'])
@@ -93,7 +93,7 @@ public function testGetChildHtml()
/** @var \PHPUnit_Framework_MockObject_MockObject */
$addressCollection = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Address\Collection::class)
->disableOriginalConstructor()
- ->setMethods(['setOrder', 'setCustomerFilter', 'load'])
+ ->setMethods(['setOrder', 'setCustomerFilter', 'load','addFieldToFilter'])
->getMock();
$layout->expects($this->atLeastOnce())->method('getChildName')->with('NameInLayout', 'pager')
@@ -108,12 +108,13 @@ public function testGetChildHtml()
->willReturnSelf();
$addressCollection->expects($this->atLeastOnce())->method('setCustomerFilter')->with([$customerId])
->willReturnSelf();
+ $addressCollection->expects(static::any())->method('addFieldToFilter')->willReturnSelf();
$this->addressCollectionFactory->expects($this->atLeastOnce())->method('create')
->willReturn($addressCollection);
$block->expects($this->atLeastOnce())->method('setCollection')->with($addressCollection)->willReturnSelf();
$this->gridBlock->setNameInLayout('NameInLayout');
$this->gridBlock->setLayout($layout);
- $this->assertEquals('OutputString', $this->gridBlock->getChildHtml('pager'));
+ $this->assertEquals($outputString, $this->gridBlock->getChildHtml('pager'));
}
/**
@@ -137,7 +138,7 @@ public function testGetAdditionalAddresses()
/** @var \PHPUnit_Framework_MockObject_MockObject */
$addressCollection = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Address\Collection::class)
->disableOriginalConstructor()
- ->setMethods(['setOrder', 'setCustomerFilter', 'load', 'getIterator'])
+ ->setMethods(['setOrder', 'setCustomerFilter', 'load', 'getIterator','addFieldToFilter'])
->getMock();
$addressDataModel = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\AddressInterface::class);
$address = $this->getMockBuilder(\Magento\Customer\Model\Address::class)
@@ -157,6 +158,7 @@ public function testGetAdditionalAddresses()
->willReturnSelf();
$addressCollection->expects($this->atLeastOnce())->method('setCustomerFilter')->with([$customerId])
->willReturnSelf();
+ $addressCollection->expects(static::any())->method('addFieldToFilter')->willReturnSelf();
$addressCollection->expects($this->atLeastOnce())->method('getIterator')
->willReturn(new \ArrayIterator($collection));
$this->addressCollectionFactory->expects($this->atLeastOnce())->method('create')
diff --git a/app/code/Magento/CustomerAnalytics/composer.json b/app/code/Magento/CustomerAnalytics/composer.json
index 7dec4279ee280..3840c534b1964 100644
--- a/app/code/Magento/CustomerAnalytics/composer.json
+++ b/app/code/Magento/CustomerAnalytics/composer.json
@@ -4,7 +4,8 @@
"require": {
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
- "magento/module-customer": "*"
+ "magento/module-customer": "*",
+ "magento/module-analytics": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/Deploy/Console/DeployStaticOptions.php b/app/code/Magento/Deploy/Console/DeployStaticOptions.php
index 89cb3e4b30345..1c02d24f7e99c 100644
--- a/app/code/Magento/Deploy/Console/DeployStaticOptions.php
+++ b/app/code/Magento/Deploy/Console/DeployStaticOptions.php
@@ -6,6 +6,7 @@
namespace Magento\Deploy\Console;
+use Magento\Deploy\Process\Queue;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
@@ -57,6 +58,11 @@ class DeployStaticOptions
*/
const JOBS_AMOUNT = 'jobs';
+ /**
+ * Key for max execution time option
+ */
+ const MAX_EXECUTION_TIME = 'max-execution-time';
+
/**
* Force run of static deploy
*/
@@ -150,6 +156,7 @@ public function getOptionsList()
* Basic options
*
* @return array
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
private function getBasicOptions()
{
@@ -216,6 +223,13 @@ private function getBasicOptions()
'Enable parallel processing using the specified number of jobs.',
self::DEFAULT_JOBS_AMOUNT
),
+ new InputOption(
+ self::MAX_EXECUTION_TIME,
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'The maximum expected execution time of deployment static process (in seconds).',
+ Queue::DEFAULT_MAX_EXEC_TIME
+ ),
new InputOption(
self::SYMLINK_LOCALE,
null,
diff --git a/app/code/Magento/Deploy/Service/DeployStaticContent.php b/app/code/Magento/Deploy/Service/DeployStaticContent.php
index 66ec6e7418afd..854bf50e0af2f 100644
--- a/app/code/Magento/Deploy/Service/DeployStaticContent.php
+++ b/app/code/Magento/Deploy/Service/DeployStaticContent.php
@@ -85,24 +85,26 @@ public function deploy(array $options)
return;
}
- $queue = $this->queueFactory->create(
- [
- 'logger' => $this->logger,
- 'options' => $options,
- 'maxProcesses' => $this->getProcessesAmount($options),
- 'deployPackageService' => $this->objectManager->create(
- \Magento\Deploy\Service\DeployPackage::class,
- [
- 'logger' => $this->logger
- ]
- )
- ]
- );
+ $queueOptions = [
+ 'logger' => $this->logger,
+ 'options' => $options,
+ 'maxProcesses' => $this->getProcessesAmount($options),
+ 'deployPackageService' => $this->objectManager->create(
+ \Magento\Deploy\Service\DeployPackage::class,
+ [
+ 'logger' => $this->logger
+ ]
+ )
+ ];
+
+ if (isset($options[Options::MAX_EXECUTION_TIME])) {
+ $queueOptions['maxExecTime'] = (int)$options[Options::MAX_EXECUTION_TIME];
+ }
$deployStrategy = $this->deployStrategyFactory->create(
$options[Options::STRATEGY],
[
- 'queue' => $queue
+ 'queue' => $this->queueFactory->create($queueOptions)
]
);
@@ -133,6 +135,8 @@ public function deploy(array $options)
}
/**
+ * Returns amount of parallel processes, returns zero if option wasn't set.
+ *
* @param array $options
* @return int
*/
@@ -142,6 +146,8 @@ private function getProcessesAmount(array $options)
}
/**
+ * Checks if need to refresh only version.
+ *
* @param array $options
* @return bool
*/
diff --git a/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php b/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php
index 75edc8cb4f6ee..396381960e544 100644
--- a/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php
+++ b/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php
@@ -5,6 +5,7 @@
*/
namespace Magento\Deploy\Test\Unit\Service;
+use Magento\Deploy\Console\DeployStaticOptions;
use Magento\Deploy\Package\Package;
use Magento\Deploy\Process\Queue;
use Magento\Deploy\Service\Bundle;
@@ -221,4 +222,35 @@ public function deployDataProvider()
]
];
}
+
+ public function testMaxExecutionTimeOptionPassed()
+ {
+ $options = [
+ DeployStaticOptions::MAX_EXECUTION_TIME => 100,
+ DeployStaticOptions::REFRESH_CONTENT_VERSION_ONLY => false,
+ DeployStaticOptions::JOBS_AMOUNT => 3,
+ DeployStaticOptions::STRATEGY => 'compact',
+ DeployStaticOptions::NO_JAVASCRIPT => true,
+ DeployStaticOptions::NO_HTML_MINIFY => true,
+ ];
+
+ $queueMock = $this->createMock(Queue::class);
+ $strategyMock = $this->createMock(CompactDeploy::class);
+ $this->queueFactory->expects($this->once())
+ ->method('create')
+ ->with([
+ 'logger' => $this->logger,
+ 'maxExecTime' => 100,
+ 'maxProcesses' => 3,
+ 'options' => $options,
+ 'deployPackageService' => null
+ ])
+ ->willReturn($queueMock);
+ $this->deployStrategyFactory->expects($this->once())
+ ->method('create')
+ ->with('compact', ['queue' => $queueMock])
+ ->willReturn($strategyMock);
+
+ $this->service->deploy($options);
+ }
}
diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php
index 69d500960d3f0..47f4fb0a6c7f3 100644
--- a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php
+++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php
@@ -13,8 +13,7 @@
use Magento\Framework\Setup\Patch\PatchVersionInterface;
/**
- * Class AddDataForIndia
- * @package Magento\Directory\Setup\Patch\Data
+ * Add Regions for India.
*/
class AddDataForIndia implements DataPatchInterface, PatchVersionInterface
{
@@ -29,7 +28,7 @@ class AddDataForIndia implements DataPatchInterface, PatchVersionInterface
private $dataInstallerFactory;
/**
- * AddDataForCroatia constructor.
+ * AddDataForIndia constructor.
*
* @param ModuleDataSetupInterface $moduleDataSetup
* @param \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory
@@ -43,7 +42,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function apply()
{
@@ -103,7 +102,7 @@ private function getDataForIndia()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public static function getDependencies()
{
@@ -113,7 +112,7 @@ public static function getDependencies()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public static function getVersion()
{
@@ -121,7 +120,7 @@ public static function getVersion()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getAliases()
{
diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForMexico.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForMexico.php
new file mode 100644
index 0000000000000..32bdf90800d6b
--- /dev/null
+++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForMexico.php
@@ -0,0 +1,127 @@
+moduleDataSetup = $moduleDataSetup;
+ $this->dataInstallerFactory = $dataInstallerFactory;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function apply()
+ {
+ /** @var DataInstaller $dataInstaller */
+ $dataInstaller = $this->dataInstallerFactory->create();
+ $dataInstaller->addCountryRegions(
+ $this->moduleDataSetup->getConnection(),
+ $this->getDataForMexico()
+ );
+ }
+
+ /**
+ * Mexican states data.
+ *
+ * @return array
+ */
+ private function getDataForMexico()
+ {
+ return [
+ ['MX', 'AGU', 'Aguascalientes'],
+ ['MX', 'BCN', 'Baja California'],
+ ['MX', 'BCS', 'Baja California Sur'],
+ ['MX', 'CAM', 'Campeche'],
+ ['MX', 'CHP', 'Chiapas'],
+ ['MX', 'CHH', 'Chihuahua'],
+ ['MX', 'CMX', 'Ciudad de México'],
+ ['MX', 'COA', 'Coahuila'],
+ ['MX', 'COL', 'Colima'],
+ ['MX', 'DUR', 'Durango'],
+ ['MX', 'MEX', 'Estado de México'],
+ ['MX', 'GUA', 'Guanajuato'],
+ ['MX', 'GRO', 'Guerrero'],
+ ['MX', 'HID', 'Hidalgo'],
+ ['MX', 'JAL', 'Jalisco'],
+ ['MX', 'MIC', 'Michoacán'],
+ ['MX', 'MOR', 'Morelos'],
+ ['MX', 'NAY', 'Nayarit'],
+ ['MX', 'NLE', 'Nuevo León'],
+ ['MX', 'OAX', 'Oaxaca'],
+ ['MX', 'PUE', 'Puebla'],
+ ['MX', 'QUE', 'Querétaro'],
+ ['MX', 'ROO', 'Quintana Roo'],
+ ['MX', 'SLP', 'San Luis Potosí'],
+ ['MX', 'SIN', 'Sinaloa'],
+ ['MX', 'SON', 'Sonora'],
+ ['MX', 'TAB', 'Tabasco'],
+ ['MX', 'TAM', 'Tamaulipas'],
+ ['MX', 'TLA', 'Tlaxcala'],
+ ['MX', 'VER', 'Veracruz'],
+ ['MX', 'YUC', 'Yucatán'],
+ ['MX', 'ZAC', 'Zacatecas']
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function getDependencies()
+ {
+ return [
+ InitializeDirectoryData::class,
+ AddDataForAustralia::class,
+ AddDataForCroatia::class,
+ AddDataForIndia::class,
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public static function getVersion()
+ {
+ return '2.0.4';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getAliases()
+ {
+ return [];
+ }
+}
diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php
index c5a18a3de99c6..be9d2700664c7 100644
--- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php
+++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php
@@ -4,15 +4,13 @@
* See COPYING.txt for license details.
*/
-/**
- * Product attribute add/edit form main tab
- *
- * @author Magento Core Team
- */
namespace Magento\Eav\Block\Adminhtml\Attribute\Edit\Main;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
+/**
+ * Product attribute add/edit form main tab
+ */
abstract class AbstractMain extends \Magento\Backend\Block\Widget\Form\Generic
{
/**
@@ -110,7 +108,6 @@ protected function _prepareForm()
/** @var \Magento\Framework\Data\Form $form */
$form = $this->_formFactory->create(
-
['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']]
);
@@ -280,10 +277,11 @@ protected function _initFormValues()
}
/**
- * Processing block html after rendering
+ * Processing block html after rendering.
+ *
* Adding js block to the end of this block
*
- * @param string $html
+ * @param string $html
* @return string
*/
protected function _afterToHtml($html)
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php
index 56188ab997b76..36ad026029056 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php
@@ -73,7 +73,7 @@ public function getOptionText($value)
}
}
// End
- if (isset($options[$value])) {
+ if (is_scalar($value) && isset($options[$value])) {
return $options[$value];
}
return false;
diff --git a/app/code/Magento/Eav/Setup/EavSetup.php b/app/code/Magento/Eav/Setup/EavSetup.php
index 6e81ddc36e9c9..29f9163a6e91d 100644
--- a/app/code/Magento/Eav/Setup/EavSetup.php
+++ b/app/code/Magento/Eav/Setup/EavSetup.php
@@ -10,12 +10,12 @@
use Magento\Eav\Model\Entity\Setup\PropertyMapperInterface;
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory;
use Magento\Framework\App\CacheInterface;
-use Magento\Framework\App\ObjectManager;
-use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Setup\ModuleDataSetupInterface;
/**
+ * Base eav setup class.
+ *
* @api
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -101,7 +101,8 @@ public function __construct(
}
/**
- * Gets setup model
+ * Gets setup model.
+ *
* @deprecated
* @return ModuleDataSetupInterface
*/
@@ -568,6 +569,8 @@ public function addAttributeGroup($entityTypeId, $setId, $name, $sortOrder = nul
}
/**
+ * Convert group name to attribute group code.
+ *
* @param string $groupName
* @return string
* @since 100.1.0
@@ -1063,7 +1066,7 @@ private function _updateAttributeAdditionalData($entityTypeId, $id, $field, $val
return $this;
}
}
-
+
$attributeId = $this->getAttributeId($entityTypeId, $id);
if (false === $attributeId) {
throw new LocalizedException(__('Attribute with ID: "%1" does not exist', $id));
diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php
index ee972c27aa8a2..8cf5df877a6eb 100644
--- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php
+++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php
@@ -101,13 +101,13 @@ public function addValueSortToCollectionDataProvider()
'expectedJoinCondition' => [
0 => [
'requisites' => ['code_t1' => "table"],
- 'condition' =>
- "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123' AND code_t1.store_id='0'",
+ 'condition' => "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123'"
+ . " AND code_t1.store_id='0'",
],
1 => [
'requisites' => ['code_t2' => "table"],
- 'condition' =>
- "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123' AND code_t2.store_id='12'",
+ 'condition' => "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123'"
+ . " AND code_t2.store_id='12'",
],
],
'expectedOrder' => 'IF(code_t2.value_id > 0, code_t2.value, code_t1.value) ASC',
@@ -118,13 +118,13 @@ public function addValueSortToCollectionDataProvider()
'expectedJoinCondition' => [
0 => [
'requisites' => ['code_t1' => "table"],
- 'condition' =>
- "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123' AND code_t1.store_id='0'",
+ 'condition' => "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123'"
+ . " AND code_t1.store_id='0'",
],
1 => [
'requisites' => ['code_t2' => "table"],
- 'condition' =>
- "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123' AND code_t2.store_id='12'",
+ 'condition' => "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123'"
+ . " AND code_t2.store_id='12'",
],
],
'expectedOrder' => 'IF(code_t2.value_id > 0, code_t2.value, code_t1.value) DESC',
@@ -135,8 +135,8 @@ public function addValueSortToCollectionDataProvider()
'expectedJoinCondition' => [
0 => [
'requisites' => ['code_t' => "table"],
- 'condition' =>
- "e.entity_id=code_t.entity_id AND code_t.attribute_id='123' AND code_t.store_id='0'",
+ 'condition' => "e.entity_id=code_t.entity_id AND code_t.attribute_id='123'"
+ . " AND code_t.store_id='0'",
],
],
'expectedOrder' => 'code_t.value DESC',
@@ -147,8 +147,8 @@ public function addValueSortToCollectionDataProvider()
'expectedJoinCondition' => [
0 => [
'requisites' => ['code_t' => "table"],
- 'condition' =>
- "e.entity_id=code_t.entity_id AND code_t.attribute_id='123' AND code_t.store_id='0'",
+ 'condition' => "e.entity_id=code_t.entity_id AND code_t.attribute_id='123'"
+ . " AND code_t.store_id='0'",
],
],
'expectedOrder' => 'code_t.value ASC',
diff --git a/app/code/Magento/Elasticsearch/Model/Config.php b/app/code/Magento/Elasticsearch/Model/Config.php
index dc08a72a9feb3..387db07c62f90 100644
--- a/app/code/Magento/Elasticsearch/Model/Config.php
+++ b/app/code/Magento/Elasticsearch/Model/Config.php
@@ -25,8 +25,6 @@ class Config implements ClientOptionsInterface
*/
const ENGINE_NAME = 'elasticsearch';
- private const ENGINE_NAME_5 = 'elasticsearch5';
-
/**
* Elasticsearch Entity type
*/
@@ -64,23 +62,31 @@ class Config implements ClientOptionsInterface
private $engineResolver;
/**
- * Constructor
+ * Available Elasticsearch engines.
*
+ * @var array
+ */
+ private $engineList;
+
+ /**
* @param ScopeConfigInterface $scopeConfig
* @param ClientResolver|null $clientResolver
* @param EngineResolverInterface|null $engineResolver
* @param string|null $prefix
+ * @param array $engineList
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
ClientResolver $clientResolver = null,
EngineResolverInterface $engineResolver = null,
- $prefix = null
+ $prefix = null,
+ $engineList = []
) {
$this->scopeConfig = $scopeConfig;
$this->clientResolver = $clientResolver ?: ObjectManager::getInstance()->get(ClientResolver::class);
$this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class);
$this->prefix = $prefix ?: $this->clientResolver->getCurrentEngine();
+ $this->engineList = $engineList;
}
/**
@@ -138,7 +144,7 @@ public function getSearchConfigData($field, $storeId = null)
*/
public function isElasticsearchEnabled()
{
- return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME, self::ENGINE_NAME_5]);
+ return in_array($this->engineResolver->getCurrentSearchEngine(), $this->engineList);
}
/**
diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml
index 9796dc93858f1..4a354a2ea528f 100644
--- a/app/code/Magento/Elasticsearch/etc/di.xml
+++ b/app/code/Magento/Elasticsearch/etc/di.xml
@@ -13,6 +13,14 @@
+
+
+
+ - elasticsearch
+ - elasticsearch5
+
+
+
diff --git a/app/code/Magento/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php
deleted file mode 100644
index 1a989e2705fdd..0000000000000
--- a/app/code/Magento/Elasticsearch6/Model/Config.php
+++ /dev/null
@@ -1,56 +0,0 @@
-engineResolver = $engineResolver;
- }
-
- /**
- * Return true if third party search engine is used
- *
- * @return bool
- */
- public function isElasticsearchEnabled()
- {
- return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME_6]);
- }
-}
diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php
index 77e1270f54fc2..d05471734bb8f 100644
--- a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php
+++ b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php
@@ -9,7 +9,7 @@
use Magento\Store\Model\ScopeInterface;
use Magento\Search\Model\QueryInterface;
use Magento\AdvancedSearch\Model\SuggestedQueriesInterface;
-use Magento\Elasticsearch6\Model\Config;
+use Magento\Elasticsearch\Model\Config;
use Magento\Elasticsearch\SearchAdapter\ConnectionManager;
use Magento\Search\Model\QueryResultFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php
index 957edc559fdcb..b3c60b70ffa8e 100644
--- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php
+++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php
@@ -67,7 +67,7 @@ class SuggestionsTest extends \PHPUnit\Framework\TestCase
*/
protected function setUp()
{
- $this->config = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Config::class)
+ $this->config = $this->getMockBuilder(\Magento\Elasticsearch\Model\Config::class)
->disableOriginalConstructor()
->setMethods(['isElasticsearchEnabled'])
->getMock();
diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml
index df71f3c3158d3..011dfa1019738 100644
--- a/app/code/Magento/Elasticsearch6/etc/di.xml
+++ b/app/code/Magento/Elasticsearch6/etc/di.xml
@@ -6,6 +6,14 @@
*/
-->
+
+
+
+ - elasticsearch6
+
+
+
+
@@ -44,7 +52,7 @@
- \Magento\Elasticsearch6\Model\Client\ElasticsearchFactory
- - \Magento\Elasticsearch6\Model\Config
+ - \Magento\Elasticsearch\Model\Config
@@ -162,4 +170,36 @@
+
+
+
+
+ - elasticsearchCategoryCollectionFactory
+
+
+
+
+
+
+
+ - elasticsearchAdvancedCollectionFactory
+
+
+
+
+
+
+
+ - Magento\Elasticsearch\Model\Advanced\ProductCollectionPrepareStrategy
+
+
+
+
+
+
+
+ - elasticsearchFulltextSearchCollectionFactory
+
+
+
diff --git a/app/code/Magento/GiftMessage/Model/CompositeConfigProvider.php b/app/code/Magento/GiftMessage/Model/CompositeConfigProvider.php
index 0fdce9e9090ac..cb370c27863ca 100644
--- a/app/code/Magento/GiftMessage/Model/CompositeConfigProvider.php
+++ b/app/code/Magento/GiftMessage/Model/CompositeConfigProvider.php
@@ -7,6 +7,9 @@
use Magento\Checkout\Model\ConfigProviderInterface;
+/**
+ * Class CompositeConfigProvider
+ */
class CompositeConfigProvider implements ConfigProviderInterface
{
/**
@@ -18,13 +21,13 @@ class CompositeConfigProvider implements ConfigProviderInterface
* @param ConfigProviderInterface[] $configProviders
*/
public function __construct(
- array $configProviders
+ array $configProviders = []
) {
$this->configProviders = $configProviders;
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getConfig()
{
diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml
new file mode 100644
index 0000000000000..a9100b4730b8c
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml
index ad9e7672ce11a..528ad23aaf2bf 100644
--- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml
+++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml
@@ -11,5 +11,11 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml
new file mode 100644
index 0000000000000..ceb4e93e4e9aa
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SimpleProductForTest1
+ SimpleProductForTest1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml
new file mode 100644
index 0000000000000..d63a5546716b1
--- /dev/null
+++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SimpleProductForTest2
+ SimpleProductForTest2
+
+
+
+
+
+ SimpleProductForTest3
+ SimpleProductForTest3
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Indexer/Model/Indexer.php b/app/code/Magento/Indexer/Model/Indexer.php
index 87a7cce58e1a5..2821a46f29416 100644
--- a/app/code/Magento/Indexer/Model/Indexer.php
+++ b/app/code/Magento/Indexer/Model/Indexer.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Indexer\Model;
use Magento\Framework\Indexer\ActionFactory;
@@ -14,6 +15,8 @@
use Magento\Framework\Indexer\StructureFactory;
/**
+ * Indexer model.
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Indexer extends \Magento\Framework\DataObject implements IndexerInterface
@@ -361,7 +364,7 @@ public function getLatestUpdated()
return $this->getView()->getUpdated();
}
}
- return $this->getState()->getUpdated();
+ return $this->getState()->getUpdated() ?: '';
}
/**
diff --git a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php
index 6b7cc12218990..ca2da9585f934 100644
--- a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php
+++ b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php
@@ -164,7 +164,12 @@ public function testGetLatestUpdated($getViewIsEnabled, $getViewGetUpdated, $get
}
}
} else {
- $this->assertEquals($getStateGetUpdated, $this->model->getLatestUpdated());
+ $getLatestUpdated = $this->model->getLatestUpdated();
+ $this->assertEquals($getStateGetUpdated, $getLatestUpdated);
+
+ if ($getStateGetUpdated === null) {
+ $this->assertNotNull($getLatestUpdated);
+ }
}
}
@@ -182,7 +187,8 @@ public function getLatestUpdatedDataProvider()
[true, '', '06-Jan-1944'],
[true, '06-Jan-1944', ''],
[true, '', ''],
- [true, '06-Jan-1944', '05-Jan-1944']
+ [true, '06-Jan-1944', '05-Jan-1944'],
+ [false, null, null],
];
}
diff --git a/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php b/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php
new file mode 100644
index 0000000000000..3d5b895575597
--- /dev/null
+++ b/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php
@@ -0,0 +1,24 @@
+poisonPillRead = $poisonPillRead;
+ $this->poisonPillCompare = $poisonPillCompare;
+ }
+
+ /**
+ * @inheritdoc
+ * @SuppressWarnings(PHPMD.ExitExpression)
+ */
+ public function invoke(QueueInterface $queue, $maxNumberOfMessages, $callback)
+ {
+ $this->poisonPillVersion = $this->poisonPillRead->getLatestVersion();
+ for ($i = $maxNumberOfMessages; $i > 0; $i--) {
+ do {
+ $message = $queue->dequeue();
+ } while ($message === null && (sleep(1) === 0));
+ if (false === $this->poisonPillCompare->isLatestVersion($this->poisonPillVersion)) {
+ $queue->reject($message);
+ exit(0);
+ }
+ $callback($message);
+ }
+ }
+}
diff --git a/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php
new file mode 100644
index 0000000000000..a8e40ea495002
--- /dev/null
+++ b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php
@@ -0,0 +1,40 @@
+poisonPillRead = $poisonPillRead;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function isLatestVersion(int $poisonPillVersion): bool
+ {
+ return $poisonPillVersion === $this->poisonPillRead->getLatestVersion();
+ }
+}
diff --git a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php
new file mode 100644
index 0000000000000..283fff8ace7c7
--- /dev/null
+++ b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php
@@ -0,0 +1,75 @@
+_init(self::QUEUE_POISON_PILL_TABLE, 'version');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function put(): int
+ {
+ $connection = $this->getConnection();
+ $table = $this->getMainTable();
+ $connection->insert($table, []);
+ return (int)$connection->lastInsertId($table);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getLatestVersion() : int
+ {
+ $select = $this->getConnection()->select()->from(
+ $this->getTable(self::QUEUE_POISON_PILL_TABLE),
+ 'version'
+ )->order(
+ 'version ' . \Magento\Framework\DB\Select::SQL_DESC
+ )->limit(
+ 1
+ );
+
+ $version = (int)$this->getConnection()->fetchOne($select);
+
+ return $version;
+ }
+}
diff --git a/app/code/Magento/MessageQueue/etc/db_schema.xml b/app/code/Magento/MessageQueue/etc/db_schema.xml
index 7a20d2bd4df5d..9cdf414dd06e1 100644
--- a/app/code/Magento/MessageQueue/etc/db_schema.xml
+++ b/app/code/Magento/MessageQueue/etc/db_schema.xml
@@ -21,4 +21,12 @@
+
diff --git a/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json b/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json
index f31981d2ec40f..d9d623a994b37 100644
--- a/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json
+++ b/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json
@@ -9,5 +9,13 @@
"PRIMARY": true,
"QUEUE_LOCK_MESSAGE_CODE": true
}
+ },
+ "queue_poison_pill": {
+ "column": {
+ "version": true
+ },
+ "constraint": {
+ "PRIMARY": true
+ }
}
-}
\ No newline at end of file
+}
diff --git a/app/code/Magento/MessageQueue/etc/di.xml b/app/code/Magento/MessageQueue/etc/di.xml
index c8f2edb862613..22cfea976a722 100644
--- a/app/code/Magento/MessageQueue/etc/di.xml
+++ b/app/code/Magento/MessageQueue/etc/di.xml
@@ -13,6 +13,10 @@
+
+
+
+
diff --git a/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml b/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml
index 34a77095d524d..ee0c32633569a 100644
--- a/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml
+++ b/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml
@@ -30,5 +30,6 @@
+
diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml
new file mode 100644
index 0000000000000..bd6f7ba362bf4
--- /dev/null
+++ b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/QuoteAnalytics/composer.json b/app/code/Magento/QuoteAnalytics/composer.json
index 90dae1ec2adca..706bed674b4a9 100644
--- a/app/code/Magento/QuoteAnalytics/composer.json
+++ b/app/code/Magento/QuoteAnalytics/composer.json
@@ -4,7 +4,8 @@
"require": {
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
- "magento/module-quote": "*"
+ "magento/module-quote": "*",
+ "magento/module-analytics": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/ReviewAnalytics/composer.json b/app/code/Magento/ReviewAnalytics/composer.json
index 73f534451580c..a82d4328ca159 100644
--- a/app/code/Magento/ReviewAnalytics/composer.json
+++ b/app/code/Magento/ReviewAnalytics/composer.json
@@ -4,7 +4,8 @@
"require": {
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
- "magento/module-review": "*"
+ "magento/module-review": "*",
+ "magento/module-analytics": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php
index d15c218a60b47..6b87c1fe39d8b 100644
--- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php
+++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php
@@ -6,6 +6,7 @@
namespace Magento\Sales\Block\Adminhtml\Order\Create\Form;
use Magento\Framework\Pricing\PriceCurrencyInterface;
+use Magento\Customer\Api\Data\AttributeMetadataInterface;
/**
* Sales Order Create Form Abstract Block
@@ -57,8 +58,7 @@ public function __construct(
}
/**
- * Prepare global layout
- * Add renderers to \Magento\Framework\Data\Form
+ * Prepare global layout. Add renderers to \Magento\Framework\Data\Form
*
* @return $this
*/
@@ -152,7 +152,7 @@ protected function _addAdditionalFormElementData(\Magento\Framework\Data\Form\El
/**
* Add rendering EAV attributes to Form element
*
- * @param \Magento\Customer\Api\Data\AttributeMetadataInterface[] $attributes
+ * @param AttributeMetadataInterface[] $attributes
* @param \Magento\Framework\Data\Form\AbstractForm $form
* @return $this
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
@@ -176,8 +176,8 @@ protected function _addAttributesToForm($attributes, \Magento\Framework\Data\For
[
'name' => $attribute->getAttributeCode(),
'label' => __($attribute->getStoreLabel()),
- 'class' => $attribute->getFrontendClass(),
- 'required' => $attribute->isRequired()
+ 'class' => $this->getValidationClasses($attribute),
+ 'required' => $attribute->isRequired(),
]
);
if ($inputType == 'multiline') {
@@ -227,4 +227,58 @@ public function getFormValues()
{
return [];
}
+
+ /**
+ * Retrieve frontend classes according validation rules
+ *
+ * @param AttributeMetadataInterface $attribute
+ *
+ * @return string
+ */
+ private function getValidationClasses(AttributeMetadataInterface $attribute) : string
+ {
+ $out = [];
+ $out[] = $attribute->getFrontendClass();
+
+ $textClasses = $this->getTextLengthValidateClasses($attribute);
+ if (!empty($textClasses)) {
+ $out = array_merge($out, $textClasses);
+ }
+
+ $out = !empty($out) ? implode(' ', array_unique(array_filter($out))) : '';
+ return $out;
+ }
+
+ /**
+ * Retrieve validation classes by min_text_length and max_text_length rules
+ *
+ * @param AttributeMetadataInterface $attribute
+ *
+ * @return array
+ */
+ private function getTextLengthValidateClasses(AttributeMetadataInterface $attribute) : array
+ {
+ $classes = [];
+
+ $validateRules = $attribute->getValidationRules();
+ if (!empty($validateRules)) {
+ foreach ($validateRules as $rule) {
+ switch ($rule->getName()) {
+ case 'min_text_length':
+ $classes[] = 'minimum-length-' . $rule->getValue();
+ break;
+
+ case 'max_text_length':
+ $classes[] = 'maximum-length-' . $rule->getValue();
+ break;
+ }
+ }
+
+ if (!empty($classes)) {
+ $classes[] = 'validate-length';
+ }
+ }
+
+ return $classes;
+ }
}
diff --git a/app/code/Magento/Sales/Model/Order/Address/Validator.php b/app/code/Magento/Sales/Model/Order/Address/Validator.php
index 31cb5bb1f60ca..5d3186781e7d7 100644
--- a/app/code/Magento/Sales/Model/Order/Address/Validator.php
+++ b/app/code/Magento/Sales/Model/Order/Address/Validator.php
@@ -49,8 +49,8 @@ class Validator
/**
* @param DirectoryHelper $directoryHelper
- * @param CountryFactory $countryFactory
- * @param EavConfig $eavConfig
+ * @param CountryFactory $countryFactory
+ * @param EavConfig $eavConfig
*/
public function __construct(
DirectoryHelper $directoryHelper,
@@ -61,6 +61,17 @@ public function __construct(
$this->countryFactory = $countryFactory;
$this->eavConfig = $eavConfig ?: ObjectManager::getInstance()
->get(EavConfig::class);
+ }
+
+ /**
+ * Validate address.
+ *
+ * @param \Magento\Sales\Model\Order\Address $address
+ * @return array
+ */
+ public function validate(Address $address)
+ {
+ $warnings = [];
if ($this->isTelephoneRequired()) {
$this->required['telephone'] = 'Phone Number';
@@ -73,16 +84,7 @@ public function __construct(
if ($this->isFaxRequired()) {
$this->required['fax'] = 'Fax';
}
- }
- /**
- *
- * @param \Magento\Sales\Model\Order\Address $address
- * @return array
- */
- public function validate(Address $address)
- {
- $warnings = [];
foreach ($this->required as $code => $label) {
if (!$address->hasData($code)) {
$warnings[] = sprintf('"%s" is required. Enter and try again.', $label);
@@ -195,7 +197,10 @@ protected function isStateRequired($countryId)
}
/**
+ * Check whether telephone is required for address.
+ *
* @return bool
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
protected function isTelephoneRequired()
{
@@ -203,7 +208,10 @@ protected function isTelephoneRequired()
}
/**
+ * Check whether company is required for address.
+ *
* @return bool
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
protected function isCompanyRequired()
{
@@ -211,7 +219,10 @@ protected function isCompanyRequired()
}
/**
+ * Check whether telephone is required for address.
+ *
* @return bool
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
protected function isFaxRequired()
{
diff --git a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php
index 2716e860243bf..a75690536e760 100644
--- a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php
+++ b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php
@@ -8,15 +8,15 @@
use Magento\Eav\Model\Config;
use Magento\Framework\App\State;
-use Magento\Quote\Model\QuoteFactory;
-use Magento\Sales\Model\OrderFactory;
-use Magento\Sales\Model\ResourceModel\Order\Address\CollectionFactory as AddressCollectionFactory;
-use Magento\Framework\App\ResourceConnection;
-use Magento\Sales\Setup\SalesSetupFactory;
+use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchVersionInterface;
-use Magento\Framework\Setup\ModuleDataSetupInterface;
+use Magento\Sales\Model\Order\Address;
+use Magento\Sales\Setup\SalesSetupFactory;
+/**
+ * Fills quote_address_id in table sales_order_address if it is empty.
+ */
class FillQuoteAddressIdInSalesOrderAddress implements DataPatchInterface, PatchVersionInterface
{
/**
@@ -24,11 +24,6 @@ class FillQuoteAddressIdInSalesOrderAddress implements DataPatchInterface, Patch
*/
private $moduleDataSetup;
- /**
- * @var SalesSetupFactory
- */
- private $salesSetupFactory;
-
/**
* @var State
*/
@@ -40,44 +35,22 @@ class FillQuoteAddressIdInSalesOrderAddress implements DataPatchInterface, Patch
private $eavConfig;
/**
- * @var AddressCollectionFactory
- */
- private $addressCollectionFactory;
-
- /**
- * @var OrderFactory
- */
- private $orderFactory;
-
- /**
- * @var QuoteFactory
- */
- private $quoteFactory;
-
- /**
- * PatchInitial constructor.
* @param ModuleDataSetupInterface $moduleDataSetup
+ * @param State $state
+ * @param Config $eavConfig
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
- SalesSetupFactory $salesSetupFactory,
State $state,
- Config $eavConfig,
- AddressCollectionFactory $addressCollectionFactory,
- OrderFactory $orderFactory,
- QuoteFactory $quoteFactory
+ Config $eavConfig
) {
$this->moduleDataSetup = $moduleDataSetup;
- $this->salesSetupFactory = $salesSetupFactory;
$this->state = $state;
$this->eavConfig = $eavConfig;
- $this->addressCollectionFactory = $addressCollectionFactory;
- $this->orderFactory = $orderFactory;
- $this->quoteFactory = $quoteFactory;
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function apply()
{
@@ -96,32 +69,12 @@ public function apply()
*/
public function fillQuoteAddressIdInSalesOrderAddress(ModuleDataSetupInterface $setup)
{
- $addressTable = $setup->getTable('sales_order_address');
- $updateOrderAddress = $setup->getConnection()
- ->select()
- ->joinInner(
- ['sales_order' => $setup->getTable('sales_order')],
- $addressTable . '.parent_id = sales_order.entity_id',
- ['quote_address_id' => 'quote_address.address_id']
- )
- ->joinInner(
- ['quote_address' => $setup->getTable('quote_address')],
- 'sales_order.quote_id = quote_address.quote_id
- AND ' . $addressTable . '.address_type = quote_address.address_type',
- []
- )
- ->where(
- $addressTable . '.quote_address_id IS NULL'
- );
- $updateOrderAddress = $setup->getConnection()->updateFromSelect(
- $updateOrderAddress,
- $addressTable
- );
- $setup->getConnection()->query($updateOrderAddress);
+ $this->fillQuoteAddressIdInSalesOrderAddressByType($setup, Address::TYPE_SHIPPING);
+ $this->fillQuoteAddressIdInSalesOrderAddressByType($setup, Address::TYPE_BILLING);
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public static function getDependencies()
{
@@ -131,7 +84,7 @@ public static function getDependencies()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public static function getVersion()
{
@@ -139,10 +92,99 @@ public static function getVersion()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getAliases()
{
return [];
}
+
+ /**
+ * Fill quote_address_id in sales_order_address by type.
+ *
+ * @param ModuleDataSetupInterface $setup
+ * @param string $addressType
+ * @throws \Zend_Db_Statement_Exception
+ */
+ private function fillQuoteAddressIdInSalesOrderAddressByType(ModuleDataSetupInterface $setup, $addressType)
+ {
+ $salesConnection = $setup->getConnection('sales');
+
+ $orderTable = $setup->getTable('sales_order', 'sales');
+ $orderAddressTable = $setup->getTable('sales_order_address', 'sales');
+
+ $query = $salesConnection
+ ->select()
+ ->from(
+ ['sales_order_address' => $orderAddressTable],
+ ['entity_id', 'address_type']
+ )
+ ->joinInner(
+ ['sales_order' => $orderTable],
+ 'sales_order_address.parent_id = sales_order.entity_id',
+ ['quote_id' => 'sales_order.quote_id']
+ )
+ ->where('sales_order_address.quote_address_id IS NULL')
+ ->where('sales_order_address.address_type = ?', $addressType)
+ ->order('sales_order_address.entity_id');
+
+ $batchSize = 5000;
+ $result = $salesConnection->query($query);
+ $count = $result->rowCount();
+ $batches = ceil($count / $batchSize);
+
+ for ($batch = $batches; $batch > 0; $batch--) {
+ $query->limitPage($batch, $batchSize);
+ $result = $salesConnection->fetchAssoc($query);
+
+ $this->fillQuoteAddressIdInSalesOrderAddressProcessBatch($setup, $result, $addressType);
+ }
+ }
+
+ /**
+ * Process filling quote_address_id in sales_order_address in batch.
+ *
+ * @param ModuleDataSetupInterface $setup
+ * @param array $orderAddresses
+ * @param string $addressType
+ */
+ private function fillQuoteAddressIdInSalesOrderAddressProcessBatch(
+ ModuleDataSetupInterface $setup,
+ array $orderAddresses,
+ $addressType
+ ) {
+ $salesConnection = $setup->getConnection('sales');
+ $quoteConnection = $setup->getConnection('checkout');
+
+ $quoteAddressTable = $setup->getTable('quote_address', 'checkout');
+ $quoteTable = $setup->getTable('quote', 'checkout');
+ $salesOrderAddressTable = $setup->getTable('sales_order_address', 'sales');
+
+ $query = $quoteConnection
+ ->select()
+ ->from(
+ ['quote_address' => $quoteAddressTable],
+ ['quote_id', 'address_id']
+ )
+ ->joinInner(
+ ['quote' => $quoteTable],
+ 'quote_address.quote_id = quote.entity_id',
+ []
+ )
+ ->where('quote.entity_id in (?)', array_column($orderAddresses, 'quote_id'))
+ ->where('address_type = ?', $addressType);
+
+ $quoteAddresses = $quoteConnection->fetchAssoc($query);
+
+ foreach ($orderAddresses as $orderAddress) {
+ $bind = [
+ 'quote_address_id' => $quoteAddresses[$orderAddress['quote_id']]['address_id'] ?? null,
+ ];
+ $where = [
+ 'entity_id = ?' => $orderAddress['entity_id']
+ ];
+
+ $salesConnection->update($salesOrderAddressTable, $bind, $where);
+ }
+ }
}
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml
new file mode 100644
index 0000000000000..8108577145421
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml
new file mode 100644
index 0000000000000..5f69f52987688
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveDuplicateErrorActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveDuplicateErrorActionGroup.xml
new file mode 100644
index 0000000000000..5b4c3115744c9
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveDuplicateErrorActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveSuccessActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveSuccessActionGroup.xml
new file mode 100644
index 0000000000000..d82f4b9dd25e8
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveSuccessActionGroup.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Data/OrderStatusData.xml b/app/code/Magento/Sales/Test/Mftf/Data/OrderStatusData.xml
new file mode 100644
index 0000000000000..aecd7fcf1b703
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Data/OrderStatusData.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+ order_status
+ orderLabel
+
+
+ pending
+ orderLabel
+
+
+ order_status
+ Suspected Fraud
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderStatusPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderStatusPage.xml
new file mode 100644
index 0000000000000..b158e4923074a
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderStatusPage.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusFormSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusFormSection.xml
new file mode 100644
index 0000000000000..1058b2d6f2177
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusFormSection.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml
new file mode 100644
index 0000000000000..b624639281187
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml
new file mode 100644
index 0000000000000..40a731410a899
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml
new file mode 100644
index 0000000000000..d1381bbb1efb0
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml
new file mode 100644
index 0000000000000..c2daaac84dd42
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml
new file mode 100644
index 0000000000000..d418751c736e1
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml
index 5a5dd925a3098..68fcd17122bd2 100644
--- a/app/code/Magento/Sales/etc/di.xml
+++ b/app/code/Magento/Sales/etc/di.xml
@@ -1015,4 +1015,9 @@
+
+
+
+
+
diff --git a/app/code/Magento/SalesAnalytics/composer.json b/app/code/Magento/SalesAnalytics/composer.json
index 64424c8f5bc61..b77dcd7e71c65 100644
--- a/app/code/Magento/SalesAnalytics/composer.json
+++ b/app/code/Magento/SalesAnalytics/composer.json
@@ -4,7 +4,8 @@
"require": {
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
- "magento/module-sales": "*"
+ "magento/module-sales": "*",
+ "magento/module-analytics": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php
index 89ec2b84572fc..cf6301cb31a9c 100644
--- a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php
+++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php
@@ -61,6 +61,7 @@ public function __construct(
public function loadAttributeOptions()
{
$attributes = [
+ 'base_subtotal_with_discount' => __('Subtotal (Excl. Tax)'),
'base_subtotal' => __('Subtotal'),
'total_qty' => __('Total Items Quantity'),
'weight' => __('Total Weight'),
diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml
new file mode 100644
index 0000000000000..e0b3d4b850bbb
--- /dev/null
+++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Search/Test/Mftf/Data/SearchTermData.xml b/app/code/Magento/Search/Test/Mftf/Data/SearchTermData.xml
new file mode 100644
index 0000000000000..1518adad01347
--- /dev/null
+++ b/app/code/Magento/Search/Test/Mftf/Data/SearchTermData.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Query text
+ 1
+ http://example.com/
+ 0
+
+
diff --git a/app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml b/app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml
new file mode 100644
index 0000000000000..0bd2dc9be4855
--- /dev/null
+++ b/app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ string
+ integer
+ string
+ integer
+
+
diff --git a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml
index 9e5bde9a2be49..81b025c9554e2 100644
--- a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml
+++ b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml
@@ -15,5 +15,6 @@
+
diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml
new file mode 100644
index 0000000000000..67ccb51bf401e
--- /dev/null
+++ b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php
index 9592ab6f57c55..c8e2d356b3ffb 100644
--- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php
+++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php
@@ -7,10 +7,15 @@
namespace Magento\Sitemap\Controller\Adminhtml\Sitemap;
use Magento\Backend\App\Action;
+use Magento\Framework\App\Action\HttpGetActionInterface;
+use Magento\Sitemap\Controller\Adminhtml\Sitemap;
use Magento\Store\Model\App\Emulation;
use Magento\Framework\App\ObjectManager;
-class Generate extends \Magento\Sitemap\Controller\Adminhtml\Sitemap
+/**
+ * Generate sitemap file
+ */
+class Generate extends Sitemap implements HttpGetActionInterface
{
/** @var \Magento\Store\Model\App\Emulation $appEmulation */
private $appEmulation;
@@ -44,14 +49,7 @@ public function execute()
// if sitemap record exists
if ($sitemap->getId()) {
try {
- //We need to emulate to get the correct frontend URL for the product images
- $this->appEmulation->startEnvironmentEmulation(
- $sitemap->getStoreId(),
- \Magento\Framework\App\Area::AREA_FRONTEND,
- true
- );
$sitemap->generateXml();
-
$this->messageManager->addSuccessMessage(
__('The sitemap "%1" has been generated.', $sitemap->getSitemapFilename())
);
@@ -59,8 +57,6 @@ public function execute()
$this->messageManager->addErrorMessage($e->getMessage());
} catch (\Exception $e) {
$this->messageManager->addExceptionMessage($e, __('We can\'t generate the sitemap right now.'));
- } finally {
- $this->appEmulation->stopEnvironmentEmulation();
}
} else {
$this->messageManager->addErrorMessage(__('We can\'t find a sitemap to generate.'));
diff --git a/app/code/Magento/Sitemap/Model/Observer.php b/app/code/Magento/Sitemap/Model/Observer.php
index bd7a84f601b77..ce74d738c4bc3 100644
--- a/app/code/Magento/Sitemap/Model/Observer.php
+++ b/app/code/Magento/Sitemap/Model/Observer.php
@@ -5,7 +5,6 @@
*/
namespace Magento\Sitemap\Model;
-use Magento\Store\Model\App\Emulation;
use Magento\Sitemap\Model\EmailNotification as SitemapEmail;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Sitemap\Model\ResourceModel\Sitemap\CollectionFactory;
@@ -57,11 +56,6 @@ class Observer
*/
private $collectionFactory;
- /**
- * @var Emulation
- */
- private $appEmulation;
-
/**
* @var $emailNotification
*/
@@ -72,17 +66,14 @@ class Observer
* @param ScopeConfigInterface $scopeConfig
* @param CollectionFactory $collectionFactory
* @param EmailNotification $emailNotification
- * @param Emulation $appEmulation
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
CollectionFactory $collectionFactory,
- SitemapEmail $emailNotification,
- Emulation $appEmulation
+ SitemapEmail $emailNotification
) {
$this->scopeConfig = $scopeConfig;
$this->collectionFactory = $collectionFactory;
- $this->appEmulation = $appEmulation;
$this->emailNotification = $emailNotification;
}
@@ -114,17 +105,9 @@ public function scheduledGenerateSitemaps()
foreach ($collection as $sitemap) {
/* @var $sitemap \Magento\Sitemap\Model\Sitemap */
try {
- $this->appEmulation->startEnvironmentEmulation(
- $sitemap->getStoreId(),
- \Magento\Framework\App\Area::AREA_FRONTEND,
- true
- );
-
$sitemap->generateXml();
} catch (\Exception $e) {
$errors[] = $e->getMessage();
- } finally {
- $this->appEmulation->stopEnvironmentEmulation();
}
}
if ($errors && $recipient) {
diff --git a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php
index 82024b3b015e5..4d678f5c1cb94 100644
--- a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php
+++ b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php
@@ -389,6 +389,7 @@ protected function _prepareProduct(array $productRow, $storeId)
*/
protected function _loadProductImages($product, $storeId)
{
+ $this->_storeManager->setCurrentStore($storeId);
/** @var $helper \Magento\Sitemap\Helper\Data */
$helper = $this->_sitemapData;
$imageIncludePolicy = $helper->getProductImageIncludePolicy($storeId);
diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php
index 5fae54ff3c5d0..09f5418bbd762 100644
--- a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php
+++ b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php
@@ -118,7 +118,7 @@ public function testScheduledGenerateSitemapsSendsExceptionEmail()
->method('getStoreId')
->willReturn($storeId);
- $this->sitemapMock->expects($this->at(1))
+ $this->sitemapMock->expects($this->once())
->method('generateXml')
->willThrowException(new \Exception($exception));
@@ -130,17 +130,6 @@ public function testScheduledGenerateSitemapsSendsExceptionEmail()
)
->willReturn('error-recipient@example.com');
- $this->appEmulationMock->expects($this->at(0))
- ->method('startEnvironmentEmulation')
- ->with(
- $storeId,
- Area::AREA_FRONTEND,
- true
- );
-
- $this->appEmulationMock->expects($this->at(1))
- ->method('stopEnvironmentEmulation');
-
$this->observer->scheduledGenerateSitemaps();
}
}
diff --git a/app/code/Magento/Store/Model/Group.php b/app/code/Magento/Store/Model/Group.php
index ccc3c65491422..19f104c9f3790 100644
--- a/app/code/Magento/Store/Model/Group.php
+++ b/app/code/Magento/Store/Model/Group.php
@@ -100,18 +100,24 @@ class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements
*/
private $eventManager;
+ /**
+ * @var \Magento\MessageQueue\Api\PoisonPillPutInterface
+ */
+ private $pillPut;
+
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
* @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory
* @param \Magento\Config\Model\ResourceModel\Config\Data $configDataResource
- * @param \Magento\Store\Model\Store $store
- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
- * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
- * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
+ * @param ResourceModel\Store\CollectionFactory $storeListFactory
+ * @param StoreManagerInterface $storeManager
+ * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
+ * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
* @param array $data
* @param \Magento\Framework\Event\ManagerInterface|null $eventManager
+ * @param \Magento\MessageQueue\Api\PoisonPillPutInterface|null $pillPut
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -125,13 +131,16 @@ public function __construct(
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = [],
- \Magento\Framework\Event\ManagerInterface $eventManager = null
+ \Magento\Framework\Event\ManagerInterface $eventManager = null,
+ \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut = null
) {
$this->_configDataResource = $configDataResource;
$this->_storeListFactory = $storeListFactory;
$this->_storeManager = $storeManager;
$this->eventManager = $eventManager ?: \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Framework\Event\ManagerInterface::class);
+ $this->pillPut = $pillPut ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\MessageQueue\Api\PoisonPillPutInterface::class);
parent::__construct(
$context,
$registry,
@@ -244,6 +253,8 @@ public function getStoreCodes()
}
/**
+ * Get stores count
+ *
* @return int
*/
public function getStoresCount()
@@ -349,6 +360,8 @@ public function isCanDelete()
}
/**
+ * Get default store id
+ *
* @return mixed
*/
public function getDefaultStoreId()
@@ -365,6 +378,8 @@ public function setDefaultStoreId($defaultStoreId)
}
/**
+ * Get root category id
+ *
* @return mixed
*/
public function getRootCategoryId()
@@ -381,6 +396,8 @@ public function setRootCategoryId($rootCategoryId)
}
/**
+ * Get website id
+ *
* @return mixed
*/
public function getWebsiteId()
@@ -397,7 +414,7 @@ public function setWebsiteId($websiteId)
}
/**
- * @return $this
+ * @inheritdoc
*/
public function beforeDelete()
{
@@ -445,6 +462,7 @@ public function afterSave()
$this->_storeManager->reinitStores();
$this->eventManager->dispatch($this->_eventPrefix . '_save', ['group' => $group]);
});
+ $this->pillPut->put();
return parent::afterSave();
}
@@ -473,7 +491,7 @@ public function getIdentities()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getName()
{
@@ -507,7 +525,7 @@ public function setCode($code)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getExtensionAttributes()
{
@@ -515,7 +533,7 @@ public function getExtensionAttributes()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setExtensionAttributes(
\Magento\Store\Api\Data\GroupExtensionInterface $extensionAttributes
@@ -524,7 +542,7 @@ public function setExtensionAttributes(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
* @since 100.1.0
*/
public function getScopeType()
@@ -533,7 +551,7 @@ public function getScopeType()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
* @since 100.1.0
*/
public function getScopeTypeName()
diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php
index c1ad5bdcfc068..b2a515b198b11 100644
--- a/app/code/Magento/Store/Model/Store.php
+++ b/app/code/Magento/Store/Model/Store.php
@@ -326,6 +326,11 @@ class Store extends AbstractExtensibleModel implements
*/
private $eventManager;
+ /**
+ * @var \Magento\MessageQueue\Api\PoisonPillPutInterface
+ */
+ private $pillPut;
+
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
@@ -352,6 +357,7 @@ class Store extends AbstractExtensibleModel implements
* @param bool $isCustomEntryPoint
* @param array $data optional generic object data
* @param \Magento\Framework\Event\ManagerInterface|null $eventManager
+ * @param \Magento\MessageQueue\Api\PoisonPillPutInterface|null $pillPut
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
@@ -380,7 +386,8 @@ public function __construct(
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
$isCustomEntryPoint = false,
array $data = [],
- \Magento\Framework\Event\ManagerInterface $eventManager = null
+ \Magento\Framework\Event\ManagerInterface $eventManager = null,
+ \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut = null
) {
$this->_coreFileStorageDatabase = $coreFileStorageDatabase;
$this->_config = $config;
@@ -401,6 +408,8 @@ public function __construct(
$this->websiteRepository = $websiteRepository;
$this->eventManager = $eventManager ?: \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Framework\Event\ManagerInterface::class);
+ $this->pillPut = $pillPut ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\MessageQueue\Api\PoisonPillPutInterface::class);
parent::__construct(
$context,
$registry,
@@ -1077,6 +1086,7 @@ public function afterSave()
$this->getResource()->addCommitCallback(function () use ($event, $store) {
$this->eventManager->dispatch($event, ['store' => $store]);
});
+ $this->pillPut->put();
return parent::afterSave();
}
diff --git a/app/code/Magento/Store/Model/Website.php b/app/code/Magento/Store/Model/Website.php
index c9a7d0013fe06..383b36fd63228 100644
--- a/app/code/Magento/Store/Model/Website.php
+++ b/app/code/Magento/Store/Model/Website.php
@@ -159,6 +159,11 @@ class Website extends \Magento\Framework\Model\AbstractExtensibleModel implement
*/
protected $_currencyFactory;
+ /**
+ * @var \Magento\MessageQueue\Api\PoisonPillPutInterface
+ */
+ private $pillPut;
+
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
@@ -174,6 +179,7 @@ class Website extends \Magento\Framework\Model\AbstractExtensibleModel implement
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
* @param array $data
+ * @param \Magento\MessageQueue\Api\PoisonPillPutInterface|null $pillPut
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -190,7 +196,8 @@ public function __construct(
\Magento\Directory\Model\CurrencyFactory $currencyFactory,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
- array $data = []
+ array $data = [],
+ \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut = null
) {
parent::__construct(
$context,
@@ -208,10 +215,12 @@ public function __construct(
$this->_websiteFactory = $websiteFactory;
$this->_storeManager = $storeManager;
$this->_currencyFactory = $currencyFactory;
+ $this->pillPut = $pillPut ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\MessageQueue\Api\PoisonPillPutInterface::class);
}
/**
- * init model
+ * Init model
*
* @return void
*/
@@ -495,6 +504,8 @@ public function getWebsiteGroupStore()
}
/**
+ * Get default group id
+ *
* @return mixed
*/
public function getDefaultGroupId()
@@ -511,6 +522,8 @@ public function setDefaultGroupId($defaultGroupId)
}
/**
+ * Get code
+ *
* @return mixed
*/
public function getCode()
@@ -543,7 +556,7 @@ public function setName($name)
}
/**
- * @return $this
+ * @inheritdoc
*/
public function beforeDelete()
{
@@ -581,7 +594,7 @@ public function afterSave()
if ($this->isObjectNew()) {
$this->_storeManager->reinitStores();
}
-
+ $this->pillPut->put();
return parent::afterSave();
}
@@ -635,8 +648,7 @@ public function getDefaultStore()
}
/**
- * Retrieve default stores select object
- * Select fields website_id, store_id
+ * Retrieve default stores select object, select fields website_id, store_id
*
* @param bool $withDefault include/exclude default admin website
* @return \Magento\Framework\DB\Select
@@ -671,7 +683,7 @@ public function getIdentities()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
* @since 100.1.0
*/
public function getScopeType()
@@ -680,7 +692,7 @@ public function getScopeType()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
* @since 100.1.0
*/
public function getScopeTypeName()
@@ -689,7 +701,7 @@ public function getScopeTypeName()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getExtensionAttributes()
{
@@ -697,7 +709,7 @@ public function getExtensionAttributes()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setExtensionAttributes(
\Magento\Store\Api\Data\WebsiteExtensionInterface $extensionAttributes
diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml
index ef8d77c8824ff..ca614ec24138c 100644
--- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml
+++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml
@@ -36,4 +36,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml
index 58e1781d69eab..cf2cabdcc2399 100644
--- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml
+++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml
@@ -26,4 +26,35 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml
index 58fd0a3f0bc2b..1721e3185402e 100644
--- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml
+++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml
@@ -19,6 +19,7 @@
+
diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/StoreFrontProductValidationActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/StoreFrontProductValidationActionGroup.xml
new file mode 100644
index 0000000000000..f11394c643ad7
--- /dev/null
+++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/StoreFrontProductValidationActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml
index f636336524f01..ae605256a2819 100644
--- a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml
+++ b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml
@@ -23,4 +23,8 @@
Custom Website
custom_website
-
+
+ website_upd
+ code_upd
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml
index 592af42f2de30..d7006fd01b2ff 100644
--- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml
+++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml
@@ -23,5 +23,6 @@
+
-
+
\ No newline at end of file
diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml
new file mode 100644
index 0000000000000..1608d0b7b5a25
--- /dev/null
+++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml
new file mode 100644
index 0000000000000..fc1dcb5ee1a24
--- /dev/null
+++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml
new file mode 100644
index 0000000000000..6b666126569ae
--- /dev/null
+++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Store/composer.json b/app/code/Magento/Store/composer.json
index ebaa32b95f48b..da408f105ccb6 100644
--- a/app/code/Magento/Store/composer.json
+++ b/app/code/Magento/Store/composer.json
@@ -7,6 +7,7 @@
"require": {
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
+ "magento/module-message-queue": "*",
"magento/module-catalog": "*",
"magento/module-config": "*",
"magento/module-directory": "*",
diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js
index 2080652210a53..424d691de5f11 100644
--- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js
+++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js
@@ -1238,6 +1238,11 @@ define([
if (!_.isUndefined(gallery)) {
gallery.updateData(imagesToUpdate);
+ } else {
+ context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) {
+ loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery');
+ loadedGallery.updateData(imagesToUpdate);
+ }.bind(this));
}
if (isInitial) {
@@ -1248,6 +1253,7 @@ define([
dataMergeStrategy: this.options.gallerySwitchStrategy
});
}
+
} else if (justAnImage && justAnImage.img) {
context.find('.product-image-photo').attr('src', justAnImage.img);
}
diff --git a/app/code/Magento/Tax/etc/di.xml b/app/code/Magento/Tax/etc/di.xml
index 096f8359fadd3..3b46b0f9e258c 100644
--- a/app/code/Magento/Tax/etc/di.xml
+++ b/app/code/Magento/Tax/etc/di.xml
@@ -143,6 +143,7 @@
- tax_calculation_rule_id
+ - main_table.code
- tax_calculation_rate_id
- cd.customer_tax_class_id
- cd.product_tax_class_id
@@ -154,6 +155,7 @@
- tax_calculation_rule_id
+ - main_table.code
- tax_calculation_rate_id
- cd.customer_tax_class_id
- cd.product_tax_class_id
diff --git a/app/code/Magento/Theme/Model/Design/Backend/File.php b/app/code/Magento/Theme/Model/Design/Backend/File.php
index b37628e54aa30..511fe30f79dcd 100644
--- a/app/code/Magento/Theme/Model/Design/Backend/File.php
+++ b/app/code/Magento/Theme/Model/Design/Backend/File.php
@@ -22,6 +22,8 @@
use Magento\Theme\Model\Design\Config\FileUploader\FileProcessor;
/**
+ * File Backend
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class File extends BackendFile
@@ -88,36 +90,29 @@ public function beforeSave()
{
$values = $this->getValue();
$value = reset($values) ?: [];
- if (!isset($value['file'])) {
+
+ // Need to check name when it is uploaded in the media gallary
+ $file = $value['file'] ?? $value['name'] ?? null;
+ if (!isset($file)) {
throw new LocalizedException(
__('%1 does not contain field \'file\'', $this->getData('field_config/field'))
);
}
if (isset($value['exists'])) {
- $this->setValue($value['file']);
+ $this->setValue($file);
return $this;
}
- $filename = basename($value['file']);
- $result = $this->_mediaDirectory->copyFile(
- $this->getTmpMediaPath($filename),
- $this->_getUploadDir() . '/' . $filename
- );
- if ($result) {
- $this->_mediaDirectory->delete($this->getTmpMediaPath($filename));
- if ($this->_addWhetherScopeInfo()) {
- $filename = $this->_prependScopeInfo($filename);
- }
- $this->setValue($filename);
- } else {
- $this->unsValue();
- }
+ $this->updateMediaDirectory(basename($file), $value['url']);
return $this;
}
/**
- * @return array
+ * After Load
+ *
+ * @return File
+ * @throws LocalizedException
*/
public function afterLoad()
{
@@ -166,6 +161,8 @@ protected function getUploadDirPath($uploadDir)
}
/**
+ * Get Value
+ *
* @return array
*/
public function getValue()
@@ -231,4 +228,49 @@ private function getMime()
}
return $this->mime;
}
+
+ /**
+ * Get Relative Media Path
+ *
+ * @param string $path
+ * @return string
+ */
+ private function getRelativeMediaPath(string $path): string
+ {
+ return str_replace('/pub/media/', '', $path);
+ }
+
+ /**
+ * Move file to the correct media directory
+ *
+ * @param string $filename
+ * @param string $url
+ * @throws LocalizedException
+ */
+ private function updateMediaDirectory(string $filename, string $url)
+ {
+ $relativeMediaPath = $this->getRelativeMediaPath($url);
+ $tmpMediaPath = $this->getTmpMediaPath($filename);
+ $mediaPath = $this->_mediaDirectory->isFile($relativeMediaPath) ? $relativeMediaPath : $tmpMediaPath;
+ $destinationMediaPath = $this->_getUploadDir() . '/' . $filename;
+
+ $result = $mediaPath === $destinationMediaPath;
+ if (!$result) {
+ $result = $this->_mediaDirectory->copyFile(
+ $mediaPath,
+ $destinationMediaPath
+ );
+ }
+ if ($result) {
+ if ($mediaPath === $tmpMediaPath) {
+ $this->_mediaDirectory->delete($mediaPath);
+ }
+ if ($this->_addWhetherScopeInfo()) {
+ $filename = $this->_prependScopeInfo($filename);
+ }
+ $this->setValue($filename);
+ } else {
+ $this->unsValue();
+ }
+ }
}
diff --git a/app/code/Magento/Theme/Test/Mftf/ActionGroup/NavigateToFaviconMediaFolderActionGroup.xml b/app/code/Magento/Theme/Test/Mftf/ActionGroup/NavigateToFaviconMediaFolderActionGroup.xml
new file mode 100644
index 0000000000000..6b98686574321
--- /dev/null
+++ b/app/code/Magento/Theme/Test/Mftf/ActionGroup/NavigateToFaviconMediaFolderActionGroup.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml
index e90548a7c94e9..c2652f33f7606 100644
--- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml
+++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml
@@ -14,10 +14,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml
new file mode 100644
index 0000000000000..f46328ac151b1
--- /dev/null
+++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php
index b983e56b8aee2..b06c655939b1c 100644
--- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php
+++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php
@@ -86,6 +86,18 @@ public function execute()
$contentType = $this->contentTypeResolver->resolve($component->getContext());
$this->getResponse()->setHeader('Content-Type', $contentType, true);
+ } else {
+ /** @var \Magento\Framework\Controller\Result\Json $resultJson */
+ $resultJson = $this->resultJsonFactory->create();
+ $resultJson->setStatusHeader(
+ \Zend\Http\Response::STATUS_CODE_403,
+ \Zend\Http\AbstractMessage::VERSION_11,
+ 'Forbidden'
+ );
+ return $resultJson->setData([
+ 'error' => $this->escaper->escapeHtml('Forbidden'),
+ 'errorcode' => 403
+ ]);
}
} catch (\Magento\Framework\Exception\LocalizedException $e) {
$this->logger->critical($e);
diff --git a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php
index 05b35fb017b4b..2bba8686490b6 100644
--- a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php
+++ b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php
@@ -3,12 +3,17 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Ui\Test\Unit\Controller\Adminhtml\Index;
+use Magento\Framework\Controller\Result\Json;
+use Magento\Framework\Escaper;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Ui\Controller\Adminhtml\Index\Render;
use Magento\Ui\Model\UiComponentTypeResolver;
-use Magento\Framework\View\Element\UiComponent\ContextInterface;
-use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Zend\Http\AbstractMessage;
+use Zend\Http\Response;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -97,6 +102,11 @@ class RenderTest extends \PHPUnit\Framework\TestCase
*/
private $loggerMock;
+ /**
+ * @var Escaper|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $escaperMock;
+
protected function setUp()
{
$this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class)
@@ -170,6 +180,10 @@ protected function setUp()
$this->uiComponentTypeResolverMock = $this->getMockBuilder(UiComponentTypeResolver::class)
->disableOriginalConstructor()
->getMock();
+ $this->escaperMock = $this->createMock(Escaper::class);
+ $this->escaperMock->expects($this->any())
+ ->method('escapeHtml')
+ ->willReturnArgument(0);
$this->objectManagerHelper = new ObjectManagerHelper($this);
@@ -181,6 +195,7 @@ protected function setUp()
'contentTypeResolver' => $this->uiComponentTypeResolverMock,
'resultJsonFactory' => $this->resultJsonFactoryMock,
'logger' => $this->loggerMock,
+ 'escaper' => $this->escaperMock,
]
);
}
@@ -201,7 +216,7 @@ public function testExecuteAjaxRequestException()
->method('appendBody')
->willThrowException(new \Exception('exception'));
- $jsonResultMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class)
+ $jsonResultMock = $this->getMockBuilder(Json::class)
->disableOriginalConstructor()
->setMethods(['setData'])
->getMock();
@@ -290,6 +305,34 @@ public function testExecuteAjaxRequestWithoutPermissions(array $dataProviderConf
$name = 'test-name';
$renderedData = 'data';
+ if (false === $isAllowed) {
+ $jsonResultMock = $this->getMockBuilder(Json::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setStatusHeader', 'setData'])
+ ->getMock();
+
+ $jsonResultMock->expects($this->at(0))
+ ->method('setStatusHeader')
+ ->with(
+ Response::STATUS_CODE_403,
+ AbstractMessage::VERSION_11,
+ 'Forbidden'
+ )
+ ->willReturnSelf();
+
+ $jsonResultMock->expects($this->at(1))
+ ->method('setData')
+ ->with([
+ 'error' => 'Forbidden',
+ 'errorcode' => 403
+ ])
+ ->willReturnSelf();
+
+ $this->resultJsonFactoryMock->expects($this->any())
+ ->method('create')
+ ->willReturn($jsonResultMock);
+ }
+
$this->requestMock->expects($this->any())
->method('getParam')
->with('namespace')
diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js
index 1d52fc78d7a85..cbbfbdb127ad7 100644
--- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js
+++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js
@@ -543,6 +543,7 @@ define([
data = this.createHeaderTemplate(cell.config);
cell.config.labelVisible = false;
_.extend(data, {
+ defaultLabelVisible: data.visible(),
label: cell.config.label,
name: cell.name,
required: !!cell.config.validation,
diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js
index 3987507ece54f..9a9d478904775 100644
--- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js
+++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js
@@ -245,7 +245,7 @@ define([
label = _.findWhere(this.parentComponent().labels(), {
name: index
});
- label.visible() !== state ? label.visible(state) : false;
+ label.defaultLabelVisible && label.visible(state);
} else {
elems[curElem].visible(state);
}
diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js
index 1b6dd9f1c57ec..0eaacdc32567b 100644
--- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js
+++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js
@@ -20,6 +20,26 @@ define([
}
},
+ /**
+ * Initializes observable properties of instance
+ *
+ * @returns {Abstract} Chainable.
+ */
+ initObservable: function () {
+ this._super();
+
+ /**
+ * equalityComparer function
+ *
+ * @returns boolean.
+ */
+ this.value.equalityComparer = function (oldValue, newValue) {
+ return !oldValue && !newValue || oldValue === newValue;
+ };
+
+ return this;
+ },
+
/**
* @param {String} value
*/
diff --git a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/default.html b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/default.html
index 1a21e1b2f1c71..6da4f82fa8b9e 100644
--- a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/default.html
+++ b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/default.html
@@ -41,7 +41,7 @@
diff --git a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/grid.html b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/grid.html
index d0b12549bd66d..e5d73a62b329e 100644
--- a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/grid.html
+++ b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/grid.html
@@ -58,7 +58,7 @@
-
+
-
+
diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml
index 77cf80ca95ac5..3692e82072afc 100644
--- a/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml
+++ b/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml
@@ -24,6 +24,7 @@
Temporary (302)
1
Default Store View
+ Update Url Rewrite
wishlist
diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml
new file mode 100644
index 0000000000000..ea370d8419583
--- /dev/null
+++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php
index cafb6a5291481..40882ae00dae1 100644
--- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php
+++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php
@@ -5,14 +5,15 @@
*/
/**
- * Wishlist for item column in customer wishlist
- *
* @author Magento Core Team
*/
namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column;
/**
+ * Model for item column in customer wishlist.
+ *
* @api
+ * @deprecated
* @since 100.0.2
*/
class Actions extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column
diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php
index 2d75956858a0a..53f67626e956d 100644
--- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php
+++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php
@@ -5,14 +5,15 @@
*/
/**
- * Wishlist block customer item cart column
- *
* @author Magento Core Team
*/
namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column;
/**
+ * Wishlist block customer item cart column.
+ *
* @api
+ * @deprecated
* @since 100.0.2
*/
class Comment extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column
diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php
index 53ca78c63524d..c4c786961694b 100644
--- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php
+++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php
@@ -5,14 +5,15 @@
*/
/**
- * Edit item in customer wishlist table
- *
* @author Magento Core Team
*/
namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column;
/**
+ * Edit item in customer wishlist table.
+ *
* @api
+ * @deprecated
* @since 100.0.2
*/
class Edit extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column
diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php
index 33fb0f7325cdd..b7eaf53fc23b5 100644
--- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php
+++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php
@@ -5,14 +5,15 @@
*/
/**
- * Wishlist block customer item cart column
- *
* @author Magento Core Team
*/
namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column;
/**
+ * Wishlist block customer item cart column.
+ *
* @api
+ * @deprecated
* @since 100.0.2
*/
class Info extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column
diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php
index 57703b9300db8..09f5014edead6 100644
--- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php
+++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php
@@ -5,14 +5,15 @@
*/
/**
- * Delete item column in customer wishlist table
- *
* @author Magento Core Team
*/
namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column;
/**
+ * Delete item column in customer wishlist table
+ *
* @api
+ * @deprecated
* @since 100.0.2
*/
class Remove extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column
diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php
new file mode 100644
index 0000000000000..fb0113eb6ae75
--- /dev/null
+++ b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php
@@ -0,0 +1,177 @@
+productCollectionFactoryMock = $this->createPartialMock(
+ CollectionFactory::class,
+ ['create']
+ );
+ $this->attributeValueProvider = new AttributeValueProvider(
+ $this->productCollectionFactoryMock
+ );
+ }
+
+ /**
+ * Get attribute text when the flat table is disabled
+ *
+ * @param int $productId
+ * @param string $attributeCode
+ * @param string $attributeText
+ * @return void
+ * @dataProvider attributeDataProvider
+ */
+ public function testGetAttributeTextWhenFlatIsDisabled(int $productId, string $attributeCode, string $attributeText)
+ {
+ $this->productMock = $this->getMockBuilder(Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getData'])
+ ->getMock();
+
+ $this->productMock->expects($this->any())
+ ->method('getData')
+ ->with($attributeCode)
+ ->willReturn($attributeText);
+
+ $productCollection = $this->getMockBuilder(Collection::class)
+ ->disableOriginalConstructor()
+ ->setMethods([
+ 'addIdFilter', 'addStoreFilter', 'addAttributeToSelect', 'isEnabledFlat', 'getFirstItem'
+ ])->getMock();
+
+ $productCollection->expects($this->any())
+ ->method('addIdFilter')
+ ->willReturnSelf();
+ $productCollection->expects($this->any())
+ ->method('addStoreFilter')
+ ->willReturnSelf();
+ $productCollection->expects($this->any())
+ ->method('addAttributeToSelect')
+ ->willReturnSelf();
+ $productCollection->expects($this->any())
+ ->method('isEnabledFlat')
+ ->willReturn(false);
+ $productCollection->expects($this->any())
+ ->method('getFirstItem')
+ ->willReturn($this->productMock);
+
+ $this->productCollectionFactoryMock->expects($this->atLeastOnce())
+ ->method('create')
+ ->willReturn($productCollection);
+
+ $actual = $this->attributeValueProvider->getRawAttributeValue($productId, $attributeCode);
+
+ $this->assertEquals($attributeText, $actual);
+ }
+
+ /**
+ * Get attribute text when the flat table is enabled
+ *
+ * @dataProvider attributeDataProvider
+ * @param int $productId
+ * @param string $attributeCode
+ * @param string $attributeText
+ * @return void
+ */
+ public function testGetAttributeTextWhenFlatIsEnabled(int $productId, string $attributeCode, string $attributeText)
+ {
+ $this->connectionMock = $this->getMockBuilder(AdapterInterface::class)->getMockForAbstractClass();
+ $this->connectionMock->expects($this->any())
+ ->method('fetchRow')
+ ->willReturn([
+ $attributeCode => $attributeText
+ ]);
+ $this->productMock = $this->getMockBuilder(Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getData'])
+ ->getMock();
+ $this->productMock->expects($this->any())
+ ->method('getData')
+ ->with($attributeCode)
+ ->willReturn($attributeText);
+
+ $productCollection = $this->getMockBuilder(Collection::class)
+ ->disableOriginalConstructor()
+ ->setMethods([
+ 'addIdFilter', 'addStoreFilter', 'addAttributeToSelect', 'isEnabledFlat', 'getConnection'
+ ])->getMock();
+
+ $productCollection->expects($this->any())
+ ->method('addIdFilter')
+ ->willReturnSelf();
+ $productCollection->expects($this->any())
+ ->method('addStoreFilter')
+ ->willReturnSelf();
+ $productCollection->expects($this->any())
+ ->method('addAttributeToSelect')
+ ->willReturnSelf();
+ $productCollection->expects($this->any())
+ ->method('isEnabledFlat')
+ ->willReturn(true);
+ $productCollection->expects($this->any())
+ ->method('getConnection')
+ ->willReturn($this->connectionMock);
+
+ $this->productCollectionFactoryMock->expects($this->atLeastOnce())
+ ->method('create')
+ ->willReturn($productCollection);
+
+ $actual = $this->attributeValueProvider->getRawAttributeValue($productId, $attributeCode);
+
+ $this->assertEquals($attributeText, $actual);
+ }
+
+ /**
+ * @return array
+ */
+ public function attributeDataProvider(): array
+ {
+ return [
+ [1, 'attribute_code', 'Attribute Text']
+ ];
+ }
+}
diff --git a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml
index d3f21dda9ccde..b20bc6a4e00ba 100644
--- a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml
+++ b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml
@@ -18,6 +18,7 @@
+
diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml
new file mode 100644
index 0000000000000..9120cc9fa684e
--- /dev/null
+++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml
@@ -0,0 +1,10 @@
+getItem()->getProduct();
+?>
+= $block->getReviewsSummaryHtml($product, 'short') ?>
diff --git a/app/code/Magento/WishlistAnalytics/composer.json b/app/code/Magento/WishlistAnalytics/composer.json
index fc69afe2907ab..747f2a4baaaa9 100644
--- a/app/code/Magento/WishlistAnalytics/composer.json
+++ b/app/code/Magento/WishlistAnalytics/composer.json
@@ -4,7 +4,8 @@
"require": {
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
- "magento/module-wishlist": "*"
+ "magento/module-wishlist": "*",
+ "magento/module-analytics": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/_headings-group.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/_headings-group.less
index 832c66b7988e0..bf7ee7850f9d0 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/_headings-group.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/_headings-group.less
@@ -42,4 +42,5 @@
color: @page-title__color;
font-size: @page-title__font-size;
margin-bottom: 0;
+ word-break: break-all;
}
diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_actions-bar.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_actions-bar.less
index 08434727ccc9c..8499ecaa48c12 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_actions-bar.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_actions-bar.less
@@ -45,6 +45,10 @@
.page-actions {
@_page-action__indent: 1.3rem;
+ &.floating-header {
+ &:extend(.page-actions-buttons all);
+ }
+
.page-main-actions & {
&._fixed {
left: @page-wrapper__indent-left;
diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less
index 6e03e1d0cebaa..e37e08f3b667d 100644
--- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less
+++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less
@@ -99,7 +99,7 @@
}
.action-menu {
- max-height: 3.85rem * @data-grid-search-control-action-menu-item__quantity; // ToDo UI: change static item height
+ max-height: 3.85rem * @data-grid-search-control-action-menu-item__quantity; // @todo: change static item height
overflow-y: auto;
z-index: @data-grid-search-menu__z-index;
}
@@ -354,6 +354,7 @@
.admin__current-filters-list-wrap {
width: 100%;
+ word-break: break-all;
}
.admin__current-filters-list {
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less
index 1cc61bef5da07..1c45fe6946ba0 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less
@@ -7,6 +7,14 @@
// Actions -> Action select
// _____________________________________________
+//
+// Variables
+// _____________________________________________
+
+@_dropdown__padding-right: @action__height;
+@_triangle__height: @button-marker-triangle__height;
+@_triangle__width: @button-marker-triangle__width;
+
// Action select have the same visual styles and functionality as native
.action-select-wrap {
@_action-select__border-color: @button__border-color;
@@ -18,9 +26,9 @@
.action-select {
.action-toggle-triangle(
- @_dropdown__padding-right: @_action-select-toggle__size;
- @_triangle__height: @button-marker-triangle__height;
- @_triangle__width: @button-marker-triangle__width;
+ @_dropdown__padding-right;
+ @_triangle__height;
+ @_triangle__width;
);
.lib-text-overflow-ellipsis();
@@ -108,12 +116,9 @@
min-width: 100%;
position: static;
- ._parent._visible {
- position: relative;
- }
-
.action-submenu {
position: absolute;
+ right: -100%;
}
}
}
diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less
index 4479c070a4e17..8dec680b58726 100644
--- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less
+++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less
@@ -55,31 +55,3 @@
}
}
}
-
-//
-// Desktop
-// _____________________________________________
-
-.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) {
- // ToDo UI: remove with global blank theme .field.required update
- .opc-wrapper {
- .fieldset {
- > .field {
- &.required,
- &._required {
- position: relative;
-
- > label {
- padding-right: 25px;
-
- &:after {
- margin-left: @indent__s;
- position: absolute;
- top: 9px;
- }
- }
- }
- }
- }
- }
-}
diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less
index 5f8134193c67f..35445b0989e86 100644
--- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less
+++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less
@@ -209,6 +209,13 @@
.fieldset {
> .field {
margin: 0 0 @indent__base;
+
+ &.choice {
+ &:before {
+ padding: 0;
+ width: 0;
+ }
+ }
&.type {
.control {
diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less
index 8cf5cd313edc5..54ba530092cc0 100644
--- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less
+++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less
@@ -388,6 +388,17 @@
position: relative;
}
}
+
+ .form.search.advanced {
+ .field.price {
+ .with-addon {
+ .input-text {
+ flex-basis: auto;
+ width: 100%;
+ }
+ }
+ }
+ }
}
//
@@ -452,6 +463,7 @@
.form.send.confirmation,
.form.password.forget,
.form.create.account,
+ .form.search.advanced,
.form.form-orders-search {
min-width: 600px;
width: 50%;
diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less
index d5f90d3e6d546..3b4da1d1ae6f5 100644
--- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less
@@ -397,6 +397,7 @@
.box-tocart {
&:extend(.abs-box-tocart all);
+
.field.qty {
}
diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less
index d477c08fc9553..e5915969c91b9 100644
--- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less
+++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less
@@ -397,7 +397,7 @@
.page-products.page-layout-3columns {
.products-grid {
.product-item {
- margin-left: 2%;
+ margin-left: 0;
width: calc(~'(100% - 4%) / 3');
&:nth-child(3n + 1) {
diff --git a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less
index 0f91f857a715c..31859a46d3efe 100644
--- a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less
@@ -213,7 +213,7 @@
// Mobile
// _____________________________________________
-.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) {
+.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) {
.block-search {
margin-top: @indent__s;
}
diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less
index 7265d7bd61c51..5ca3322403102 100644
--- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less
+++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less
@@ -416,7 +416,7 @@
}
}
-.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) {
+.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) {
.minicart-wrapper {
margin-top: @indent__s;
}
diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less
index 5b0f717ff15bc..bd8ddde98c506 100755
--- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less
@@ -408,6 +408,7 @@
.form.send.confirmation,
.form.password.forget,
.form.create.account,
+ .form.search.advanced,
.form.form-orders-search {
min-width: 600px;
width: 50%;
@@ -608,4 +609,15 @@
position: relative;
}
}
+
+ .form.search.advanced {
+ .field.price {
+ .with-addon {
+ .input-text {
+ flex-basis: auto;
+ width: 100%;
+ }
+ }
+ }
+ }
}
diff --git a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less
index fe49d6679a613..e9c40f6386246 100644
--- a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less
@@ -70,6 +70,10 @@
clear: left;
}
}
+
+ .box-tocart {
+ margin-top: @indent__s;
+ }
}
}
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less
index f2b9c9274bbcf..1be46c8239ee2 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less
@@ -398,6 +398,17 @@
&.orders-recent {
&:extend(.abs-account-table-margin-mobile all);
&:extend(.abs-no-border-top all);
+ .table-order-items {
+ &.table {
+ tbody {
+ > tr {
+ > td.col {
+ padding-left: 0;
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less
index 4d990a82cb7e4..1f1ea93d0b54a 100644
--- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less
@@ -453,7 +453,7 @@
}
}
-.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) {
+.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) {
.logo {
margin-bottom: 13px;
margin-top: 4px;
diff --git a/app/etc/di.xml b/app/etc/di.xml
index 19543375aad58..d0b45ea16c855 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -38,7 +38,7 @@
-
+
@@ -1757,4 +1757,11 @@
+
+
+ Magento\Framework\Lock\Backend\Cache
+ 10000
+ 20
+
+
diff --git a/dev/tests/acceptance/tests/_data/catalog_import_products.csv b/dev/tests/acceptance/tests/_data/catalog_import_products.csv
new file mode 100644
index 0000000000000..7732f15d4ce3a
--- /dev/null
+++ b/dev/tests/acceptance/tests/_data/catalog_import_products.csv
@@ -0,0 +1,4 @@
+sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,configurable_variations,configurable_variation_labels,associated_skus
+SimpleProductForTest1,,Default,simple,"Default","base,second_website",SimpleProductAfterImport1,,,1.0000,1,"Taxable Goods","Catalog, Search",250.0000,,,,simple-product-for-test-1,,,,,,,,,,,,"3/4/19, 5:53 AM","3/4/19, 4:47 PM",,,"Block after Info Column",,,,,,,,,,,"Use config",,,100.0000,0.0000,1,0,0,1,1.0000,1,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,0,,,,,,,,,,,,,,,,,,,
+SimpleProductForTest2,,Default,simple,"Default",base,SimpleProductAfterImport2,,,1.0000,1,"Taxable Goods","Catalog, Search",300.0000,,,,simple-product-for-test-2,,,,,,,,,,,,"3/4/19, 5:53 AM","3/4/19, 4:47 PM",,,"Block after Info Column",,,,,,,,,,,"Use config",,,100.0000,0.0000,1,0,0,1,1.0000,1,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,0,,,,,,,,,,,,,,,,,,,
+SimpleProductForTest3,,Default,simple,"Default","base,second_website",SimpleProductAfterImport3,,,1.0000,1,"Taxable Goods","Catalog, Search",350.0000,,,,simple-product-for-test-3,,,,,,,,,,,,"3/4/19, 5:53 AM","3/4/19, 4:47 PM",,,"Block after Info Column",,,,,,,,,,,"Use config",,,100.0000,0.0000,1,0,0,1,1.0000,1,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,0,,,,,,,,,,,,,,,,,,,
\ No newline at end of file
diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php
index 6dbf2b1aa6a12..03492f7ae1a9e 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php
@@ -195,6 +195,13 @@ class ConditionsElement extends SimpleElement
*/
protected $exception;
+ /**
+ * Condition option text selector.
+ *
+ * @var string
+ */
+ private $conditionOptionTextSelector = '//option[normalize-space(text())="%s"]';
+
/**
* @inheritdoc
*/
@@ -265,7 +272,7 @@ protected function addSingleCondition($condition, ElementInterface $context)
$this->addCondition($condition['type'], $context);
$createdCondition = $context->find($this->created, Locator::SELECTOR_XPATH);
$this->waitForCondition($createdCondition);
- $this->fillCondition($condition['rules'], $createdCondition);
+ $this->fillCondition($condition['rules'], $createdCondition, $condition['type']);
}
/**
@@ -282,10 +289,16 @@ protected function addCondition($type, ElementInterface $context)
$count = 0;
do {
- $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click();
-
try {
- $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'select')->setValue($type);
+ $specificType = $newCondition->find(
+ sprintf($this->conditionOptionTextSelector, $type),
+ Locator::SELECTOR_XPATH
+ )->isPresent();
+ $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click();
+ $condition = $specificType
+ ? $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'selectcondition')
+ : $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'select');
+ $condition->setValue($type);
$isSetType = true;
} catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) {
$isSetType = false;
@@ -306,13 +319,14 @@ protected function addCondition($type, ElementInterface $context)
*
* @param array $rules
* @param ElementInterface $element
+ * @param string|null $type
* @return void
* @throws \Exception
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
- protected function fillCondition(array $rules, ElementInterface $element)
+ protected function fillCondition(array $rules, ElementInterface $element, $type = null)
{
$this->resetKeyParam();
foreach ($rules as $rule) {
@@ -333,7 +347,7 @@ protected function fillCondition(array $rules, ElementInterface $element)
if ($this->fillGrid($rule, $param)) {
$isSet = true;
- } elseif ($this->fillSelect($rule, $param)) {
+ } elseif ($this->fillSelect($rule, $param, $type)) {
$isSet = true;
} elseif ($this->fillText($rule, $param)) {
$isSet = true;
@@ -390,11 +404,15 @@ protected function fillGrid($rule, ElementInterface $param)
*
* @param string $rule
* @param ElementInterface $param
+ * @param string|null $type
* @return bool
*/
- protected function fillSelect($rule, ElementInterface $param)
+ protected function fillSelect($rule, ElementInterface $param, $type = null)
{
- $value = $param->find('select', Locator::SELECTOR_TAG_NAME, 'select');
+ //Avoid confusion between regions like: "Baja California" and "California".
+ $value = strpos($type, 'State/Province') === false
+ ? $param->find('select', Locator::SELECTOR_TAG_NAME, 'select')
+ : $param->find('select', Locator::SELECTOR_TAG_NAME, 'selectstate');
if ($value->isVisible()) {
$value->setValue($rule);
$this->click();
diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php
new file mode 100644
index 0000000000000..15a799eac5188
--- /dev/null
+++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php
@@ -0,0 +1,20 @@
+stepFactory = $stepFactory;
$this->fixtureFactory = $fixtureFactory;
$this->adminExportIndex = $adminExportIndex;
$this->catalogProductIndex = $catalogProductIndexPage;
+ $this->cron = $cron;
}
/**
@@ -130,8 +140,12 @@ public function test(
if ($website) {
$website->persist();
$this->setupCurrencyForCustomWebsite($website, $currencyCustomWebsite);
+ $this->cron->run();
+ $this->cron->run();
}
$products = $this->prepareProducts($products, $website);
+ $this->cron->run();
+ $this->cron->run();
$this->adminExportIndex->open();
$this->adminExportIndex->getExportedGrid()->deleteAllExportedFiles();
$exportData = $this->fixtureFactory->createByCode(
diff --git a/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.xml b/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.xml
index d069499da4aab..07646c2aceda8 100644
--- a/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.xml
+++ b/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.xml
@@ -50,7 +50,6 @@
- MC-13864 Consumer always read config from memory
price_scope_website
csv_with_advanced_pricing
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml
index adae65a1d06d6..799f9e30fd972 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml
@@ -17,5 +17,6 @@
+
diff --git a/dev/tests/functional/tests/app/Magento/BundleImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/BundleImportExport/Test/TestCase/ExportProductsTest.xml
index 3ad8cff31eaf8..bfbe233b9dc1b 100644
--- a/dev/tests/functional/tests/app/Magento/BundleImportExport/Test/TestCase/ExportProductsTest.xml
+++ b/dev/tests/functional/tests/app/Magento/BundleImportExport/Test/TestCase/ExportProductsTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
default
- bundleProduct
diff --git a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.php b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.php
index e55558482c1f3..b5cd056fb99ad 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.php
+++ b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.php
@@ -104,6 +104,8 @@ public function test(
$exportData->persist();
$this->adminExportIndex->getExportForm()->fill($exportData);
$this->adminExportIndex->getFilterExport()->clickContinue();
+ $this->cron->run();
+ $this->cron->run();
$this->assertExportProduct->processAssert($export, $exportedFields, $products);
}
diff --git a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml
index b94f21371496a..be22eab8ac717 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
default
- catalogProductSimple
@@ -27,6 +28,7 @@
+ mftf_migrated:yes
default
- catalogProductSimple
@@ -43,6 +45,7 @@
+ mftf_migrated:yes
default
- catalogProductSimple
@@ -58,7 +61,7 @@
- >MC-13864 Consumer always read config from memory
+ mftf_migrated:yes
default
- catalogProductSimple
diff --git a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ImportProductsTest.xml b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ImportProductsTest.xml
index edb0aad954fbb..77e5e2b91d93f 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ImportProductsTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ImportProductsTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
- Products
- Add/Update
@@ -38,6 +39,7 @@
+ mftf_migrated:yes
Products
Replace
Stop on Error
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml
index 0437e0a5e999b..8c465544a3283 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
catalogProductSimple::sku
Main Website/Main Website Store/Default Store View
http://example.com/
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml
index a9cc0dfd34f9f..8fdd7ef715521 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
default
diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml
index 3bf4e521c4a04..3ef2b65c0224b 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
catalogSearchQuery::default,catalogSearchQuery::default,catalogSearchQuery::default
diff --git a/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlCategoryEntityTest.xml
index 398054f1f0ed3..8b15da5ecd2ef 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlCategoryEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlCategoryEntityTest.xml
@@ -14,7 +14,7 @@
Yes
Subcategory%isolation%
subcategory-%isolation%
- test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1
+ test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1, mftf_migrated:yes
diff --git a/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlProductEntity.xml b/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlProductEntity.xml
index 1116821f756a9..8110ed1ed00b1 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlProductEntity.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlProductEntity.xml
@@ -8,7 +8,7 @@
- test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1
+ test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1, mftf_migrated:yes
simple-product-%isolation%
Simple Product %isolation%
simple_sku_%isolation%
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.xml
index 8b2460718097c..b4c97a11b9145 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.xml
@@ -9,7 +9,7 @@
https://github.com/magento-engcom/msi/issues/1624
- test_type:extended_acceptance_test, severity:S0
+ test_type:extended_acceptance_test, severity:S0, mftf_migrated:yes
catalogProductSimple::default
simple_order_qty_2
true
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml
index e0ea721a51f1b..5caa3ba9b924e 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml
@@ -8,7 +8,7 @@
- severity:S0
+ severity:S0,mftf_migrated:yes
default
100
3
@@ -20,7 +20,7 @@
- severity:S0
+ severity:S0,mftf_migrated:yes
with_two_custom_option
50
11
diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml
index 93240586ec92c..0a2ce7ab7f183 100644
--- a/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml
+++ b/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml
@@ -30,6 +30,7 @@
+ mftf_migrated:yes
default
- configurableProduct
@@ -45,7 +46,7 @@
- >MC-13864 Consumer always read config from memory
+ mftf_migrated:yes
default
- configurableProduct
diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/VerifyDisabledCustomerGroupFieldTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/VerifyDisabledCustomerGroupFieldTest.xml
index 70a912a3b5ffe..e88e5161e474e 100644
--- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/VerifyDisabledCustomerGroupFieldTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/VerifyDisabledCustomerGroupFieldTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
NOT_LOGGED_IN
- customer_group_code
diff --git a/dev/tests/functional/tests/app/Magento/GroupedImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/GroupedImportExport/Test/TestCase/ExportProductsTest.xml
index cffcdbf45a6dc..a110dc6a89f8c 100644
--- a/dev/tests/functional/tests/app/Magento/GroupedImportExport/Test/TestCase/ExportProductsTest.xml
+++ b/dev/tests/functional/tests/app/Magento/GroupedImportExport/Test/TestCase/ExportProductsTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
default
- groupedProduct
diff --git a/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/FlushStaticFilesCacheButtonVisibilityTest.xml b/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/FlushStaticFilesCacheButtonVisibilityTest.xml
index cbdce59057195..bc529729f1217 100644
--- a/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/FlushStaticFilesCacheButtonVisibilityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/FlushStaticFilesCacheButtonVisibilityTest.xml
@@ -8,7 +8,7 @@
- severity:S3
+ severity:S3, mftf_migrated:yes
diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCustomOrderStatusEntityTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCustomOrderStatusEntityTest.xml
index 38ce04fa56d81..e05d0fea6b129 100644
--- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCustomOrderStatusEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCustomOrderStatusEntityTest.xml
@@ -8,17 +8,20 @@
+ mftf_migrated:yes
order_status%isolation%
orderLabel%isolation%
+ mftf_migrated:yes
pending
orderLabel%isolation%
+ mftf_migrated:yes
order_status%isolation%
Suspected Fraud
diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateWebsiteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateWebsiteEntityTest.xml
index 5a547f69280e1..e35ef853d1b68 100644
--- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateWebsiteEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateWebsiteEntityTest.xml
@@ -8,7 +8,7 @@
- severity:S1
+ severity:S1, mftf_migrated:yes
website_%isolation%
code_%isolation%
diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreEntityTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreEntityTest.xml
index 306a9fd2024a4..cd37c555fdb1d 100644
--- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreEntityTest.xml
@@ -8,7 +8,7 @@
- severity:S2
+ severity:S2, mftf_migrated:yes
custom
Yes
diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/UpdateWebsiteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/UpdateWebsiteEntityTest.xml
index ac857ad035f44..5db0e7f8baad4 100644
--- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/UpdateWebsiteEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/UpdateWebsiteEntityTest.xml
@@ -8,7 +8,7 @@
- severity:S2
+ severity:S2, mftf_migrated:yes
custom_website
website_upd%isolation%
code_upd%isolation%
diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.xml
index 60de554d594d2..8f12930aa417b 100644
--- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
product/%catalogProductSimple::product_100_dollar%
Main Website/Main Website Store/Default Store View
test_%isolation%.html
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccess.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccess.php
index f7c56ae1b9653..ecfbc8d353888 100644
--- a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccess.php
+++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccess.php
@@ -10,6 +10,7 @@
use Magento\Mtf\Client\BrowserInterface;
use Magento\Mtf\Constraint\AbstractConstraint;
use Magento\User\Test\Fixture\User;
+use Magento\User\Test\TestStep\LoginUserOnBackendWithErrorStep;
/**
* Asserts that user has only related permissions.
@@ -18,6 +19,8 @@ class AssertUserRoleRestrictedAccess extends AbstractConstraint
{
const DENIED_ACCESS = 'Sorry, you need permissions to view this content.';
+ protected $loginStep = 'Magento\User\Test\TestStep\LoginUserOnBackendStep';
+
/**
* Asserts that user has only related permissions.
*
@@ -36,7 +39,7 @@ public function processAssert(
$denyUrl
) {
$this->objectManager->create(
- \Magento\User\Test\TestStep\LoginUserOnBackendStep::class,
+ $this->loginStep,
['user' => $user]
)->run();
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccessWithError.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccessWithError.php
new file mode 100644
index 0000000000000..b001893abb4c4
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccessWithError.php
@@ -0,0 +1,17 @@
+objectManager->create(
- \Magento\User\Test\TestStep\LoginUserOnBackendStep::class,
+ $this->loginStep,
['user' => $user]
)->run();
\PHPUnit\Framework\Assert::assertTrue(
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLoginWithError.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLoginWithError.php
new file mode 100644
index 0000000000000..9fed1f4df8573
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLoginWithError.php
@@ -0,0 +1,20 @@
+
-
+
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php
index cc1d0fc980fbf..58450abc71633 100644
--- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php
+++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php
@@ -121,6 +121,11 @@ public function testUpdateAdminUserRolesEntity(
*/
public function tearDown()
{
+ sleep(3);
+ $modalMessage = $this->dashboard->getModalMessage();
+ if ($modalMessage->isVisible()) {
+ $modalMessage->acceptAlert();
+ }
$this->dashboard->getAdminPanelHeader()->logOut();
}
}
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.xml
index 224ccbce10f96..db6a13d0f3551 100644
--- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.xml
@@ -29,8 +29,8 @@
-
-
+
+
custom_admin_with_default_role
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php
new file mode 100644
index 0000000000000..51d48058c8ae5
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php
@@ -0,0 +1,57 @@
+dashboard = $dashboard;
+ $this->browser = $browser;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function run()
+ {
+ $modalMessage = $this->dashboard->getModalMessage();
+ try {
+ $this->browser->waitUntil(
+ function () use ($modalMessage) {
+ return $modalMessage->isVisible() ? true : null;
+ }
+ );
+ $modalMessage->acceptAlert();
+ } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) {
+ //There is no modal to accept.
+ }
+ }
+}
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php
index 4f7e6deed7a85..c244e27d42899 100644
--- a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php
+++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php
@@ -50,7 +50,7 @@ class LoginUserOnBackendStep implements TestStepInterface
*
* @var BrowserInterface
*/
- private $browser;
+ protected $browser;
/**
* Array of error messages on admin login form.
@@ -108,8 +108,6 @@ public function run()
}
}
}
-
- $this->dashboard->getSystemMessageDialog()->closePopup();
}
/**
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendWithErrorStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendWithErrorStep.php
new file mode 100644
index 0000000000000..094f90d0a5d70
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendWithErrorStep.php
@@ -0,0 +1,53 @@
+closeErrorAlertStep = $closeErrorAlertStep;
+ }
+
+ /**
+ * Run step flow.
+ *
+ * @return void
+ */
+ public function run()
+ {
+ parent::run();
+ $this->closeErrorAlertStep->run();
+ }
+}
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendStep.php
index 70a4080a0b4d5..7f366312bba24 100644
--- a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendStep.php
+++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendStep.php
@@ -48,7 +48,6 @@ public function __construct(AdminAuthLogin $adminAuth, Dashboard $dashboard)
public function run()
{
$this->adminAuth->open();
- $this->dashboard->getSystemMessageDialog()->closePopup();
$this->dashboard->getAdminPanelHeader()->logOut();
}
}
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendWithErrorStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendWithErrorStep.php
new file mode 100644
index 0000000000000..ce49e86afc065
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendWithErrorStep.php
@@ -0,0 +1,40 @@
+closeErrorAlertStep = $closeErrorAlertStep;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function run()
+ {
+ $this->adminAuth->open();
+ $this->closeErrorAlertStep->run();
+ $this->dashboard->getAdminPanelHeader()->logOut();
+ }
+}
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/User/Test/etc/di.xml
new file mode 100644
index 0000000000000..1298bd56a8fb0
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/User/Test/etc/di.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ \Magento\User\Test\TestStep\LogoutUserOnBackendWithErrorStep
+
+
+
diff --git a/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php b/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php
index 9ca351aa1cf98..32240e68ae73e 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php
@@ -95,17 +95,9 @@ public function initialize()
$this->amqpHelper->deleteConnection($connectionName);
}
$this->amqpHelper->clearQueue("async.operations.all");
- foreach ($this->consumers as $consumer) {
- foreach ($this->getConsumerProcessIds($consumer) as $consumerProcessId) {
- exec("kill {$consumerProcessId}");
- }
- }
- foreach ($this->consumers as $consumer) {
- if (!$this->getConsumerProcessIds($consumer)) {
- exec("{$this->getConsumerStartCommand($consumer, true)} > /dev/null &");
- }
- sleep(5);
- }
+
+ $this->stopConsumers();
+ $this->startConsumers();
if (file_exists($this->logFilePath)) {
// try to remove before failing the test
@@ -230,4 +222,19 @@ public function getPublisher()
{
return $this->publisher;
}
+
+ /**
+ * Start consumers
+ *
+ * @return void
+ */
+ public function startConsumers(): void
+ {
+ foreach ($this->consumers as $consumer) {
+ if (!$this->getConsumerProcessIds($consumer)) {
+ exec("{$this->getConsumerStartCommand($consumer, true)} > /dev/null &");
+ }
+ sleep(5);
+ }
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php
index 07af21505f180..89f1e5e5d53d6 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php
@@ -21,6 +21,8 @@ public function testAjaxBlockAction()
public function testTunnelAction()
{
+ $this->markTestSkipped('MAGETWO-98800: TunnelAction fails when Google Chart API is not available');
+
$testUrl = \Magento\Backend\Block\Dashboard\Graph::API_URL . '?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World';
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $testUrl);
diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Paypal/ReviewTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Paypal/ReviewTest.php
new file mode 100644
index 0000000000000..fc79048f15f45
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Paypal/ReviewTest.php
@@ -0,0 +1,43 @@
+controller = $this->_objectManager->create(Review::class);
+ }
+
+ /**
+ * Test controller implements correct interfaces
+ *
+ */
+ public function testInterfaceImplementation()
+ {
+ $this->assertInstanceOf(HttpGetActionInterface::class, $this->controller);
+ $this->assertInstanceOf(HttpPostActionInterface::class, $this->controller);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php
index b97bd9f822666..e9cb2f2d6c9d4 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php
@@ -13,7 +13,6 @@
* @magentoDbIsolation disabled
* @magentoIndexerDimensionMode catalog_product_price website_and_customer_group
* @group indexer_dimension
- * @magentoAppArea frontend
*/
class FixedBundlePriceCalculatorWithDimensionTest extends BundlePriceAbstract
{
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php
index a2967878402d0..3ec8c806dcbb1 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php
@@ -5,13 +5,49 @@
*/
namespace Magento\Catalog\Controller\Adminhtml\Product\Action;
+use Magento\Catalog\Model\Product\Visibility;
+use Magento\Catalog\Model\ProductRepository;
use Magento\Framework\App\Request\Http as HttpRequest;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\MessageQueue\PublisherConsumerController;
/**
* @magentoAppArea adminhtml
*/
class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendController
{
+ /** @var PublisherConsumerController */
+ private $publisherConsumerController;
+ private $consumers = ['product_action_attribute.update'];
+
+ protected function setUp()
+ {
+ $this->publisherConsumerController = Bootstrap::getObjectManager()->create(PublisherConsumerController::class, [
+ 'consumers' => $this->consumers,
+ 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt",
+ 'maxMessages' => null,
+ 'appInitParams' => Bootstrap::getInstance()->getAppInitParams()
+ ]);
+
+ try {
+ $this->publisherConsumerController->startConsumers();
+ } catch (\Magento\TestFramework\MessageQueue\EnvironmentPreconditionException $e) {
+ $this->markTestSkipped($e->getMessage());
+ } catch (\Magento\TestFramework\MessageQueue\PreconditionFailedException $e) {
+ $this->fail(
+ $e->getMessage()
+ );
+ }
+
+ parent::setUp();
+ }
+
+ protected function tearDown()
+ {
+ $this->publisherConsumerController->stopConsumers();
+ parent::tearDown();
+ }
+
/**
* @covers \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute\Save::execute
*
@@ -20,7 +56,7 @@ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendContr
*/
public function testSaveActionRedirectsSuccessfully()
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $objectManager = Bootstrap::getObjectManager();
/** @var $session \Magento\Backend\Model\Session */
$session = $objectManager->get(\Magento\Backend\Model\Session::class);
@@ -59,13 +95,14 @@ public function testSaveActionRedirectsSuccessfully()
*/
public function testSaveActionChangeVisibility($attributes)
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\Catalog\Model\ProductRepository::class
+ $objectManager = Bootstrap::getObjectManager();
+ /** @var ProductRepository $repository */
+ $repository = Bootstrap::getObjectManager()->create(
+ ProductRepository::class
);
$product = $repository->get('simple');
$product->setOrigData();
- $product->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE);
+ $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE);
$product->save();
/** @var $session \Magento\Backend\Model\Session */
@@ -75,15 +112,29 @@ public function testSaveActionChangeVisibility($attributes)
$this->getRequest()->setMethod(HttpRequest::METHOD_POST);
$this->dispatch('backend/catalog/product_action_attribute/save/store/0');
+
/** @var \Magento\Catalog\Model\Category $category */
- $categoryFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ $categoryFactory = Bootstrap::getObjectManager()->get(
\Magento\Catalog\Model\CategoryFactory::class
);
/** @var \Magento\Catalog\Block\Product\ListProduct $listProduct */
- $listProduct = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ $listProduct = Bootstrap::getObjectManager()->get(
\Magento\Catalog\Block\Product\ListProduct::class
);
+ $this->publisherConsumerController->waitForAsynchronousResult(
+ function () use ($repository) {
+ sleep(3);
+ return $repository->get(
+ 'simple',
+ false,
+ null,
+ true
+ )->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE;
+ },
+ []
+ );
+
$category = $categoryFactory->create()->load(2);
$layer = $listProduct->getLayer();
$layer->setCurrentCategory($category);
@@ -105,7 +156,7 @@ public function testSaveActionChangeVisibility($attributes)
*/
public function testValidateActionWithMassUpdate($attributes)
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $objectManager = Bootstrap::getObjectManager();
/** @var $session \Magento\Backend\Model\Session */
$session = $objectManager->get(\Magento\Backend\Model\Session::class);
@@ -156,8 +207,8 @@ public function validateActionDataProvider()
public function saveActionVisibilityAttrDataProvider()
{
return [
- ['arguments' => ['visibility' => \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH]],
- ['arguments' => ['visibility' => \Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG]]
+ ['arguments' => ['visibility' => Visibility::VISIBILITY_BOTH]],
+ ['arguments' => ['visibility' => Visibility::VISIBILITY_IN_CATALOG]]
];
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php
index 994076badddae..60ccdb88676aa 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php
@@ -22,6 +22,7 @@ class ResetQuoteAddressesTest extends \PHPUnit\Framework\TestCase
/**
* @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php
*
+ * @magentoAppArea frontend
* @return void
*/
public function testAfterRemoveItem(): void
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
index ea7a7710acbc3..10b632c002475 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
@@ -751,6 +751,21 @@ public function loginPostRedirectDataProvider()
];
}
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address.php
+ * @magentoAppArea frontend
+ */
+ public function testCheckVisitorModel()
+ {
+ /** @var \Magento\Customer\Model\Visitor $visitor */
+ $visitor = $this->_objectManager->get(\Magento\Customer\Model\Visitor::class);
+ $this->login(1);
+ $this->assertNull($visitor->getId());
+ $this->dispatch('customer/account/index');
+ $this->assertNotNull($visitor->getId());
+ }
+
/**
* @param string $email
* @return void
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php
index 978815f665341..a52c5bb9e21b7 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php
@@ -43,7 +43,7 @@ protected function setUp()
$contentManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class)
->disableOriginalConstructor()
->getMock();
- $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class)
+ $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Client\Elasticsearch::class)
->disableOriginalConstructor()
->getMock();
$contentManager
@@ -78,7 +78,7 @@ protected function setUp()
/**
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
* @return void
*/
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php
index 61add5f7d0ea7..3eea2497daa1f 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php
@@ -10,7 +10,7 @@
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Elasticsearch\SearchAdapter\ConnectionManager;
-use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient;
+use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient;
use Magento\Elasticsearch\Model\Config;
use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver;
@@ -95,7 +95,7 @@ private function search($text)
}
/**
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search
*/
public function testSearchConfigurableProductBySimpleProductName()
@@ -104,7 +104,7 @@ public function testSearchConfigurableProductBySimpleProductName()
}
/**
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search
*/
public function testSearchConfigurableProductBySimpleProductAttributeMultiselect()
@@ -113,7 +113,7 @@ public function testSearchConfigurableProductBySimpleProductAttributeMultiselect
}
/**
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search
*/
public function testSearchConfigurableProductBySimpleProductAttributeSelect()
@@ -122,7 +122,7 @@ public function testSearchConfigurableProductBySimpleProductAttributeSelect()
}
/**
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search
*/
public function testSearchConfigurableProductBySimpleProductAttributeShortDescription()
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php
index 014aaf7679bc9..77533e83b719c 100755
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php
@@ -13,7 +13,7 @@
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Elasticsearch\SearchAdapter\ConnectionManager;
-use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient;
+use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient;
use Magento\Elasticsearch\Model\Config;
use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver;
use Magento\Indexer\Model\Indexer;
@@ -87,7 +87,7 @@ protected function setUp()
}
/**
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
* @return void
*/
@@ -106,7 +106,7 @@ public function testReindexAll(): void
/**
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
* @return void
*/
@@ -131,7 +131,7 @@ public function testReindexRowAfterEdit(): void
}
/**
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
* @return void
*/
@@ -170,7 +170,7 @@ public function testReindexRowAfterMassAction(): void
}
/**
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
* @magentoAppArea adminhtml
* @return void
@@ -192,7 +192,7 @@ public function testReindexRowAfterDelete(): void
/**
* @magentoDbIsolation enabled
* @magentoAppArea adminhtml
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
* @magentoDataFixture Magento/Elasticsearch/_files/configurable_products.php
* @return void
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php
index d40ce9e8a0706..7d4aa8e005e4e 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php
@@ -68,7 +68,7 @@ protected function setUp()
/**
* Test search of all products after full reindex
*
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable
* @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php
*/
@@ -82,7 +82,7 @@ public function testSearchAll()
/**
* Test search of specific product after full reindex
*
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable
* @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php
*/
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php
index dc288a18fadb7..6bb7d6ac568fc 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php
@@ -5,8 +5,6 @@
*/
namespace Magento\Elasticsearch\SearchAdapter;
-use Magento\Elasticsearch\Model\Config;
-
/**
* Class AdapterTest
*
@@ -26,7 +24,7 @@ class AdapterTest extends \Magento\Framework\Search\Adapter\Mysql\AdapterTest
/**
* @var string
*/
- protected $searchEngine = Config::ENGINE_NAME;
+ protected $searchEngine = 'elasticsearch6';
/**
* Get request config path
@@ -43,12 +41,12 @@ protected function getRequestConfigPath()
*/
protected function createAdapter()
{
- return $this->objectManager->create(\Magento\Elasticsearch\SearchAdapter\Adapter::class);
+ return $this->objectManager->create(\Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter::class);
}
/**
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testMatchQuery()
@@ -58,7 +56,7 @@ public function testMatchQuery()
/**
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testMatchOrderedQuery()
@@ -70,7 +68,7 @@ public function testMatchOrderedQuery()
/**
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testAggregationsQuery()
@@ -80,7 +78,7 @@ public function testAggregationsQuery()
/**
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testMatchQueryFilters()
@@ -92,7 +90,7 @@ public function testMatchQueryFilters()
* Range filter test with all fields filled
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testRangeFilterWithAllFields()
@@ -104,7 +102,7 @@ public function testRangeFilterWithAllFields()
* Range filter test with all fields filled
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testRangeFilterWithoutFromField()
@@ -116,7 +114,7 @@ public function testRangeFilterWithoutFromField()
* Range filter test with all fields filled
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testRangeFilterWithoutToField()
@@ -128,7 +126,7 @@ public function testRangeFilterWithoutToField()
* Term filter test
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testTermFilter()
@@ -140,7 +138,7 @@ public function testTermFilter()
* Term filter test
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testTermFilterArray()
@@ -152,7 +150,7 @@ public function testTermFilterArray()
* Term filter test
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testWildcardFilter()
@@ -164,7 +162,7 @@ public function testWildcardFilter()
* Request limits test
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testSearchLimit()
@@ -176,7 +174,7 @@ public function testSearchLimit()
* Bool filter test
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testBoolFilter()
@@ -188,7 +186,7 @@ public function testBoolFilter()
* Test bool filter with nested negative bool filter
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testBoolFilterWithNestedNegativeBoolFilter()
@@ -200,7 +198,7 @@ public function testBoolFilterWithNestedNegativeBoolFilter()
* Test range inside nested negative bool filter
*
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testBoolFilterWithNestedRangeInNegativeBoolFilter()
@@ -213,7 +211,7 @@ public function testBoolFilterWithNestedRangeInNegativeBoolFilter()
*
* @dataProvider elasticSearchAdvancedSearchDataProvider
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
* @param string $nameQuery
* @param string $descriptionQuery
@@ -259,7 +257,7 @@ public function elasticSearchAdvancedSearchDataProvider()
/**
* @magentoAppIsolation enabled
* @magentoDataFixture Magento/Framework/Search/_files/filterable_attribute.php
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testCustomFilterableAttribute()
@@ -274,7 +272,7 @@ public function testCustomFilterableAttribute()
*
* @magentoAppIsolation enabled
* @magentoDataFixture Magento/Framework/Search/_files/filterable_attributes.php
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
* @dataProvider filterByAttributeValuesDataProvider
* @param string $requestName
@@ -294,7 +292,7 @@ public function testFilterByAttributeValues($requestName, $additionalData)
* @param $rangeFilter
* @param $expectedRecordsCount
* @magentoDataFixture Magento/Framework/Search/_files/date_attribute.php
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
* @magentoAppIsolation enabled
* @dataProvider dateDataProvider
@@ -309,7 +307,7 @@ public function testAdvancedSearchDateField($rangeFilter, $expectedRecordsCount)
/**
* @magentoDataFixture Magento/Framework/Search/_files/product_configurable.php
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testAdvancedSearchCompositeProductWithOutOfStockOption()
@@ -320,7 +318,7 @@ public function testAdvancedSearchCompositeProductWithOutOfStockOption()
/**
* @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_disabled_child.php
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testAdvancedSearchCompositeProductWithDisabledChild()
@@ -333,7 +331,7 @@ public function testAdvancedSearchCompositeProductWithDisabledChild()
/**
* @magentoDataFixture Magento/Framework/Search/_files/search_weight_products.php
* @magentoAppIsolation enabled
- * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch6
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest
*/
public function testSearchQueryBoost()
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php
new file mode 100644
index 0000000000000..e64b3c505acf1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php
@@ -0,0 +1,55 @@
+objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->model = $this->objectManager->create(
+ \Magento\Framework\Lock\Backend\FileLock::class,
+ ['path' => '/tmp']
+ );
+ }
+
+ public function testLockAndUnlock()
+ {
+ $name = 'test_lock';
+
+ $this->assertFalse($this->model->isLocked($name));
+
+ $this->assertTrue($this->model->lock($name));
+ $this->assertTrue($this->model->isLocked($name));
+ $this->assertFalse($this->model->lock($name, 2));
+
+ $this->assertTrue($this->model->unlock($name));
+ $this->assertFalse($this->model->isLocked($name));
+ }
+
+ public function testUnlockWithoutExistingLock()
+ {
+ $name = 'test_lock';
+
+ $this->assertFalse($this->model->isLocked($name));
+ $this->assertFalse($this->model->unlock($name));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php
new file mode 100644
index 0000000000000..8d0caad5d55e4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php
@@ -0,0 +1,90 @@
+markTestSkipped('php extension Zookeeper is not installed.');
+ }
+
+ $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->configReader = $this->objectManager->get(FileReader::class);
+ $this->lockBackendFactory = $this->objectManager->create(LockBackendFactory::class);
+ $this->arrayManager = $this->objectManager->create(ArrayManager::class);
+ $config = $this->configReader->load(ConfigFilePool::APP_ENV);
+
+ if ($this->arrayManager->get('lock/provider', $config) !== 'zookeeper') {
+ $this->markTestSkipped('Zookeeper is not configured during installation.');
+ }
+
+ $this->model = $this->lockBackendFactory->create();
+ $this->assertInstanceOf(ZookeeperLock::class, $this->model);
+ }
+
+ public function testLockAndUnlock()
+ {
+ $name = 'test_lock';
+
+ $this->assertFalse($this->model->isLocked($name));
+
+ $this->assertTrue($this->model->lock($name));
+ $this->assertTrue($this->model->isLocked($name));
+ $this->assertFalse($this->model->lock($name, 2));
+
+ $this->assertTrue($this->model->unlock($name));
+ $this->assertFalse($this->model->isLocked($name));
+ }
+
+ public function testUnlockWithoutExistingLock()
+ {
+ $name = 'test_lock';
+
+ $this->assertFalse($this->model->isLocked($name));
+ $this->assertFalse($this->model->unlock($name));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_request_merged.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_request_merged.php
index 8586f47a0f7fa..0aaa3f4e15bda 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_request_merged.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_request_merged.php
@@ -35,6 +35,7 @@
'match_query' => [
'value' => '$match_term_override$',
'name' => 'match_query',
+ 'boost' => '1',
'match' => [
0 => [
'field' => 'match_field',
@@ -50,6 +51,7 @@
],
'must_query' => [
'name' => 'must_query',
+ 'boost' => '1',
'filterReference' => [
0 => [
'clause' => 'must',
@@ -60,6 +62,7 @@
],
'should_query' => [
'name' => 'should_query',
+ 'boost' => '1',
'filterReference' => [
0 => [
'clause' => 'should',
@@ -70,6 +73,7 @@
],
'not_query' => [
'name' => 'not_query',
+ 'boost' => '1',
'filterReference' => [
0 => [
'clause' => 'not',
@@ -80,6 +84,7 @@
],
'match_query_2' => [
'value' => '$match_term_override$',
+ 'boost' => '1',
'name' => 'match_query_2',
'match' => [
0 => [
@@ -163,6 +168,7 @@
'queries' => [
'filter_query' => [
'name' => 'filter_query',
+ 'boost' => '1',
'filterReference' => [
0 =>
[
@@ -230,6 +236,7 @@
'new_match_query' => [
'value' => '$match_term$',
'name' => 'new_match_query',
+ 'boost' => '1',
'match' => [
0 =>
[
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php
new file mode 100644
index 0000000000000..47705262caaf3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php
@@ -0,0 +1,92 @@
+login(1);
+ $this->prepareRequestData();
+ $this->dispatch('wishlist/index/send/');
+
+ $this->assertSessionMessages(
+ $this->equalTo(['Your wish list has been shared.']),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+
+ /**
+ * Test share wishlist with incorrect data
+ *
+ * @magentoDataFixture Magento/Wishlist/_files/wishlist.php
+ */
+ public function testShareWishlistWithoutEmails()
+ {
+ $this->login(1);
+ $this->prepareRequestData(true);
+ $this->dispatch('wishlist/index/send/');
+
+ $this->assertSessionMessages(
+ $this->equalTo(['Please enter an email address.']),
+ MessageInterface::TYPE_ERROR
+ );
+ }
+
+ /**
+ * Login the user
+ *
+ * @param string $customerId Customer to mark as logged in for the session
+ * @return void
+ */
+ protected function login($customerId)
+ {
+ /** @var Session $session */
+ $session = $this->_objectManager->get(Session::class);
+ $session->loginById($customerId);
+ }
+
+ /**
+ * Prepares the request with data
+ *
+ * @param bool $invalidData
+ * @return void
+ */
+ private function prepareRequestData($invalidData = false)
+ {
+ Bootstrap::getInstance()->loadArea(Area::AREA_FRONTEND);
+ $emails = !$invalidData ? 'email-1@example.com,email-2@example.com' : '';
+
+ /** @var FormKey $formKey */
+ $formKey = $this->_objectManager->get(FormKey::class);
+ $post = [
+ 'emails' => $emails,
+ 'message' => '',
+ 'form_key' => $formKey->getFormKey(),
+ ];
+
+ $this->getRequest()->setMethod(Request::METHOD_POST);
+ $this->getRequest()->setPostValue($post);
+ }
+}
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js
index 2e238eb993029..fc60fbb0bdccc 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js
@@ -151,12 +151,14 @@ define([
}
}],
result = [{
+ defaultLabelVisible: true,
label: 'Label 2',
name: 'Name 2',
required: false,
columnsHeaderClasses: '',
sortOrder: 5
}, {
+ defaultLabelVisible: true,
label: 'Label 1',
name: 'Name 1',
required: false,
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php
new file mode 100644
index 0000000000000..b1a6da5e43822
--- /dev/null
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php
@@ -0,0 +1,43 @@
+]*class=[\'"]([^\'"]+)[\'"]#i', $contents, $matches)) {
+ $classes = array_pop($matches);
+ foreach ($classes as $class) {
+ $classParts = explode('\\', $class);
+ $module = implode('\\', array_slice($classParts, 0, 2));
+ if (strtolower($currentModule) !== strtolower($module)) {
+ $dependenciesInfo[] = [
+ 'module' => $module,
+ 'type' => RuleInterface::TYPE_HARD,
+ 'source' => $file,
+ ];
+ }
+ }
+ }
+
+ return $dependenciesInfo;
+ }
+}
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
index a4113abed8030..e2e0357a38f77 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
@@ -16,6 +16,7 @@
use Magento\TestFramework\Dependency\LayoutRule;
use Magento\TestFramework\Dependency\PhpRule;
use Magento\TestFramework\Dependency\ReportsConfigRule;
+use Magento\TestFramework\Dependency\AnalyticsConfigRule;
use Magento\TestFramework\Dependency\VirtualType\VirtualTypeMapper;
/**
@@ -78,6 +79,17 @@ class DependencyTest extends \PHPUnit\Framework\TestCase
*/
protected static $_listRoutesXml = [];
+ /**
+ * List of analytics.xml
+ *
+ * Format: array(
+ * '{Module_Name}' => '{Filename}'
+ * )
+ *
+ * @var array
+ */
+ protected static $_listAnalyticsXml = [];
+
/**
* List of routers
*
@@ -176,6 +188,7 @@ public static function setUpBeforeClass()
self::_prepareListConfigXml();
self::_prepareListDbSchemaXml();
self::_prepareListRoutesXml();
+ self::_prepareListAnalyticsXml();
self::_prepareMapRouters();
self::_prepareMapLayoutBlocks();
@@ -240,6 +253,7 @@ protected static function _initRules()
),
new DiRule(new VirtualTypeMapper()),
new ReportsConfigRule($dbRuleTables),
+ new AnalyticsConfigRule(),
];
}
@@ -571,6 +585,20 @@ protected static function _prepareListRoutesXml()
}
}
+ /**
+ * Prepare list of analytics.xml files
+ */
+ protected static function _prepareListAnalyticsXml()
+ {
+ $files = Files::init()->getDbSchemaFiles('analytics.xml', [], false);
+ foreach ($files as $file) {
+ if (preg_match('/(?[A-Z][a-z]+)[_\/\\\\](?[A-Z][a-zA-Z]+)/', $file, $matches)) {
+ $module = $matches['namespace'] . '\\' . $matches['module'];
+ self::$_listAnalyticsXml[$module] = $file;
+ }
+ }
+ }
+
/**
* Prepare map of routers
*/
diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt
index 3e788c1eba0ee..35ba5803b09cc 100644
--- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt
+++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt
@@ -212,3 +212,5 @@ Magento/Elasticsearch6/Model/Client
Magento/CatalogSearch/Model/ResourceModel/Fulltext
Magento/Elasticsearch/Model/Layer/Search
Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver
+Magento/Elasticsearch6/Model/Client
+Magento/Config/App/Config/Type
diff --git a/lib/internal/Magento/Framework/App/FrontControllerInterface.php b/lib/internal/Magento/Framework/App/FrontControllerInterface.php
index a552d88e68f50..afd3091097d19 100644
--- a/lib/internal/Magento/Framework/App/FrontControllerInterface.php
+++ b/lib/internal/Magento/Framework/App/FrontControllerInterface.php
@@ -8,7 +8,7 @@
/**
* Application front controller responsible for dispatching application requests.
* Front controller contains logic common for all actions.
- * Evary application area has own front controller
+ * Every application area has own front controller.
*
* @api
*/
diff --git a/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php
new file mode 100644
index 0000000000000..216d8e9a0a01b
--- /dev/null
+++ b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php
@@ -0,0 +1,116 @@
+locker = $locker;
+ $this->lockTimeout = $lockTimeout;
+ $this->delayTimeout = $delayTimeout;
+ }
+
+ /**
+ * Load data.
+ *
+ * @param string $lockName
+ * @param callable $dataLoader
+ * @param callable $dataCollector
+ * @param callable $dataSaver
+ * @return mixed
+ */
+ public function lockedLoadData(
+ string $lockName,
+ callable $dataLoader,
+ callable $dataCollector,
+ callable $dataSaver
+ ) {
+ $cachedData = $dataLoader(); //optimistic read
+
+ while ($cachedData === false && $this->locker->isLocked($lockName)) {
+ usleep($this->delayTimeout * 1000);
+ $cachedData = $dataLoader();
+ }
+
+ while ($cachedData === false) {
+ try {
+ if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) {
+ $data = $dataCollector();
+ $dataSaver($data);
+ $cachedData = $data;
+ }
+ } finally {
+ $this->locker->unlock($lockName);
+ }
+
+ if ($cachedData === false) {
+ usleep($this->delayTimeout * 1000);
+ $cachedData = $dataLoader();
+ }
+ }
+
+ return $cachedData;
+ }
+
+ /**
+ * Clean data.
+ *
+ * @param string $lockName
+ * @param callable $dataCleaner
+ * @return void
+ */
+ public function lockedCleanData(string $lockName, callable $dataCleaner)
+ {
+ while ($this->locker->isLocked($lockName)) {
+ usleep($this->delayTimeout * 1000);
+ }
+ try {
+ if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) {
+ $dataCleaner();
+ }
+ } finally {
+ $this->locker->unlock($lockName);
+ }
+ }
+}
diff --git a/lib/internal/Magento/Framework/Config/Dom.php b/lib/internal/Magento/Framework/Config/Dom.php
index 1d995bab007e5..f4721660d8da6 100644
--- a/lib/internal/Magento/Framework/Config/Dom.php
+++ b/lib/internal/Magento/Framework/Config/Dom.php
@@ -318,7 +318,7 @@ public static function validateDomDocument(
libxml_set_external_entity_loader([self::$urnResolver, 'registerEntityLoader']);
$errors = [];
try {
- $result = $dom->schemaValidate($schema);
+ $result = $dom->schemaValidate($schema, LIBXML_SCHEMA_CREATE);
if (!$result) {
$errors = self::getXmlErrors($errorFormat);
}
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php b/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php
index 5c8f66683877c..0508b5e4fb359 100644
--- a/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php
@@ -135,6 +135,48 @@ public function validateDataProvider()
];
}
+ /**
+ * @param string $xml
+ * @param string $expectedValue
+ * @dataProvider validateWithDefaultValueDataProvider
+ */
+ public function testValidateWithDefaultValue($xml, $expectedValue)
+ {
+ if (!function_exists('libxml_set_external_entity_loader')) {
+ $this->markTestSkipped('Skipped on HHVM. Will be fixed in MAGETWO-45033');
+ }
+
+ $actualErrors = [];
+
+ $dom = new \Magento\Framework\Config\Dom($xml, $this->validationStateMock);
+ $dom->validate(__DIR__ . '/_files/sample.xsd', $actualErrors);
+
+ $actualValue = $dom->getDom()
+ ->getElementsByTagName('root')->item(0)
+ ->getElementsByTagName('node')->item(0)
+ ->getAttribute('attribute_with_default_value');
+
+ $this->assertEmpty($actualErrors);
+ $this->assertEquals($expectedValue, $actualValue);
+ }
+
+ /**
+ * @return array
+ */
+ public function validateWithDefaultValueDataProvider()
+ {
+ return [
+ 'default_value' => [
+ ' ',
+ 'default_value'
+ ],
+ 'custom_value' => [
+ ' ',
+ 'non_default_value'
+ ],
+ ];
+ }
+
public function testValidateCustomErrorFormat()
{
$xml = ' ';
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/sample.xsd b/lib/internal/Magento/Framework/Config/Test/Unit/_files/sample.xsd
index 1f635b7081e05..701a2eb18c2a1 100644
--- a/lib/internal/Magento/Framework/Config/Test/Unit/_files/sample.xsd
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/sample.xsd
@@ -21,6 +21,7 @@
+
diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
index 90186707177c9..fb62b720e0b1c 100644
--- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
+++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
@@ -2154,7 +2154,6 @@ public function createTable(Table $table)
*/
public function createTemporaryTable(\Magento\Framework\DB\Ddl\Table $table)
{
- $columns = $table->getColumns();
$sqlFragment = array_merge(
$this->_getColumnsDefinition($table),
$this->_getIndexesDefinition($table),
diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php
index ca50cdb2440f4..adcffe01b910e 100644
--- a/lib/internal/Magento/Framework/Locale/Format.php
+++ b/lib/internal/Magento/Framework/Locale/Format.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Framework\Locale;
+/**
+ * Price locale format.
+ */
class Format implements \Magento\Framework\Locale\FormatInterface
{
/**
@@ -38,7 +41,8 @@ public function __construct(
}
/**
- * Returns the first found number from a string
+ * Returns the first found number from a string.
+ *
* Parsing depends on given locale (grouping and decimal)
*
* Examples for input:
@@ -100,7 +104,7 @@ public function getPriceFormat($localeCode = null, $currencyCode = null)
}
$formatter = new \NumberFormatter(
- $localeCode . '@currency=' . $currency->getCode(),
+ $currency->getCode() ? $localeCode . '@currency=' . $currency->getCode() : $localeCode,
\NumberFormatter::CURRENCY
);
$format = $formatter->getPattern();
diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php
index b401da8960f05..d058bfd41ab1a 100644
--- a/lib/internal/Magento/Framework/Locale/Resolver.php
+++ b/lib/internal/Magento/Framework/Locale/Resolver.php
@@ -6,7 +6,12 @@
namespace Magento\Framework\Locale;
use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\DeploymentConfig;
+use Magento\Framework\App\ObjectManager;
+/**
+ * Manages locale config information.
+ */
class Resolver implements ResolverInterface
{
/**
@@ -52,26 +57,34 @@ class Resolver implements ResolverInterface
*/
private $defaultLocalePath;
+ /**
+ * @var DeploymentConfig
+ */
+ private $deploymentConfig;
+
/**
* @param ScopeConfigInterface $scopeConfig
* @param string $defaultLocalePath
* @param string $scopeType
* @param mixed $locale
+ * @param DeploymentConfig|null $deploymentConfig
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
$defaultLocalePath,
$scopeType,
- $locale = null
+ $locale = null,
+ DeploymentConfig $deploymentConfig = null
) {
$this->scopeConfig = $scopeConfig;
$this->defaultLocalePath = $defaultLocalePath;
$this->scopeType = $scopeType;
+ $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->create(DeploymentConfig::class);
$this->setLocale($locale);
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getDefaultLocalePath()
{
@@ -79,7 +92,7 @@ public function getDefaultLocalePath()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setDefaultLocale($locale)
{
@@ -88,12 +101,15 @@ public function setDefaultLocale($locale)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getDefaultLocale()
{
if (!$this->defaultLocale) {
- $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType);
+ $locale = false;
+ if ($this->deploymentConfig->isAvailable() && $this->deploymentConfig->isDbAvailable()) {
+ $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType);
+ }
if (!$locale) {
$locale = self::DEFAULT_LOCALE;
}
@@ -103,7 +119,7 @@ public function getDefaultLocale()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function setLocale($locale = null)
{
@@ -116,7 +132,7 @@ public function setLocale($locale = null)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getLocale()
{
@@ -127,7 +143,7 @@ public function getLocale()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function emulate($scopeId)
{
@@ -147,7 +163,7 @@ public function emulate($scopeId)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function revert()
{
diff --git a/lib/internal/Magento/Framework/Lock/Backend/Cache.php b/lib/internal/Magento/Framework/Lock/Backend/Cache.php
index 61818cbb8c53c..dfe6bbb828352 100644
--- a/lib/internal/Magento/Framework/Lock/Backend/Cache.php
+++ b/lib/internal/Magento/Framework/Lock/Backend/Cache.php
@@ -14,6 +14,11 @@
*/
class Cache implements \Magento\Framework\Lock\LockManagerInterface
{
+ /**
+ * Prefix for marking that key is locked or not.
+ */
+ const LOCK_PREFIX = 'LOCKED_RECORD_INFO_';
+
/**
* @var FrontendInterface
*/
@@ -26,12 +31,13 @@ public function __construct(FrontendInterface $cache)
{
$this->cache = $cache;
}
+
/**
* @inheritdoc
*/
public function lock(string $name, int $timeout = -1): bool
{
- return $this->cache->save('1', $name, [], $timeout);
+ return $this->cache->save('1', $this->getIdentifier($name), [], $timeout);
}
/**
@@ -39,7 +45,7 @@ public function lock(string $name, int $timeout = -1): bool
*/
public function unlock(string $name): bool
{
- return $this->cache->remove($name);
+ return $this->cache->remove($this->getIdentifier($name));
}
/**
@@ -47,6 +53,17 @@ public function unlock(string $name): bool
*/
public function isLocked(string $name): bool
{
- return (bool)$this->cache->test($name);
+ return (bool)$this->cache->test($this->getIdentifier($name));
+ }
+
+ /**
+ * Get cache locked identifier based on cache identifier.
+ *
+ * @param string $cacheIdentifier
+ * @return string
+ */
+ private function getIdentifier(string $cacheIdentifier): string
+ {
+ return self::LOCK_PREFIX . $cacheIdentifier;
}
}
diff --git a/lib/internal/Magento/Framework/Lock/Backend/FileLock.php b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php
new file mode 100644
index 0000000000000..d168e910a4ab7
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php
@@ -0,0 +1,194 @@
+fileDriver = $fileDriver;
+ $this->path = rtrim($path, '/') . '/';
+
+ try {
+ if (!$this->fileDriver->isExists($this->path)) {
+ $this->fileDriver->createDirectory($this->path);
+ }
+ } catch (FileSystemException $exception) {
+ throw new RuntimeException(
+ new Phrase('Cannot create the directory for locks: %1', [$this->path]),
+ $exception
+ );
+ }
+ }
+
+ /**
+ * Acquires a lock by name
+ *
+ * @param string $name The lock name
+ * @param int $timeout Timeout in seconds. A negative timeout value means infinite timeout
+ * @return bool Returns true if the lock is acquired, otherwise returns false
+ * @throws RuntimeException Throws RuntimeException if cannot acquires the lock because FS problems
+ */
+ public function lock(string $name, int $timeout = -1): bool
+ {
+ try {
+ $lockFile = $this->getLockPath($name);
+ $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+');
+ $skipDeadline = $timeout < 0;
+ $deadline = microtime(true) + $timeout;
+
+ while (!$this->tryToLock($fileResource)) {
+ if (!$skipDeadline && $deadline <= microtime(true)) {
+ $this->fileDriver->fileClose($fileResource);
+ return false;
+ }
+ usleep($this->sleepCycle);
+ }
+ } catch (FileSystemException $exception) {
+ throw new RuntimeException(new Phrase('Cannot acquire a lock.'), $exception);
+ }
+
+ $this->locks[$lockFile] = $fileResource;
+ return true;
+ }
+
+ /**
+ * Checks if a lock exists by name
+ *
+ * @param string $name The lock name
+ * @return bool Returns true if the lock exists, otherwise returns false
+ * @throws RuntimeException Throws RuntimeException if cannot check that the lock exists
+ */
+ public function isLocked(string $name): bool
+ {
+ $lockFile = $this->getLockPath($name);
+ $result = false;
+
+ try {
+ if ($this->fileDriver->isExists($lockFile)) {
+ $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+');
+ if ($this->tryToLock($fileResource)) {
+ $result = false;
+ } else {
+ $result = true;
+ }
+ $this->fileDriver->fileClose($fileResource);
+ }
+ } catch (FileSystemException $exception) {
+ throw new RuntimeException(new Phrase('Cannot verify that the lock exists.'), $exception);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Remove the lock by name
+ *
+ * @param string $name The lock name
+ * @return bool If the lock is removed returns true, otherwise returns false
+ */
+ public function unlock(string $name): bool
+ {
+ $lockFile = $this->getLockPath($name);
+
+ if (isset($this->locks[$lockFile]) && $this->tryToUnlock($this->locks[$lockFile])) {
+ unset($this->locks[$lockFile]);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the full path to the lock file by name
+ *
+ * @param string $name The lock name
+ * @return string The path to the lock file
+ */
+ private function getLockPath(string $name): string
+ {
+ return $this->path . $name;
+ }
+
+ /**
+ * Tries to lock a file resource
+ *
+ * @param resource $resource The file resource
+ * @return bool If the lock is acquired returns true, otherwise returns false
+ */
+ private function tryToLock($resource): bool
+ {
+ try {
+ return $this->fileDriver->fileLock($resource, LOCK_EX | LOCK_NB);
+ } catch (FileSystemException $exception) {
+ return false;
+ }
+ }
+
+ /**
+ * Tries to unlock a file resource
+ *
+ * @param resource $resource The file resource
+ * @return bool If the lock is removed returns true, otherwise returns false
+ */
+ private function tryToUnlock($resource): bool
+ {
+ try {
+ return $this->fileDriver->fileLock($resource, LOCK_UN | LOCK_NB);
+ } catch (FileSystemException $exception) {
+ return false;
+ }
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php
new file mode 100644
index 0000000000000..cbba981ae1b51
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php
@@ -0,0 +1,280 @@
+\Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone']];
+
+ /**
+ * The mapping list of the lock name with the full lock path
+ *
+ * @var array
+ */
+ private $locks = [];
+
+ /**
+ * The default path to storage locks
+ */
+ const DEFAULT_PATH = '/magento/locks';
+
+ /**
+ * @param string $host The host to connect to Zookeeper
+ * @param string $path The base path to locks in Zookeeper
+ * @throws RuntimeException
+ */
+ public function __construct(string $host, string $path = self::DEFAULT_PATH)
+ {
+ if (!$path) {
+ throw new RuntimeException(
+ new Phrase('The path needs to be a non-empty string.')
+ );
+ }
+
+ if (!$host) {
+ throw new RuntimeException(
+ new Phrase('The host needs to be a non-empty string.')
+ );
+ }
+
+ $this->host = $host;
+ $this->path = rtrim($path, '/') . '/';
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * You can see the lock algorithm by the link
+ * @link https://zookeeper.apache.org/doc/r3.1.2/recipes.html#sc_recipes_Locks
+ *
+ * @throws RuntimeException
+ */
+ public function lock(string $name, int $timeout = -1): bool
+ {
+ $skipDeadline = $timeout < 0;
+ $lockPath = $this->getFullPathToLock($name);
+ $deadline = microtime(true) + $timeout;
+
+ if (!$this->checkAndCreateParentNode($lockPath)) {
+ throw new RuntimeException(new Phrase('Failed creating the path %1', [$lockPath]));
+ }
+
+ $lockKey = $this->getProvider()
+ ->create($lockPath, '1', $this->acl, \Zookeeper::EPHEMERAL | \Zookeeper::SEQUENCE);
+
+ if (!$lockKey) {
+ throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath]));
+ }
+
+ while ($this->isAnyLock($lockKey, $this->getIndex($lockKey))) {
+ if (!$skipDeadline && $deadline <= microtime(true)) {
+ $this->getProvider()->delete($lockKey);
+ return false;
+ }
+
+ usleep($this->sleepCycle);
+ }
+
+ $this->locks[$name] = $lockKey;
+
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function unlock(string $name): bool
+ {
+ if (!isset($this->locks[$name])) {
+ return false;
+ }
+
+ return $this->getProvider()->delete($this->locks[$name]);
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function isLocked(string $name): bool
+ {
+ return $this->isAnyLock($this->getFullPathToLock($name));
+ }
+
+ /**
+ * Gets full path to lock by its name
+ *
+ * @param string $name
+ * @return string
+ */
+ private function getFullPathToLock(string $name): string
+ {
+ return $this->path . $name . '/' . $this->lockName;
+ }
+
+ /**
+ * Initiolizes and returns Zookeeper provider
+ *
+ * @return \Zookeeper
+ * @throws RuntimeException
+ */
+ private function getProvider(): \Zookeeper
+ {
+ if (!$this->zookeeper) {
+ $this->zookeeper = new \Zookeeper($this->host);
+ }
+
+ $deadline = microtime(true) + $this->connectionTimeout;
+ while ($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) {
+ if ($deadline <= microtime(true)) {
+ throw new RuntimeException(new Phrase('Zookeeper connection timed out!'));
+ }
+ usleep($this->sleepCycle);
+ }
+
+ return $this->zookeeper;
+ }
+
+ /**
+ * Checks and creates base path recursively
+ *
+ * @param string $path
+ * @return bool
+ * @throws RuntimeException
+ */
+ private function checkAndCreateParentNode(string $path): bool
+ {
+ $path = dirname($path);
+ if ($this->getProvider()->exists($path)) {
+ return true;
+ }
+
+ if (!$this->checkAndCreateParentNode($path)) {
+ return false;
+ }
+
+ if ($this->getProvider()->create($path, '1', $this->acl)) {
+ return true;
+ }
+
+ return $this->getProvider()->exists($path);
+ }
+
+ /**
+ * Gets int increment of lock key
+ *
+ * @param string $key
+ * @return int|null
+ */
+ private function getIndex(string $key)
+ {
+ if (!preg_match('/' . $this->lockName . '([0-9]+)$/', $key, $matches)) {
+ return null;
+ }
+
+ return intval($matches[1]);
+ }
+
+ /**
+ * Checks if there is any sequence node under parent of $fullKey.
+ *
+ * At first checks that the $fullKey node is present, if not - returns false.
+ * If $indexKey is non-null and there is a smaller index than $indexKey then returns true,
+ * otherwise returns false.
+ *
+ * @param string $fullKey The full path without any sequence info
+ * @param int|null $indexKey The index to compare
+ * @return bool
+ * @throws RuntimeException
+ */
+ private function isAnyLock(string $fullKey, int $indexKey = null): bool
+ {
+ $parent = dirname($fullKey);
+
+ if (!$this->getProvider()->exists($parent)) {
+ return false;
+ }
+
+ $children = $this->getProvider()->getChildren($parent);
+
+ if (null === $indexKey && !empty($children)) {
+ return true;
+ }
+
+ foreach ($children as $childKey) {
+ $childIndex = $this->getIndex($childKey);
+
+ if (null === $childIndex) {
+ continue;
+ }
+
+ if ($childIndex < $indexKey) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php
new file mode 100644
index 0000000000000..b142085ef6563
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php
@@ -0,0 +1,111 @@
+ DatabaseLock::class,
+ self::LOCK_ZOOKEEPER => ZookeeperLock::class,
+ self::LOCK_CACHE => CacheLock::class,
+ self::LOCK_FILE => FileLock::class,
+ ];
+
+ /**
+ * @param ObjectManagerInterface $objectManager The Object Manager instance
+ * @param DeploymentConfig $deploymentConfig The Application deployment configuration
+ */
+ public function __construct(
+ ObjectManagerInterface $objectManager,
+ DeploymentConfig $deploymentConfig
+ ) {
+ $this->objectManager = $objectManager;
+ $this->deploymentConfig = $deploymentConfig;
+ }
+
+ /**
+ * Creates an instance of LockManagerInterface using information from deployment config
+ *
+ * @return LockManagerInterface
+ * @throws RuntimeException
+ */
+ public function create(): LockManagerInterface
+ {
+ $provider = $this->deploymentConfig->get('lock/provider', self::LOCK_DB);
+ $config = $this->deploymentConfig->get('lock/config', []);
+
+ if (!isset($this->lockers[$provider])) {
+ throw new RuntimeException(new Phrase('Unknown locks provider: %1', [$provider]));
+ }
+
+ if (self::LOCK_ZOOKEEPER === $provider && !extension_loaded(self::LOCK_ZOOKEEPER)) {
+ throw new RuntimeException(new Phrase('php extension Zookeeper is not installed.'));
+ }
+
+ return $this->objectManager->create($this->lockers[$provider], $config);
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/Proxy.php b/lib/internal/Magento/Framework/Lock/Proxy.php
new file mode 100644
index 0000000000000..2718bf6cb3456
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Proxy.php
@@ -0,0 +1,83 @@
+factory = $factory;
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function isLocked(string $name): bool
+ {
+ return $this->getLocker()->isLocked($name);
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function lock(string $name, int $timeout = -1): bool
+ {
+ return $this->getLocker()->lock($name, $timeout);
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @throws RuntimeException
+ */
+ public function unlock(string $name): bool
+ {
+ return $this->getLocker()->unlock($name);
+ }
+
+ /**
+ * Gets LockManagerInterface implementation using Factory
+ *
+ * @return LockManagerInterface
+ * @throws RuntimeException
+ */
+ private function getLocker(): LockManagerInterface
+ {
+ if (!$this->locker) {
+ $this->locker = $this->factory->create();
+ }
+
+ return $this->locker;
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php
new file mode 100644
index 0000000000000..62521b9de3082
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php
@@ -0,0 +1,68 @@
+markTestSkipped('Test was skipped because php extension Zookeeper is not installed.');
+ }
+ }
+
+ /**
+ * @expectedException \Magento\Framework\Exception\RuntimeException
+ * @expectedExceptionMessage The path needs to be a non-empty string.
+ * @return void
+ */
+ public function testConstructionWithPathException()
+ {
+ $this->zookeeperProvider = new ZookeeperProvider($this->host, '');
+ }
+
+ /**
+ * @expectedException \Magento\Framework\Exception\RuntimeException
+ * @expectedExceptionMessage The host needs to be a non-empty string.
+ * @return void
+ */
+ public function testConstructionWithHostException()
+ {
+ $this->zookeeperProvider = new ZookeeperProvider('', $this->path);
+ }
+
+ /**
+ * @return void
+ */
+ public function testConstructionWithoutException()
+ {
+ $this->zookeeperProvider = new ZookeeperProvider($this->host, $this->path);
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php
new file mode 100644
index 0000000000000..ebf2f54f3e093
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php
@@ -0,0 +1,116 @@
+objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class);
+ $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class);
+ $this->factory = new LockBackendFactory($this->objectManagerMock, $this->deploymentConfigMock);
+ }
+
+ /**
+ * @expectedException \Magento\Framework\Exception\RuntimeException
+ * @expectedExceptionMessage Unknown locks provider: someProvider
+ */
+ public function testCreateWithException()
+ {
+ $this->deploymentConfigMock->expects($this->exactly(2))
+ ->method('get')
+ ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []])
+ ->willReturnOnConsecutiveCalls('someProvider', []);
+
+ $this->factory->create();
+ }
+
+ /**
+ * @param string $lockProvider
+ * @param string $lockProviderClass
+ * @param array $config
+ * @dataProvider createDataProvider
+ */
+ public function testCreate(string $lockProvider, string $lockProviderClass, array $config)
+ {
+ $lockManagerMock = $this->getMockForAbstractClass(LockManagerInterface::class);
+ $this->deploymentConfigMock->expects($this->exactly(2))
+ ->method('get')
+ ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []])
+ ->willReturnOnConsecutiveCalls($lockProvider, $config);
+ $this->objectManagerMock->expects($this->once())
+ ->method('create')
+ ->with($lockProviderClass, $config)
+ ->willReturn($lockManagerMock);
+
+ $this->assertSame($lockManagerMock, $this->factory->create());
+ }
+
+ /**
+ * @return array
+ */
+ public function createDataProvider(): array
+ {
+ $data = [
+ 'db' => [
+ 'lockProvider' => LockBackendFactory::LOCK_DB,
+ 'lockProviderClass' => DatabaseLock::class,
+ 'config' => ['prefix' => 'somePrefix'],
+ ],
+ 'cache' => [
+ 'lockProvider' => LockBackendFactory::LOCK_CACHE,
+ 'lockProviderClass' => CacheLock::class,
+ 'config' => [],
+ ],
+ 'file' => [
+ 'lockProvider' => LockBackendFactory::LOCK_FILE,
+ 'lockProviderClass' => FileLock::class,
+ 'config' => ['path' => '/my/path'],
+ ],
+ ];
+
+ if (extension_loaded('zookeeper')) {
+ $data['zookeeper'] = [
+ 'lockProvider' => LockBackendFactory::LOCK_ZOOKEEPER,
+ 'lockProviderClass' => ZookeeperLock::class,
+ 'config' => ['host' => 'some host'],
+ ];
+ }
+
+ return $data;
+ }
+}
diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php
new file mode 100644
index 0000000000000..c71dad701d715
--- /dev/null
+++ b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php
@@ -0,0 +1,106 @@
+factoryMock = $this->createMock(LockBackendFactory::class);
+ $this->lockerMock = $this->getMockForAbstractClass(LockManagerInterface::class);
+ $this->proxy = new Proxy($this->factoryMock);
+ }
+
+ /**
+ * @return void
+ */
+ public function testIsLocked()
+ {
+ $lockName = 'testLock';
+ $this->factoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->lockerMock);
+ $this->lockerMock->expects($this->exactly(2))
+ ->method('isLocked')
+ ->with($lockName)
+ ->willReturn(true);
+
+ $this->assertTrue($this->proxy->isLocked($lockName));
+
+ // Call one more time to check that method Factory::create is called one time
+ $this->assertTrue($this->proxy->isLocked($lockName));
+ }
+
+ /**
+ * @return void
+ */
+ public function testLock()
+ {
+ $lockName = 'testLock';
+ $timeout = 123;
+ $this->factoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->lockerMock);
+ $this->lockerMock->expects($this->exactly(2))
+ ->method('lock')
+ ->with($lockName, $timeout)
+ ->willReturn(true);
+
+ $this->assertTrue($this->proxy->lock($lockName, $timeout));
+
+ // Call one more time to check that method Factory::create is called one time
+ $this->assertTrue($this->proxy->lock($lockName, $timeout));
+ }
+
+ /**
+ * @return void
+ */
+ public function testUnlock()
+ {
+ $lockName = 'testLock';
+ $this->factoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->lockerMock);
+ $this->lockerMock->expects($this->exactly(2))
+ ->method('unlock')
+ ->with($lockName)
+ ->willReturn(true);
+
+ $this->assertTrue($this->proxy->unlock($lockName));
+
+ // Call one more time to check that method Factory::create is called one time
+ $this->assertTrue($this->proxy->unlock($lockName));
+ }
+}
diff --git a/lib/internal/Magento/Framework/MessageQueue/CallbackInvoker.php b/lib/internal/Magento/Framework/MessageQueue/CallbackInvoker.php
index cb80bc4becaec..48c33c48f12e6 100644
--- a/lib/internal/Magento/Framework/MessageQueue/CallbackInvoker.php
+++ b/lib/internal/Magento/Framework/MessageQueue/CallbackInvoker.php
@@ -9,7 +9,7 @@
/**
* Class CallbackInvoker to invoke callbacks for consumer classes
*/
-class CallbackInvoker
+class CallbackInvoker implements CallbackInvokerInterface
{
/**
* Run short running process
diff --git a/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php b/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php
new file mode 100644
index 0000000000000..36658f2e4eebe
--- /dev/null
+++ b/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php
@@ -0,0 +1,24 @@
+getMockForAbstractClass();
$configuration->expects($this->atLeastOnce())->method('getHandlers')->willReturn([]);
$this->messageStatusProcessor->expects($this->exactly(2))->method('acknowledgeMessages');
- $mergedMessage = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
+ $mergedMessage = $this->getMockBuilder(\Magento\Framework\Api\CustomAttributesDataInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();
$message = $this->getMockBuilder(\Magento\Framework\MessageQueue\EnvelopeInterface::class)
@@ -116,7 +116,7 @@ public function testProcessWithConnectionLostException()
$exception = new \Magento\Framework\MessageQueue\ConnectionLostException(__('Exception Message'));
$configuration->expects($this->atLeastOnce())->method('getHandlers')->willThrowException($exception);
$this->messageStatusProcessor->expects($this->once())->method('acknowledgeMessages');
- $mergedMessage = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
+ $mergedMessage = $this->getMockBuilder(\Magento\Framework\Api\CustomAttributesDataInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();
$message = $this->getMockBuilder(\Magento\Framework\MessageQueue\EnvelopeInterface::class)
@@ -158,7 +158,7 @@ public function testProcessWithException()
$configuration->expects($this->atLeastOnce())->method('getHandlers')->willThrowException($exception);
$this->messageStatusProcessor->expects($this->once())->method('acknowledgeMessages');
$this->messageStatusProcessor->expects($this->atLeastOnce())->method('rejectMessages');
- $mergedMessage = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
+ $mergedMessage = $this->getMockBuilder(\Magento\Framework\Api\CustomAttributesDataInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();
$message = $this->getMockBuilder(\Magento\Framework\MessageQueue\EnvelopeInterface::class)
diff --git a/lib/internal/Magento/Framework/Oauth/Oauth.php b/lib/internal/Magento/Framework/Oauth/Oauth.php
index 5e48fb5ed30f9..919b0e4c86ba0 100644
--- a/lib/internal/Magento/Framework/Oauth/Oauth.php
+++ b/lib/internal/Magento/Framework/Oauth/Oauth.php
@@ -9,6 +9,9 @@
use Magento\Framework\Encryption\Helper\Security;
use Magento\Framework\Phrase;
+/**
+ * Authorization service.
+ */
class Oauth implements OauthInterface
{
/**
@@ -61,7 +64,7 @@ public static function getSupportedSignatureMethods()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getRequestToken($params, $requestUrl, $httpMethod = 'POST')
{
@@ -74,7 +77,7 @@ public function getRequestToken($params, $requestUrl, $httpMethod = 'POST')
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function getAccessToken($params, $requestUrl, $httpMethod = 'POST')
{
@@ -102,7 +105,7 @@ public function getAccessToken($params, $requestUrl, $httpMethod = 'POST')
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function validateAccessTokenRequest($params, $requestUrl, $httpMethod = 'POST')
{
@@ -125,7 +128,7 @@ public function validateAccessTokenRequest($params, $requestUrl, $httpMethod = '
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function validateAccessToken($accessToken)
{
@@ -133,7 +136,7 @@ public function validateAccessToken($accessToken)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function buildAuthorizationHeader(
$params,
@@ -199,7 +202,7 @@ protected function _validateSignature($params, $consumerSecret, $httpMethod, $re
);
if (!Security::compareStrings($calculatedSign, $params['oauth_signature'])) {
- throw new Exception(new Phrase('The signatire is invalid. Verify and try again.'));
+ throw new Exception(new Phrase('The signature is invalid. Verify and try again.'));
}
}
diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php
index f4d83ece134cf..e1b423d738a20 100644
--- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php
+++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php
@@ -7,6 +7,7 @@
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Ddl\Table;
+use Magento\Framework\DB\Select;
use Magento\Framework\Search\Adapter\Mysql\Aggregation\Builder as AggregationBuilder;
use Magento\Framework\Search\AdapterInterface;
use Magento\Framework\Search\RequestInterface;
@@ -49,6 +50,16 @@ class Adapter implements AdapterInterface
*/
private $temporaryStorageFactory;
+ /**
+ * Query Select Parts to be skipped when prepare query for count
+ *
+ * @var array
+ */
+ private $countSqlSkipParts = [
+ \Magento\Framework\DB\Select::LIMIT_COUNT => true,
+ \Magento\Framework\DB\Select::LIMIT_OFFSET => true,
+ ];
+
/**
* @param Mapper $mapper
* @param ResponseFactory $responseFactory
@@ -86,7 +97,7 @@ public function query(RequestInterface $request)
$response = [
'documents' => $documents,
'aggregations' => $aggregations,
- 'total' => count($documents)
+ 'total' => $this->getSize($query)
];
return $this->responseFactory->create($response);
}
@@ -115,4 +126,39 @@ private function getConnection()
{
return $this->resource->getConnection();
}
+
+ /**
+ * Get rows size
+ *
+ * @param Select $query
+ * @return int
+ */
+ private function getSize(Select $query): int
+ {
+ $sql = $this->getSelectCountSql($query);
+ $parentSelect = $this->getConnection()->select();
+ $parentSelect->from(['core_select' => $sql]);
+ $parentSelect->reset(\Magento\Framework\DB\Select::COLUMNS);
+ $parentSelect->columns('COUNT(*)');
+ $totalRecords = $this->getConnection()->fetchOne($parentSelect);
+
+ return intval($totalRecords);
+ }
+
+ /**
+ * Reset limit and offset
+ *
+ * @param Select $query
+ * @return Select
+ */
+ private function getSelectCountSql(Select $query): Select
+ {
+ foreach ($this->countSqlSkipParts as $part => $toSkip) {
+ if ($toSkip) {
+ $query->reset($part);
+ }
+ }
+
+ return $query;
+ }
}
diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/AdapterTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/AdapterTest.php
index a35e1fd8b6151..fbb56361bfe71 100644
--- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/AdapterTest.php
+++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/AdapterTest.php
@@ -161,10 +161,16 @@ public function testQuery()
$select = $this->getMockBuilder(\Magento\Framework\DB\Select::class)
->disableOriginalConstructor()
->getMock();
- $this->connectionAdapter->expects($this->once())
+
+ $this->connectionAdapter->expects($this->exactly(2))
->method('select')
->willReturn($select);
+ $this->connectionAdapter->expects($this->once())
+ ->method('fetchOne')
+ ->with($select)
+ ->willReturn($selectResult['total']);
+
$table = $this->getMockBuilder(\Magento\Framework\DB\Ddl\Table::class)
->disableOriginalConstructor()
->getMock();
diff --git a/lib/internal/Magento/Framework/Setup/OldDbValidator.php b/lib/internal/Magento/Framework/Setup/OldDbValidator.php
index 4c224a6c713ef..018b010e8fe4a 100644
--- a/lib/internal/Magento/Framework/Setup/OldDbValidator.php
+++ b/lib/internal/Magento/Framework/Setup/OldDbValidator.php
@@ -13,7 +13,7 @@
/**
* Old Validator for database
*
- * Used in order to support backward compatability of modules that are installed
+ * Used in order to support backward compatibility of modules that are installed
* in old way (with Install/Upgrade Schema/Data scripts)
*/
class OldDbValidator implements UpToDateValidatorInterface
diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php
index 335006555d2f1..6c4746d8218ea 100644
--- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php
+++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php
@@ -3,9 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Framework\View\Element;
+use Magento\Framework\Cache\LockGuardedCacheLoader;
use Magento\Framework\DataObject\IdentityInterface;
+use Magento\Framework\App\ObjectManager;
/**
* Base class for all blocks.
@@ -175,14 +178,23 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl
*/
protected $_cache;
+ /**
+ * @var LockGuardedCacheLoader
+ */
+ private $lockQuery;
+
/**
* Constructor
*
* @param \Magento\Framework\View\Element\Context $context
* @param array $data
+ * @param LockGuardedCacheLoader|null $lockQuery
*/
- public function __construct(\Magento\Framework\View\Element\Context $context, array $data = [])
- {
+ public function __construct(
+ \Magento\Framework\View\Element\Context $context,
+ array $data = [],
+ LockGuardedCacheLoader $lockQuery = null
+ ) {
$this->_request = $context->getRequest();
$this->_layout = $context->getLayout();
$this->_eventManager = $context->getEventManager();
@@ -204,6 +216,8 @@ public function __construct(\Magento\Framework\View\Element\Context $context, ar
$this->jsLayout = $data['jsLayout'];
unset($data['jsLayout']);
}
+ $this->lockQuery = $lockQuery
+ ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class);
parent::__construct($data);
$this->_construct();
}
@@ -658,19 +672,6 @@ public function toHtml()
}
$html = $this->_loadCache();
- if ($html === false) {
- if ($this->hasData('translate_inline')) {
- $this->inlineTranslation->suspend($this->getData('translate_inline'));
- }
-
- $this->_beforeToHtml();
- $html = $this->_toHtml();
- $this->_saveCache($html);
-
- if ($this->hasData('translate_inline')) {
- $this->inlineTranslation->resume();
- }
- }
$html = $this->_afterToHtml($html);
/** @var \Magento\Framework\DataObject */
@@ -1083,23 +1084,54 @@ protected function getCacheLifetime()
/**
* Load block html from cache storage
*
- * @return string|false
+ * @return string
*/
protected function _loadCache()
{
+ $collectAction = function () {
+ if ($this->hasData('translate_inline')) {
+ $this->inlineTranslation->suspend($this->getData('translate_inline'));
+ }
+
+ $this->_beforeToHtml();
+ return $this->_toHtml();
+ };
+
if ($this->getCacheLifetime() === null || !$this->_cacheState->isEnabled(self::CACHE_GROUP)) {
- return false;
- }
- $cacheKey = $this->getCacheKey();
- $cacheData = $this->_cache->load($cacheKey);
- if ($cacheData) {
- $cacheData = str_replace(
- $this->_getSidPlaceholder($cacheKey),
- $this->_sidResolver->getSessionIdQueryParam($this->_session) . '=' . $this->_session->getSessionId(),
- $cacheData
- );
+ $html = $collectAction();
+ if ($this->hasData('translate_inline')) {
+ $this->inlineTranslation->resume();
+ }
+ return $html;
}
- return $cacheData;
+ $loadAction = function () {
+ $cacheKey = $this->getCacheKey();
+ $cacheData = $this->_cache->load($cacheKey);
+ if ($cacheData) {
+ $cacheData = str_replace(
+ $this->_getSidPlaceholder($cacheKey),
+ $this->_sidResolver->getSessionIdQueryParam($this->_session)
+ . '='
+ . $this->_session->getSessionId(),
+ $cacheData
+ );
+ }
+ return $cacheData;
+ };
+
+ $saveAction = function ($data) {
+ $this->_saveCache($data);
+ if ($this->hasData('translate_inline')) {
+ $this->inlineTranslation->resume();
+ }
+ };
+
+ return (string)$this->lockQuery->lockedLoadData(
+ $this->getCacheKey(),
+ $loadAction,
+ $collectAction,
+ $saveAction
+ );
}
/**
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php
index 5f7508438a6ed..dba775ea894f4 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php
@@ -6,6 +6,7 @@
namespace Magento\Framework\View\Test\Unit\Element;
+use Magento\Framework\Cache\LockGuardedCacheLoader;
use Magento\Framework\View\Element\AbstractBlock;
use Magento\Framework\View\Element\Context;
use Magento\Framework\Config\View;
@@ -13,7 +14,6 @@
use Magento\Framework\Event\ManagerInterface as EventManagerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Cache\StateInterface as CacheStateInterface;
-use Magento\Framework\App\CacheInterface;
use Magento\Framework\Session\SidResolverInterface;
use Magento\Framework\Session\SessionManagerInterface;
@@ -42,11 +42,6 @@ class AbstractBlockTest extends \PHPUnit\Framework\TestCase
*/
private $cacheStateMock;
- /**
- * @var CacheInterface|\PHPUnit_Framework_MockObject_MockObject
- */
- private $cacheMock;
-
/**
* @var SidResolverInterface|\PHPUnit_Framework_MockObject_MockObject
*/
@@ -57,6 +52,11 @@ class AbstractBlockTest extends \PHPUnit\Framework\TestCase
*/
private $sessionMock;
+ /**
+ * @var LockGuardedCacheLoader|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $lockQuery;
+
/**
* @return void
*/
@@ -65,7 +65,10 @@ protected function setUp()
$this->eventManagerMock = $this->getMockForAbstractClass(EventManagerInterface::class);
$this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class);
$this->cacheStateMock = $this->getMockForAbstractClass(CacheStateInterface::class);
- $this->cacheMock = $this->getMockForAbstractClass(CacheInterface::class);
+ $this->lockQuery = $this->getMockBuilder(LockGuardedCacheLoader::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['lockedLoadData'])
+ ->getMockForAbstractClass();
$this->sidResolverMock = $this->getMockForAbstractClass(SidResolverInterface::class);
$this->sessionMock = $this->getMockForAbstractClass(SessionManagerInterface::class);
$contextMock = $this->createMock(Context::class);
@@ -78,9 +81,6 @@ protected function setUp()
$contextMock->expects($this->once())
->method('getCacheState')
->willReturn($this->cacheStateMock);
- $contextMock->expects($this->once())
- ->method('getCache')
- ->willReturn($this->cacheMock);
$contextMock->expects($this->once())
->method('getSidResolver')
->willReturn($this->sidResolverMock);
@@ -89,7 +89,11 @@ protected function setUp()
->willReturn($this->sessionMock);
$this->block = $this->getMockForAbstractClass(
AbstractBlock::class,
- ['context' => $contextMock]
+ [
+ 'context' => $contextMock,
+ 'data' => [],
+ 'lockQuery' => $this->lockQuery
+ ]
);
}
@@ -219,10 +223,7 @@ public function testToHtmlWhenModuleIsDisabled()
/**
* @param string|bool $cacheLifetime
* @param string|bool $dataFromCache
- * @param string $dataForSaveCache
* @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectsDispatchEvent
- * @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectsCacheLoad
- * @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectsCacheSave
* @param string $expectedResult
* @return void
* @dataProvider getCacheLifetimeDataProvider
@@ -230,10 +231,7 @@ public function testToHtmlWhenModuleIsDisabled()
public function testGetCacheLifetimeViaToHtml(
$cacheLifetime,
$dataFromCache,
- $dataForSaveCache,
$expectsDispatchEvent,
- $expectsCacheLoad,
- $expectsCacheSave,
$expectedResult
) {
$moduleName = 'Test';
@@ -252,13 +250,9 @@ public function testGetCacheLifetimeViaToHtml(
->method('isEnabled')
->with(AbstractBlock::CACHE_GROUP)
->willReturn(true);
- $this->cacheMock->expects($expectsCacheLoad)
- ->method('load')
- ->with(AbstractBlock::CACHE_KEY_PREFIX . $cacheKey)
+ $this->lockQuery->expects($this->any())
+ ->method('lockedLoadData')
->willReturn($dataFromCache);
- $this->cacheMock->expects($expectsCacheSave)
- ->method('save')
- ->with($dataForSaveCache, AbstractBlock::CACHE_KEY_PREFIX . $cacheKey);
$this->sidResolverMock->expects($this->any())
->method('getSessionIdQueryParam')
->with($this->sessionMock)
@@ -279,46 +273,31 @@ public function getCacheLifetimeDataProvider()
[
'cacheLifetime' => null,
'dataFromCache' => 'dataFromCache',
- 'dataForSaveCache' => '',
'expectsDispatchEvent' => $this->exactly(2),
- 'expectsCacheLoad' => $this->never(),
- 'expectsCacheSave' => $this->never(),
'expectedResult' => '',
],
[
'cacheLifetime' => false,
'dataFromCache' => 'dataFromCache',
- 'dataForSaveCache' => '',
'expectsDispatchEvent' => $this->exactly(2),
- 'expectsCacheLoad' => $this->never(),
- 'expectsCacheSave' => $this->never(),
'expectedResult' => '',
],
[
'cacheLifetime' => 120,
'dataFromCache' => 'dataFromCache',
- 'dataForSaveCache' => '',
'expectsDispatchEvent' => $this->exactly(2),
- 'expectsCacheLoad' => $this->once(),
- 'expectsCacheSave' => $this->never(),
'expectedResult' => 'dataFromCache',
],
[
'cacheLifetime' => '120string',
'dataFromCache' => 'dataFromCache',
- 'dataForSaveCache' => '',
'expectsDispatchEvent' => $this->exactly(2),
- 'expectsCacheLoad' => $this->once(),
- 'expectsCacheSave' => $this->never(),
'expectedResult' => 'dataFromCache',
],
[
'cacheLifetime' => 120,
'dataFromCache' => false,
- 'dataForSaveCache' => '',
'expectsDispatchEvent' => $this->exactly(2),
- 'expectsCacheLoad' => $this->once(),
- 'expectsCacheSave' => $this->once(),
'expectedResult' => '',
],
];
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php
index b911a38dbb488..4c76087bfea12 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php
@@ -3,10 +3,18 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Framework\View\Test\Unit\Element\Html;
class LinkTest extends \PHPUnit\Framework\TestCase
{
+ private $objectManager;
+
+ protected function setUp()
+ {
+ $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ }
+
/**
* @var array
*/
@@ -24,24 +32,8 @@ class LinkTest extends \PHPUnit\Framework\TestCase
*/
protected $link;
- /**
- * @param \Magento\Framework\View\Element\Html\Link $link
- * @param string $expected
- *
- * @dataProvider getLinkAttributesDataProvider
- */
- public function testGetLinkAttributes($link, $expected)
- {
- $this->assertEquals($expected, $link->getLinkAttributes());
- }
-
- /**
- * @return array
- */
- public function getLinkAttributesDataProvider()
+ public function testGetLinkAttributes()
{
- $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
-
$escaperMock = $this->getMockBuilder(\Magento\Framework\Escaper::class)
->setMethods(['escapeHtml'])->disableOriginalConstructor()->getMock();
@@ -54,13 +46,19 @@ public function getLinkAttributesDataProvider()
$urlBuilderMock->expects($this->any())
->method('getUrl')
- ->will($this->returnArgument('http://site.com/link.html'));
+ ->willReturn('http://site.com/link.html');
$validtorMock = $this->getMockBuilder(\Magento\Framework\View\Element\Template\File\Validator::class)
->setMethods(['isValid'])->disableOriginalConstructor()->getMock();
+ $validtorMock->expects($this->any())
+ ->method('isValid')
+ ->willReturn(false);
$scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config::class)
->setMethods(['isSetFlag'])->disableOriginalConstructor()->getMock();
+ $scopeConfigMock->expects($this->any())
+ ->method('isSetFlag')
+ ->willReturn(true);
$resolverMock = $this->getMockBuilder(\Magento\Framework\View\Element\Template\File\Resolver::class)
->setMethods([])->disableOriginalConstructor()->getMock();
@@ -72,48 +70,48 @@ public function getLinkAttributesDataProvider()
$contextMock->expects($this->any())
->method('getValidator')
- ->will($this->returnValue($validtorMock));
+ ->willReturn($validtorMock);
$contextMock->expects($this->any())
->method('getResolver')
- ->will($this->returnValue($resolverMock));
+ ->willReturn($resolverMock);
$contextMock->expects($this->any())
->method('getEscaper')
- ->will($this->returnValue($escaperMock));
+ ->willReturn($escaperMock);
$contextMock->expects($this->any())
->method('getUrlBuilder')
- ->will($this->returnValue($urlBuilderMock));
+ ->willReturn($urlBuilderMock);
$contextMock->expects($this->any())
->method('getScopeConfig')
- ->will($this->returnValue($scopeConfigMock));
+ ->willReturn($scopeConfigMock);
/** @var \Magento\Framework\View\Element\Html\Link $linkWithAttributes */
- $linkWithAttributes = $objectManagerHelper->getObject(
+ $linkWithAttributes = $this->objectManager->getObject(
\Magento\Framework\View\Element\Html\Link::class,
['context' => $contextMock]
);
+
+ $this->assertEquals(
+ 'href="http://site.com/link.html"',
+ $linkWithAttributes->getLinkAttributes()
+ );
+
/** @var \Magento\Framework\View\Element\Html\Link $linkWithoutAttributes */
- $linkWithoutAttributes = $objectManagerHelper->getObject(
+ $linkWithoutAttributes = $this->objectManager->getObject(
\Magento\Framework\View\Element\Html\Link::class,
['context' => $contextMock]
);
-
foreach ($this->allowedAttributes as $attribute) {
- $linkWithAttributes->setDataUsingMethod($attribute, $attribute);
+ $linkWithoutAttributes->setDataUsingMethod($attribute, $attribute);
}
- return [
- 'full' => [
- 'link' => $linkWithAttributes,
- 'expected' => 'shape="shape" tabindex="tabindex" onfocus="onfocus" onblur="onblur" id="id"',
- ],
- 'empty' => [
- 'link' => $linkWithoutAttributes,
- 'expected' => '',
- ],
- ];
+ $this->assertEquals(
+ 'href="http://site.com/link.html" shape="shape" tabindex="tabindex"'
+ . ' onfocus="onfocus" onblur="onblur" id="id"',
+ $linkWithoutAttributes->getLinkAttributes()
+ );
}
}
diff --git a/lib/web/css/source/lib/_resets.less b/lib/web/css/source/lib/_resets.less
index 4499c314ce6ca..08d16842b849c 100644
--- a/lib/web/css/source/lib/_resets.less
+++ b/lib/web/css/source/lib/_resets.less
@@ -105,13 +105,6 @@
.lib-css(box-shadow, @focus__box-shadow);
}
}
-
- input[type="radio"],
- input[type="checkbox"] {
- &:focus {
- box-shadow: none;
- }
- }
}
//
diff --git a/lib/web/mage/adminhtml/globals.js b/lib/web/mage/adminhtml/globals.js
index 12c97fdfcd2c5..683606e576497 100644
--- a/lib/web/mage/adminhtml/globals.js
+++ b/lib/web/mage/adminhtml/globals.js
@@ -12,7 +12,7 @@ define([
/**
* Set of a temporary methods used to provide
- * backward compatability with a legacy code.
+ * backward compatibility with a legacy code.
*/
window.setLocation = function (url) {
window.location.href = url;
diff --git a/lib/web/mage/backend/floating-header.js b/lib/web/mage/backend/floating-header.js
index 06861277559a4..a6f767259488a 100644
--- a/lib/web/mage/backend/floating-header.js
+++ b/lib/web/mage/backend/floating-header.js
@@ -48,6 +48,7 @@ define([
this.element.wrapInner($('
', {
'class': 'page-actions-inner', 'data-title': title
}));
+ this.element.removeClass('floating-header');
},
/**
diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx
index 765d0a616f77c..0e1860405e946 100644
--- a/setup/performance-toolkit/benchmark.jmx
+++ b/setup/performance-toolkit/benchmark.jmx
@@ -27267,843 +27267,6 @@ if (testLabel
mpaf/tool/fragments/_system/thread_group.jmx
-
- 1
- false
- 1
- ${productGridMassActionPercentage}
- mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
-
-
-
-var testLabel = "${testLabel}" ? " (${testLabel})" : "";
-if (testLabel
- && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy'
-) {
- if (sampler.getName().indexOf(testLabel) == -1) {
- sampler.setName(sampler.getName() + testLabel);
- }
-} else if (sampler.getName().indexOf("SetUp - ") == -1) {
- sampler.setName("SetUp - " + sampler.getName());
-}
-
- javascript
- mpaf/tool/fragments/_system/setup_label.jmx
-
-
-
- vars.put("testLabel", "Product Grid Mass Actions");
-
- true
-
-
-
-
-
- function getFormKeyFromResponse()
- {
- var url = prev.getUrlAsString(),
- responseCode = prev.getResponseCode(),
- formKey = null;
- searchPattern = /var FORM_KEY = '(.+)'/;
- if (responseCode == "200" && url) {
- response = prev.getResponseDataAsString();
- formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null;
- }
- return formKey;
- }
-
- formKey = vars.get("form_key_storage");
-
- currentFormKey = getFormKeyFromResponse();
-
- if (currentFormKey != null && currentFormKey != formKey) {
- vars.put("form_key_storage", currentFormKey);
- }
-
- javascript
- mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx
-
-
-
- formKey = vars.get("form_key_storage");
- if (formKey
- && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy'
- && sampler.getMethod() == "POST")
- {
- arguments = sampler.getArguments();
- for (i=0; i<arguments.getArgumentCount(); i++)
- {
- argument = arguments.getArgument(i);
- if (argument.getName() == 'form_key' && argument.getValue() != formKey) {
- log.info("admin form key updated: " + argument.getValue() + " => " + formKey);
- argument.setValue(formKey);
- }
- }
- }
-
- javascript
-
-
-
-
-
- false
- mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx
-
-
-
- mpaf/tool/fragments/ce/simple_controller.jmx
-
-
-
- get-admin-email
- mpaf/tool/fragments/ce/lock_controller.jmx
-
-
-
- mpaf/tool/fragments/ce/get_admin_email.jmx
-
-adminUserList = props.get("adminUserList");
-adminUserListIterator = props.get("adminUserListIterator");
-adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool"));
-
-if (adminUsersDistribution == 1) {
- adminUser = adminUserList.poll();
-} else {
- if (!adminUserListIterator.hasNext()) {
- adminUserListIterator = adminUserList.descendingIterator();
- }
-
- adminUser = adminUserListIterator.next();
-}
-
-if (adminUser == null) {
- SampleResult.setResponseMessage("adminUser list is empty");
- SampleResult.setResponseData("adminUser list is empty","UTF-8");
- IsSuccess=false;
- SampleResult.setSuccessful(false);
- SampleResult.setStopThread(true);
-}
-vars.put("admin_user", adminUser);
-
-
-
- true
-
-
-
-
-
-
-
-
-
-
- 60000
- 200000
- ${request_protocol}
-
- ${base_path}${admin_path}/admin/
- GET
- true
- false
- true
- false
- false
-
- mpaf/tool/fragments/ce/admin_login/admin_login.jmx
-
-
-
- Welcome
- <title>Magento Admin</title>
-
- Assertion.response_data
- false
- 2
-
-
-
- false
- admin_form_key
- <input name="form_key" type="hidden" value="([^'"]+)" />
- $1$
-
- 1
-
-
-
-
- ^.+$
-
- Assertion.response_data
- false
- 1
- variable
- admin_form_key
-
-
-
-
-
-
-
-
- true
-
- =
- true
- dummy
-
-
- true
- ${admin_form_key}
- =
- true
- form_key
-
-
- true
- ${admin_password}
- =
- true
- login[password]
-
-
- true
- ${admin_user}
- =
- true
- login[username]
-
-
-
-
-
- 60000
- 200000
- ${request_protocol}
-
- ${base_path}${admin_path}/admin/dashboard/
- POST
- true
- false
- true
- false
- Java
- false
-
- mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx
-
-
-
- false
- admin_form_key
- <input name="form_key" type="hidden" value="([^'"]+)" />
- $1$
-
- 1
- mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx
-
-
-
-
-
- mpaf/tool/fragments/ce/simple_controller.jmx
-
-
-
-
-
-
- true
- ${admin_form_key}
- =
- true
- form_key
-
-
- true
- product_listing
- =
- true
- namespace
- true
-
-
- true
-
- =
- true
- search
- true
-
-
- true
- true
- =
- true
- filters[placeholder]
- true
-
-
- true
- 20
- =
- true
- paging[pageSize]
- true
-
-
- true
- 1
- =
- true
- paging[current]
- true
-
-
- true
- entity_id
- =
- true
- sorting[field]
- true
-
-
- true
- asc
- =
- true
- sorting[direction]
- true
-
-
- true
- true
- =
- true
- isAjax
- true
-
-
-
-
-
- 60000
- 200000
- ${request_protocol}
-
- ${base_path}${admin_path}/mui/index/render/
- GET
- true
- false
- true
- false
- false
-
- mpaf/tool/fragments/ce/admin_browse_products_grid/get_product_pages_count.jmx
-
-
- $.totalRecords
- 0
- true
- false
- true
-
-
-
- products_number
- $.totalRecords
-
-
- BODY
-
-
-
- false
-
-
- var productsPageSize = Integer.parseInt(vars.get("products_page_size"));
-var productsTotal = Integer.parseInt(vars.get("products_number"));
-var pageCountProducts = Math.round(productsTotal/productsPageSize);
-
-vars.put("pages_count_product", String.valueOf(pageCountProducts));
-
-
-
-
-
-
-import java.util.Random;
-Random random = new Random();
-if (${seedForRandom} > 0) {
-random.setSeed(${seedForRandom});
-}
-var productsPageSize = Integer.parseInt(vars.get("products_page_size"));
-var totalNumberOfPages = Integer.parseInt(vars.get("pages_count_product"));
-
-// Randomly select a page.
-var randomProductsPage = random.nextInt(totalNumberOfPages) + 1;
-
-// Get the first and last product id on that page.
-var lastProductIdOnPage = randomProductsPage * productsPageSize;
-var firstProductIdOnPage = lastProductIdOnPage - productsPageSize + 1;
-
-var randomProductId1 = Math.floor(random.nextInt(productsPageSize)) + firstProductIdOnPage;
-var randomProductId2 = Math.floor(random.nextInt(productsPageSize)) + firstProductIdOnPage;
-var randomProductId3 = Math.floor(random.nextInt(productsPageSize)) + firstProductIdOnPage;
-
-vars.put("page_number", String.valueOf(randomProductsPage));
-vars.put("productId1", String.valueOf(randomProductId1));
-vars.put("productId2", String.valueOf(randomProductId2));
-vars.put("productId3", String.valueOf(randomProductId3));
-
-var randomQuantity = random.nextInt(1000) + 1;
-var randomPrice = random.nextInt(500) + 10;
-var randomVisibility = random.nextInt(4) + 1;
-
-vars.put("quantity", String.valueOf(randomQuantity));
-vars.put("price", String.valueOf(randomPrice));
-vars.put("visibility", String.valueOf(randomVisibility));
-
-
-
- false
- mpaf/tool/fragments/ce/admin_browse_products_grid/products_grid_mass_actions/setup.jmx
-
-
-
-
-
-
- true
- ${admin_form_key}
- =
- true
- form_key
- true
-
-
- true
- product_listing
- =
- true
- namespace
- true
-
-
- true
-
- =
- true
- search
- true
-
-
- true
- true
- =
- true
- filters[placeholder]
- true
-
-
- true
- ${products_page_size}
- =
- true
- paging[pageSize]
- true
-
-
- true
- ${page_number}
- =
- true
- paging[current]
- true
-
-
- true
- entity_id
- =
- true
- sorting[field]
- true
-
-
- true
- asc
- =
- true
- sorting[direction]
- true
-
-
- true
- true
- =
- true
- isAjax
- true
-
-
-
-
-
- 60000
- 200000
- ${request_protocol}
-
- ${base_path}${admin_path}/mui/index/render/
- GET
- true
- false
- true
- false
- false
-
- mpaf/tool/fragments/ce/admin_browse_products_grid/products_grid_mass_actions/display_grid.jmx
-
-
-
- totalRecords
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- true
- ${productId1}
- =
- true
- selected[0]
-
-
- true
- ${productId2}
- =
- true
- selected[1]
-
-
- true
- ${productId3}
- =
- true
- selected[2]
- true
-
-
- true
- true
- =
- true
- filters[placeholder]
- false
-
-
- true
- ${admin_form_key}
- =
- true
- form_key
- false
-
-
- true
- product_listing
- =
- true
- namespace
- false
-
-
-
-
-
- 60000
- 200000
- ${request_protocol}
-
- ${base_path}${admin_path}/catalog/product_action_attribute/edit
- GET
- true
- false
- true
- false
- false
-
- mpaf/tool/fragments/ce/admin_browse_products_grid/products_grid_mass_actions/display_update_attributes.jmx
-
-
-
- Update Attributes
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
- true
- true
- =
- true
- isAjax
- true
-
-
- true
- ${admin_form_key}
- =
- true
- form_key
- true
-
-
- true
- 1
- =
- true
- product[product_has_weight]
- true
-
-
- true
- 1
- =
- true
- product[use_config_gift_message_available]
- true
-
-
- true
- 1
- =
- true
- product[use_config_gift_wrapping_available]
- true
-
-
- true
- ${quantity}
- =
- true
- inventory[qty]
- true
-
-
- true
- ${price}
- =
- true
- attributes[price]
-
-
- true
- ${visibility}
- =
- true
- attributes[visibility]
-
-
-
-
-
- 60000
- 200000
- ${request_protocol}
-
- ${base_path}${admin_path}/catalog/product_action_attribute/validate
- POST
- true
- false
- true
- false
- false
-
- mpaf/tool/fragments/ce/admin_browse_products_grid/products_grid_mass_actions/change_attributes.jmx
-
-
-
- {"error":false}
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
- true
- true
- =
- true
- isAjax
- false
-
-
- true
- ${admin_form_key}
- =
- true
- form_key
- false
-
-
- true
- 1
- =
- true
- product[product_has_weight]
- true
-
-
- true
- 1
- =
- true
- product[use_config_gift_message_available]
-
-
- true
- 1
- =
- true
- product[use_config_gift_wrapping_available]
- true
-
-
- true
- ${quantity}
- =
- true
- inventory[qty]
-
-
- true
- on
- =
- true
- toggle_price
- true
-
-
- true
- ${price}
- =
- true
- attributes[price]
-
-
- true
- on
- =
- true
- toggle_price
- true
-
-
- true
- ${visibility}
- =
- true
- attributes[visibility]
-
-
- true
- on
- =
- true
- toggle_visibility
- true
-
-
-
-
-
- 60000
- 200000
- ${request_protocol}
-
- ${base_path}${admin_path}/catalog/product_action_attribute/save/store/0/active_tab/attributes
- POST
- true
- false
- true
- true
- false
-
-
-
-
-
- were updated.
-
- Assertion.response_data
- false
- 2
-
-
-
-
-
-
-
-
-
-
-
- 60000
- 200000
- ${request_protocol}
-
- ${base_path}${admin_path}/admin/auth/logout/
- GET
- true
- false
- true
- false
- false
-
- mpaf/tool/fragments/ce/setup/admin_logout.jmx
-
-
-
- false
-
-
-
- adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool"));
- if (adminUsersDistribution == 1) {
- adminUserList = props.get("adminUserList");
- adminUserList.add(vars.get("admin_user"));
- }
-
- mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx
-
-
-
-
-
1
false
diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList.php b/setup/src/Magento/Setup/Model/ConfigOptionsList.php
index afe1a5d9e2591..3f2aedae1373c 100644
--- a/setup/src/Magento/Setup/Model/ConfigOptionsList.php
+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList.php
@@ -50,7 +50,8 @@ class ConfigOptionsList implements ConfigOptionsListInterface
private $configOptionsListClasses = [
\Magento\Setup\Model\ConfigOptionsList\Session::class,
\Magento\Setup\Model\ConfigOptionsList\Cache::class,
- \Magento\Setup\Model\ConfigOptionsList\PageCache::class
+ \Magento\Setup\Model\ConfigOptionsList\PageCache::class,
+ \Magento\Setup\Model\ConfigOptionsList\Lock::class,
];
/**
diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php
new file mode 100644
index 0000000000000..66f41128c46b1
--- /dev/null
+++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php
@@ -0,0 +1,342 @@
+ [
+ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
+ self::INPUT_KEY_LOCK_DB_PREFIX => self::CONFIG_PATH_LOCK_DB_PREFIX,
+ ],
+ LockBackendFactory::LOCK_ZOOKEEPER => [
+ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
+ self::INPUT_KEY_LOCK_ZOOKEEPER_HOST => self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST,
+ self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH,
+ ],
+ LockBackendFactory::LOCK_CACHE => [
+ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
+ ],
+ LockBackendFactory::LOCK_FILE => [
+ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER,
+ self::INPUT_KEY_LOCK_FILE_PATH => self::CONFIG_PATH_LOCK_FILE_PATH,
+ ],
+ ];
+
+ /**
+ * The list of default values
+ *
+ * @var array
+ */
+ private $defaultConfigValues = [
+ self::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB,
+ self::INPUT_KEY_LOCK_DB_PREFIX => null,
+ self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => ZookeeperLock::DEFAULT_PATH,
+ ];
+
+ /**
+ * @inheritdoc
+ */
+ public function getOptions()
+ {
+ return [
+ new SelectConfigOption(
+ self::INPUT_KEY_LOCK_PROVIDER,
+ SelectConfigOption::FRONTEND_WIZARD_SELECT,
+ $this->validLockProviders,
+ self::CONFIG_PATH_LOCK_PROVIDER,
+ 'Lock provider name',
+ LockBackendFactory::LOCK_DB
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_LOCK_DB_PREFIX,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_LOCK_DB_PREFIX,
+ 'Installation specific lock prefix to avoid lock conflicts'
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_LOCK_ZOOKEEPER_HOST,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST,
+ 'Host and port to connect to Zookeeper cluster. For example: 127.0.0.1:2181'
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_LOCK_ZOOKEEPER_PATH,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH,
+ 'The path where Zookeeper will save locks. The default path is: ' . ZookeeperLock::DEFAULT_PATH
+ ),
+ new TextConfigOption(
+ self::INPUT_KEY_LOCK_FILE_PATH,
+ TextConfigOption::FRONTEND_WIZARD_TEXT,
+ self::CONFIG_PATH_LOCK_FILE_PATH,
+ 'The path where file locks will be saved.'
+ ),
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function createConfig(array $options, DeploymentConfig $deploymentConfig)
+ {
+ $configData = new ConfigData(ConfigFilePool::APP_ENV);
+ $configData->setOverrideWhenSave(true);
+ $lockProvider = $this->getLockProvider($options, $deploymentConfig);
+
+ $this->setDefaultConfiguration($configData, $deploymentConfig, $lockProvider);
+
+ foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) {
+ if (isset($options[$input])) {
+ $configData->set($path, $options[$input]);
+ }
+ }
+
+ return $configData;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function validate(array $options, DeploymentConfig $deploymentConfig)
+ {
+ $lockProvider = $this->getLockProvider($options, $deploymentConfig);
+ switch ($lockProvider) {
+ case LockBackendFactory::LOCK_ZOOKEEPER:
+ $errors = $this->validateZookeeperConfig($options, $deploymentConfig);
+ break;
+ case LockBackendFactory::LOCK_FILE:
+ $errors = $this->validateFileConfig($options, $deploymentConfig);
+ break;
+ case LockBackendFactory::LOCK_CACHE:
+ case LockBackendFactory::LOCK_DB:
+ $errors = [];
+ break;
+ default:
+ $errors[] = 'The lock provider ' . $lockProvider . ' does not exist.';
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Validates File locks configuration
+ *
+ * @param array $options
+ * @param DeploymentConfig $deploymentConfig
+ * @return array
+ */
+ private function validateFileConfig(array $options, DeploymentConfig $deploymentConfig): array
+ {
+ $errors = [];
+
+ $path = $options[self::INPUT_KEY_LOCK_FILE_PATH]
+ ?? $deploymentConfig->get(
+ self::CONFIG_PATH_LOCK_FILE_PATH,
+ $this->getDefaultValue(self::INPUT_KEY_LOCK_FILE_PATH)
+ );
+
+ if (!$path) {
+ $errors[] = 'The path needs to be a non-empty string.';
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Validates Zookeeper configuration
+ *
+ * @param array $options
+ * @param DeploymentConfig $deploymentConfig
+ * @return array
+ */
+ private function validateZookeeperConfig(array $options, DeploymentConfig $deploymentConfig): array
+ {
+ $errors = [];
+
+ if (!extension_loaded(LockBackendFactory::LOCK_ZOOKEEPER)) {
+ $errors[] = 'php extension Zookeeper is not installed.';
+ }
+
+ $host = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST]
+ ?? $deploymentConfig->get(
+ self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST,
+ $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST)
+ );
+ $path = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH]
+ ?? $deploymentConfig->get(
+ self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH,
+ $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH)
+ );
+
+ if (!$path) {
+ $errors[] = 'Zookeeper path needs to be a non-empty string.';
+ }
+
+ if (!$host) {
+ $errors[] = 'Zookeeper host is should be set.';
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Returns the name of lock provider
+ *
+ * @param array $options
+ * @param DeploymentConfig $deploymentConfig
+ * @return string
+ */
+ private function getLockProvider(array $options, DeploymentConfig $deploymentConfig): string
+ {
+ if (!isset($options[self::INPUT_KEY_LOCK_PROVIDER])) {
+ return (string) $deploymentConfig->get(
+ self::CONFIG_PATH_LOCK_PROVIDER,
+ $this->getDefaultValue(self::INPUT_KEY_LOCK_PROVIDER)
+ );
+ }
+
+ return (string) $options[self::INPUT_KEY_LOCK_PROVIDER];
+ }
+
+ /**
+ * Sets default configuration for locks
+ *
+ * @param ConfigData $configData
+ * @param DeploymentConfig $deploymentConfig
+ * @param string $lockProvider
+ * @return ConfigData
+ */
+ private function setDefaultConfiguration(
+ ConfigData $configData,
+ DeploymentConfig $deploymentConfig,
+ string $lockProvider
+ ) {
+ foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) {
+ $configData->set($path, $deploymentConfig->get($path, $this->getDefaultValue($input)));
+ }
+
+ return $configData;
+ }
+
+ /**
+ * Returns default value by input key
+ *
+ * If default value is not set returns null
+ *
+ * @param string $inputKey
+ * @return mixed|null
+ */
+ private function getDefaultValue(string $inputKey)
+ {
+ if (isset($this->defaultConfigValues[$inputKey])) {
+ return $this->defaultConfigValues[$inputKey];
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php
new file mode 100644
index 0000000000000..1a46bddf5f21a
--- /dev/null
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php
@@ -0,0 +1,232 @@
+deploymentConfigMock = $this->createMock(DeploymentConfig::class);
+ $this->lockConfigOptionsList = new LockConfigOptionsList();
+ }
+
+ /**
+ * @return void
+ */
+ public function testGetOptions()
+ {
+ $options = $this->lockConfigOptionsList->getOptions();
+ $this->assertSame(5, count($options));
+
+ $this->assertArrayHasKey(0, $options);
+ $this->assertInstanceOf(SelectConfigOption::class, $options[0]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER, $options[0]->getName());
+
+ $this->assertArrayHasKey(1, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[1]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX, $options[1]->getName());
+
+ $this->assertArrayHasKey(2, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[2]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST, $options[2]->getName());
+
+ $this->assertArrayHasKey(3, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[3]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH, $options[3]->getName());
+
+ $this->assertArrayHasKey(4, $options);
+ $this->assertInstanceOf(TextConfigOption::class, $options[4]);
+ $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH, $options[4]->getName());
+ }
+
+ /**
+ * @param array $options
+ * @param array $expectedResult
+ * @dataProvider createConfigDataProvider
+ */
+ public function testCreateConfig(array $options, array $expectedResult)
+ {
+ $this->deploymentConfigMock->expects($this->any())
+ ->method('get')
+ ->willReturnArgument(1);
+ $data = $this->lockConfigOptionsList->createConfig($options, $this->deploymentConfigMock);
+ $this->assertInstanceOf(ConfigData::class, $data);
+ $this->assertTrue($data->isOverrideWhenSave());
+ $this->assertSame($expectedResult, $data->getData());
+ }
+
+ /**
+ * @return array
+ */
+ public function createConfigDataProvider(): array
+ {
+ return [
+ 'Check default values' => [
+ 'options' => [],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_DB,
+ 'config' => [
+ 'prefix' => null,
+ ],
+ ],
+ ],
+ ],
+ 'Check default value for cache lock' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_CACHE,
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_CACHE,
+ ],
+ ],
+ ],
+ 'Check default value for zookeeper lock' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER,
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_ZOOKEEPER,
+ 'config' => [
+ 'host' => null,
+ 'path' => ZookeeperLock::DEFAULT_PATH,
+ ],
+ ],
+ ],
+ ],
+ 'Check specific db lock options' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB,
+ LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX => 'my_prefix'
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_DB,
+ 'config' => [
+ 'prefix' => 'my_prefix',
+ ],
+ ],
+ ],
+ ],
+ 'Check specific zookeeper lock options' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER,
+ LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '123.45.67.89:10',
+ LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '/some/path',
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_ZOOKEEPER,
+ 'config' => [
+ 'host' => '123.45.67.89:10',
+ 'path' => '/some/path',
+ ],
+ ],
+ ],
+ ],
+ 'Check specific file lock options' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE,
+ LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '/my/path'
+ ],
+ 'expectedResult' => [
+ 'lock' => [
+ 'provider' => LockBackendFactory::LOCK_FILE,
+ 'config' => [
+ 'path' => '/my/path',
+ ],
+ ],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @param array $options
+ * @param array $expectedResult
+ * @dataProvider validateDataProvider
+ */
+ public function testValidate(array $options, array $expectedResult)
+ {
+ $this->deploymentConfigMock->expects($this->any())
+ ->method('get')
+ ->willReturnArgument(1);
+ $this->assertSame(
+ $expectedResult,
+ $this->lockConfigOptionsList->validate($options, $this->deploymentConfigMock)
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function validateDataProvider(): array
+ {
+ return [
+ 'Wrong lock provider' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => 'SomeProvider',
+ ],
+ 'expectedResult' => [
+ 'The lock provider SomeProvider does not exist.',
+ ],
+ ],
+ 'Empty host and path for Zookeeper' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER,
+ LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '',
+ LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '',
+ ],
+ 'expectedResult' => extension_loaded('zookeeper')
+ ? [
+ 'Zookeeper path needs to be a non-empty string.',
+ 'Zookeeper host is should be set.',
+ ]
+ : [
+ 'php extension Zookeeper is not installed.',
+ 'Zookeeper path needs to be a non-empty string.',
+ 'Zookeeper host is should be set.',
+ ],
+ ],
+ 'Empty path for File lock' => [
+ 'options' => [
+ LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE,
+ LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '',
+ ],
+ 'expectedResult' => [
+ 'The path needs to be a non-empty string.',
+ ],
+ ],
+ ];
+ }
+}
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php
index d7f680309c9ef..a85b468cebc92 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php
@@ -7,6 +7,7 @@
namespace Magento\Setup\Test\Unit\Model;
use Magento\Framework\Config\ConfigOptionsListConstants;
+use Magento\Setup\Model\ConfigOptionsList\Lock;
use Magento\Setup\Model\ConfigGenerator;
use Magento\Setup\Model\ConfigOptionsList;
use Magento\Setup\Validator\DbValidator;
@@ -82,7 +83,7 @@ public function testCreateOptions()
$this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock);
$this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock);
- $configData = $this->object->createConfig([], $this->deploymentConfig);
+ $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig);
$this->assertGreaterThanOrEqual(6, count($configData));
}
@@ -96,7 +97,7 @@ public function testCreateOptionsWithOptionalNull()
$this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock);
$this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock);
- $configData = $this->object->createConfig([], $this->deploymentConfig);
+ $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig);
$this->assertGreaterThanOrEqual(6, count($configData));
}
@@ -109,7 +110,8 @@ public function testValidateSuccess()
ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name',
ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host',
ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user',
- ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass'
+ ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass',
+ Lock::INPUT_KEY_LOCK_PROVIDER => 'db'
];
$this->prepareValidationMocks();
@@ -127,7 +129,8 @@ public function testValidateInvalidSessionHandler()
ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name',
ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host',
ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user',
- ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass'
+ ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass',
+ Lock::INPUT_KEY_LOCK_PROVIDER => 'db'
];
$this->prepareValidationMocks();
@@ -141,7 +144,8 @@ public function testValidateEmptyEncryptionKey()
{
$options = [
ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true,
- ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => ''
+ ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => '',
+ Lock::INPUT_KEY_LOCK_PROVIDER => 'db'
];
$this->assertEquals(
['Invalid encryption key. Encryption key must be 32 character string without any white space.'],
@@ -167,7 +171,8 @@ public function testValidateCacheHosts($hosts, $expectedError)
{
$options = [
ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true,
- ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts
+ ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts,
+ Lock::INPUT_KEY_LOCK_PROVIDER => 'db'
];
$result = $this->object->validate($options, $this->deploymentConfig);
if ($expectedError) {