diff --git a/app/code/Magento/Backend/App/AbstractAction.php b/app/code/Magento/Backend/App/AbstractAction.php
index 99ee86b2b6407..3f658ee90bf4e 100644
--- a/app/code/Magento/Backend/App/AbstractAction.php
+++ b/app/code/Magento/Backend/App/AbstractAction.php
@@ -217,6 +217,7 @@ public function dispatch(\Magento\Framework\App\RequestInterface $request)
$this->_view->loadLayout(['default', 'adminhtml_denied'], true, true, false);
$this->_view->renderLayout();
$this->_request->setDispatched(true);
+
return $this->_response;
}
@@ -226,6 +227,11 @@ public function dispatch(\Magento\Framework\App\RequestInterface $request)
$this->_processLocaleSettings();
+ // Need to preload isFirstPageAfterLogin (see https://github.com/magento/magento2/issues/15510)
+ if ($this->_auth->isLoggedIn()) {
+ $this->_auth->getAuthStorage()->isFirstPageAfterLogin();
+ }
+
return parent::dispatch($request);
}
diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php
index ff0399e4f507f..b3f467ce37c88 100644
--- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php
+++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php
@@ -68,10 +68,7 @@ public function __construct(
$this->_storeManager = $storeManager;
$this->_currencyLocator = $currencyLocator;
$this->_localeCurrency = $localeCurrency;
- $defaultBaseCurrencyCode = $this->_scopeConfig->getValue(
- \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
- 'default'
- );
+ $defaultBaseCurrencyCode = $currencyLocator->getDefaultCurrency($this->_request);
$this->_defaultBaseCurrency = $currencyFactory->create()->load($defaultBaseCurrencyCode);
}
diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Checkbox.php b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Checkbox.php
index 0b3a938255de1..b220e2c98d77c 100644
--- a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Checkbox.php
+++ b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Checkbox.php
@@ -17,7 +17,7 @@ class Checkbox extends \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Op
/**
* @var string
*/
- protected $_template = 'product/composite/fieldset/options/type/checkbox.phtml';
+ protected $_template = 'Magento_Bundle::product/composite/fieldset/options/type/checkbox.phtml';
/**
* @param string $elementId
diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Multi.php b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Multi.php
index 304b3a5cf34ed..a4b8c6bde73aa 100644
--- a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Multi.php
+++ b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Multi.php
@@ -17,7 +17,7 @@ class Multi extends \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Optio
/**
* @var string
*/
- protected $_template = 'product/composite/fieldset/options/type/multi.phtml';
+ protected $_template = 'Magento_Bundle::product/composite/fieldset/options/type/multi.phtml';
/**
* @param string $elementId
diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Radio.php b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Radio.php
index e011ab36e8029..1519b3a67ac97 100644
--- a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Radio.php
+++ b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Radio.php
@@ -17,7 +17,7 @@ class Radio extends \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Optio
/**
* @var string
*/
- protected $_template = 'product/composite/fieldset/options/type/radio.phtml';
+ protected $_template = 'Magento_Bundle::product/composite/fieldset/options/type/radio.phtml';
/**
* @param string $elementId
diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Select.php b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Select.php
index f1206db359b5c..502dfa32044a3 100644
--- a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Select.php
+++ b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Composite/Fieldset/Options/Type/Select.php
@@ -17,7 +17,7 @@ class Select extends \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Opti
/**
* @var string
*/
- protected $_template = 'product/composite/fieldset/options/type/select.phtml';
+ protected $_template = 'Magento_Bundle::product/composite/fieldset/options/type/select.phtml';
/**
* @param string $elementId
diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle.php b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle.php
index f124740a766ab..8be512a3e6348 100644
--- a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle.php
+++ b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle.php
@@ -20,7 +20,7 @@ class Bundle extends \Magento\Backend\Block\Widget implements \Magento\Backend\B
/**
* @var string
*/
- protected $_template = 'product/edit/bundle.phtml';
+ protected $_template = 'Magento_Bundle::product/edit/bundle.phtml';
/**
* Core registry
diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option.php b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option.php
index 13c5dcc81afb3..19da6bc6244e5 100644
--- a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option.php
+++ b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option.php
@@ -26,7 +26,7 @@ class Option extends \Magento\Backend\Block\Widget
/**
* @var string
*/
- protected $_template = 'product/edit/bundle/option.phtml';
+ protected $_template = 'Magento_Bundle::product/edit/bundle/option.phtml';
/**
* Core registry
diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option/Search.php b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option/Search.php
index 5b73c22b5781a..cf4814d3cd778 100644
--- a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option/Search.php
+++ b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option/Search.php
@@ -15,7 +15,7 @@ class Search extends \Magento\Backend\Block\Widget
/**
* @var string
*/
- protected $_template = 'product/edit/bundle/option/search.phtml';
+ protected $_template = 'Magento_Bundle::product/edit/bundle/option/search.phtml';
/**
* @return void
diff --git a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option/Selection.php b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option/Selection.php
index 353808dc66a72..cf88f9b93d32f 100644
--- a/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option/Selection.php
+++ b/app/code/Magento/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Bundle/Option/Selection.php
@@ -15,7 +15,7 @@ class Selection extends \Magento\Backend\Block\Widget
/**
* @var string
*/
- protected $_template = 'product/edit/bundle/option/selection.phtml';
+ protected $_template = 'Magento_Bundle::product/edit/bundle/option/selection.phtml';
/**
* Catalog data
diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Checkbox.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Checkbox.php
index 8ca0cf8a5159e..83730d4eae2bd 100644
--- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Checkbox.php
+++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Checkbox.php
@@ -16,5 +16,5 @@ class Checkbox extends \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Op
/**
* @var string
*/
- protected $_template = 'catalog/product/view/type/bundle/option/checkbox.phtml';
+ protected $_template = 'Magento_Bundle::catalog/product/view/type/bundle/option/checkbox.phtml';
}
diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Multi.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Multi.php
index 3319db8cff1d5..79e94a18a789e 100644
--- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Multi.php
+++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Multi.php
@@ -16,7 +16,7 @@ class Multi extends \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Optio
/**
* @var string
*/
- protected $_template = 'catalog/product/view/type/bundle/option/multi.phtml';
+ protected $_template = 'Magento_Bundle::catalog/product/view/type/bundle/option/multi.phtml';
/**
* @inheritdoc
diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Radio.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Radio.php
index 84a619dafab52..07c113bd8e4bb 100644
--- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Radio.php
+++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Radio.php
@@ -16,5 +16,5 @@ class Radio extends \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Optio
/**
* @var string
*/
- protected $_template = 'catalog/product/view/type/bundle/option/radio.phtml';
+ protected $_template = 'Magento_Bundle::catalog/product/view/type/bundle/option/radio.phtml';
}
diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Select.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Select.php
index d7f1cf41057a8..63f0d35bda0f0 100644
--- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Select.php
+++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option/Select.php
@@ -16,5 +16,5 @@ class Select extends \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Opti
/**
* @var string
*/
- protected $_template = 'catalog/product/view/type/bundle/option/select.phtml';
+ protected $_template = 'Magento_Bundle::catalog/product/view/type/bundle/option/select.phtml';
}
diff --git a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php
index e496c36f4de75..91f3a785df36b 100644
--- a/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php
+++ b/app/code/Magento/Captcha/Model/Customer/Plugin/AjaxLogin.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Captcha\Model\Customer\Plugin;
use Magento\Captcha\Helper\Data as CaptchaHelper;
@@ -81,27 +82,37 @@ public function aroundExecute(
if ($content) {
$loginParams = $this->serializer->unserialize($content);
}
- $username = isset($loginParams['username']) ? $loginParams['username'] : null;
- $captchaString = isset($loginParams[$captchaInputName]) ? $loginParams[$captchaInputName] : null;
- $loginFormId = isset($loginParams[$captchaFormIdField]) ? $loginParams[$captchaFormIdField] : null;
+ $username = $loginParams['username'] ?? null;
+ $captchaString = $loginParams[$captchaInputName] ?? null;
+ $loginFormId = $loginParams[$captchaFormIdField] ?? null;
- foreach ($this->formIds as $formId) {
- $captchaModel = $this->helper->getCaptcha($formId);
- if ($captchaModel->isRequired($username) && !in_array($loginFormId, $this->formIds)) {
- $resultJson = $this->resultJsonFactory->create();
- return $resultJson->setData(['errors' => true, 'message' => __('Provided form does not exist')]);
- }
+ if (!in_array($loginFormId, $this->formIds) && $this->helper->getCaptcha($loginFormId)->isRequired($username)) {
+ return $this->returnJsonError(__('Provided form does not exist'));
+ }
- if ($formId == $loginFormId) {
- $captchaModel->logAttempt($username);
- if (!$captchaModel->isCorrect($captchaString)) {
- $this->sessionManager->setUsername($username);
- /** @var \Magento\Framework\Controller\Result\Json $resultJson */
- $resultJson = $this->resultJsonFactory->create();
- return $resultJson->setData(['errors' => true, 'message' => __('Incorrect CAPTCHA')]);
+ foreach ($this->formIds as $formId) {
+ if ($formId === $loginFormId) {
+ $captchaModel = $this->helper->getCaptcha($formId);
+ if ($captchaModel->isRequired($username)) {
+ $captchaModel->logAttempt($username);
+ if (!$captchaModel->isCorrect($captchaString)) {
+ $this->sessionManager->setUsername($username);
+ return $this->returnJsonError(__('Incorrect CAPTCHA'));
+ }
}
}
}
return $proceed();
}
+
+ /**
+ *
+ * @param \Magento\Framework\Phrase $phrase
+ * @return \Magento\Framework\Controller\Result\Json
+ */
+ private function returnJsonError(\Magento\Framework\Phrase $phrase): \Magento\Framework\Controller\Result\Json
+ {
+ $resultJson = $this->resultJsonFactory->create();
+ return $resultJson->setData(['errors' => true, 'message' => $phrase]);
+ }
}
diff --git a/app/code/Magento/Captcha/Model/DefaultModel.php b/app/code/Magento/Captcha/Model/DefaultModel.php
index e5c72ba1ae82e..cf6690df5c85d 100644
--- a/app/code/Magento/Captcha/Model/DefaultModel.php
+++ b/app/code/Magento/Captcha/Model/DefaultModel.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Captcha\Model;
+use Magento\Captcha\Helper\Data;
+
/**
* Implementation of \Zend\Captcha\Image
*
@@ -29,7 +31,7 @@ class DefaultModel extends \Zend\Captcha\Image implements \Magento\Captcha\Model
const DEFAULT_WORD_LENGTH_TO = 5;
/**
- * @var \Magento\Captcha\Helper\Data
+ * @var Data
* @since 100.2.0
*/
protected $captchaData;
@@ -125,8 +127,8 @@ public function getBlockName()
*/
public function isRequired($login = null)
{
- if ($this->isUserAuth()
- && !$this->isShownToLoggedInUser()
+ if (($this->isUserAuth()
+ && !$this->isShownToLoggedInUser())
|| !$this->isEnabled()
|| !in_array(
$this->formId,
@@ -431,12 +433,14 @@ public function getWordLen()
*/
private function isShowAlways()
{
- if ((string)$this->captchaData->getConfig('mode') == \Magento\Captcha\Helper\Data::MODE_ALWAYS) {
+ $captchaMode = (string)$this->captchaData->getConfig('mode');
+
+ if ($captchaMode === Data::MODE_ALWAYS) {
return true;
}
- if ((string)$this->captchaData->getConfig('mode') == \Magento\Captcha\Helper\Data::MODE_AFTER_FAIL
- && $this->getAllowedAttemptsForSameLogin() == 0
+ if ($captchaMode === Data::MODE_AFTER_FAIL
+ && $this->getAllowedAttemptsForSameLogin() === 0
) {
return true;
}
diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php
index be9574fff2cfa..bda0d9705d3df 100644
--- a/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php
+++ b/app/code/Magento/Captcha/Test/Unit/Model/Customer/Plugin/AjaxLoginTest.php
@@ -3,22 +3,23 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Captcha\Test\Unit\Model\Customer\Plugin;
class AjaxLoginTest extends \PHPUnit\Framework\TestCase
{
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Checkout\Model\Session
*/
protected $sessionManagerMock;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Captcha\Helper\Data
*/
protected $captchaHelperMock;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Controller\Result\JsonFactory
*/
protected $jsonFactoryMock;
@@ -38,12 +39,12 @@ class AjaxLoginTest extends \PHPUnit\Framework\TestCase
protected $requestMock;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Customer\Controller\Ajax\Login
*/
protected $loginControllerMock;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Serialize\Serializer\Json
*/
protected $serializerMock;
@@ -72,8 +73,12 @@ protected function setUp()
$this->loginControllerMock->expects($this->any())->method('getRequest')
->will($this->returnValue($this->requestMock));
- $this->captchaHelperMock->expects($this->once())->method('getCaptcha')
- ->with('user_login')->will($this->returnValue($this->captchaMock));
+
+ $this->captchaHelperMock
+ ->expects($this->exactly(1))
+ ->method('getCaptcha')
+ ->will($this->returnValue($this->captchaMock));
+
$this->formIds = ['user_login'];
$this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class);
@@ -103,11 +108,18 @@ public function testAroundExecute()
$this->captchaMock->expects($this->once())->method('logAttempt')->with($username);
$this->captchaMock->expects($this->once())->method('isCorrect')->with($captchaString)
->will($this->returnValue(true));
- $this->serializerMock->expects(($this->once()))->method('unserialize')->will($this->returnValue($requestData));
+ $this->serializerMock->expects($this->once())->method('unserialize')->will($this->returnValue($requestData));
$closure = function () {
return 'result';
};
+
+ $this->captchaHelperMock
+ ->expects($this->exactly(1))
+ ->method('getCaptcha')
+ ->with('user_login')
+ ->will($this->returnValue($this->captchaMock));
+
$this->assertEquals('result', $this->model->aroundExecute($this->loginControllerMock, $closure));
}
@@ -128,18 +140,21 @@ public function testAroundExecuteIncorrectCaptcha()
$this->captchaMock->expects($this->once())->method('logAttempt')->with($username);
$this->captchaMock->expects($this->once())->method('isCorrect')
->with($captchaString)->will($this->returnValue(false));
- $this->serializerMock->expects(($this->once()))->method('unserialize')->will($this->returnValue($requestData));
+ $this->serializerMock->expects($this->once())->method('unserialize')->will($this->returnValue($requestData));
$this->sessionManagerMock->expects($this->once())->method('setUsername')->with($username);
$this->jsonFactoryMock->expects($this->once())->method('create')
->will($this->returnValue($this->resultJsonMock));
- $this->resultJsonMock->expects($this->once())->method('setData')
- ->with(['errors' => true, 'message' => __('Incorrect CAPTCHA')])->will($this->returnValue('response'));
+ $this->resultJsonMock
+ ->expects($this->once())
+ ->method('setData')
+ ->with(['errors' => true, 'message' => __('Incorrect CAPTCHA')])
+ ->will($this->returnSelf());
$closure = function () {
};
- $this->assertEquals('response', $this->model->aroundExecute($this->loginControllerMock, $closure));
+ $this->assertEquals($this->resultJsonMock, $this->model->aroundExecute($this->loginControllerMock, $closure));
}
/**
@@ -151,7 +166,7 @@ public function testAroundExecuteCaptchaIsNotRequired($username, $requestContent
{
$this->requestMock->expects($this->once())->method('getContent')
->will($this->returnValue(json_encode($requestContent)));
- $this->serializerMock->expects(($this->once()))->method('unserialize')
+ $this->serializerMock->expects($this->once())->method('unserialize')
->will($this->returnValue($requestContent));
$this->captchaMock->expects($this->once())->method('isRequired')->with($username)
@@ -168,16 +183,39 @@ public function testAroundExecuteCaptchaIsNotRequired($username, $requestContent
/**
* @return array
*/
- public function aroundExecuteCaptchaIsNotRequired()
+ public function aroundExecuteCaptchaIsNotRequired(): array
{
return [
[
'username' => 'name',
'requestData' => ['username' => 'name', 'captcha_string' => 'string'],
],
+ [
+ 'username' => 'name',
+ 'requestData' =>
+ [
+ 'username' => 'name',
+ 'captcha_string' => 'string',
+ 'captcha_form_id' => $this->formIds[0]
+ ],
+ ],
[
'username' => null,
- 'requestData' => ['captcha_string' => 'string'],
+ 'requestData' =>
+ [
+ 'username' => null,
+ 'captcha_string' => 'string',
+ 'captcha_form_id' => $this->formIds[0]
+ ],
+ ],
+ [
+ 'username' => 'name',
+ 'requestData' =>
+ [
+ 'username' => 'name',
+ 'captcha_string' => 'string',
+ 'captcha_form_id' => null
+ ],
],
];
}
diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php
index 89eb6c9be929f..eb9cc83125541 100644
--- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php
+++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php
@@ -36,7 +36,14 @@ public function execute()
$productName = $this->_objectManager->get(
\Magento\Framework\Escaper::class
)->escapeHtml($product->getName());
- $this->messageManager->addSuccess(__('You added product %1 to the comparison list.', $productName));
+ $this->messageManager->addComplexSuccessMessage(
+ 'addCompareSuccessMessage',
+ [
+ 'product_name' => $productName,
+ 'compare_list_url' => $this->_url->getUrl('catalog/product_compare')
+ ]
+ );
+
$this->_eventManager->dispatch('catalog_product_compare_add_product', ['product' => $product]);
}
diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php
index b5dbdb68606ff..709f27d031ebe 100644
--- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php
+++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Action/Row.php
@@ -61,6 +61,7 @@ public function __construct(
* @param int|null $id
* @return \Magento\Catalog\Model\Indexer\Product\Flat\Action\Row
* @throws \Magento\Framework\Exception\LocalizedException
+ * @throws \Zend_Db_Statement_Exception
*/
public function execute($id = null)
{
@@ -75,17 +76,43 @@ public function execute($id = null)
if ($tableExists) {
$this->flatItemEraser->removeDeletedProducts($ids, $store->getId());
}
- if (isset($ids[0])) {
- if (!$tableExists) {
- $this->_flatTableBuilder->build(
- $store->getId(),
- [$ids[0]],
- $this->_valueFieldSuffix,
- $this->_tableDropSuffix,
- false
- );
+
+ /* @var $status \Magento\Eav\Model\Entity\Attribute */
+ $status = $this->_productIndexerHelper->getAttribute('status');
+ $statusTable = $status->getBackend()->getTable();
+ $statusConditions = [
+ 'store_id IN(0,' . (int)$store->getId() . ')',
+ 'attribute_id = ' . (int)$status->getId(),
+ 'entity_id = ' . (int)$id
+ ];
+ $select = $this->_connection->select();
+ $select->from(
+ $statusTable,
+ ['value']
+ )->where(
+ implode(' AND ', $statusConditions)
+ )->order(
+ 'store_id DESC'
+ );
+ $result = $this->_connection->query($select);
+ $status = $result->fetch(1);
+
+ if ($status['value'] == \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) {
+ if (isset($ids[0])) {
+ if (!$tableExists) {
+ $this->_flatTableBuilder->build(
+ $store->getId(),
+ [$ids[0]],
+ $this->_valueFieldSuffix,
+ $this->_tableDropSuffix,
+ false
+ );
+ }
+ $this->flatItemWriter->write($store->getId(), $ids[0], $this->_valueFieldSuffix);
}
- $this->flatItemWriter->write($store->getId(), $ids[0], $this->_valueFieldSuffix);
+ }
+ if ($status['value'] == \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED) {
+ $this->flatItemEraser->deleteProductsFromStore($id, $store->getId());
}
}
return $this;
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php
index 1de0a480c9fbc..edf2a3a1c05bd 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php
@@ -307,7 +307,7 @@ protected function prepareFinalPriceDataForType($entityIds, $type)
$query = $select->insertFromSelect($finalPriceTable->getTableName(), [], false);
$this->getConnection()->query($query);
- $this->applyDiscountPrices($finalPriceTable);
+ $this->modifyPriceIndex($finalPriceTable);
return $this;
}
@@ -512,12 +512,12 @@ protected function _prepareCustomOptionPriceTable()
}
/**
- * Apply discount prices to final price index table.
+ * Modify data in price index table.
*
* @param IndexTableStructure $finalPriceTable
* @return void
*/
- private function applyDiscountPrices(IndexTableStructure $finalPriceTable)
+ private function modifyPriceIndex(IndexTableStructure $finalPriceTable)
{
foreach ($this->priceModifiers as $priceModifier) {
$priceModifier->modifyPrice($finalPriceTable);
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php
index 41b3d36227431..2572356cf855d 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Action/RowTest.php
@@ -10,6 +10,9 @@
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class RowTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -61,6 +64,8 @@ protected function setUp()
{
$objectManager = new ObjectManager($this);
+ $attributeTable = 'catalog_product_entity_int';
+ $statusId = 22;
$this->connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class);
$this->resource = $this->createMock(\Magento\Framework\App\ResourceConnection::class);
$this->resource->expects($this->any())->method('getConnection')
@@ -70,10 +75,36 @@ protected function setUp()
$this->store = $this->createMock(\Magento\Store\Model\Store::class);
$this->store->expects($this->any())->method('getId')->will($this->returnValue('store_id_1'));
$this->storeManager->expects($this->any())->method('getStores')->will($this->returnValue([$this->store]));
- $this->productIndexerHelper = $this->createMock(\Magento\Catalog\Helper\Product\Flat\Indexer::class);
$this->flatItemEraser = $this->createMock(\Magento\Catalog\Model\Indexer\Product\Flat\Action\Eraser::class);
$this->flatItemWriter = $this->createMock(\Magento\Catalog\Model\Indexer\Product\Flat\Action\Indexer::class);
$this->flatTableBuilder = $this->createMock(\Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder::class);
+ $this->productIndexerHelper = $this->createMock(\Magento\Catalog\Helper\Product\Flat\Indexer::class);
+ $statusAttributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->productIndexerHelper->expects($this->any())->method('getAttribute')
+ ->with('status')
+ ->willReturn($statusAttributeMock);
+ $backendMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $backendMock->expects($this->any())->method('getTable')->willReturn($attributeTable);
+ $statusAttributeMock->expects($this->any())->method('getBackend')->willReturn(
+ $backendMock
+ );
+ $statusAttributeMock->expects($this->any())->method('getId')->willReturn($statusId);
+ $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->connection->expects($this->any())->method('select')->willReturn($selectMock);
+ $selectMock->expects($this->any())->method('from')->with(
+ $attributeTable,
+ ['value']
+ )->willReturnSelf();
+ $selectMock->expects($this->any())->method('where')->willReturnSelf();
+ $pdoMock = $this->createMock(\Zend_Db_Statement_Pdo::class);
+ $this->connection->expects($this->any())->method('query')->with($selectMock)->will($this->returnValue($pdoMock));
+ $pdoMock->expects($this->any())->method('fetch')->will($this->returnValue(['value' => 1]));
$this->model = $objectManager->getObject(
\Magento\Catalog\Model\Indexer\Product\Flat\Action\Row::class, [
@@ -82,7 +113,7 @@ protected function setUp()
'productHelper' => $this->productIndexerHelper,
'flatItemEraser' => $this->flatItemEraser,
'flatItemWriter' => $this->flatItemWriter,
- 'flatTableBuilder' => $this->flatTableBuilder
+ 'flatTableBuilder' => $this->flatTableBuilder,
]);
}
diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml
index d62ecdb632ac4..14deaec9b4586 100644
--- a/app/code/Magento/Catalog/etc/frontend/di.xml
+++ b/app/code/Magento/Catalog/etc/frontend/di.xml
@@ -79,6 +79,18 @@
recently_compared_product
+
+
+
+ -
+
- \Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE
+ -
+
- Magento_Catalog::messages/addCompareSuccessMessage.phtml
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml
new file mode 100644
index 0000000000000..5f44c42e17c57
--- /dev/null
+++ b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml
@@ -0,0 +1,14 @@
+
+= $block->escapeHtml(__(
+ 'You added product %1 to the comparison list.',
+ $block->getData('product_name'),
+ $block->getData('compare_list_url')),
+ ['a']
+);
diff --git a/app/code/Magento/Catalog/view/frontend/web/js/storage-manager.js b/app/code/Magento/Catalog/view/frontend/web/js/storage-manager.js
index fcba9e8dcf468..3b0e9c5e8e260 100644
--- a/app/code/Magento/Catalog/view/frontend/web/js/storage-manager.js
+++ b/app/code/Magento/Catalog/view/frontend/web/js/storage-manager.js
@@ -129,7 +129,7 @@ define([
},
/**
- * Prepare storages congfig.
+ * Prepare storages config.
*
* @returns {Object} Chainable.
*/
diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php
new file mode 100644
index 0000000000000..51e31c71ba362
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php
@@ -0,0 +1,85 @@
+stockConfiguration = $stockConfiguration;
+ $this->stockItem = $stockItem;
+ }
+
+ /**
+ * Remove out of stock products data from price index.
+ *
+ * @param IndexTableStructure $priceTable
+ * @param array $entityIds
+ * @return void
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = [])
+ {
+ if ($this->stockConfiguration->isShowOutOfStock()) {
+ return;
+ }
+
+ $connection = $this->stockItem->getConnection();
+ $select = $connection->select();
+ $select->from(
+ ['price_index' => $priceTable->getTableName()],
+ []
+ );
+ $select->joinInner(
+ ['stock_item' => $this->stockItem->getMainTable()],
+ 'stock_item.product_id = price_index.' . $priceTable->getEntityField()
+ . ' AND stock_item.stock_id = ' . Stock::DEFAULT_STOCK_ID,
+ []
+ );
+ if ($this->stockConfiguration->getManageStock()) {
+ $stockStatus = $connection->getCheckSql(
+ 'use_config_manage_stock = 0 AND manage_stock = 0',
+ Stock::STOCK_IN_STOCK,
+ 'is_in_stock'
+ );
+ } else {
+ $stockStatus = $connection->getCheckSql(
+ 'use_config_manage_stock = 0 AND manage_stock = 1',
+ 'is_in_stock',
+ Stock::STOCK_IN_STOCK
+ );
+ }
+ $select->where($stockStatus . ' = ?', Stock::STOCK_OUT_OF_STOCK);
+
+ $query = $select->deleteFromSelect('price_index');
+ $connection->query($query);
+ }
+}
diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php
new file mode 100644
index 0000000000000..316f7000ae4af
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Model/Plugin/PriceIndexUpdater.php
@@ -0,0 +1,77 @@
+priceIndexProcessor = $priceIndexProcessor;
+ }
+
+ /**
+ * @param Item $subject
+ * @param Item $result
+ * @param AbstractModel $model
+ * @return Item
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterSave(Item $subject, Item $result, AbstractModel $model)
+ {
+ $fields = [
+ 'is_in_stock',
+ 'use_config_manage_stock',
+ 'manage_stock',
+ ];
+ foreach ($fields as $field) {
+ if ($model->dataHasChangedFor($field)) {
+ $this->priceIndexProcessor->reindexRow($model->getProductId());
+ break;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param Item $subject
+ * @param $result
+ * @param int $websiteId
+ * @return void
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterUpdateSetOutOfStock(Item $subject, $result, int $websiteId)
+ {
+ $this->priceIndexProcessor->markIndexerAsInvalid();
+ }
+
+ /**
+ * @param Item $subject
+ * @param $result
+ * @param int $websiteId
+ * @return void
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterUpdateSetInStock(Item $subject, $result, int $websiteId)
+ {
+ $this->priceIndexProcessor->markIndexerAsInvalid();
+ }
+}
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php
index 4a39ac2868046..53f00529b9bcc 100644
--- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php
@@ -206,6 +206,8 @@ protected function _initConfig()
/**
* Set items out of stock basing on their quantities and config settings
*
+ * @deprecated
+ * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetOutOfStock
* @param string|int $website
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @return void
@@ -241,6 +243,8 @@ public function updateSetOutOfStock($website = null)
/**
* Set items in stock basing on their quantities and config settings
*
+ * @deprecated
+ * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetInStock
* @param int|string $website
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @return void
@@ -274,6 +278,8 @@ public function updateSetInStock($website)
/**
* Update items low stock date basing on their quantities and config settings
*
+ * @deprecated
+ * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateLowStockDate
* @param int|string $website
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @return void
diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php
index 895fffaa4f80b..586efe8e1a0d3 100644
--- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php
+++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php
@@ -6,10 +6,14 @@
namespace Magento\CatalogInventory\Model\ResourceModel\Stock;
use Magento\CatalogInventory\Api\Data\StockItemInterface;
+use Magento\CatalogInventory\Api\StockConfigurationInterface;
+use Magento\CatalogInventory\Model\Stock;
use Magento\CatalogInventory\Model\Indexer\Stock\Processor;
-use Magento\Framework\App\ResourceConnection as AppResource;
use Magento\Framework\Model\AbstractModel;
-use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface;
+use Magento\Framework\Model\ResourceModel\Db\Context;
+use Magento\Framework\DB\Select;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Stdlib\DateTime\DateTime;
/**
* Stock item resource model
@@ -29,17 +33,36 @@ class Item extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
protected $stockIndexerProcessor;
/**
- * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
+ * @var StockConfigurationInterface
+ */
+ private $stockConfiguration;
+
+ /**
+ * @var DateTime
+ */
+ private $dateTime;
+
+ /**
+ * @param Context $context
* @param Processor $processor
* @param string $connectionName
+ * @param StockConfigurationInterface $stockConfiguration
+ * @param DateTime $dateTime
*/
public function __construct(
- \Magento\Framework\Model\ResourceModel\Db\Context $context,
+ Context $context,
Processor $processor,
- $connectionName = null
+ $connectionName = null,
+ StockConfigurationInterface $stockConfiguration = null,
+ DateTime $dateTime = null
) {
$this->stockIndexerProcessor = $processor;
parent::__construct($context, $connectionName);
+
+ $this->stockConfiguration = $stockConfiguration ??
+ ObjectManager::getInstance()->get(StockConfigurationInterface::class);
+ $this->dateTime = $dateTime ??
+ ObjectManager::getInstance()->get(DateTime::class);
}
/**
@@ -139,4 +162,118 @@ public function setProcessIndexEvents($process = true)
$this->processIndexEvents = $process;
return $this;
}
+
+ /**
+ * Set items out of stock basing on their quantities and config settings
+ *
+ * @param int $websiteId
+ * @return void
+ */
+ public function updateSetOutOfStock(int $websiteId)
+ {
+ $connection = $this->getConnection();
+
+ $values = [
+ 'is_in_stock' => Stock::STOCK_OUT_OF_STOCK,
+ 'stock_status_changed_auto' => 1,
+ ];
+ $select = $this->buildProductsSelectByConfigTypes();
+ $where = [
+ 'website_id = ' . $websiteId,
+ 'is_in_stock = ' . Stock::STOCK_IN_STOCK,
+ '(use_config_manage_stock = 1 AND 1 = ' . $this->stockConfiguration->getManageStock() . ')'
+ . ' OR (use_config_manage_stock = 0 AND manage_stock = 1)',
+ '(use_config_min_qty = 1 AND qty <= ' . $this->stockConfiguration->getMinQty() . ')'
+ . ' OR (use_config_min_qty = 0 AND qty <= min_qty)',
+ 'product_id IN (' . $select->assemble() . ')',
+ ];
+ $backordersWhere = '(use_config_backorders = 0 AND backorders = ' . Stock::BACKORDERS_NO . ')';
+ if (Stock::BACKORDERS_NO == $this->stockConfiguration->getBackorders()) {
+ $where[] = $backordersWhere . ' OR use_config_backorders = 1';
+ } else {
+ $where[] = $backordersWhere;
+ }
+ $connection->update($this->getMainTable(), $values, $where);
+
+ $this->stockIndexerProcessor->markIndexerAsInvalid();
+ }
+
+ /**
+ * Set items in stock basing on their quantities and config settings
+ *
+ * @param int $websiteId
+ * @return void
+ */
+ public function updateSetInStock(int $websiteId)
+ {
+ $connection = $this->getConnection();
+
+ $values = [
+ 'is_in_stock' => Stock::STOCK_IN_STOCK,
+ ];
+ $select = $this->buildProductsSelectByConfigTypes();
+ $where = [
+ 'website_id = ' . $websiteId,
+ 'stock_status_changed_auto = 1',
+ '(use_config_min_qty = 1 AND qty > ' . $this->stockConfiguration->getMinQty() . ')'
+ . ' OR (use_config_min_qty = 0 AND qty > min_qty)',
+ 'product_id IN (' . $select->assemble() . ')',
+ ];
+ $manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)';
+ if ($this->stockConfiguration->getManageStock()) {
+ $where[] = $manageStockWhere . ' OR use_config_manage_stock = 1';
+ } else {
+ $where[] = $manageStockWhere;
+ }
+ $connection->update($this->getMainTable(), $values, $where);
+
+ $this->stockIndexerProcessor->markIndexerAsInvalid();
+ }
+
+ /**
+ * Update items low stock date basing on their quantities and config settings
+ *
+ * @param int $websiteId
+ * @return void
+ */
+ public function updateLowStockDate(int $websiteId)
+ {
+ $connection = $this->getConnection();
+
+ $condition = $connection->quoteInto(
+ '(use_config_notify_stock_qty = 1 AND qty < ?)',
+ $this->stockConfiguration->getNotifyStockQty()
+ ) . ' OR (use_config_notify_stock_qty = 0 AND qty < notify_stock_qty)';
+ $currentDbTime = $connection->quoteInto('?', $this->dateTime->gmtDate());
+ $conditionalDate = $connection->getCheckSql($condition, $currentDbTime, 'NULL');
+ $value = [
+ 'low_stock_date' => new \Zend_Db_Expr($conditionalDate),
+ ];
+ $select = $this->buildProductsSelectByConfigTypes();
+ $where = [
+ 'website_id = ' . $websiteId,
+ 'product_id IN (' . $select->assemble() . ')'
+ ];
+ $manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)';
+ if ($this->stockConfiguration->getManageStock()) {
+ $where[] = $manageStockWhere . ' OR use_config_manage_stock = 1';
+ } else {
+ $where[] = $manageStockWhere;
+ }
+ $connection->update($this->getMainTable(), $value, $where);
+ }
+
+ /**
+ * Build select for products with types from config
+ *
+ * @return Select
+ */
+ private function buildProductsSelectByConfigTypes(): Select
+ {
+ $select = $this->getConnection()->select()
+ ->from($this->getTable('catalog_product_entity'), 'entity_id')
+ ->where('type_id IN (?)', array_keys($this->stockConfiguration->getIsQtyTypeIds(true)));
+
+ return $select;
+ }
}
diff --git a/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php
new file mode 100644
index 0000000000000..976110ec76cf3
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Observer/InvalidatePriceIndexUponConfigChangeObserver.php
@@ -0,0 +1,49 @@
+priceIndexProcessor = $priceIndexProcessor;
+ }
+
+ /**
+ * Invalidate product price index on catalog inventory config changes.
+ *
+ * @param Observer $observer
+ * @return void
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function execute(Observer $observer)
+ {
+ $changedPaths = (array) $observer->getEvent()->getChangedPaths();
+
+ if (\in_array(Configuration::XML_PATH_SHOW_OUT_OF_STOCK, $changedPaths, true)) {
+ $priceIndexer = $this->priceIndexProcessor->getIndexer();
+ $priceIndexer->invalidate();
+ }
+ }
+}
diff --git a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php
index 47ee6512920d8..21c78eca93695 100644
--- a/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php
+++ b/app/code/Magento/CatalogInventory/Observer/UpdateItemsStockUponConfigChangeObserver.php
@@ -3,11 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-
namespace Magento\CatalogInventory\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;
+use Magento\CatalogInventory\Model\Configuration;
+use Magento\CatalogInventory\Model\ResourceModel\Stock\Item;
/**
* Catalog inventory module observer
@@ -15,16 +16,16 @@
class UpdateItemsStockUponConfigChangeObserver implements ObserverInterface
{
/**
- * @var \Magento\CatalogInventory\Model\ResourceModel\Stock
+ * @var Item
*/
- protected $resourceStock;
+ protected $resourceStockItem;
/**
- * @param \Magento\CatalogInventory\Model\ResourceModel\Stock $resourceStock
+ * @param Item $resourceStockItem
*/
- public function __construct(\Magento\CatalogInventory\Model\ResourceModel\Stock $resourceStock)
+ public function __construct(Item $resourceStockItem)
{
- $this->resourceStock = $resourceStock;
+ $this->resourceStockItem = $resourceStockItem;
}
/**
@@ -35,9 +36,18 @@ public function __construct(\Magento\CatalogInventory\Model\ResourceModel\Stock
*/
public function execute(EventObserver $observer)
{
- $website = $observer->getEvent()->getWebsite();
- $this->resourceStock->updateSetOutOfStock($website);
- $this->resourceStock->updateSetInStock($website);
- $this->resourceStock->updateLowStockDate($website);
+ $website = (int) $observer->getEvent()->getWebsite();
+ $changedPaths = (array) $observer->getEvent()->getChangedPaths();
+
+ if (\array_intersect([
+ Configuration::XML_PATH_MANAGE_STOCK,
+ Configuration::XML_PATH_MIN_QTY,
+ Configuration::XML_PATH_BACKORDERS,
+ Configuration::XML_PATH_NOTIFY_STOCK_QTY,
+ ], $changedPaths)) {
+ $this->resourceStockItem->updateSetOutOfStock($website);
+ $this->resourceStockItem->updateSetInStock($website);
+ $this->resourceStockItem->updateLowStockDate($website);
+ }
}
}
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php
index 70a179b484379..7b82b5927d22c 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/UpdateItemsStockUponConfigChangeObserverTest.php
@@ -15,9 +15,9 @@ class UpdateItemsStockUponConfigChangeObserverTest extends \PHPUnit\Framework\Te
protected $observer;
/**
- * @var \Magento\CatalogInventory\Model\ResourceModel\Stock|\PHPUnit_Framework_MockObject_MockObject
+ * @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Item|\PHPUnit_Framework_MockObject_MockObject
*/
- protected $resourceStock;
+ protected $resourceStockItem;
/**
* @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject
@@ -31,11 +31,11 @@ class UpdateItemsStockUponConfigChangeObserverTest extends \PHPUnit\Framework\Te
protected function setUp()
{
- $this->resourceStock = $this->createMock(\Magento\CatalogInventory\Model\ResourceModel\Stock::class);
+ $this->resourceStockItem = $this->createMock(\Magento\CatalogInventory\Model\ResourceModel\Stock\Item::class);
$this->event = $this->getMockBuilder(\Magento\Framework\Event::class)
->disableOriginalConstructor()
- ->setMethods(['getWebsite'])
+ ->setMethods(['getWebsite', 'getChangedPaths'])
->getMock();
$this->eventObserver = $this->getMockBuilder(\Magento\Framework\Event\Observer::class)
@@ -50,7 +50,7 @@ protected function setUp()
$this->observer = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
\Magento\CatalogInventory\Observer\UpdateItemsStockUponConfigChangeObserver::class,
[
- 'resourceStock' => $this->resourceStock,
+ 'resourceStockItem' => $this->resourceStockItem,
]
);
}
@@ -58,13 +58,16 @@ protected function setUp()
public function testUpdateItemsStockUponConfigChange()
{
$websiteId = 1;
- $this->resourceStock->expects($this->once())->method('updateSetOutOfStock');
- $this->resourceStock->expects($this->once())->method('updateSetInStock');
- $this->resourceStock->expects($this->once())->method('updateLowStockDate');
+ $this->resourceStockItem->expects($this->once())->method('updateSetOutOfStock');
+ $this->resourceStockItem->expects($this->once())->method('updateSetInStock');
+ $this->resourceStockItem->expects($this->once())->method('updateLowStockDate');
$this->event->expects($this->once())
->method('getWebsite')
->will($this->returnValue($websiteId));
+ $this->event->expects($this->once())
+ ->method('getChangedPaths')
+ ->will($this->returnValue([\Magento\CatalogInventory\Model\Configuration::XML_PATH_MANAGE_STOCK]));
$this->observer->execute($this->eventObserver);
}
diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml
index 65bc277121429..264f25d24647e 100644
--- a/app/code/Magento/CatalogInventory/etc/di.xml
+++ b/app/code/Magento/CatalogInventory/etc/di.xml
@@ -118,4 +118,14 @@
+
+
+
+ - Magento\CatalogInventory\Model\Indexer\ProductPriceIndexFilter
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogInventory/etc/events.xml b/app/code/Magento/CatalogInventory/etc/events.xml
index 3197501e9b70b..328edbade6068 100644
--- a/app/code/Magento/CatalogInventory/etc/events.xml
+++ b/app/code/Magento/CatalogInventory/etc/events.xml
@@ -38,6 +38,7 @@
+
diff --git a/app/code/Magento/CatalogInventory/etc/mview.xml b/app/code/Magento/CatalogInventory/etc/mview.xml
index c3d73ff43e8eb..72dda16e8b5bb 100644
--- a/app/code/Magento/CatalogInventory/etc/mview.xml
+++ b/app/code/Magento/CatalogInventory/etc/mview.xml
@@ -11,4 +11,9 @@
+
+
+
+
+
diff --git a/app/code/Magento/CatalogRule/etc/mview.xml b/app/code/Magento/CatalogRule/etc/mview.xml
index 4b1166941bdc8..35efe33461afc 100644
--- a/app/code/Magento/CatalogRule/etc/mview.xml
+++ b/app/code/Magento/CatalogRule/etc/mview.xml
@@ -24,4 +24,9 @@
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html
index 8bf1a87d34e6e..2daca51a2f5da 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html
@@ -30,8 +30,12 @@
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php
index bc1515aadb0ca..11ad1ef2a43e9 100644
--- a/app/code/Magento/Config/Model/Config.php
+++ b/app/code/Magento/Config/Model/Config.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Config\Model;
+use Magento\Config\Model\Config\Structure\Element\Group;
+use Magento\Config\Model\Config\Structure\Element\Field;
+
/**
* Backend config model
* Used to save configuration
@@ -126,11 +129,12 @@ public function save()
$oldConfig = $this->_getConfig(true);
+ /** @var \Magento\Framework\DB\Transaction $deleteTransaction */
$deleteTransaction = $this->_transactionFactory->create();
- /* @var $deleteTransaction \Magento\Framework\DB\Transaction */
+ /** @var \Magento\Framework\DB\Transaction $saveTransaction */
$saveTransaction = $this->_transactionFactory->create();
- /* @var $saveTransaction \Magento\Framework\DB\Transaction */
+ $changedPaths = [];
// Extends for old config data
$extraOldGroups = [];
@@ -145,6 +149,9 @@ public function save()
$saveTransaction,
$deleteTransaction
);
+
+ $groupChangedPaths = $this->getChangedPaths($sectionId, $groupId, $groupData, $oldConfig, $extraOldGroups);
+ $changedPaths = \array_merge($changedPaths, $groupChangedPaths);
}
try {
@@ -157,7 +164,11 @@ public function save()
// website and store codes can be used in event implementation, so set them as well
$this->_eventManager->dispatch(
"admin_system_config_changed_section_{$this->getSection()}",
- ['website' => $this->getWebsite(), 'store' => $this->getStore()]
+ [
+ 'website' => $this->getWebsite(),
+ 'store' => $this->getStore(),
+ 'changed_paths' => $changedPaths,
+ ]
);
} catch (\Exception $e) {
// re-init configuration
@@ -168,6 +179,144 @@ public function save()
return $this;
}
+ /**
+ * Map field name if they were cloned
+ *
+ * @param Group $group
+ * @param string $fieldId
+ * @return string
+ */
+ private function getOriginalFieldId(Group $group, string $fieldId): string
+ {
+ if ($group->shouldCloneFields()) {
+ $cloneModel = $group->getCloneModel();
+
+ /** @var \Magento\Config\Model\Config\Structure\Element\Field $field */
+ foreach ($group->getChildren() as $field) {
+ foreach ($cloneModel->getPrefixes() as $prefix) {
+ if ($prefix['field'] . $field->getId() === $fieldId) {
+ $fieldId = $field->getId();
+ break(2);
+ }
+ }
+ }
+ }
+
+ return $fieldId;
+ }
+
+ /**
+ * Get field object
+ *
+ * @param string $sectionId
+ * @param string $groupId
+ * @param string $fieldId
+ * @return Field
+ */
+ private function getField(string $sectionId, string $groupId, string $fieldId): Field
+ {
+ /** @var \Magento\Config\Model\Config\Structure\Element\Group $group */
+ $group = $this->_configStructure->getElement($sectionId . '/' . $groupId);
+ $fieldPath = $group->getPath() . '/' . $this->getOriginalFieldId($group, $fieldId);
+ $field = $this->_configStructure->getElement($fieldPath);
+
+ return $field;
+ }
+
+ /**
+ * Get field path
+ *
+ * @param Field $field
+ * @param array &$oldConfig Need for compatibility with _processGroup()
+ * @param array &$extraOldGroups Need for compatibility with _processGroup()
+ * @return string
+ */
+ private function getFieldPath(Field $field, array &$oldConfig, array &$extraOldGroups): string
+ {
+ $path = $field->getGroupPath() . '/' . $field->getId();
+
+ /**
+ * Look for custom defined field path
+ */
+ $configPath = $field->getConfigPath();
+ if ($configPath && strrpos($configPath, '/') > 0) {
+ // Extend old data with specified section group
+ $configGroupPath = substr($configPath, 0, strrpos($configPath, '/'));
+ if (!isset($extraOldGroups[$configGroupPath])) {
+ $oldConfig = $this->extendConfig($configGroupPath, true, $oldConfig);
+ $extraOldGroups[$configGroupPath] = true;
+ }
+ $path = $configPath;
+ }
+
+ return $path;
+ }
+
+ /**
+ * Check is config value changed
+ *
+ * @param array $oldConfig
+ * @param string $path
+ * @param array $fieldData
+ * @return bool
+ */
+ private function isValueChanged(array $oldConfig, string $path, array $fieldData): bool
+ {
+ if (isset($oldConfig[$path]['value'])) {
+ $result = !isset($fieldData['value']) || $oldConfig[$path]['value'] !== $fieldData['value'];
+ } else {
+ $result = empty($fieldData['inherit']);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get changed paths
+ *
+ * @param string $sectionId
+ * @param string $groupId
+ * @param array $groupData
+ * @param array &$oldConfig
+ * @param array &$extraOldGroups
+ * @return array
+ */
+ private function getChangedPaths(
+ string $sectionId,
+ string $groupId,
+ array $groupData,
+ array &$oldConfig,
+ array &$extraOldGroups
+ ): array {
+ $changedPaths = [];
+
+ if (isset($groupData['fields'])) {
+ foreach ($groupData['fields'] as $fieldId => $fieldData) {
+ $field = $this->getField($sectionId, $groupId, $fieldId);
+ $path = $this->getFieldPath($field, $oldConfig, $extraOldGroups);
+ if ($this->isValueChanged($oldConfig, $path, $fieldData)) {
+ $changedPaths[] = $path;
+ }
+ }
+ }
+
+ if (isset($groupData['groups'])) {
+ $subSectionId = $sectionId . '/' . $groupId;
+ foreach ($groupData['groups'] as $subGroupId => $subGroupData) {
+ $subGroupChangedPaths = $this->getChangedPaths(
+ $subSectionId,
+ $subGroupId,
+ $subGroupData,
+ $oldConfig,
+ $extraOldGroups
+ );
+ $changedPaths = \array_merge($changedPaths, $subGroupChangedPaths);
+ }
+ }
+
+ return $changedPaths;
+ }
+
/**
* Process group data
*
@@ -181,8 +330,6 @@ public function save()
* @param \Magento\Framework\DB\Transaction $deleteTransaction
* @return void
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
protected function _processGroup(
$groupId,
@@ -195,92 +342,42 @@ protected function _processGroup(
\Magento\Framework\DB\Transaction $deleteTransaction
) {
$groupPath = $sectionPath . '/' . $groupId;
- $scope = $this->getScope();
- $scopeId = $this->getScopeId();
- $scopeCode = $this->getScopeCode();
- /**
- *
- * Map field names if they were cloned
- */
- /** @var $group \Magento\Config\Model\Config\Structure\Element\Group */
- $group = $this->_configStructure->getElement($groupPath);
- // set value for group field entry by fieldname
- // use extra memory
- $fieldsetData = [];
if (isset($groupData['fields'])) {
- if ($group->shouldCloneFields()) {
- $cloneModel = $group->getCloneModel();
- $mappedFields = [];
-
- /** @var $field \Magento\Config\Model\Config\Structure\Element\Field */
- foreach ($group->getChildren() as $field) {
- foreach ($cloneModel->getPrefixes() as $prefix) {
- $mappedFields[$prefix['field'] . $field->getId()] = $field->getId();
- }
- }
- }
- foreach ($groupData['fields'] as $fieldId => $fieldData) {
- $fieldsetData[$fieldId] = is_array(
- $fieldData
- ) && isset(
- $fieldData['value']
- ) ? $fieldData['value'] : null;
- }
+ /** @var \Magento\Config\Model\Config\Structure\Element\Group $group */
+ $group = $this->_configStructure->getElement($groupPath);
+ // set value for group field entry by fieldname
+ // use extra memory
+ $fieldsetData = [];
foreach ($groupData['fields'] as $fieldId => $fieldData) {
- $originalFieldId = $fieldId;
- if ($group->shouldCloneFields() && isset($mappedFields[$fieldId])) {
- $originalFieldId = $mappedFields[$fieldId];
- }
- /** @var $field \Magento\Config\Model\Config\Structure\Element\Field */
- $field = $this->_configStructure->getElement($groupPath . '/' . $originalFieldId);
-
+ $field = $this->getField($sectionPath, $groupId, $fieldId);
/** @var \Magento\Framework\App\Config\ValueInterface $backendModel */
- $backendModel = $field->hasBackendModel() ? $field
- ->getBackendModel() : $this
- ->_configValueFactory
- ->create();
+ $backendModel = $field->hasBackendModel()
+ ? $field->getBackendModel()
+ : $this->_configValueFactory->create();
+ if (!isset($fieldData['value'])) {
+ $fieldData['value'] = null;
+ }
+ $fieldsetData[$fieldId] = $fieldData['value'];
$data = [
'field' => $fieldId,
'groups' => $groups,
'group_id' => $group->getId(),
- 'scope' => $scope,
- 'scope_id' => $scopeId,
- 'scope_code' => $scopeCode,
+ 'scope' => $this->getScope(),
+ 'scope_id' => $this->getScopeId(),
+ 'scope_code' => $this->getScopeCode(),
'field_config' => $field->getData(),
- 'fieldset_data' => $fieldsetData
+ 'fieldset_data' => $fieldsetData,
];
$backendModel->addData($data);
-
$this->_checkSingleStoreMode($field, $backendModel);
- if (false == isset($fieldData['value'])) {
- $fieldData['value'] = null;
- }
-
- $path = $field->getGroupPath() . '/' . $fieldId;
- /**
- * Look for custom defined field path
- */
- if ($field && $field->getConfigPath()) {
- $configPath = $field->getConfigPath();
- if (!empty($configPath) && strrpos($configPath, '/') > 0) {
- // Extend old data with specified section group
- $configGroupPath = substr($configPath, 0, strrpos($configPath, '/'));
- if (!isset($extraOldGroups[$configGroupPath])) {
- $oldConfig = $this->extendConfig($configGroupPath, true, $oldConfig);
- $extraOldGroups[$configGroupPath] = true;
- }
- $path = $configPath;
- }
- }
-
- $inherit = !empty($fieldData['inherit']);
-
+ $path = $this->getFieldPath($field, $extraOldGroups, $oldConfig);
$backendModel->setPath($path)->setValue($fieldData['value']);
+ $inherit = !empty($fieldData['inherit']);
if (isset($oldConfig[$path])) {
$backendModel->setConfigId($oldConfig[$path]['config_id']);
diff --git a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php
index 2ddbbd5ffe1e8..5e11e46439c8b 100644
--- a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php
@@ -152,52 +152,50 @@ public function testSaveToCheckAdminSystemConfigChangedSectionEvent()
public function testSaveToCheckScopeDataSet()
{
$transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class);
-
$this->_transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock));
$this->_configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([]));
- $this->_eventManagerMock->expects(
- $this->at(0)
- )->method(
- 'dispatch'
- )->with(
- $this->equalTo('admin_system_config_changed_section_'),
- $this->arrayHasKey('website')
- );
-
- $this->_eventManagerMock->expects(
- $this->at(0)
- )->method(
- 'dispatch'
- )->with(
- $this->equalTo('admin_system_config_changed_section_'),
- $this->arrayHasKey('store')
- );
+ $this->_eventManagerMock->expects($this->at(0))
+ ->method('dispatch')
+ ->with(
+ $this->equalTo('admin_system_config_changed_section_section'),
+ $this->arrayHasKey('website')
+ );
+ $this->_eventManagerMock->expects($this->at(0))
+ ->method('dispatch')
+ ->with(
+ $this->equalTo('admin_system_config_changed_section_section'),
+ $this->arrayHasKey('store')
+ );
$group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class);
+ $group->method('getPath')->willReturn('section/1');
$field = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Field::class);
-
- $this->_configStructure->expects(
- $this->at(0)
- )->method(
- 'getElement'
- )->with(
- '/1'
- )->will(
- $this->returnValue($group)
- );
-
- $this->_configStructure->expects(
- $this->at(1)
- )->method(
- 'getElement'
- )->with(
- '/1/key'
- )->will(
- $this->returnValue($field)
- );
+ $field->method('getGroupPath')->willReturn('section/1');
+ $field->method('getId')->willReturn('key');
+
+ $this->_configStructure->expects($this->at(0))
+ ->method('getElement')
+ ->with('section/1')
+ ->will($this->returnValue($group));
+ $this->_configStructure->expects($this->at(1))
+ ->method('getElement')
+ ->with('section/1')
+ ->will($this->returnValue($group));
+ $this->_configStructure->expects($this->at(2))
+ ->method('getElement')
+ ->with('section/1/key')
+ ->will($this->returnValue($field));
+ $this->_configStructure->expects($this->at(3))
+ ->method('getElement')
+ ->with('section/1')
+ ->will($this->returnValue($group));
+ $this->_configStructure->expects($this->at(4))
+ ->method('getElement')
+ ->with('section/1/key')
+ ->will($this->returnValue($field));
$website = $this->createMock(\Magento\Store\Model\Website::class);
$website->expects($this->any())->method('getCode')->will($this->returnValue('website_code'));
@@ -206,19 +204,16 @@ public function testSaveToCheckScopeDataSet()
$this->_storeManager->expects($this->any())->method('isSingleStoreMode')->will($this->returnValue(true));
$this->_model->setWebsite('website');
-
+ $this->_model->setSection('section');
$this->_model->setGroups(['1' => ['fields' => ['key' => ['data']]]]);
$backendModel = $this->createPartialMock(
\Magento\Framework\App\Config\Value::class,
['setPath', 'addData', '__sleep', '__wakeup']
);
- $backendModel->expects(
- $this->once()
- )->method(
- 'addData'
- )->with(
- [
+ $backendModel->expects($this->once())
+ ->method('addData')
+ ->with([
'field' => 'key',
'groups' => [1 => ['fields' => ['key' => ['data']]]],
'group_id' => null,
@@ -227,17 +222,11 @@ public function testSaveToCheckScopeDataSet()
'scope_code' => 'website_code',
'field_config' => null,
'fieldset_data' => ['key' => null],
- ]
- );
- $backendModel->expects(
- $this->once()
- )->method(
- 'setPath'
- )->with(
- '/key'
- )->will(
- $this->returnValue($backendModel)
- );
+ ]);
+ $backendModel->expects($this->once())
+ ->method('setPath')
+ ->with('section/1/key')
+ ->will($this->returnValue($backendModel));
$this->_dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel));
diff --git a/app/code/Magento/Contact/Controller/Index/Post.php b/app/code/Magento/Contact/Controller/Index/Post.php
index ee2d23b74df24..4392cb2148a51 100644
--- a/app/code/Magento/Contact/Controller/Index/Post.php
+++ b/app/code/Magento/Contact/Controller/Index/Post.php
@@ -12,7 +12,6 @@
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Framework\Controller\Result\Redirect;
use Magento\Framework\Exception\LocalizedException;
-use Magento\Framework\HTTP\PhpEnvironment\Request;
use Psr\Log\LoggerInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\DataObject;
@@ -67,7 +66,7 @@ public function __construct(
*/
public function execute()
{
- if (!$this->isPostRequest()) {
+ if (!$this->getRequest()->isPost()) {
return $this->resultRedirectFactory->create()->setPath('*/*/');
}
try {
@@ -101,16 +100,6 @@ private function sendEmail($post)
);
}
- /**
- * @return bool
- */
- private function isPostRequest()
- {
- /** @var Request $request */
- $request = $this->getRequest();
- return !empty($request->getPostValue());
- }
-
/**
* @return array
* @throws \Exception
diff --git a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php
index 0e1ddf21c2a08..0213173737b9b 100644
--- a/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php
+++ b/app/code/Magento/Contact/Test/Unit/Controller/Index/PostTest.php
@@ -78,7 +78,7 @@ protected function setUp()
$this->createMock(\Magento\Framework\Message\ManagerInterface::class);
$this->requestStub = $this->createPartialMock(
\Magento\Framework\App\Request\Http::class,
- ['getPostValue', 'getParams', 'getParam']
+ ['getPostValue', 'getParams', 'getParam', 'isPost']
);
$this->redirectResultMock = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class);
$this->redirectResultMock->method('setPath')->willReturnSelf();
@@ -174,6 +174,10 @@ public function testExecuteValidPost()
*/
private function stubRequestPostData($post)
{
+ $this->requestStub
+ ->expects($this->once())
+ ->method('isPost')
+ ->willReturn(!empty($post));
$this->requestStub->method('getPostValue')->willReturn($post);
$this->requestStub->method('getParams')->willReturn($post);
$this->requestStub->method('getParam')->willReturnCallback(
diff --git a/app/code/Magento/Customer/Block/Account/Dashboard/Info.php b/app/code/Magento/Customer/Block/Account/Dashboard/Info.php
index ded7238edc755..87132c3afb8bc 100644
--- a/app/code/Magento/Customer/Block/Account/Dashboard/Info.php
+++ b/app/code/Magento/Customer/Block/Account/Dashboard/Info.php
@@ -102,7 +102,7 @@ public function getSubscriptionObject()
$this->_subscription = $this->_createSubscriber();
$customer = $this->getCustomer();
if ($customer) {
- $this->_subscription->loadByEmail($customer->getEmail());
+ $this->_subscription->loadByCustomerId($customer->getId());
}
}
return $this->_subscription;
diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml
index bd383ba237583..d37d6ee49edfb 100644
--- a/app/code/Magento/Customer/etc/di.xml
+++ b/app/code/Magento/Customer/etc/di.xml
@@ -340,9 +340,9 @@
- - customer_group_code
- - customer_group_id
- - class_name
+ - main_table.customer_group_code
+ - main_table.customer_group_id
+ - tax_class_table.class_name
@@ -350,9 +350,9 @@
- - customer_group_code
- - customer_group_id
- - class_name
+ - main_table.customer_group_code
+ - main_table.customer_group_id
+ - tax_class_table.class_name
- ASC
diff --git a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php
index d22795f127138..829df74a1b0ed 100644
--- a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php
+++ b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php
@@ -256,7 +256,10 @@ public function reindexRow($id)
$this->indexer->reindexRow($id);
$dependentIndexerIds = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($this->indexer->getId());
foreach ($dependentIndexerIds as $indexerId) {
- $this->indexerRegistry->get($indexerId)->reindexRow($id);
+ $dependentIndexer = $this->indexerRegistry->get($indexerId);
+ if (!$dependentIndexer->isScheduled()) {
+ $dependentIndexer->reindexRow($id);
+ }
}
}
@@ -268,7 +271,10 @@ public function reindexList($ids)
$this->indexer->reindexList($ids);
$dependentIndexerIds = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($this->indexer->getId());
foreach ($dependentIndexerIds as $indexerId) {
- $this->indexerRegistry->get($indexerId)->reindexList($ids);
+ $dependentIndexer = $this->indexerRegistry->get($indexerId);
+ if (!$dependentIndexer->isScheduled()) {
+ $dependentIndexer->reindexList($ids);
+ }
}
}
}
diff --git a/app/code/Magento/NewRelicReporting/Model/Config.php b/app/code/Magento/NewRelicReporting/Model/Config.php
index 32e1078c01c9d..bcc87ec72d53f 100644
--- a/app/code/Magento/NewRelicReporting/Model/Config.php
+++ b/app/code/Magento/NewRelicReporting/Model/Config.php
@@ -161,6 +161,16 @@ public function getNewRelicAppName()
return (string)$this->scopeConfig->getValue('newrelicreporting/general/app_name');
}
+ /**
+ * Returns configured separate apps value
+ *
+ * @return bool
+ */
+ public function isSeparateApps()
+ {
+ return (bool)$this->scopeConfig->getValue('newrelicreporting/general/separate_apps');
+ }
+
/**
* Returns config setting for overall cron to be enabled
*
diff --git a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php
index 845ed0429d2c3..ec21e06976b8b 100644
--- a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php
+++ b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php
@@ -41,6 +41,19 @@ public function reportError($exception)
}
}
+ /**
+ * Wrapper for 'newrelic_set_appname'
+ *
+ * @param string $appName
+ * @return void
+ */
+ public function setAppName(string $appName)
+ {
+ if (extension_loaded('newrelic')) {
+ newrelic_set_appname($appName);
+ }
+ }
+
/**
* Checks whether newrelic-php5 agent is installed
*
diff --git a/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php b/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php
new file mode 100644
index 0000000000000..0be7c72689e7f
--- /dev/null
+++ b/app/code/Magento/NewRelicReporting/Plugin/StatePlugin.php
@@ -0,0 +1,92 @@
+config = $config;
+ $this->newRelicWrapper = $newRelicWrapper;
+ $this->logger = $logger;
+ }
+
+ /**
+ * Set separate appname
+ *
+ * @param State $subject
+ * @param null $result
+ * @return void
+ *
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterSetAreaCode(State $state, $result)
+ {
+ if (!$this->shouldSetAppName()) {
+ return $result;
+ }
+
+ try {
+ $this->newRelicWrapper->setAppName($this->appName($state));
+ } catch (LocalizedException $e) {
+ $this->logger->critical($e);
+ return $result;
+ }
+ }
+
+ private function appName(State $state)
+ {
+ $code = $state->getAreaCode();
+ $current = $this->config->getNewRelicAppName();
+
+ return $current . ';' . $current . '_' . $code;
+ }
+
+ private function shouldSetAppName()
+ {
+ if (!$this->config->isNewRelicEnabled()) {
+ return false;
+ }
+
+ if (!$this->config->getNewRelicAppName()) {
+ return false;
+ }
+
+ if (!$this->config->isSeparateApps()) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml b/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml
index 582b7c752386a..98f9c55adbdf0 100644
--- a/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml
+++ b/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml
@@ -46,6 +46,11 @@
This is located by navigating to Settings from the New Relic APM website
+
+
+ Magento\Config\Model\Config\Source\Yesno
+ In addition to the main app (which includes all PHP execution), separate apps for adminhtml and frontend will be created. Requires New Relic Application Name to be set.
+
diff --git a/app/code/Magento/NewRelicReporting/etc/di.xml b/app/code/Magento/NewRelicReporting/etc/di.xml
index 2dccc45c1129b..bab7d6611f14b 100644
--- a/app/code/Magento/NewRelicReporting/etc/di.xml
+++ b/app/code/Magento/NewRelicReporting/etc/di.xml
@@ -30,6 +30,9 @@
+
+
+
diff --git a/app/code/Magento/NewRelicReporting/i18n/en_US.csv b/app/code/Magento/NewRelicReporting/i18n/en_US.csv
index 433b1b22fcddd..5ea64d3d43439 100644
--- a/app/code/Magento/NewRelicReporting/i18n/en_US.csv
+++ b/app/code/Magento/NewRelicReporting/i18n/en_US.csv
@@ -21,3 +21,5 @@ General,General
"This is located by navigating to Settings from the New Relic APM website","This is located by navigating to Settings from the New Relic APM website"
Cron,Cron
"Enable Cron","Enable Cron"
+"Send Adminhtml and Frontend as Separate Apps","Send Adminhtml and Frontend as Separate Apps"
+"In addition to the main app (which includes all PHP execution), separate apps for adminhtml and frontend will be created. Requires New Relic Application Name to be set.","In addition to the main app (which includes all PHP execution), separate apps for adminhtml and frontend will be created. Requires New Relic Application Name to be set."
diff --git a/app/code/Magento/Quote/Model/CouponManagement.php b/app/code/Magento/Quote/Model/CouponManagement.php
index 87398ad36cfab..a46cc344cadb7 100644
--- a/app/code/Magento/Quote/Model/CouponManagement.php
+++ b/app/code/Magento/Quote/Model/CouponManagement.php
@@ -56,6 +56,9 @@ public function set($cartId, $couponCode)
if (!$quote->getItemsCount()) {
throw new NoSuchEntityException(__('Cart %1 doesn\'t contain products', $cartId));
}
+ if (!$quote->getStoreId()) {
+ throw new NoSuchEntityException(__('Cart isn\'t assigned to correct store'));
+ }
$quote->getShippingAddress()->setCollectShippingRates(true);
try {
diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php
index 536c58861a253..dca9aa36fb25d 100644
--- a/app/code/Magento/Quote/Model/Quote.php
+++ b/app/code/Magento/Quote/Model/Quote.php
@@ -1400,7 +1400,7 @@ public function getAllVisibleItems()
{
$items = [];
foreach ($this->getItemsCollection() as $item) {
- if (!$item->isDeleted() && !$item->getParentItemId()) {
+ if (!$item->isDeleted() && !$item->getParentItemId() && !$item->getParentItem()) {
$items[] = $item;
}
}
diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php
index d3967794b300a..01c21bbbe50a7 100644
--- a/app/code/Magento/Quote/Model/QuoteRepository.php
+++ b/app/code/Magento/Quote/Model/QuoteRepository.php
@@ -212,7 +212,7 @@ protected function loadQuote($loadMethod, $loadField, $identifier, array $shared
if ($sharedStoreIds) {
$quote->setSharedStoreIds($sharedStoreIds);
}
- $quote->$loadMethod($identifier)->setStoreId($this->storeManager->getStore()->getId());
+ $quote->setStoreId($this->storeManager->getStore()->getId())->$loadMethod($identifier);
if (!$quote->getId()) {
throw NoSuchEntityException::singleField($loadField, $identifier);
}
diff --git a/app/code/Magento/Quote/Plugin/UpdateQuoteStore.php b/app/code/Magento/Quote/Plugin/UpdateQuoteStore.php
new file mode 100644
index 0000000000000..de2fc76b9179f
--- /dev/null
+++ b/app/code/Magento/Quote/Plugin/UpdateQuoteStore.php
@@ -0,0 +1,69 @@
+quoteRepository = $quoteRepository;
+ $this->checkoutSession = $checkoutSession;
+ }
+
+ /**
+ * Update store id in active quote after store view switching.
+ *
+ * @param StoreCookieManagerInterface $subject
+ * @param null $result
+ * @param StoreInterface $store
+ * @return void
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterSetStoreCookie(
+ StoreCookieManagerInterface $subject,
+ $result,
+ StoreInterface $store
+ ) {
+ $storeCodeFromCookie = $subject->getStoreCodeFromCookie();
+ if (null === $storeCodeFromCookie) {
+ return;
+ }
+
+ $quote = $this->checkoutSession->getQuote();
+ if ($quote->getIsActive() && $store->getCode() != $storeCodeFromCookie) {
+ $quote->setStoreId(
+ $store->getId()
+ );
+ $this->quoteRepository->save($quote);
+ }
+ }
+}
diff --git a/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php
index 27824ed44ef3f..444d7ced635d7 100644
--- a/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php
+++ b/app/code/Magento/Quote/Test/Unit/Model/CouponManagementTest.php
@@ -49,6 +49,7 @@ protected function setUp()
'save',
'getShippingAddress',
'getCouponCode',
+ 'getStoreId',
'__wakeup'
]);
$this->quoteAddressMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Address::class, [
@@ -100,6 +101,9 @@ public function testSetWhenCouldNotApplyCoupon()
$cartId = 33;
$couponCode = '153a-ABC';
+ $this->storeMock->expects($this->any())->method('getId')->will($this->returnValue(1));
+ $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($this->returnValue(1));
+
$this->quoteRepositoryMock->expects($this->once())
->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock));
$this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(12));
@@ -127,6 +131,9 @@ public function testSetWhenCouponCodeIsInvalid()
$cartId = 33;
$couponCode = '153a-ABC';
+ $this->storeMock->expects($this->any())->method('getId')->will($this->returnValue(1));
+ $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($this->returnValue(1));
+
$this->quoteRepositoryMock->expects($this->once())
->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock));
$this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(12));
@@ -146,6 +153,9 @@ public function testSet()
$cartId = 33;
$couponCode = '153a-ABC';
+ $this->storeMock->expects($this->any())->method('getId')->will($this->returnValue(1));
+ $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($this->returnValue(1));
+
$this->quoteRepositoryMock->expects($this->once())
->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock));
$this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(12));
diff --git a/app/code/Magento/Quote/etc/frontend/di.xml b/app/code/Magento/Quote/etc/frontend/di.xml
new file mode 100644
index 0000000000000..25acd6763ba56
--- /dev/null
+++ b/app/code/Magento/Quote/etc/frontend/di.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Magento\Quote\Model\QuoteRepository\Proxy
+ Magento\Checkout\Model\Session\Proxy
+
+
+
+
+
+
diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php
index 67d2ce4f4f148..d19b248c8008f 100644
--- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php
+++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php
@@ -6,8 +6,29 @@
*/
namespace Magento\Sitemap\Controller\Adminhtml\Sitemap;
+use Magento\Backend\App\Action;
+use Magento\Store\Model\App\Emulation;
+use Magento\Framework\App\ObjectManager;
+
class Generate extends \Magento\Sitemap\Controller\Adminhtml\Sitemap
{
+ /** @var \Magento\Store\Model\App\Emulation $appEmulation */
+ private $appEmulation;
+
+ /**
+ * Generate constructor.
+ * @param Action\Context $context
+ * @param \Magento\Store\Model\App\Emulation|null $appEmulation
+ */
+ public function __construct(
+ Action\Context $context,
+ Emulation $appEmulation = null
+ ) {
+ parent::__construct($context);
+ $this->appEmulation = $appEmulation ?: ObjectManager::getInstance()
+ ->get(\Magento\Store\Model\App\Emulation::class);
+ }
+
/**
* Generate sitemap
*
@@ -23,6 +44,12 @@ 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->addSuccess(
@@ -32,6 +59,8 @@ public function execute()
$this->messageManager->addError($e->getMessage());
} catch (\Exception $e) {
$this->messageManager->addException($e, __('We can\'t generate the sitemap right now.'));
+ } finally {
+ $this->appEmulation->stopEnvironmentEmulation();
}
} else {
$this->messageManager->addError(__('We can\'t find a sitemap to generate.'));
diff --git a/app/code/Magento/Ui/Model/Export/ConvertToCsv.php b/app/code/Magento/Ui/Model/Export/ConvertToCsv.php
index 9eba829982533..40b10749db21e 100644
--- a/app/code/Magento/Ui/Model/Export/ConvertToCsv.php
+++ b/app/code/Magento/Ui/Model/Export/ConvertToCsv.php
@@ -6,6 +6,7 @@
namespace Magento\Ui\Model\Export;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\WriteInterface;
@@ -31,11 +32,17 @@ class ConvertToCsv
*/
protected $pageSize = null;
+ /**
+ * @var Filter
+ */
+ protected $filter;
+
/**
* @param Filesystem $filesystem
* @param Filter $filter
* @param MetadataProvider $metadataProvider
* @param int $pageSize
+ * @throws FileSystemException
*/
public function __construct(
Filesystem $filesystem,
diff --git a/app/code/Magento/Ui/Model/Export/ConvertToXml.php b/app/code/Magento/Ui/Model/Export/ConvertToXml.php
index b707742063dbd..5f6e45a948f24 100644
--- a/app/code/Magento/Ui/Model/Export/ConvertToXml.php
+++ b/app/code/Magento/Ui/Model/Export/ConvertToXml.php
@@ -10,6 +10,7 @@
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Convert\Excel;
use Magento\Framework\Convert\ExcelFactory;
+use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\WriteInterface;
@@ -50,12 +51,18 @@ class ConvertToXml
*/
protected $fields;
+ /**
+ * @var Filter
+ */
+ protected $filter;
+
/**
* @param Filesystem $filesystem
* @param Filter $filter
* @param MetadataProvider $metadataProvider
* @param ExcelFactory $excelFactory
* @param SearchResultIteratorFactory $iteratorFactory
+ * @throws FileSystemException
*/
public function __construct(
Filesystem $filesystem,
@@ -88,6 +95,7 @@ protected function getOptions()
* Returns DB fields list
*
* @return array
+ * @throws LocalizedException
*/
protected function getFields()
{
@@ -103,6 +111,7 @@ protected function getFields()
*
* @param DocumentInterface $document
* @return array
+ * @throws LocalizedException
*/
public function getRowData(DocumentInterface $document)
{
diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows-grid.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows-grid.js
index d4962c0658bdf..27a32b04a7e1c 100644
--- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows-grid.js
+++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows-grid.js
@@ -109,9 +109,8 @@ define([
* @param {String|Number} recordId
*/
deleteRecord: function (index, recordId) {
- this._super();
-
this.updateInsertData(recordId);
+ this._super();
},
/**
diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js
index 4641ae96c9ece..b81119f2bd5f3 100644
--- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js
+++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js
@@ -133,7 +133,7 @@ define([
/**
* Retrieves from the list file which matches
- * search criteria implemented in itertor function.
+ * search criteria implemented in iterator function.
*
* @param {Function} fn - Function that will be invoked
* for each file in the list.
@@ -193,7 +193,7 @@ define([
},
/**
- * Returns path to the files' preview image.
+ * Returns path to the file's preview image.
*
* @param {Object} file
* @returns {String}
@@ -310,7 +310,7 @@ define([
/**
* Abstract handler which is invoked when files are choosed for upload.
- * May be used for implementation of aditional validation rules,
+ * May be used for implementation of additional validation rules,
* e.g. total files and a total size rules.
*
* @abstract
diff --git a/app/code/Magento/UrlRewrite/Block/Catalog/Product/Edit.php b/app/code/Magento/UrlRewrite/Block/Catalog/Product/Edit.php
index fa06a47bb56ff..ca3756a356a85 100644
--- a/app/code/Magento/UrlRewrite/Block/Catalog/Product/Edit.php
+++ b/app/code/Magento/UrlRewrite/Block/Catalog/Product/Edit.php
@@ -55,7 +55,7 @@ protected function _prepareLayoutFeatures()
}
if ($this->_getProduct()->getId()) {
- $this->_addProductLinkBlock($this->_getProduct());
+ $this->_addProductLinkBlock();
}
if ($this->_getCategory()->getId()) {
diff --git a/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php b/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php
index 4468a5af6cce1..1e3a9c64e927e 100644
--- a/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php
+++ b/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php
@@ -39,7 +39,7 @@ class Generator extends AbstractSchemaGenerator
const UNAUTHORIZED_DESCRIPTION = '401 Unauthorized';
/** Array signifier */
- const ARRAY_SIGNIFIER = '[]';
+ const ARRAY_SIGNIFIER = '[0]';
/**
* Swagger factory instance.
diff --git a/app/code/Magento/Wishlist/Block/Rss/EmailLink.php b/app/code/Magento/Wishlist/Block/Rss/EmailLink.php
index 8a5db15dfb5db..4a5f116cd8293 100644
--- a/app/code/Magento/Wishlist/Block/Rss/EmailLink.php
+++ b/app/code/Magento/Wishlist/Block/Rss/EmailLink.php
@@ -24,7 +24,7 @@ class EmailLink extends Link
protected $_template = 'rss/email.phtml';
/**
- * @return string
+ * @return array
*/
protected function getLinkParams()
{
diff --git a/app/code/Magento/Wishlist/Block/Rss/Link.php b/app/code/Magento/Wishlist/Block/Rss/Link.php
index de068d68c2207..3e716c863862b 100644
--- a/app/code/Magento/Wishlist/Block/Rss/Link.php
+++ b/app/code/Magento/Wishlist/Block/Rss/Link.php
@@ -72,7 +72,7 @@ public function isRssAllowed()
}
/**
- * @return string
+ * @return array
*/
protected function getLinkParams()
{
diff --git a/app/code/Magento/Wishlist/Helper/Data.php b/app/code/Magento/Wishlist/Helper/Data.php
index 6c34adffcd851..968a7f1db8c39 100644
--- a/app/code/Magento/Wishlist/Helper/Data.php
+++ b/app/code/Magento/Wishlist/Helper/Data.php
@@ -503,7 +503,7 @@ public function isAllowInCart()
/**
* Retrieve customer name
*
- * @return string|void
+ * @return string|null
*/
public function getCustomerName()
{
diff --git a/app/code/Magento/Wishlist/Plugin/Ui/DataProvider/WishlistSettings.php b/app/code/Magento/Wishlist/Plugin/Ui/DataProvider/WishlistSettings.php
index 7f37486c7936c..df8bb3191bf9f 100644
--- a/app/code/Magento/Wishlist/Plugin/Ui/DataProvider/WishlistSettings.php
+++ b/app/code/Magento/Wishlist/Plugin/Ui/DataProvider/WishlistSettings.php
@@ -32,7 +32,7 @@ public function __construct(Data $helper)
/**
* Add tax data to result
*
- * @param \Magento\Checkout\CustomerData\Cart $subject
+ * @param \Magento\Catalog\Ui\DataProvider\Product\Listing\DataProvider $subject
* @param array $result
* @return array
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
diff --git a/app/design/frontend/Magento/luma/web/css/source/_actions-toolbar.less b/app/design/frontend/Magento/luma/web/css/source/_actions-toolbar.less
index 096b93675a92c..d81d0a8388a78 100644
--- a/app/design/frontend/Magento/luma/web/css/source/_actions-toolbar.less
+++ b/app/design/frontend/Magento/luma/web/css/source/_actions-toolbar.less
@@ -22,10 +22,6 @@
&:last-child {
margin-bottom: 0;
}
-
- &.primary {
- // &:extend(.abs-button-l all);
- }
}
&:last-child {
diff --git a/app/design/frontend/Magento/luma/web/css/source/_forms.less b/app/design/frontend/Magento/luma/web/css/source/_forms.less
index 1c9ffb1606b26..7c5027aef113b 100644
--- a/app/design/frontend/Magento/luma/web/css/source/_forms.less
+++ b/app/design/frontend/Magento/luma/web/css/source/_forms.less
@@ -91,11 +91,7 @@
.select-styling() {
.lib-css(appearance, none, 1);
appearance: none;
- background: @select__background url('../images/select-bg.svg') no-repeat 100% 45%;
background-size: 30px 60px;
- border: 1px solid @border-color__base;
- height: 32px;
- padding-right: 25px;
text-indent: .01em;
text-overflow: '';
diff --git a/app/design/frontend/Magento/luma/web/css/source/_theme.less b/app/design/frontend/Magento/luma/web/css/source/_theme.less
index b94ffc08b77ec..957e622d6a0bb 100644
--- a/app/design/frontend/Magento/luma/web/css/source/_theme.less
+++ b/app/design/frontend/Magento/luma/web/css/source/_theme.less
@@ -166,6 +166,13 @@
// Forms
// ---------------------------------------------
+
+// Select
+@select__background: @form-element-input__background url('../images/select-bg.svg') no-repeat 100% 45%;
+@select__border: 1px solid @border-color__base;
+@select__height: 32px;
+@select__padding: 4px 25px @indent__xs @indent__s;
+
// Form fieldset
@form-fieldset-legend__font-size: 18px;
@form-fieldset-legend__font-weight: @font-weight__light;
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php
index 3a66629116de9..f616bd27e23f4 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php
@@ -91,13 +91,24 @@ public function selectShippingMethod(array $method)
* @param array $method
* @return bool
*/
- public function isShippingMethodAvaiable(array $method)
+ public function isShippingMethodAvailable(array $method)
{
$this->waitForShippingRates();
$selector = sprintf($this->shippingMethod, $method['shipping_method'], $method['shipping_service']);
return $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)->isVisible();
}
+ /**
+ * @deprecated
+ * @see isShippingMethodAvailable
+ * @param array $method
+ * @return bool
+ */
+ public function isShippingMethodAvaiable(array $method)
+ {
+ return $this->isShippingMethodAvailable($method);
+ }
+
/**
* Click continue button.
*
diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php
index 482b7d213e671..ea5de8d06be00 100644
--- a/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php
+++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertCityBasedShippingRateChanged.php
@@ -30,10 +30,10 @@ public function processAssert(CheckoutOnepage $checkoutOnepage, $shippingMethod,
'Shipping rate has not been changed.'
);
}
- $shippingAvailability = $isShippingAvailable ? 'avaiable' : 'unavailable';
+ $shippingAvailability = $isShippingAvailable ? 'available' : 'unavailable';
\PHPUnit_Framework_Assert::assertEquals(
$isShippingAvailable,
- $checkoutOnepage->getShippingMethodBlock()->isShippingMethodAvaiable($shippingMethod),
+ $checkoutOnepage->getShippingMethodBlock()->isShippingMethodAvailable($shippingMethod),
"Shipping rates for {$shippingMethod['shipping_service']} should be $shippingAvailability."
);
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php
index 21cbd6b9fb92e..3f2a0cdeaba0f 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php
@@ -49,7 +49,7 @@ public function testAddAction()
);
$this->assertSessionMessages(
- $this->equalTo(['You added product Simple Product 1 Name to the comparison list.']),
+ $this->equalTo(['You added product Simple Product 1 Name to the comparison list.']),
MessageInterface::TYPE_SUCCESS
);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php
index a33b70bbd0160..64719a8b289df 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceTest.php
@@ -8,6 +8,7 @@
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
+use Magento\CatalogInventory\Api\StockRegistryInterface;
/**
* Tests product model:
@@ -23,9 +24,15 @@ class ProductPriceTest extends \PHPUnit\Framework\TestCase
*/
protected $_model;
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
protected function setUp()
{
$this->_model = Bootstrap::getObjectManager()->create(Product::class);
+ $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class);
}
public function testGetPrice()
@@ -80,8 +87,7 @@ public function testSetGetFinalPrice()
*/
public function testGetMinPrice()
{
- $productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class);
- $product = $productRepository->get('simple');
+ $product = $this->productRepository->get('simple');
$collection = Bootstrap::getObjectManager()->create(Collection::class);
$collection->addIdFilter($product->getId());
$collection->addPriceData();
@@ -90,4 +96,28 @@ public function testGetMinPrice()
$product = $collection->getFirstItem();
$this->assertEquals(333, $product->getData('min_price'));
}
+
+ /**
+ * @magentoDbIsolation disabled
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php
+ */
+ public function testGetMinPriceForComposite()
+ {
+ $confProduct = $this->productRepository->get('configurable');
+ $collection = Bootstrap::getObjectManager()->create(Collection::class);
+ $collection->addIdFilter($confProduct->getId());
+ $collection->addPriceData();
+ $collection->load();
+ $product = $collection->getFirstItem();
+ $this->assertEquals(10, $product->getData('min_price'));
+
+ $childProduct = $this->productRepository->get('simple_10');
+ $stockRegistry = Bootstrap::getObjectManager()->get(StockRegistryInterface::class);
+ $stockItem = $stockRegistry->getStockItem($childProduct->getId());
+ $stockItem->setIsInStock(false);
+ $stockRegistry->updateStockItemBySku($childProduct->getSku(), $stockItem);
+ $collection->clear()->load();
+ $product = $collection->getFirstItem();
+ $this->assertEquals(20, $product->getData('min_price'));
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
index 7393c6ebc163f..a5e55e181cbaf 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
@@ -50,6 +50,7 @@ public function testAddPriceDataOnSchedule()
{
$this->processor->getIndexer()->setScheduled(true);
$this->assertTrue($this->processor->getIndexer()->isScheduled());
+
$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
@@ -81,7 +82,6 @@ public function testAddPriceDataOnSchedule()
$product = reset($items);
$this->assertCount(2, $items);
$this->assertEquals(15, $product->getPrice());
- $this->processor->getIndexer()->reindexList([1]);
$this->processor->getIndexer()->setScheduled(false);
}
diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php
index d28e765921d6e..495d19a2745e5 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php
@@ -12,13 +12,6 @@
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SortOrder;
-/**
- * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/attribute.php
- * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/product_with_attribute.php
- * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php
- * @magentoDbIsolation enabled
- * @magentoAppIsolation enabled
- */
class PriceTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -31,6 +24,12 @@ protected function setUp()
$this->resourceRule = Bootstrap::getObjectManager()->get(Rule::class);
}
+ /**
+ * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/configurable_product.php
+ * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
+ */
public function testPriceApplying()
{
$customerGroupId = 1;
@@ -44,7 +43,6 @@ public function testPriceApplying()
/** @var \Magento\Catalog\Model\Product $simpleProduct */
$simpleProduct = $collection->getFirstItem();
$simpleProduct->setPriceCalculation(false);
-
$rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), $websiteId, $customerGroupId, $simpleProductId);
$this->assertEquals($rulePrice, $simpleProduct->getFinalPrice());
@@ -55,11 +53,14 @@ public function testPriceApplying()
$collection->load();
/** @var \Magento\Catalog\Model\Product $confProduct */
$confProduct = $collection->getFirstItem();
-
- $this->assertEquals($simpleProduct->getMinimalPrice(), $confProduct->getMinimalPrice());
+ $this->assertEquals($simpleProduct->getFinalPrice(), $confProduct->getMinimalPrice());
}
/**
+ * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/simple_products.php
+ * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
* @magentoAppArea frontend
*/
public function testSortByPrice()
@@ -70,10 +71,10 @@ public function testSortByPrice()
$searchCriteria->setSortOrders([$sortOrder]);
$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
$searchResults = $productRepository->getList($searchCriteria);
- $products = $searchResults->getItems();
+ /** @var \Magento\Catalog\Model\Product[] $products */
+ $products = array_values($searchResults->getItems());
- /** @var \Magento\Catalog\Model\Product $product1 */
- $product1 = array_values($products)[0];
+ $product1 = $products[0];
$product1->setPriceCalculation(false);
$this->assertEquals('simple1', $product1->getSku());
$rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product1->getId());
diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php
similarity index 52%
rename from dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php
rename to dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php
index 917d29b1ae5f2..971f9f82dc1ec 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product.php
@@ -5,12 +5,12 @@
*/
require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute.php';
+require __DIR__ . '/simple_products.php';
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$storeManager = $objectManager->get(\Magento\Store\Model\StoreManager::class);
$store = $storeManager->getStore('default');
-
$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
$installer = $objectManager->get(\Magento\Catalog\Setup\CategorySetup::class);
@@ -18,63 +18,22 @@
$attributeValues = [];
$associatedProductIds = [];
-/** @var Magento\Eav\Model\Entity\Attribute\Option[] $options */
+$attributeRepository = $objectManager->get(\Magento\Eav\Api\AttributeRepositoryInterface::class);
+$attribute = $attributeRepository->get('catalog_product', 'test_configurable');
$options = $attribute->getOptions();
array_shift($options); //remove the first option which is empty
-
-$product = $objectManager->create(\Magento\Catalog\Model\Product::class)
- ->setTypeId('simple')
- ->setId(1)
- ->setAttributeSetId($attributeSetId)
- ->setWebsiteIds([1])
- ->setName('Simple Product 1')
- ->setSku('simple1')
- ->setPrice(10)
- ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
- ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
- ->setStockData([
- 'use_config_manage_stock' => 1,
- 'qty' => 100,
- 'is_qty_decimal' => 0,
- 'is_in_stock' => 1,
- ]);
-$option = array_shift($options);
-$product->setTestConfigurable($option->getValue());
-$productRepository->save($product);
-$attributeValues[] = [
- 'label' => 'test',
- 'attribute_id' => $attribute->getId(),
- 'value_index' => $option->getValue(),
-];
-$associatedProductIds[] = $product->getId();
-$productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class);
-$productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId());
-
-$product = $objectManager->create(\Magento\Catalog\Model\Product::class)
- ->setTypeId('simple')
- ->setId(2)
- ->setAttributeSetId($attributeSetId)
- ->setWebsiteIds([1])
- ->setName('Simple Product 2')
- ->setSku('simple2')
- ->setPrice(9.9)
- ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
- ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
- ->setStockData([
- 'use_config_manage_stock' => 1,
- 'qty' => 100,
- 'is_qty_decimal' => 0,
- 'is_in_stock' => 1,
- ]);
-$option = array_shift($options);
-$product->setTestConfigurable($option->getValue());
-$productRepository->save($product);
-$attributeValues[] = [
- 'label' => 'test',
- 'attribute_id' => $attribute->getId(),
- 'value_index' => $option->getValue(),
-];
-$associatedProductIds[] = $product->getId();
+foreach (['simple1', 'simple2'] as $sku) {
+ $option = array_shift($options);
+ $product = $productRepository->get($sku);
+ $product->setTestConfigurable($option->getValue());
+ $productRepository->save($product);
+ $attributeValues[] = [
+ 'label' => 'test',
+ 'attribute_id' => $attribute->getId(),
+ 'value_index' => $option->getValue(),
+ ];
+ $associatedProductIds[] = $product->getId();
+}
$product = $objectManager->create(\Magento\Catalog\Model\Product::class)
->setTypeId('configurable')
@@ -87,7 +46,9 @@
->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
->setStockData([
'use_config_manage_stock' => 1,
- 'is_in_stock' => 0,
+ 'qty' => 100,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 1,
]);
$configurableAttributesData = [
[
diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php
new file mode 100644
index 0000000000000..d4311d523d3c7
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/configurable_product_rollback.php
@@ -0,0 +1,31 @@
+getInstance()->reinitialize();
+
+/** @var $objectManager \Magento\TestFramework\ObjectManager */
+$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+/** @var \Magento\Framework\Registry $registry */
+$registry = $objectManager->get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+try {
+ $product = $productRepository->get('configurable', false, null, true);
+ $productRepository->delete($product);
+} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+ //Nothing to delete
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+require __DIR__ . '/simple_products_rollback.php';
+require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute_rollback.php';
diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php
new file mode 100644
index 0000000000000..2855ebb4c92b3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php
@@ -0,0 +1,54 @@
+get(\Magento\Store\Model\StoreManager::class);
+$store = $storeManager->getStore('default');
+$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+
+$installer = $objectManager->get(\Magento\Catalog\Setup\CategorySetup::class);
+$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default');
+
+$product = $objectManager->create(\Magento\Catalog\Model\Product::class)
+ ->setTypeId('simple')
+ ->setId(1)
+ ->setAttributeSetId($attributeSetId)
+ ->setWebsiteIds([1])
+ ->setName('Simple Product 1')
+ ->setSku('simple1')
+ ->setPrice(10)
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setStockData([
+ 'use_config_manage_stock' => 1,
+ 'qty' => 100,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 1,
+ ]);
+$productRepository->save($product);
+$productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class);
+$productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId());
+
+$product = $objectManager->create(\Magento\Catalog\Model\Product::class)
+ ->setTypeId('simple')
+ ->setId(2)
+ ->setAttributeSetId($attributeSetId)
+ ->setWebsiteIds([1])
+ ->setName('Simple Product 2')
+ ->setSku('simple2')
+ ->setPrice(9.9)
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setStockData([
+ 'use_config_manage_stock' => 1,
+ 'qty' => 100,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 1,
+ ]);
+$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php
similarity index 87%
rename from dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php
rename to dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php
index 71b164ce6d67e..a46d1db297e2d 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php
@@ -17,7 +17,7 @@
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
-foreach (['simple1', 'simple2', 'configurable'] as $sku) {
+foreach (['simple1', 'simple2'] as $sku) {
try {
$product = $productRepository->get($sku, false, null, true);
$productRepository->delete($product);
@@ -29,4 +29,4 @@
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', false);
-require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute_rollback.php';
+require __DIR__ . '/attribute_rollback.php';
diff --git a/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php
index 2d76632cae0b7..1275e62896469 100644
--- a/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php
+++ b/dev/tests/integration/testsuite/Magento/Contact/Controller/IndexTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Contact\Controller;
+use Zend\Http\Request;
+
/**
* Contact index controller test
*/
@@ -19,6 +21,7 @@ public function testPostAction()
'hideit' => '',
];
$this->getRequest()->setPostValue($params);
+ $this->getRequest()->setMethod(Request::METHOD_POST);
$this->dispatch('contact/index/post');
$this->assertRedirect($this->stringContains('contact/index'));
@@ -38,6 +41,7 @@ public function testPostAction()
public function testInvalidPostAction($params, $expectedMessage)
{
$this->getRequest()->setPostValue($params);
+ $this->getRequest()->setMethod(Request::METHOD_POST);
$this->dispatch('contact/index/post');
$this->assertRedirect($this->stringContains('contact/index'));
diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php
index cf0da5599914f..86aa61a99e1e8 100644
--- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php
+++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php
@@ -154,3 +154,10 @@
$product->setTypeHasRequiredOptions(false)->setRequiredOptions(false);
}
$product->save();
+
+$stockRegistry = $objectManager->get(\Magento\CatalogInventory\Api\StockRegistryInterface::class);
+$stockItem = $stockRegistry->getStockItem($product->getId());
+$stockItem->setUseConfigManageStock(true);
+$stockItem->setQty(100);
+$stockItem->setIsInStock(true);
+$stockRegistry->updateStockItemBySku($product->getSku(), $stockItem);
diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/Magento/ModuleA/view/adminhtml/product/product.css b/dev/tests/integration/testsuite/Magento/Framework/View/_files/Magento/ModuleA/view/adminhtml/product/product.css
index b29e2c0fd9b88..e079dd3715e4f 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/Magento/ModuleA/view/adminhtml/product/product.css
+++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/Magento/ModuleA/view/adminhtml/product/product.css
@@ -13,7 +13,7 @@
overflow: hidden;
padding: 5px 6px 3px;
margin-left: 12px;
- color: #7a7976;;
+ color: #7a7976;
}
[class^=" catalog-product-"] .page-actions .action-back:hover,
diff --git a/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php b/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php
new file mode 100644
index 0000000000000..92b0ec0f6a732
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/NewRelicReporting/Plugin/SeparateAppsTest.php
@@ -0,0 +1,47 @@
+objectManager = Bootstrap::getObjectManager();
+ }
+
+ /**
+ * @magentoConfigFixture default/newrelicreporting/general/enable 1
+ * @magentoConfigFixture default/newrelicreporting/general/app_name beverly_hills
+ * @magentoConfigFixture default/newrelicreporting/general/separate_apps 1
+ */
+ public function testAppNameIsSetWhenConfiguredCorrectly()
+ {
+ $newRelicWrapper = $this->getMockBuilder(NewRelicWrapper::class)
+ ->setMethods(['setAppName'])
+ ->getMock();
+
+ $this->objectManager->configure([NewRelicWrapper::class => ['shared' => true]]);
+ $this->objectManager->addSharedInstance($newRelicWrapper, NewRelicWrapper::class);
+
+ $newRelicWrapper->expects($this->once())
+ ->method('setAppName')
+ ->with($this->equalTo('beverly_hills;beverly_hills_90210'));
+
+ $state = $this->objectManager->get(State::class);
+
+ $state->setAreaCode('90210');
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php
new file mode 100644
index 0000000000000..cbacab1139c10
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Plugin/UpdateQuoteStoreTest.php
@@ -0,0 +1,130 @@
+objectManager = BootstrapHelper::getObjectManager();
+ $this->quoteRepository = $this->objectManager->create(CartRepositoryInterface::class);
+ }
+
+ /**
+ * Tests that active quote store id updates after store cookie change.
+ *
+ * @magentoDataFixture Magento/Quote/_files/empty_quote.php
+ * @magentoDataFixture Magento/Store/_files/second_store.php
+ * @throws \ReflectionException
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ public function testUpdateQuoteStoreAfterChangeStoreCookie()
+ {
+ $secondStoreCode = 'fixture_second_store';
+ $reservedOrderId = 'reserved_order_id';
+
+ /** @var StoreManagerInterface $storeManager */
+ $storeManager = $this->objectManager->get(StoreManagerInterface::class);
+ $currentStore = $storeManager->getStore();
+
+ $quote = $this->getQuote($reservedOrderId);
+ $this->assertEquals(
+ $currentStore->getId(),
+ $quote->getStoreId(),
+ 'Current store id and quote store id are not match'
+ );
+
+ /** @var Session $checkoutSession */
+ $checkoutSession = $this->objectManager->get(Session::class);
+ $checkoutSession->setQuoteId($quote->getId());
+
+ $storeRepository = $this->objectManager->create(StoreRepository::class);
+ $secondStore = $storeRepository->get($secondStoreCode);
+
+ $storeCookieManager = $this->getStoreCookieManager($currentStore);
+ $storeCookieManager->setStoreCookie($secondStore);
+
+ $updatedQuote = $this->getQuote($reservedOrderId);
+ $this->assertEquals(
+ $secondStore->getId(),
+ $updatedQuote->getStoreId(),
+ 'Active quote store id should be equal second store id'
+ );
+ }
+
+ /**
+ * Retrieves quote by reserved order id.
+ *
+ * @param string $reservedOrderId
+ * @return Quote
+ */
+ private function getQuote(string $reservedOrderId): Quote
+ {
+ /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+ $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
+ $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
+ ->create();
+
+ $items = $this->quoteRepository->getList($searchCriteria)->getItems();
+
+ return array_pop($items);
+ }
+
+ /**
+ * Returns instance of StoreCookieManagerInterface with mocked cookieManager dependency.
+ *
+ * Mock is needed since integration test framework use own cookie manager with
+ * behavior that differs from real environment.
+ *
+ * @param $currentStore
+ * @return StoreCookieManagerInterface
+ * @throws \ReflectionException
+ */
+ private function getStoreCookieManager(StoreInterface $currentStore): StoreCookieManagerInterface
+ {
+ /** @var StoreCookieManagerInterface $storeCookieManager */
+ $storeCookieManager = $this->objectManager->get(StoreCookieManagerInterface::class);
+ $cookieManagerMock = $this->getMockBuilder(\Magento\Framework\Stdlib\Cookie\PhpCookieManager::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $cookieManagerMock->method('getCookie')
+ ->willReturn($currentStore->getCode());
+
+ $reflection = new \ReflectionClass($storeCookieManager);
+ $cookieManager = $reflection->getProperty('cookieManager');
+ $cookieManager->setAccessible(true);
+ $cookieManager->setValue($storeCookieManager, $cookieManagerMock);
+
+ return $storeCookieManager;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php
index 88c66fedd10ed..3e093876349d0 100644
--- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php
@@ -5,6 +5,7 @@
*/
namespace Magento\Quote\Model;
+use Magento\Store\Model\StoreRepository;
use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
@@ -50,6 +51,34 @@ protected function setUp()
$this->filterBuilder = $this->objectManager->create(FilterBuilder::class);
}
+ /**
+ * Tests that quote saved with custom store id has same store id after getting via repository.
+ *
+ * @magentoDataFixture Magento/Sales/_files/quote.php
+ * @magentoDataFixture Magento/Store/_files/second_store.php
+ */
+ public function testGetQuoteWithCustomStoreId()
+ {
+ $secondStoreCode = 'fixture_second_store';
+ $reservedOrderId = 'test01';
+
+ $storeRepository = $this->objectManager->create(StoreRepository::class);
+ $secondStore = $storeRepository->get($secondStoreCode);
+
+ // Set store_id in quote to second store_id
+ $quote = $this->getQuote($reservedOrderId);
+ $quote->setStoreId($secondStore->getId());
+ $this->quoteRepository->save($quote);
+
+ $savedQuote = $this->quoteRepository->get($quote->getId());
+
+ $this->assertEquals(
+ $secondStore->getId(),
+ $savedQuote->getStoreId(),
+ 'Quote store id should be equal with store id value in DB'
+ );
+ }
+
/**
* @magentoDataFixture Magento/Sales/_files/quote.php
*/
@@ -126,6 +155,24 @@ public function testSaveWithNotExistingCustomerAddress()
);
}
+ /**
+ * Returns quote by reserved order id.
+ *
+ * @param string $reservedOrderId
+ * @return CartInterface
+ */
+ private function getQuote(string $reservedOrderId)
+ {
+ $searchCriteria = $this->getSearchCriteria($reservedOrderId);
+ $searchResult = $this->quoteRepository->getList($searchCriteria);
+ $items = $searchResult->getItems();
+
+ /** @var CartInterface $quote */
+ $quote = array_pop($items);
+
+ return $quote;
+ }
+
/**
* Get search criteria
*
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php
index fb07246656693..b31e77c0b09eb 100644
--- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php
@@ -6,6 +6,14 @@
namespace Magento\SalesRule\Model\Rule\Condition;
+use Magento\Quote\Api\CartRepositoryInterface;
+use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Quote\Api\Data\CartInterface;
+use Magento\SalesRule\Api\RuleRepositoryInterface;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class ProductTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -94,4 +102,71 @@ public function validateProductConditionDataProvider()
]
];
}
+
+ /**
+ * Ensure that SalesRules filtering on quote items quantity validates configurable product correctly
+ *
+ * 1. Load a quote with a configured product and a sales rule set to filter items with quantity 2.
+ * 2. Attempt to validate the sales rule against the quote and assert the output is negative.
+ *
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
+ * @magentoDataFixture Magento/SalesRule/_files/cart_rule_10_percent_off.php
+ */
+ public function testValidateQtySalesRuleWithConfigurable()
+ {
+ // Load the quote that contains a child of a configurable product with quantity 1.
+ $quote = $this->getQuote('test_cart_with_configurable');
+
+ // Load the SalesRule looking for products with quantity 2.
+ $rule = $this->getSalesRule('10% Off on orders with two items');
+
+ $this->assertFalse(
+ $rule->validate($quote->getBillingAddress())
+ );
+ }
+
+ /**
+ * Gets quote by reserved order id.
+ *
+ * @param string $reservedOrderId
+ * @return CartInterface
+ */
+ private function getQuote($reservedOrderId)
+ {
+ /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+ $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
+ $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
+ ->create();
+
+ /** @var CartRepositoryInterface $quoteRepository */
+ $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
+ $items = $quoteRepository->getList($searchCriteria)->getItems();
+ return array_pop($items);
+ }
+
+ /**
+ * Gets rule by name.
+ *
+ * @param string $name
+ * @return \Magento\SalesRule\Model\Rule
+ * @throws \Magento\Framework\Exception\InputException
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ private function getSalesRule(string $name): \Magento\SalesRule\Model\Rule
+ {
+ /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+ $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
+ $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name)
+ ->create();
+
+ /** @var CartRepositoryInterface $quoteRepository */
+ $ruleRepository = $this->objectManager->get(RuleRepositoryInterface::class);
+ $items = $ruleRepository->getList($searchCriteria)->getItems();
+
+ $rule = array_pop($items);
+ /** @var \Magento\SalesRule\Model\Converter\ToModel $converter */
+ $converter = $this->objectManager->get(\Magento\SalesRule\Model\Converter\ToModel::class);
+
+ return $converter->toModel($rule);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php
new file mode 100644
index 0000000000000..0761e5753da75
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_10_percent_off.php
@@ -0,0 +1,64 @@
+get(StoreManagerInterface::class)
+ ->getWebsite()
+ ->getId();
+
+/** @var Rule $salesRule */
+$salesRule = $objectManager->create(Rule::class);
+$salesRule->setData(
+ [
+ 'name' => '10% Off on orders with two items',
+ 'is_active' => 1,
+ 'customer_group_ids' => [GroupManagement::NOT_LOGGED_IN_ID],
+ 'coupon_type' => Rule::COUPON_TYPE_NO_COUPON,
+ 'simple_action' => 'by_percent',
+ 'discount_amount' => 10,
+ 'discount_step' => 0,
+ 'stop_rules_processing' => 1,
+ 'website_ids' => [$websiteId]
+ ]
+);
+
+$salesRule->getConditions()->loadArray([
+ 'type' => \Magento\SalesRule\Model\Rule\Condition\Combine::class,
+ 'attribute' => null,
+ 'operator' => null,
+ 'value' => '1',
+ 'is_value_processed' => null,
+ 'aggregator' => 'all',
+ 'conditions' =>
+ [
+ [
+ 'type' => \Magento\SalesRule\Model\Rule\Condition\Product\Subselect::class,
+ 'attribute' => 'qty',
+ 'operator' => '==',
+ 'value' => '2',
+ 'is_value_processed' => null,
+ 'aggregator' => 'all',
+ 'conditions' =>
+ [
+ [
+ 'type' => \Magento\SalesRule\Model\Rule\Condition\Product::class,
+ 'attribute' => 'attribute_set_id',
+ 'operator' => '==',
+ 'value' => '4',
+ 'is_value_processed' => false,
+ ],
+ ],
+ ],
+ ],
+]);
+
+$salesRule->save();
diff --git a/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesSortingSniff.php b/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesSortingSniff.php
index 29b6d9a8cae9d..e59b5da37d0e2 100644
--- a/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesSortingSniff.php
+++ b/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesSortingSniff.php
@@ -100,6 +100,14 @@ public function process(File $phpcsFile, $stackPtr)
*/
private function validatePropertiesSorting(File $phpcsFile, $stackPtr, array $properties)
{
+ // Fix needed for cases when incorrect properties passed for validation due to bug in PHP tokens.
+ $symbolsForSkip = ['(', 'block', 'field'];
+ $properties = array_filter(
+ $properties,
+ function ($var) use ($symbolsForSkip) {
+ return !in_array($var, $symbolsForSkip);
+ }
+ );
$originalProperties = $properties;
sort($properties);
diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt
index c2af57533502d..9bfcc344b1db6 100644
--- a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt
+++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt
@@ -3,6 +3,9 @@ app/code/Magento/Customer/view/frontend/web/js/zxcvbn.js
app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-bundle.js
app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-standalone-preset.js
+// MINIFIED FILES
+app/code/**/*.min.js
+
// TO REFACTORING
app/code/Magento/Authorizenet/view/adminhtml/web/js/direct-post.js
app/code/Magento/Catalog/view/adminhtml/web/catalog/product/composite/configure.js
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php
index f80151a41573d..4911670e099c2 100644
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php
@@ -6,5 +6,6 @@
return [
'/\.(jpe?g|png|gif|ttf|swf|eot|woff|pdf|mp3|pdf|jar|jbf)$/',
'/pub\/opt\/magento\/var/',
- '/COPYING\.txt/'
+ '/COPYING\.txt/',
+ '/app\/code\/(?!Magento)[^\/]*/'
];
diff --git a/dev/tools/grunt/tools/collect-validation-files.js b/dev/tools/grunt/tools/collect-validation-files.js
index 7347898c5b8fc..15fb5e50be0a2 100644
--- a/dev/tools/grunt/tools/collect-validation-files.js
+++ b/dev/tools/grunt/tools/collect-validation-files.js
@@ -3,10 +3,6 @@
* See COPYING.txt for license details.
*/
-/**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
'use strict';
var glob = require('glob'),
@@ -29,17 +25,27 @@ module.exports = {
getFilesForValidate: function () {
var blackListFiles = glob.sync(pc.static.blacklist + '*.txt'),
whiteListFiles = glob.sync(pc.static.whitelist + '*.txt'),
- blackList = this.readFiles(blackListFiles),
- whiteList = this.readFiles(whiteListFiles),
- files = [];
+ blackList = this.readFiles(blackListFiles).filter(this.isListEntryValid),
+ whiteList = this.readFiles(whiteListFiles).filter(this.isListEntryValid),
+ files = [],
+ entireBlackList = [];
+
+ fst.arrayRead(blackList, function (data) {
+ entireBlackList = _.union(entireBlackList, data);
+ });
fst.arrayRead(whiteList, function (data) {
- files = _.difference(data, blackList);
+ files = _.difference(data, entireBlackList);
});
return files;
},
+ isListEntryValid: function(line) {
+ line = line.trim();
+ return line.length > 0 && line.startsWith('// ') !== true;
+ },
+
getFiles: function (file) {
if (file) {
return file.split(',');
diff --git a/lib/internal/Magento/Framework/App/HttpRequestInterface.php b/lib/internal/Magento/Framework/App/HttpRequestInterface.php
new file mode 100644
index 0000000000000..685db5f47ffc7
--- /dev/null
+++ b/lib/internal/Magento/Framework/App/HttpRequestInterface.php
@@ -0,0 +1,52 @@
+ 'name',
'/config/indexer/fieldset' => 'name',
'/config/indexer/fieldset/field' => 'name',
+ '/config/indexer/dependencies/indexer' => 'id',
];
/**
diff --git a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php
index 4d611c830e91a..06b00afe42ad9 100644
--- a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php
+++ b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php
@@ -431,6 +431,9 @@ public function load($handles = [])
if ($result) {
$this->addUpdate($result);
$this->pageLayout = $this->_loadCache($cacheIdPageLayout);
+ foreach ($this->getHandles() as $handle) {
+ $this->allHandles[$handle] = $this->handleProcessed;
+ }
return $this;
}
diff --git a/lib/internal/Magento/Framework/View/Page/Config.php b/lib/internal/Magento/Framework/View/Page/Config.php
index e3ea9a58661f2..e8872f12d4908 100644
--- a/lib/internal/Magento/Framework/View/Page/Config.php
+++ b/lib/internal/Magento/Framework/View/Page/Config.php
@@ -117,6 +117,7 @@ class Config
'description' => null,
'keywords' => null,
'robots' => null,
+ 'title' => null,
];
/**
diff --git a/lib/internal/Magento/Framework/View/Page/Config/Renderer.php b/lib/internal/Magento/Framework/View/Page/Config/Renderer.php
index 9563cbfbcc532..93c8c5c338627 100644
--- a/lib/internal/Magento/Framework/View/Page/Config/Renderer.php
+++ b/lib/internal/Magento/Framework/View/Page/Config/Renderer.php
@@ -136,6 +136,12 @@ public function renderMetadata()
protected function processMetadataContent($name, $content)
{
$method = 'get' . $this->string->upperCaseWords($name, '_', '');
+ if ($name === 'title') {
+ if (!$content) {
+ $content = $this->escaper->escapeHtml($this->pageConfig->$method()->get());
+ }
+ return $content;
+ }
if (method_exists($this->pageConfig, $method)) {
$content = $this->pageConfig->$method();
}
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php
index 400e9cf3d1ed7..23d391c7f4f8d 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php
@@ -13,7 +13,7 @@
use Magento\Framework\View\Page\Config;
/**
- * @covers Magento\Framework\View\Page\Config
+ * @covers \Magento\Framework\View\Page\Config
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
@@ -139,6 +139,7 @@ public function testMetadata()
'description' => null,
'keywords' => null,
'robots' => null,
+ 'title' => null,
'name' => 'test_value',
'html_encoded' => '<title><span class="test">Test</span></title>',
];
diff --git a/lib/web/css/docs/source/docs.less b/lib/web/css/docs/source/docs.less
index 4039d0562da53..952beb5c0f970 100644
--- a/lib/web/css/docs/source/docs.less
+++ b/lib/web/css/docs/source/docs.less
@@ -21,7 +21,6 @@
@import '_icons.less';
@import '_loaders.less';
@import '_messages.less';
-//@import 'navigation.less';
@import '_layout.less';
@import '_pages.less';
@import '_popups.less';
diff --git a/lib/web/css/source/lib/_forms.less b/lib/web/css/source/lib/_forms.less
index 800054e58c3dd..b1c7a49da4a7a 100644
--- a/lib/web/css/source/lib/_forms.less
+++ b/lib/web/css/source/lib/_forms.less
@@ -465,11 +465,9 @@
.lib-css(margin, @_margin);
.lib-css(padding, @_padding);
letter-spacing: -.31em;
- //word-spacing: -.43em;
> * {
letter-spacing: normal;
- //word-spacing: normal;
}
> .legend {
diff --git a/lib/web/css/source/lib/variables/_forms.less b/lib/web/css/source/lib/variables/_forms.less
index 69d0146fb3555..fff6141c67fd5 100644
--- a/lib/web/css/source/lib/variables/_forms.less
+++ b/lib/web/css/source/lib/variables/_forms.less
@@ -121,7 +121,7 @@
@select__disabled__font-style: @form-element-input__disabled__font-style;
// Focus state
-@select__focus__background: @form-element-input__focus__background;
+@select__focus__background: false;
@select__focus__border: @form-element-input__focus__border;
@select__focus__color: @form-element-input__focus__color;
@select__focus__font-style: @form-element-input__focus__font-style;
diff --git a/lib/web/extjs/resources/css/ytheme-magento.css b/lib/web/extjs/resources/css/ytheme-magento.css
index 7be66ddd5efe5..fa54395806264 100644
--- a/lib/web/extjs/resources/css/ytheme-magento.css
+++ b/lib/web/extjs/resources/css/ytheme-magento.css
@@ -15,7 +15,7 @@
background:url(../images/magento/loading_bg.gif) no-repeat #eae2ca;
padding:85px 15px 15px 15px;
font-size:14px;
- font-weight:bold;;
+ font-weight:bold;
color:#611B06;
width:206px;
text-align:center;