Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes, optimizations, or just good suggestions.
@@ -22,8 +22,8 @@ To learn about issues, click [here][2]. To open an issue, click [here][3].
To suggest documentation improvements, click [here][4].
-[1]:
-[2]:
+[1]:
+[2]:
[3]:
[4]:
diff --git a/app/bootstrap.php b/app/bootstrap.php
index 3d474cea45432..e77c6d432c816 100644
--- a/app/bootstrap.php
+++ b/app/bootstrap.php
@@ -14,12 +14,12 @@
if (!defined('PHP_VERSION_ID') || !(PHP_VERSION_ID === 70002 || PHP_VERSION_ID === 70004 || PHP_VERSION_ID >= 70006)) {
if (PHP_SAPI == 'cli') {
echo 'Magento supports 7.0.2, 7.0.4, and 7.0.6 or later. ' .
- 'Please read http://devdocs.magento.com/guides/v1.0/install-gde/system-requirements.html';
+ 'Please read http://devdocs.magento.com/guides/v2.2/install-gde/system-requirements.html';
} else {
echo <<
Magento supports PHP 7.0.2, 7.0.4, and 7.0.6 or later. Please read
-
+
Magento System Requirements.
HTML;
diff --git a/app/code/Magento/Backend/Model/Menu/Item.php b/app/code/Magento/Backend/Model/Menu/Item.php
index fe6564d24e891..42febe94d0abf 100644
--- a/app/code/Magento/Backend/Model/Menu/Item.php
+++ b/app/code/Magento/Backend/Model/Menu/Item.php
@@ -467,15 +467,15 @@ public function toArray()
{
return [
'parent_id' => $this->_parentId,
- 'module_name' => $this->_moduleName,
+ 'module' => $this->_moduleName,
'sort_index' => $this->_sortIndex,
- 'depends_on_config' => $this->_dependsOnConfig,
+ 'dependsOnConfig' => $this->_dependsOnConfig,
'id' => $this->_id,
'resource' => $this->_resource,
'path' => $this->_path,
'action' => $this->_action,
- 'depends_on_module' => $this->_dependsOnModule,
- 'tooltip' => $this->_tooltip,
+ 'dependsOnModule' => $this->_dependsOnModule,
+ 'toolTip' => $this->_tooltip,
'title' => $this->_title,
'target' => $this->target,
'sub_menu' => isset($this->_submenu) ? $this->_submenu->toArray() : null
@@ -492,15 +492,15 @@ public function toArray()
public function populateFromArray(array $data)
{
$this->_parentId = $this->_getArgument($data, 'parent_id');
- $this->_moduleName = $this->_getArgument($data, 'module_name', 'Magento_Backend');
+ $this->_moduleName = $this->_getArgument($data, 'module', 'Magento_Backend');
$this->_sortIndex = $this->_getArgument($data, 'sort_index');
- $this->_dependsOnConfig = $this->_getArgument($data, 'depends_on_config');
+ $this->_dependsOnConfig = $this->_getArgument($data, 'dependsOnConfig');
$this->_id = $this->_getArgument($data, 'id');
$this->_resource = $this->_getArgument($data, 'resource');
$this->_path = $this->_getArgument($data, 'path', '');
$this->_action = $this->_getArgument($data, 'action');
- $this->_dependsOnModule = $this->_getArgument($data, 'depends_on_module');
- $this->_tooltip = $this->_getArgument($data, 'tooltip', '');
+ $this->_dependsOnModule = $this->_getArgument($data, 'dependsOnModule');
+ $this->_tooltip = $this->_getArgument($data, 'toolTip');
$this->_title = $this->_getArgument($data, 'title');
$this->target = $this->_getArgument($data, 'target');
if (isset($data['sub_menu'])) {
diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/ItemTest.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/ItemTest.php
index 74368537c39c7..ad172cbfbd165 100644
--- a/app/code/Magento/Backend/Test/Unit/Model/Menu/ItemTest.php
+++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/ItemTest.php
@@ -56,9 +56,9 @@ class ItemTest extends \PHPUnit\Framework\TestCase
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
];
protected function setUp()
diff --git a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_constructor_data.php b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_constructor_data.php
index b0c74461980a2..82f07e264b963 100644
--- a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_constructor_data.php
+++ b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_constructor_data.php
@@ -12,21 +12,21 @@
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
],
[
'parent_id' => null,
- 'module_name' => 'Magento_Backend',
+ 'module' => 'Magento_Backend',
'sort_index' => null,
- 'depends_on_config' => 'system/config/isEnabled',
+ 'dependsOnConfig' => 'system/config/isEnabled',
'id' => 'item',
'resource' => 'Magento_Config::config',
'path' => '',
'action' => '/system/config',
- 'depends_on_module' => 'Magento_Backend',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'toolTip' => 'Item tooltip',
'title' => 'Item Title',
'sub_menu' => null,
'target' => null
@@ -38,43 +38,43 @@
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
],
[
'parent_id' => '1',
- 'module_name' => 'Magento_Module1',
+ 'module' => 'Magento_Module1',
'sort_index' => '50',
- 'depends_on_config' => null,
+ 'dependsOnConfig' => null,
'id' => '5',
'resource' => null,
'path' => null,
'action' => null,
- 'depends_on_module' => null,
- 'tooltip' => null,
+ 'dependsOnModule' => null,
+ 'toolTip' => null,
'title' => null,
'sub_menu' => [
'id' => 'item',
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
],
],
[
'parent_id' => '1',
- 'module_name' => 'Magento_Module1',
+ 'module' => 'Magento_Module1',
'sort_index' => '50',
- 'depends_on_config' => null,
+ 'dependsOnConfig' => null,
'id' => '5',
'resource' => null,
'path' => '',
'action' => null,
- 'depends_on_module' => null,
- 'tooltip' => '',
+ 'dependsOnModule' => null,
+ 'toolTip' => '',
'title' => null,
'sub_menu' => ['submenuArray'],
'target' => null
@@ -83,51 +83,51 @@
'data with submenu to constructor' => [
[
'parent_id' => '1',
- 'module_name' => 'Magento_Module1',
+ 'module' => 'Magento_Module1',
'sort_index' => '50',
- 'depends_on_config' => null,
+ 'dependsOnConfig' => null,
'id' => '5',
'resource' => null,
'path' => null,
'action' => null,
- 'depends_on_module' => null,
- 'tooltip' => null,
+ 'dependsOnModule' => null,
+ 'toolTip' => null,
'title' => null,
'sub_menu' => [
'id' => 'item',
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
],
],
[
'parent_id' => '1',
- 'module_name' => 'Magento_Module1',
+ 'module' => 'Magento_Module1',
'sort_index' => '50',
'sub_menu' => [
'id' => 'item',
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
],
],
[
'parent_id' => '1',
- 'module_name' => 'Magento_Module1',
+ 'module' => 'Magento_Module1',
'sort_index' => '50',
- 'depends_on_config' => null,
+ 'dependsOnConfig' => null,
'id' => null,
'resource' => null,
'path' => '',
'action' => null,
- 'depends_on_module' => null,
- 'tooltip' => '',
+ 'dependsOnModule' => null,
+ 'toolTip' => '',
'title' => null,
'sub_menu' => ['submenuArray'],
'target' => null
diff --git a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php
index 30a43b0158ae3..b1a310d7d440b 100644
--- a/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php
+++ b/app/code/Magento/Backend/Test/Unit/Model/_files/menu_item_data.php
@@ -11,22 +11,22 @@
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
'sub_menu' => null,
],
[
'parent_id' => null,
- 'module_name' => 'Magento_Backend',
+ 'module' => 'Magento_Backend',
'sort_index' => null,
- 'depends_on_config' => 'system/config/isEnabled',
+ 'dependsOnConfig' => 'system/config/isEnabled',
'id' => 'item',
'resource' => 'Magento_Config::config',
'path' => '',
'action' => '/system/config',
- 'depends_on_module' => 'Magento_Backend',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'toolTip' => 'Item tooltip',
'title' => 'Item Title',
'sub_menu' => null,
'target' => null
@@ -35,46 +35,46 @@
'with submenu' => [
[
'parent_id' => '1',
- 'module_name' => 'Magento_Module1',
+ 'module' => 'Magento_Module1',
'sort_index' => '50',
- 'depends_on_config' => null,
+ 'dependsOnConfig' => null,
'id' => '5',
'resource' => null,
'path' => null,
'action' => null,
- 'depends_on_module' => null,
- 'tooltip' => null,
+ 'dependsOnModule' => null,
+ 'toolTip' => null,
'title' => null,
'sub_menu' => [
'id' => 'item',
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
],
],
[
'parent_id' => '1',
- 'module_name' => 'Magento_Module1',
+ 'module' => 'Magento_Module1',
'sort_index' => '50',
- 'depends_on_config' => null,
+ 'dependsOnConfig' => null,
'id' => '5',
'resource' => null,
'path' => null,
'action' => null,
- 'depends_on_module' => null,
- 'tooltip' => '',
+ 'dependsOnModule' => null,
+ 'toolTip' => '',
'title' => null,
'sub_menu' => [
'id' => 'item',
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
],
'target' => null
]
@@ -82,38 +82,38 @@
'small set of data' => [
[
'parent_id' => '1',
- 'module_name' => 'Magento_Module1',
+ 'module' => 'Magento_Module1',
'sort_index' => '50',
'sub_menu' => [
'id' => 'item',
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
],
],
[
'parent_id' => '1',
- 'module_name' => 'Magento_Module1',
+ 'module' => 'Magento_Module1',
'sort_index' => '50',
- 'depends_on_config' => null,
+ 'dependsOnConfig' => null,
'id' => null,
'resource' => null,
'path' => '',
'action' => null,
- 'depends_on_module' => null,
- 'tooltip' => '',
+ 'dependsOnModule' => null,
+ 'toolTip' => '',
'title' => null,
'sub_menu' => [
'id' => 'item',
'title' => 'Item Title',
'action' => '/system/config',
'resource' => 'Magento_Config::config',
- 'depends_on_module' => 'Magento_Backend',
- 'depends_on_config' => 'system/config/isEnabled',
- 'tooltip' => 'Item tooltip',
+ 'dependsOnModule' => 'Magento_Backend',
+ 'dependsOnConfig' => 'system/config/isEnabled',
+ 'toolTip' => 'Item tooltip',
],
'target' => null
]
diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml
index 27fd16cc920dc..b6324416d26d5 100644
--- a/app/code/Magento/Backend/etc/adminhtml/system.xml
+++ b/app/code/Magento/Backend/etc/adminhtml/system.xml
@@ -215,7 +215,7 @@
Magento\Directory\Model\Config\Source\Country
-
+ Magento\Directory\Model\Config\Source\Country
diff --git a/app/code/Magento/Backup/Model/Db.php b/app/code/Magento/Backup/Model/Db.php
index 776141249f92b..8fbd5da1c9842 100644
--- a/app/code/Magento/Backup/Model/Db.php
+++ b/app/code/Magento/Backup/Model/Db.php
@@ -154,7 +154,7 @@ public function createBackup(\Magento\Framework\Backup\Db\BackupInterface $backu
if ($tableStatus->getDataLength() > self::BUFFER_LENGTH) {
if ($tableStatus->getAvgRowLength() < self::BUFFER_LENGTH) {
- $limit = floor(self::BUFFER_LENGTH / $tableStatus->getAvgRowLength());
+ $limit = floor(self::BUFFER_LENGTH / max($tableStatus->getAvgRowLength(), 1));
$multiRowsLength = ceil($tableStatus->getRows() / $limit);
} else {
$limit = 1;
@@ -173,6 +173,7 @@ public function createBackup(\Magento\Framework\Backup\Db\BackupInterface $backu
}
}
$backup->write($this->getResource()->getTableForeignKeysSql());
+ $backup->write($this->getResource()->getTableTriggersSql());
$backup->write($this->getResource()->getFooter());
$this->getResource()->commitTransaction();
diff --git a/app/code/Magento/Backup/Model/ResourceModel/Db.php b/app/code/Magento/Backup/Model/ResourceModel/Db.php
index 3fbaf44ebb063..f50a3c5b091ad 100644
--- a/app/code/Magento/Backup/Model/ResourceModel/Db.php
+++ b/app/code/Magento/Backup/Model/ResourceModel/Db.php
@@ -114,6 +114,30 @@ public function getTableForeignKeysSql($tableName = null)
return $fkScript;
}
+ /**
+ * Return triggers fro table(s)
+ *
+ * @param string|null $tableName
+ * @param bool $addDropIfExists
+ * @return string
+ */
+ public function getTableTriggersSql($tableName = null, $addDropIfExists = true)
+ {
+ $triggerScript = '';
+ if (!$tableName) {
+ $tables = $this->getTables();
+ foreach ($tables as $table) {
+ $tableTriggerScript = $this->_resourceHelper->getTableTriggersSql($table, $addDropIfExists);
+ if (!empty($tableTriggerScript)) {
+ $triggerScript .= "\n" . $tableTriggerScript;
+ }
+ }
+ } else {
+ $triggerScript = $this->getTableTriggersSql($tableName, $addDropIfExists);
+ }
+ return $triggerScript;
+ }
+
/**
* Retrieve table status
*
diff --git a/app/code/Magento/Backup/Model/ResourceModel/Helper.php b/app/code/Magento/Backup/Model/ResourceModel/Helper.php
index b5418865339c3..6d7084a87546c 100644
--- a/app/code/Magento/Backup/Model/ResourceModel/Helper.php
+++ b/app/code/Magento/Backup/Model/ResourceModel/Helper.php
@@ -337,4 +337,40 @@ public function restoreTransactionIsolationLevel()
{
$this->getConnection()->query('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ');
}
+
+ /**
+ * Get create script for triggers
+ *
+ * @param string $tableName
+ * @param boolean $addDropIfExists
+ * @param boolean $stripDefiner
+ * @return string
+ */
+ public function getTableTriggersSql($tableName, $addDropIfExists = false, $stripDefiner = true)
+ {
+ $script = "--\n-- Triggers structure for table `{$tableName}`\n--\n";
+ $triggers = $this->getConnection()->query('SHOW TRIGGERS LIKE \''. $tableName . '\'')->fetchAll();
+
+ if (!$triggers) {
+ return '';
+ }
+ foreach ($triggers as $trigger) {
+ if ($addDropIfExists) {
+ $script .= 'DROP TRIGGER IF EXISTS ' . $trigger['Trigger'] . ";\n";
+ }
+ $script .= "delimiter ;;\n";
+
+ $triggerData = $this->getConnection()->query('SHOW CREATE TRIGGER '. $trigger['Trigger'])->fetch();
+ if ($stripDefiner) {
+ $cleanedScript = preg_replace('/DEFINER=[^\s]*/', '', $triggerData['SQL Original Statement']);
+ $script .= $cleanedScript . "\n";
+ } else {
+ $script .= $triggerData['SQL Original Statement'] . "\n";
+ }
+ $script .= ";;\n";
+ $script .= "delimiter ;\n";
+ }
+
+ return $script;
+ }
}
diff --git a/app/code/Magento/Braintree/Gateway/Validator/ErrorCodeValidator.php b/app/code/Magento/Braintree/Gateway/Validator/ErrorCodeValidator.php
new file mode 100644
index 0000000000000..d38b04e46b0db
--- /dev/null
+++ b/app/code/Magento/Braintree/Gateway/Validator/ErrorCodeValidator.php
@@ -0,0 +1,49 @@
+getErrorCodes($response->errors)];
+ }
+
+ /**
+ * Retrieves list of error codes from Braintree response.
+ *
+ * @param ErrorCollection $collection
+ * @return array
+ */
+ private function getErrorCodes(ErrorCollection $collection)
+ {
+ $result = [];
+ /** @var Validation $error */
+ foreach ($collection->deepAll() as $error) {
+ $result[] = $error->code;
+ }
+
+ return $result;
+ }
+}
diff --git a/app/code/Magento/Braintree/Gateway/Validator/GeneralResponseValidator.php b/app/code/Magento/Braintree/Gateway/Validator/GeneralResponseValidator.php
index 8028bace0cf24..7bafa35e13185 100644
--- a/app/code/Magento/Braintree/Gateway/Validator/GeneralResponseValidator.php
+++ b/app/code/Magento/Braintree/Gateway/Validator/GeneralResponseValidator.php
@@ -18,16 +18,26 @@ class GeneralResponseValidator extends AbstractValidator
*/
protected $subjectReader;
+ /**
+ * @var ErrorCodeValidator
+ */
+ private $errorCodeValidator;
+
/**
* Constructor
*
* @param ResultInterfaceFactory $resultFactory
* @param SubjectReader $subjectReader
+ * @param ErrorCodeValidator $errorCodeValidator
*/
- public function __construct(ResultInterfaceFactory $resultFactory, SubjectReader $subjectReader)
- {
+ public function __construct(
+ ResultInterfaceFactory $resultFactory,
+ SubjectReader $subjectReader,
+ ErrorCodeValidator $errorCodeValidator
+ ) {
parent::__construct($resultFactory);
$this->subjectReader = $subjectReader;
+ $this->errorCodeValidator = $errorCodeValidator;
}
/**
@@ -62,9 +72,10 @@ protected function getResponseValidators()
function ($response) {
return [
property_exists($response, 'success') && $response->success === true,
- [__('Braintree error response.')]
+ [$response->message ?? __('Braintree error response.')]
];
- }
+ },
+ $this->errorCodeValidator
];
}
}
diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php
index c8a46da504fef..de8423618ab8d 100644
--- a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php
+++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/GeneralResponseValidatorTest.php
@@ -5,12 +5,14 @@
*/
namespace Magento\Braintree\Test\Unit\Gateway\Validator;
-use Braintree\Transaction;
+use Braintree\Result\Error;
+use Magento\Braintree\Gateway\SubjectReader;
+use Magento\Braintree\Gateway\Validator\ErrorCodeValidator;
+use Magento\Braintree\Gateway\Validator\GeneralResponseValidator;
use Magento\Framework\Phrase;
-use Magento\Payment\Gateway\Validator\ResultInterface;
+use Magento\Payment\Gateway\Validator\Result;
use Magento\Payment\Gateway\Validator\ResultInterfaceFactory;
-use Magento\Braintree\Gateway\Validator\GeneralResponseValidator;
-use Magento\Braintree\Gateway\SubjectReader;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
class GeneralResponseValidatorTest extends \PHPUnit\Framework\TestCase
{
@@ -20,14 +22,9 @@ class GeneralResponseValidatorTest extends \PHPUnit\Framework\TestCase
private $responseValidator;
/**
- * @var ResultInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
- */
- private $resultInterfaceFactoryMock;
-
- /**
- * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject
+ * @var ResultInterfaceFactory|MockObject
*/
- private $subjectReaderMock;
+ private $resultInterfaceFactory;
/**
* Set up
@@ -36,23 +33,20 @@ class GeneralResponseValidatorTest extends \PHPUnit\Framework\TestCase
*/
protected function setUp()
{
- $this->resultInterfaceFactoryMock = $this->getMockBuilder(
- \Magento\Payment\Gateway\Validator\ResultInterfaceFactory::class
- )->disableOriginalConstructor()
- ->setMethods(['create'])
- ->getMock();
- $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class)
+ $this->resultInterfaceFactory = $this->getMockBuilder(ResultInterfaceFactory::class)
->disableOriginalConstructor()
+ ->setMethods(['create'])
->getMock();
$this->responseValidator = new GeneralResponseValidator(
- $this->resultInterfaceFactoryMock,
- $this->subjectReaderMock
+ $this->resultInterfaceFactory,
+ new SubjectReader(),
+ new ErrorCodeValidator()
);
}
/**
- * Run test for validate method
+ * Checks a case when the validator processes successful and failed transactions.
*
* @param array $validationSubject
* @param bool $isValid
@@ -61,45 +55,52 @@ protected function setUp()
*
* @dataProvider dataProviderTestValidate
*/
- public function testValidate(array $validationSubject, $isValid, $messages)
+ public function testValidate(array $validationSubject, bool $isValid, $messages)
{
- /** @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject $resultMock */
- $resultMock = $this->createMock(ResultInterface::class);
+ $result = new Result($isValid, $messages);
- $this->subjectReaderMock->expects(self::once())
- ->method('readResponseObject')
- ->with($validationSubject)
- ->willReturn($validationSubject['response']['object']);
-
- $this->resultInterfaceFactoryMock->expects(self::once())
- ->method('create')
+ $this->resultInterfaceFactory->method('create')
->with([
'isValid' => $isValid,
'failsDescription' => $messages
])
- ->willReturn($resultMock);
+ ->willReturn($result);
- $actualMock = $this->responseValidator->validate($validationSubject);
+ $actual = $this->responseValidator->validate($validationSubject);
- self::assertEquals($resultMock, $actualMock);
+ self::assertEquals($result, $actual);
}
/**
+ * Gets variations for different type of response.
+ *
* @return array
*/
public function dataProviderTestValidate()
{
- $successTrue = new \stdClass();
- $successTrue->success = true;
+ $successTransaction = new \stdClass();
+ $successTransaction->success = true;
+
+ $failureTransaction = new \stdClass();
+ $failureTransaction->success = false;
+ $failureTransaction->message = 'Transaction was failed.';
- $successFalse = new \stdClass();
- $successFalse->success = false;
+ $errors = [
+ 'errors' => [
+ [
+ 'code' => 81804,
+ 'attribute' => 'base',
+ 'message' => 'Cannot process transaction.'
+ ]
+ ]
+ ];
+ $errorTransaction = new Error(['errors' => $errors]);
return [
[
'validationSubject' => [
'response' => [
- 'object' => $successTrue
+ 'object' => $successTransaction
],
],
'isValid' => true,
@@ -108,12 +109,24 @@ public function dataProviderTestValidate()
[
'validationSubject' => [
'response' => [
- 'object' => $successFalse
+ 'object' => $failureTransaction
+ ]
+ ],
+ 'isValid' => false,
+ [
+ __('Transaction was failed.')
+ ]
+ ],
+ [
+ 'validationSubject' => [
+ 'response' => [
+ 'object' => $errorTransaction
]
],
'isValid' => false,
[
- __('Braintree error response.')
+ __('Braintree error response.'),
+ 81804
]
]
];
diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/PaymentNonceResponseValidatorTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/PaymentNonceResponseValidatorTest.php
index 03363b5463d78..a59bbb6f0d387 100644
--- a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/PaymentNonceResponseValidatorTest.php
+++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/PaymentNonceResponseValidatorTest.php
@@ -5,15 +5,13 @@
*/
namespace Magento\Braintree\Test\Unit\Gateway\Validator;
-use Braintree\Transaction;
+use Magento\Braintree\Gateway\SubjectReader;
+use Magento\Braintree\Gateway\Validator\ErrorCodeValidator;
use Magento\Braintree\Gateway\Validator\PaymentNonceResponseValidator;
-use Magento\Payment\Gateway\Validator\ResultInterface;
+use Magento\Payment\Gateway\Validator\Result;
use Magento\Payment\Gateway\Validator\ResultInterfaceFactory;
-use Magento\Braintree\Gateway\SubjectReader;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
-/**
- * Class PaymentNonceResponseValidatorTest
- */
class PaymentNonceResponseValidatorTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -22,35 +20,24 @@ class PaymentNonceResponseValidatorTest extends \PHPUnit\Framework\TestCase
private $validator;
/**
- * @var ResultInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
+ * @var ResultInterfaceFactory|MockObject
*/
private $resultInterfaceFactory;
- /**
- * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject
- */
- private $subjectReader;
-
protected function setUp()
{
$this->resultInterfaceFactory = $this->getMockBuilder(ResultInterfaceFactory::class)
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
- $this->subjectReader = $this->getMockBuilder(SubjectReader::class)
- ->disableOriginalConstructor()
- ->setMethods(['readResponseObject'])
- ->getMock();
$this->validator = new PaymentNonceResponseValidator(
$this->resultInterfaceFactory,
- $this->subjectReader
+ new SubjectReader(),
+ new ErrorCodeValidator()
);
}
- /**
- * @covers \Magento\Braintree\Gateway\Validator\PaymentNonceResponseValidator::validate
- */
public function testFailedValidate()
{
$obj = new \stdClass();
@@ -61,23 +48,12 @@ public function testFailedValidate()
]
];
- $this->subjectReader->expects(static::once())
- ->method('readResponseObject')
- ->willReturn($obj);
-
- $result = $this->createMock(ResultInterface::class);
- $this->resultInterfaceFactory->expects(self::once())
- ->method('create')
- ->with([
- 'isValid' => false,
- 'failsDescription' => [
- __('Payment method nonce can\'t be retrieved.')
- ]
- ])
+ $result = new Result(false, [__('Payment method nonce can\'t be retrieved.')]);
+ $this->resultInterfaceFactory->method('create')
->willReturn($result);
$actual = $this->validator->validate($subject);
- static::assertEquals($result, $actual);
+ self::assertEquals($result, $actual);
}
public function testValidateSuccess()
@@ -93,20 +69,11 @@ public function testValidateSuccess()
]
];
- $this->subjectReader->expects(static::once())
- ->method('readResponseObject')
- ->willReturn($obj);
-
- $result = $this->createMock(ResultInterface::class);
- $this->resultInterfaceFactory->expects(self::once())
- ->method('create')
- ->with([
- 'isValid' => true,
- 'failsDescription' => []
- ])
+ $result = new Result(true);
+ $this->resultInterfaceFactory->method('create')
->willReturn($result);
$actual = $this->validator->validate($subject);
- static::assertEquals($result, $actual);
+ self::assertEquals($result, $actual);
}
}
diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/ResponseValidatorTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/ResponseValidatorTest.php
index 4bd446079f9a7..ba4d6cae062ee 100644
--- a/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/ResponseValidatorTest.php
+++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Validator/ResponseValidatorTest.php
@@ -5,15 +5,16 @@
*/
namespace Magento\Braintree\Test\Unit\Gateway\Validator;
+use Braintree\Result\Successful;
use Braintree\Transaction;
+use Magento\Braintree\Gateway\SubjectReader;
+use Magento\Braintree\Gateway\Validator\ErrorCodeValidator;
+use Magento\Braintree\Gateway\Validator\ResponseValidator;
use Magento\Framework\Phrase;
+use Magento\Payment\Gateway\Validator\Result;
use Magento\Payment\Gateway\Validator\ResultInterface;
use Magento\Payment\Gateway\Validator\ResultInterfaceFactory;
-use Magento\Braintree\Gateway\Validator\ResponseValidator;
-use Magento\Braintree\Gateway\SubjectReader;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
-use Braintree\Result\Error;
-use Braintree\Result\Successful;
/**
* Class ResponseValidatorTest
@@ -30,11 +31,6 @@ class ResponseValidatorTest extends \PHPUnit\Framework\TestCase
*/
private $resultInterfaceFactory;
- /**
- * @var SubjectReader|MockObject
- */
- private $subjectReader;
-
/**
* Set up
*
@@ -46,13 +42,11 @@ protected function setUp()
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
- $this->subjectReader = $this->getMockBuilder(SubjectReader::class)
- ->disableOriginalConstructor()
- ->getMock();
$this->responseValidator = new ResponseValidator(
$this->resultInterfaceFactory,
- $this->subjectReader
+ new SubjectReader(),
+ new ErrorCodeValidator()
);
}
@@ -65,11 +59,6 @@ public function testValidateReadResponseException()
'response' => null
];
- $this->subjectReader->expects(self::once())
- ->method('readResponseObject')
- ->with($validationSubject)
- ->willThrowException(new \InvalidArgumentException());
-
$this->responseValidator->validate($validationSubject);
}
@@ -82,11 +71,6 @@ public function testValidateReadResponseObjectException()
'response' => ['object' => null]
];
- $this->subjectReader->expects(self::once())
- ->method('readResponseObject')
- ->with($validationSubject)
- ->willThrowException(new \InvalidArgumentException());
-
$this->responseValidator->validate($validationSubject);
}
@@ -103,19 +87,9 @@ public function testValidateReadResponseObjectException()
public function testValidate(array $validationSubject, $isValid, $messages)
{
/** @var ResultInterface|MockObject $result */
- $result = $this->createMock(ResultInterface::class);
-
- $this->subjectReader->expects(self::once())
- ->method('readResponseObject')
- ->with($validationSubject)
- ->willReturn($validationSubject['response']['object']);
-
- $this->resultInterfaceFactory->expects(self::once())
- ->method('create')
- ->with([
- 'isValid' => $isValid,
- 'failsDescription' => $messages
- ])
+ $result = new Result($isValid, $messages);
+
+ $this->resultInterfaceFactory->method('create')
->willReturn($result);
$actual = $this->responseValidator->validate($validationSubject);
@@ -141,8 +115,6 @@ public function dataProviderTestValidate()
$transactionDeclined->transaction = new \stdClass();
$transactionDeclined->transaction->status = Transaction::SETTLEMENT_DECLINED;
- $errorResult = new Error(['errors' => []]);
-
return [
[
'validationSubject' => [
@@ -175,18 +147,6 @@ public function dataProviderTestValidate()
[
__('Wrong transaction status')
]
- ],
- [
- 'validationSubject' => [
- 'response' => [
- 'object' => $errorResult,
- ]
- ],
- 'isValid' => false,
- [
- __('Braintree error response.'),
- __('Wrong transaction status')
- ]
]
];
}
diff --git a/app/code/Magento/Braintree/etc/adminhtml/braintree_error_mapping.xml b/app/code/Magento/Braintree/etc/adminhtml/braintree_error_mapping.xml
new file mode 100644
index 0000000000000..611f9372518fc
--- /dev/null
+++ b/app/code/Magento/Braintree/etc/adminhtml/braintree_error_mapping.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ Credit card type is not accepted by this merchant account.
+ Transaction can only be voided if status is authorized, submitted_for_settlement, or - for PayPal - settlement_pending.
+ Credit transactions cannot be refunded.
+ Cannot refund a transaction unless it is settled.
+ Cannot submit for settlement unless status is authorized.
+ Customer does not have any credit cards.
+ Transaction has already been completely refunded.
+ Payment instrument type is not accepted by this merchant account.
+ Processor authorization code cannot be set unless for a voice authorization.
+ Refund amount is too large.
+ Settlement amount is too large.
+ Cannot provide a billing address unless also providing a credit card.
+ Cannot refund a transaction with a suspended merchant account.
+ Merchant account does not support refunds.
+ Cannot refund a transaction transaction in settling status on this merchant account. Try again after the transaction has settled.
+ PayPal is not enabled for your merchant account.
+ Partial settlements are not supported by this processor.
+ Cannot submit for partial settlement.
+ Transaction can not be voided if status of a PayPal partial settlement child transaction is settlement_pending.
+ Too many concurrent attempts to refund this transaction. Try again later.
+ Too many concurrent attempts to void this transaction. Try again later.
+
+
diff --git a/app/code/Magento/Braintree/etc/adminhtml/di.xml b/app/code/Magento/Braintree/etc/adminhtml/di.xml
index 90fc927ed4f80..bb5763469cad3 100644
--- a/app/code/Magento/Braintree/etc/adminhtml/di.xml
+++ b/app/code/Magento/Braintree/etc/adminhtml/di.xml
@@ -45,6 +45,19 @@
+
+
+
+ Magento\Braintree\Gateway\ErrorMapper\VirtualErrorMessageMapper
+
+
+
+
+ Magento\Braintree\Gateway\ErrorMapper\VirtualErrorMessageMapper
+
+
+
+
diff --git a/app/code/Magento/Braintree/etc/braintree_error_mapping.xml b/app/code/Magento/Braintree/etc/braintree_error_mapping.xml
new file mode 100644
index 0000000000000..81da0a252e567
--- /dev/null
+++ b/app/code/Magento/Braintree/etc/braintree_error_mapping.xml
@@ -0,0 +1,64 @@
+
+
+
+
+ Credit card type is not accepted by this merchant account.
+ CVV is required.
+ CVV must be 4 digits for American Express and 3 digits for other card types.
+ Expiration date is required.
+ Expiration date is invalid.
+ Expiration year is invalid. It must be between 1975 and 2201.
+ Expiration month is invalid.
+ Expiration year is invalid.
+ Credit card number is required.
+ Credit card number is invalid.
+ Credit card number must be 12-19 digits.
+ Cardholder name is too long.
+ CVV verification failed.
+ Postal code verification failed.
+ Credit card number is prohibited.
+ Addresses must have at least one field filled in.
+ Company is too long.
+ Extended address is too long.
+ First name is too long.
+ Last name is too long.
+ Locality is too long.
+ Postal code is required.
+ Postal code may contain no more than 9 letter or number characters.
+ Region is too long.
+ Street address is required.
+ Street address is too long.
+ Postal code can only contain letters, numbers, spaces, and hyphens.
+ US state codes must be two characters to meet PayPal Seller Protection requirements.
+ Incomplete PayPal account information.
+ Invalid PayPal account information.
+ PayPal Accounts are not accepted by this merchant account.
+ Credit card type is not accepted by this merchant account.
+ Credit card type is not accepted by this merchant account.
+ Billing address format is invalid.
+ Country name is not an accepted country.
+ Country code is not accepted. Please contact the store administrator.
+ Provided country information is inconsistent.
+ Country code is not accepted. Please contact the store administrator.
+ Country code is not accepted. Please contact the store administrator.
+ Customer has already reached the maximum of 50 addresses.
+ Address is invalid. Please contact the store administrator.
+ Address is invalid. Please contact the store administrator.
+ Address is invalid. Please contact the store administrator.
+ Address is invalid. Please contact the store administrator.
+ Address is invalid. Please contact the store administrator.
+ Address is invalid. Please contact the store administrator.
+ Address is invalid. Please contact the store administrator.
+ Address is invalid. Please contact the store administrator.
+ Address is invalid. Please contact the store administrator.
+ Error communicating with PayPal.
+ PayPal authentication expired.
+ Error executing PayPal order.
+ Error executing PayPal billing agreement.
+
+
diff --git a/app/code/Magento/Braintree/etc/di.xml b/app/code/Magento/Braintree/etc/di.xml
index 86c2911815754..a834503c5882a 100644
--- a/app/code/Magento/Braintree/etc/di.xml
+++ b/app/code/Magento/Braintree/etc/di.xml
@@ -193,6 +193,23 @@
+
+
+ braintree_error_mapping.xml
+
+
+
+
+ Magento\Braintree\Gateway\ErrorMapper\VirtualConfigReader
+ braintree_error_mapper
+
+
+
+
+ Magento\Braintree\Gateway\ErrorMapper\VirtualMappingData
+
+
+
@@ -201,6 +218,7 @@
Magento\Braintree\Gateway\Http\Client\TransactionSaleBraintreeAuthorizationHandlerMagento\Braintree\Gateway\Validator\ResponseValidator
+ Magento\Braintree\Gateway\ErrorMapper\VirtualErrorMessageMapper
@@ -240,6 +258,7 @@
Magento\Braintree\Gateway\Http\Client\TransactionSubmitForSettlementMagento\Braintree\Gateway\Response\TransactionIdHandlerMagento\Braintree\Gateway\Validator\ResponseValidator
+ Magento\Braintree\Gateway\ErrorMapper\VirtualErrorMessageMapper
@@ -258,6 +277,7 @@
Magento\Braintree\Gateway\Http\Client\TransactionSaleBraintreeVaultResponseHandlerMagento\Braintree\Gateway\Validator\ResponseValidator
+ Magento\Braintree\Gateway\ErrorMapper\VirtualErrorMessageMapper
@@ -296,6 +316,7 @@
Magento\Braintree\Gateway\Http\Client\TransactionSaleMagento\Braintree\Gateway\Response\TransactionIdHandlerMagento\Braintree\Gateway\Validator\ResponseValidator
+ Magento\Braintree\Gateway\ErrorMapper\VirtualErrorMessageMapper
diff --git a/app/code/Magento/Braintree/i18n/en_US.csv b/app/code/Magento/Braintree/i18n/en_US.csv
index 116f459a1c1c8..194ad14d49751 100644
--- a/app/code/Magento/Braintree/i18n/en_US.csv
+++ b/app/code/Magento/Braintree/i18n/en_US.csv
@@ -129,3 +129,65 @@ Amount,Amount
"Refund Ids","Refund Ids"
"Settlement Batch ID","Settlement Batch ID"
Currency,Currency
+"Addresses must have at least one field filled in.","Addresses must have at least one field filled in."
+"Company is too long.","Company is too long."
+"Extended address is too long.","Extended address is too long."
+"First name is too long.","First name is too long."
+"Last name is too long.","Last name is too long."
+"Locality is too long.","Locality is too long."
+"Postal code can only contain letters, numbers, spaces, and hyphens.","Postal code can only contain letters, numbers, spaces, and hyphens."
+"Postal code is required.","Postal code is required."
+"Postal code may contain no more than 9 letter or number characters.","Postal code may contain no more than 9 letter or number characters."
+"Region is too long.","Region is too long."
+"Street address is required.","Street address is required."
+"Street address is too long.","Street address is too long."
+"US state codes must be two characters to meet PayPal Seller Protection requirements.","US state codes must be two characters to meet PayPal Seller Protection requirements."
+"Country name is not an accepted country.","Country name is not an accepted country."
+"Provided country information is inconsistent.","Provided country information is inconsistent."
+"Country code is not accepted. Please contact the store administrator.","Country code is not accepted. Please contact the store administrator."
+"Customer has already reached the maximum of 50 addresses.","Customer has already reached the maximum of 50 addresses."
+"Address is invalid. Please contact the store administrator.","Address is invalid. Please contact the store administrator."
+"Address is invalid.","Address is invalid."
+"Billing address format is invalid.","Billing address format is invalid."
+"Cardholder name is too long.","Cardholder name is too long."
+"Credit card type is not accepted by this merchant account.","Credit card type is not accepted by this merchant account."
+"CVV is required.","CVV is required."
+"CVV must be 4 digits for American Express and 3 digits for other card types.","CVV must be 4 digits for American Express and 3 digits for other card types."
+"Expiration date is required.","Expiration date is required."
+"Expiration date is invalid.","Expiration date is invalid."
+"Expiration year is invalid. It must be between 1975 and 2201.","Expiration year is invalid. It must be between 1975 and 2201."
+"Expiration month is invalid.","Expiration month is invalid."
+"Expiration year is invalid.","Expiration year is invalid."
+"Credit card number is required.","Credit card number is required."
+"Credit card number is invalid.","Credit card number is invalid."
+"Credit card number must be 12-19 digits.","Credit card number must be 12-19 digits."
+"CVV verification failed.","CVV verification failed."
+"Postal code verification failed.","Postal code verification failed."
+"Credit card number is prohibited.","Credit card number is prohibited."
+"Incomplete PayPal account information.","Incomplete PayPal account information."
+"Invalid PayPal account information.","Invalid PayPal account information."
+"PayPal Accounts are not accepted by this merchant account.","PayPal Accounts are not accepted by this merchant account."
+"Error communicating with PayPal.","Error communicating with PayPal."
+"PayPal authentication expired.","PayPal authentication expired."
+"Error executing PayPal order.","Error executing PayPal order."
+"Error executing PayPal billing agreement.","Error executing PayPal billing agreement."
+"Cannot provide a billing address unless also providing a credit card.","Cannot provide a billing address unless also providing a credit card."
+"Transaction can only be voided if status is authorized, submitted_for_settlement, or - for PayPal - settlement_pending.","Transaction can only be voided if status is authorized, submitted_for_settlement, or - for PayPal - settlement_pending."
+"Credit transactions cannot be refunded.","Credit transactions cannot be refunded."
+"Cannot refund a transaction unless it is settled.","Cannot refund a transaction unless it is settled."
+"Cannot submit for settlement unless status is authorized.","Cannot submit for settlement unless status is authorized."
+"Customer does not have any credit cards.","Customer does not have any credit cards."
+"Transaction has already been completely refunded.","Transaction has already been completely refunded."
+"Payment instrument type is not accepted by this merchant account.","Payment instrument type is not accepted by this merchant account."
+"Processor authorization code cannot be set unless for a voice authorization.","Processor authorization code cannot be set unless for a voice authorization."
+"Refund amount is too large.","Refund amount is too large."
+"Settlement amount is too large.","Settlement amount is too large."
+"Cannot refund a transaction with a suspended merchant account.","Cannot refund a transaction with a suspended merchant account."
+"Merchant account does not support refunds.","Merchant account does not support refunds."
+"PayPal is not enabled for your merchant account.","PayPal is not enabled for your merchant account."
+"Cannot refund a transaction transaction in settling status on this merchant account. Try again after the transaction has settled.","Cannot refund a transaction transaction in settling status on this merchant account. Try again after the transaction has settled."
+"Cannot submit for partial settlement.","Cannot submit for partial settlement."
+"Partial settlements are not supported by this processor.","Partial settlements are not supported by this processor."
+"Transaction can not be voided if status of a PayPal partial settlement child transaction is settlement_pending.","Transaction can not be voided if status of a PayPal partial settlement child transaction is settlement_pending."
+"Too many concurrent attempts to refund this transaction. Try again later.","Too many concurrent attempts to refund this transaction. Try again later."
+"Too many concurrent attempts to void this transaction. Try again later.","Too many concurrent attempts to void this transaction. Try again later."
\ No newline at end of file
diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js
index 73a7b10d5d30f..d078cacb96c2d 100644
--- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js
+++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js
@@ -79,6 +79,7 @@ define(
*/
onError: function (response) {
braintree.showError($t('Payment ' + this.getTitle() + ' can\'t be initialized'));
+ this.isPlaceOrderActionAllowed(true);
throw response.message;
},
diff --git a/app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php b/app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php
index 6688648a3c4fd..7f21d9e69c6e0 100644
--- a/app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php
+++ b/app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php
@@ -127,7 +127,7 @@ protected function processBundleOptionsData(\Magento\Catalog\Model\Product $prod
}
$options = [];
foreach ($bundleOptionsData as $key => $optionData) {
- if ((bool)$optionData['delete']) {
+ if (!empty($optionData['delete'])) {
continue;
}
diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Option.php b/app/code/Magento/Bundle/Model/ResourceModel/Option.php
index 2ad7e57f522d6..46fd8b910f6f1 100644
--- a/app/code/Magento/Bundle/Model/ResourceModel/Option.php
+++ b/app/code/Magento/Bundle/Model/ResourceModel/Option.php
@@ -81,31 +81,39 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object)
{
parent::_afterSave($object);
- $condition = [
+ $conditions = [
'option_id = ?' => $object->getId(),
'store_id = ? OR store_id = 0' => $object->getStoreId(),
'parent_product_id = ?' => $object->getParentId()
];
$connection = $this->getConnection();
- $connection->delete($this->getTable('catalog_product_bundle_option_value'), $condition);
- $data = new \Magento\Framework\DataObject();
- $data->setOptionId($object->getId())
- ->setStoreId($object->getStoreId())
- ->setParentProductId($object->getParentId())
- ->setTitle($object->getTitle());
-
- $connection->insert($this->getTable('catalog_product_bundle_option_value'), $data->getData());
-
- /**
- * also saving default value if this store view scope
- */
+ if ($this->isOptionPresent($conditions)) {
+ $connection->update(
+ $this->getTable('catalog_product_bundle_option_value'),
+ [
+ 'title' => $object->getTitle()
+ ],
+ $conditions
+ );
+ } else {
+ $data = new \Magento\Framework\DataObject();
+ $data->setOptionId($object->getId())
+ ->setStoreId($object->getStoreId())
+ ->setParentProductId($object->getParentId())
+ ->setTitle($object->getTitle());
- if ($object->getStoreId()) {
- $data->setStoreId(0);
- $data->setTitle($object->getDefaultTitle());
$connection->insert($this->getTable('catalog_product_bundle_option_value'), $data->getData());
+
+ /**
+ * also saving default value if this store view scope
+ */
+ if ($object->getStoreId()) {
+ $data->setStoreId(0);
+ $data->setTitle($object->getDefaultTitle());
+ $connection->insert($this->getTable('catalog_product_bundle_option_value'), $data->getData());
+ }
}
return $this;
@@ -210,4 +218,26 @@ public function save(\Magento\Framework\Model\AbstractModel $object)
return $this;
}
+
+ /**
+ * Is Bundle option present in the database
+ *
+ * @param array $conditions
+ *
+ * @return bool
+ */
+ private function isOptionPresent($conditions)
+ {
+ $connection = $this->getConnection();
+
+ $select = $connection->select()->from($this->getTable('catalog_product_bundle_option_value'));
+ foreach ($conditions as $condition => $conditionValue) {
+ $select->where($condition, $conditionValue);
+ }
+ $select->limit(1);
+
+ $rowSelect = $connection->fetchRow($select);
+
+ return (is_array($rowSelect) && !empty($rowSelect));
+ }
}
diff --git a/app/code/Magento/Bundle/view/frontend/templates/sales/order/creditmemo/items/renderer.phtml b/app/code/Magento/Bundle/view/frontend/templates/sales/order/creditmemo/items/renderer.phtml
index b4c2c00e5ac43..b9d075966c5d1 100644
--- a/app/code/Magento/Bundle/view/frontend/templates/sales/order/creditmemo/items/renderer.phtml
+++ b/app/code/Magento/Bundle/view/frontend/templates/sales/order/creditmemo/items/renderer.phtml
@@ -12,7 +12,6 @@
getChildren($parentItem) ?>
getItem()->getOrderItem()->getOrder() ?>
-
diff --git a/app/code/Magento/Bundle/view/frontend/templates/sales/order/invoice/items/renderer.phtml b/app/code/Magento/Bundle/view/frontend/templates/sales/order/invoice/items/renderer.phtml
index f3866d7b1b682..e18d75ce77b9c 100644
--- a/app/code/Magento/Bundle/view/frontend/templates/sales/order/invoice/items/renderer.phtml
+++ b/app/code/Magento/Bundle/view/frontend/templates/sales/order/invoice/items/renderer.phtml
@@ -12,7 +12,6 @@
getItem()->getOrderItem()->getOrder() ?>
getChildren($parentItem) ?>
-
diff --git a/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml b/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml
index 84cbd54d3bdcc..063d66edb9e70 100644
--- a/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml
+++ b/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml
@@ -10,7 +10,6 @@
?>
getItem() ?>
getChildrenItems()); ?>
-
diff --git a/app/code/Magento/Bundle/view/frontend/templates/sales/order/shipment/items/renderer.phtml b/app/code/Magento/Bundle/view/frontend/templates/sales/order/shipment/items/renderer.phtml
index bd99afc59a8c0..0cd39156b2513 100644
--- a/app/code/Magento/Bundle/view/frontend/templates/sales/order/shipment/items/renderer.phtml
+++ b/app/code/Magento/Bundle/view/frontend/templates/sales/order/shipment/items/renderer.phtml
@@ -12,7 +12,6 @@
getItem() ?>
getOrderItem()], $parentItem->getOrderItem()->getChildrenItems()) ?>
getChildren($parentItem) ?>
-
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php
index 6f8a45c6ac7ed..a5b6b34d324f7 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php
@@ -228,7 +228,7 @@ public function getStoreSwitcherHtml()
public function getLoadTreeUrl($expanded = null)
{
$params = ['_current' => true, 'id' => null, 'store' => null];
- if (is_null($expanded) && $this->_backendSession->getIsTreeWasExpanded() || $expanded == true) {
+ if ($expanded === null && $this->_backendSession->getIsTreeWasExpanded() || $expanded == true) {
$params['expand_all'] = true;
}
return $this->getUrl('*/*/categoriesJson', $params);
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Options/Option.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Options/Option.php
index 64856a5c69dc7..339239ea491e0 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Options/Option.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Options/Option.php
@@ -313,9 +313,9 @@ public function getOptionValues()
$value['checkboxScopeTitle'] = $this->getCheckboxScopeHtml(
$option->getOptionId(),
'title',
- is_null($option->getStoreTitle())
+ $option->getStoreTitle() === null
);
- $value['scopeTitleDisabled'] = is_null($option->getStoreTitle()) ? 'disabled' : null;
+ $value['scopeTitleDisabled'] = $option->getStoreTitle() === null ? 'disabled' : null;
}
if ($option->getGroupByType() == ProductCustomOptionInterface::OPTION_GROUP_SELECT) {
@@ -341,22 +341,22 @@ public function getOptionValues()
$value['optionValues'][$i]['checkboxScopeTitle'] = $this->getCheckboxScopeHtml(
$_value->getOptionId(),
'title',
- is_null($_value->getStoreTitle()),
+ $_value->getStoreTitle() === null,
$_value->getOptionTypeId()
);
- $value['optionValues'][$i]['scopeTitleDisabled'] = is_null(
- $_value->getStoreTitle()
+ $value['optionValues'][$i]['scopeTitleDisabled'] = (
+ $_value->getStoreTitle() === null
) ? 'disabled' : null;
if ($scope == \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE) {
$value['optionValues'][$i]['checkboxScopePrice'] = $this->getCheckboxScopeHtml(
$_value->getOptionId(),
'price',
- is_null($_value->getstorePrice()),
+ $_value->getstorePrice() === null,
$_value->getOptionTypeId(),
['$(this).up(1).previous()']
);
- $value['optionValues'][$i]['scopePriceDisabled'] = is_null(
- $_value->getStorePrice()
+ $value['optionValues'][$i]['scopePriceDisabled'] = (
+ $_value->getStorePrice() === null
) ? 'disabled' : null;
}
}
@@ -379,9 +379,9 @@ public function getOptionValues()
$value['checkboxScopePrice'] = $this->getCheckboxScopeHtml(
$option->getOptionId(),
'price',
- is_null($option->getStorePrice())
+ $option->getStorePrice() === null
);
- $value['scopePriceDisabled'] = is_null($option->getStorePrice()) ? 'disabled' : null;
+ $value['scopePriceDisabled'] = $option->getStorePrice() === null ? 'disabled' : null;
}
}
$values[] = new \Magento\Framework\DataObject($value);
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php
index 95d9b1ae61208..3f9dac98033aa 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php
@@ -121,7 +121,7 @@ public function getItems()
* getIdentities() depends on _itemCollection populated, but it can be empty if the block is hidden
* @see https://github.com/magento/magento2/issues/5897
*/
- if (is_null($this->_itemCollection)) {
+ if ($this->_itemCollection === null) {
$this->_prepareData();
}
return $this->_itemCollection;
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php
index f97d1a788dafb..40afd44305262 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php
@@ -140,7 +140,7 @@ public function getItemCollection()
* getIdentities() depends on _itemCollection populated, but it can be empty if the block is hidden
* @see https://github.com/magento/magento2/issues/5897
*/
- if (is_null($this->_itemCollection)) {
+ if ($this->_itemCollection === null) {
$this->_prepareData();
}
return $this->_itemCollection;
@@ -151,7 +151,7 @@ public function getItemCollection()
*/
public function getItems()
{
- if (is_null($this->_items)) {
+ if ($this->_items === null) {
$this->_items = $this->getItemCollection()->getItems();
}
return $this->_items;
diff --git a/app/code/Magento/Catalog/Helper/Product/View.php b/app/code/Magento/Catalog/Helper/Product/View.php
index 46ac05168715b..ae948a362ab5a 100644
--- a/app/code/Magento/Catalog/Helper/Product/View.php
+++ b/app/code/Magento/Catalog/Helper/Product/View.php
@@ -122,18 +122,18 @@ public function initProductLayout(ResultPage $resultPage, $product, $params = nu
// Load default page handles and page configurations
if ($params && $params->getBeforeHandles()) {
foreach ($params->getBeforeHandles() as $handle) {
- $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle);
$resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false);
+ $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle);
}
}
-
- $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku]);
+
$resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], null, false);
+ $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku]);
if ($params && $params->getAfterHandles()) {
foreach ($params->getAfterHandles() as $handle) {
- $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle);
$resultPage->addPageLayoutHandles(['type' => $product->getTypeId()], $handle, false);
+ $resultPage->addPageLayoutHandles(['id' => $product->getId(), 'sku' => $urlSafeSku], $handle);
}
}
diff --git a/app/code/Magento/Catalog/Model/Config.php b/app/code/Magento/Catalog/Model/Config.php
index 227821463b7f0..b3b5204887ea1 100644
--- a/app/code/Magento/Catalog/Model/Config.php
+++ b/app/code/Magento/Catalog/Model/Config.php
@@ -407,7 +407,7 @@ public function getSourceOptionId($source, $value)
*/
public function getProductAttributes()
{
- if (is_null($this->_productAttributes)) {
+ if ($this->_productAttributes === null) {
$this->_productAttributes = array_keys($this->getAttributesUsedInProductListing());
}
return $this->_productAttributes;
@@ -430,7 +430,7 @@ protected function _getResource()
*/
public function getAttributesUsedInProductListing()
{
- if (is_null($this->_usedInProductListing)) {
+ if ($this->_usedInProductListing === null) {
$this->_usedInProductListing = [];
$entityType = \Magento\Catalog\Model\Product::ENTITY;
$attributesData = $this->_getResource()->setStoreId($this->getStoreId())->getAttributesUsedInListing();
@@ -453,7 +453,7 @@ public function getAttributesUsedInProductListing()
*/
public function getAttributesUsedForSortBy()
{
- if (is_null($this->_usedForSortBy)) {
+ if ($this->_usedForSortBy === null) {
$this->_usedForSortBy = [];
$entityType = \Magento\Catalog\Model\Product::ENTITY;
$attributesData = $this->_getResource()->getAttributesUsedForSortBy();
diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/Price.php b/app/code/Magento/Catalog/Model/Layer/Filter/Price.php
index ec5e2bff81ab3..68ef96c0f36a1 100644
--- a/app/code/Magento/Catalog/Model/Layer/Filter/Price.php
+++ b/app/code/Magento/Catalog/Model/Layer/Filter/Price.php
@@ -150,7 +150,7 @@ public function apply(\Magento\Framework\App\RequestInterface $request)
public function getCustomerGroupId()
{
$customerGroupId = $this->_getData('customer_group_id');
- if (is_null($customerGroupId)) {
+ if ($customerGroupId === null) {
$customerGroupId = $this->_customerSession->getCustomerGroupId();
}
@@ -176,7 +176,7 @@ public function setCustomerGroupId($customerGroupId)
public function getCurrencyRate()
{
$rate = $this->_getData('currency_rate');
- if (is_null($rate)) {
+ if ($rate === null) {
$rate = $this->_storeManager->getStore($this->getStoreId())
->getCurrentCurrencyRate();
}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php
index cd686c05908ce..84770a4a93ed4 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php
@@ -91,7 +91,7 @@ public function __construct(
*/
protected function _getWebsiteCurrencyRates()
{
- if (is_null($this->_rates)) {
+ if ($this->_rates === null) {
$this->_rates = [];
$baseCurrency = $this->_config->getValue(
\Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
diff --git a/app/code/Magento/Catalog/Model/Product/Option/Value.php b/app/code/Magento/Catalog/Model/Product/Option/Value.php
index d92646769b13b..10aae63ed349c 100644
--- a/app/code/Magento/Catalog/Model/Product/Option/Value.php
+++ b/app/code/Magento/Catalog/Model/Product/Option/Value.php
@@ -179,7 +179,7 @@ public function setProduct($product)
*/
public function getProduct()
{
- if (is_null($this->_product)) {
+ if ($this->_product === null) {
$this->_product = $this->getOption()->getProduct();
}
return $this->_product;
diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php
index 83feea903f993..f260b01c02ef4 100644
--- a/app/code/Magento/Catalog/Model/ProductRepository.php
+++ b/app/code/Magento/Catalog/Model/ProductRepository.php
@@ -6,10 +6,8 @@
*/
namespace Magento\Catalog\Model;
-use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
-use Magento\Framework\Api\Data\ImageContentInterface;
use Magento\Framework\Api\Data\ImageContentInterfaceFactory;
use Magento\Framework\Api\ImageContentValidatorInterface;
use Magento\Framework\Api\ImageProcessorInterface;
@@ -18,10 +16,8 @@
use Magento\Framework\DB\Adapter\DeadlockException;
use Magento\Framework\DB\Adapter\LockWaitException;
use Magento\Framework\Exception\CouldNotSaveException;
-use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\Framework\Exception\StateException;
use Magento\Framework\Exception\ValidatorException;
/**
@@ -116,11 +112,15 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
protected $fileSystem;
/**
+ * @deprecated
+ * @see \Magento\Catalog\Model\MediaGalleryProcessor
* @var ImageContentInterfaceFactory
*/
protected $contentFactory;
/**
+ * @deprecated
+ * @see \Magento\Catalog\Model\MediaGalleryProcessor
* @var ImageProcessorInterface
*/
protected $imageProcessor;
@@ -131,7 +131,7 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
protected $extensionAttributesJoinProcessor;
/**
- * @var \Magento\Catalog\Model\Product\Gallery\Processor
+ * @var ProductRepository\MediaGalleryProcessor
*/
protected $mediaGalleryProcessor;
@@ -329,6 +329,9 @@ protected function initializeProductData(array $productData, $createNew)
unset($productData['media_gallery']);
if ($createNew) {
$product = $this->productFactory->create();
+ if (isset($productData['price']) && !isset($productData['product_type'])) {
+ $product->setTypeId(Product\Type::TYPE_SIMPLE);
+ }
if ($this->storeManager->hasSingleStore()) {
$product->setWebsiteIds([$this->storeManager->getStore(true)->getWebsiteId()]);
}
@@ -375,53 +378,6 @@ private function assignProductToWebsites(\Magento\Catalog\Model\Product $product
$product->setWebsiteIds($websiteIds);
}
- /**
- * @param ProductInterface $product
- * @param array $newEntry
- * @return $this
- * @throws InputException
- * @throws StateException
- * @throws \Magento\Framework\Exception\LocalizedException
- */
- protected function processNewMediaGalleryEntry(
- ProductInterface $product,
- array $newEntry
- ) {
- /** @var ImageContentInterface $contentDataObject */
- $contentDataObject = $newEntry['content'];
-
- /** @var \Magento\Catalog\Model\Product\Media\Config $mediaConfig */
- $mediaConfig = $product->getMediaConfig();
- $mediaTmpPath = $mediaConfig->getBaseTmpMediaPath();
-
- $relativeFilePath = $this->imageProcessor->processImageContent($mediaTmpPath, $contentDataObject);
- $tmpFilePath = $mediaConfig->getTmpMediaShortUrl($relativeFilePath);
-
- if (!$product->hasGalleryAttribute()) {
- throw new StateException(__('Requested product does not support images.'));
- }
-
- $imageFileUri = $this->getMediaGalleryProcessor()->addImage(
- $product,
- $tmpFilePath,
- isset($newEntry['types']) ? $newEntry['types'] : [],
- true,
- isset($newEntry['disabled']) ? $newEntry['disabled'] : true
- );
- // Update additional fields that are still empty after addImage call
- $this->getMediaGalleryProcessor()->updateImage(
- $product,
- $imageFileUri,
- [
- 'label' => $newEntry['label'],
- 'position' => $newEntry['position'],
- 'disabled' => $newEntry['disabled'],
- 'media_type' => $newEntry['media_type'],
- ]
- );
- return $this;
- }
-
/**
* Process product links, creating new links, updating and deleting existing links
*
@@ -480,67 +436,6 @@ private function processLinks(\Magento\Catalog\Api\Data\ProductInterface $produc
return $this;
}
- /**
- * Process Media gallery data before save product.
- *
- * Compare Media Gallery Entries Data with existing Media Gallery
- * * If Media entry has not value_id set it as new
- * * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag
- * * Merge Existing and new media gallery
- *
- * @param ProductInterface $product contains only existing media gallery items
- * @param array $mediaGalleryEntries array which contains all media gallery items
- * @return $this
- * @throws InputException
- * @throws StateException
- * @throws LocalizedException
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- */
- protected function processMediaGallery(ProductInterface $product, $mediaGalleryEntries)
- {
- $existingMediaGallery = $product->getMediaGallery('images');
- $newEntries = [];
- $entriesById = [];
- if (!empty($existingMediaGallery)) {
- foreach ($mediaGalleryEntries as $entry) {
- if (isset($entry['id'])) {
- $entriesById[$entry['id']] = $entry;
- } else {
- $newEntries[] = $entry;
- }
- }
- foreach ($existingMediaGallery as $key => &$existingEntry) {
- if (isset($entriesById[$existingEntry['value_id']])) {
- $updatedEntry = $entriesById[$existingEntry['value_id']];
- if (array_key_exists('file', $updatedEntry) && $updatedEntry['file'] === null) {
- unset($updatedEntry['file']);
- }
- $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry);
- } else {
- //set the removed flag
- $existingEntry['removed'] = true;
- }
- }
- unset($existingEntry);
- $product->setData('media_gallery', ["images" => $existingMediaGallery]);
- } else {
- $newEntries = $mediaGalleryEntries;
- }
-
- $this->getMediaGalleryProcessor()->clearMediaAttribute($product, array_keys($product->getMediaAttributes()));
- $images = $product->getMediaGallery('images');
- if ($images) {
- foreach ($images as $image) {
- if (!isset($image['removed']) && !empty($image['types'])) {
- $this->getMediaGalleryProcessor()->setMediaAttribute($product, $image['types'], $image['file']);
- }
- }
- }
- $this->processEntries($product, $newEntries, $entriesById);
-
- return $this;
- }
-
/**
* {@inheritdoc}
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
@@ -577,7 +472,10 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO
$this->processLinks($product, $productLinks);
if (isset($productDataArray['media_gallery_entries'])) {
- $this->processMediaGallery($product, $productDataArray['media_gallery_entries']);
+ $this->getMediaGalleryProcessor()->processMediaGallery(
+ $product,
+ $productDataArray['media_gallery_entries']
+ );
}
if (!$product->getOptionsReadonly()) {
@@ -749,13 +647,13 @@ public function cleanCache()
}
/**
- * @return Product\Gallery\Processor
+ * @return ProductRepository\MediaGalleryProcessor
*/
private function getMediaGalleryProcessor()
{
if (null === $this->mediaGalleryProcessor) {
$this->mediaGalleryProcessor = \Magento\Framework\App\ObjectManager::getInstance()
- ->get(\Magento\Catalog\Model\Product\Gallery\Processor::class);
+ ->get(ProductRepository\MediaGalleryProcessor::class);
}
return $this->mediaGalleryProcessor;
}
@@ -775,60 +673,4 @@ private function getCollectionProcessor()
}
return $this->collectionProcessor;
}
-
- /**
- * Convert extension attribute for product media gallery.
- *
- * @param array $newEntry
- * @param array $extensionAttributes
- * @return void
- */
- private function processExtensionAttributes(array &$newEntry, array $extensionAttributes)
- {
- foreach ($extensionAttributes as $code => $value) {
- if (is_array($value)) {
- $this->processExtensionAttributes($newEntry, $value);
- } else {
- $newEntry[$code] = $value;
- }
- }
- unset($newEntry['extension_attributes']);
- }
-
- /**
- * Convert entries into product media gallery data and set to product.
- *
- * @param ProductInterface $product
- * @param array $newEntries
- * @param array $entriesById
- * @throws InputException
- * @throws LocalizedException
- * @throws StateException
- * @return void
- */
- private function processEntries(ProductInterface $product, array $newEntries, array $entriesById)
- {
- foreach ($newEntries as $newEntry) {
- if (!isset($newEntry['content'])) {
- throw new InputException(__('The image content is not valid.'));
- }
- /** @var ImageContentInterface $contentDataObject */
- $contentDataObject = $this->contentFactory->create()
- ->setName($newEntry['content'][ImageContentInterface::NAME])
- ->setBase64EncodedData($newEntry['content'][ImageContentInterface::BASE64_ENCODED_DATA])
- ->setType($newEntry['content'][ImageContentInterface::TYPE]);
- $newEntry['content'] = $contentDataObject;
- $this->processNewMediaGalleryEntry($product, $newEntry);
-
- $finalGallery = $product->getData('media_gallery');
- $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById));
- if (isset($newEntry['extension_attributes'])) {
- $this->processExtensionAttributes($newEntry, $newEntry['extension_attributes']);
- }
- $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]);
- $entriesById[$newEntryId] = $newEntry;
- $finalGallery['images'][$newEntryId] = $newEntry;
- $product->setData('media_gallery', $finalGallery);
- }
- }
}
diff --git a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php
new file mode 100644
index 0000000000000..4cc31d98fdfc2
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php
@@ -0,0 +1,218 @@
+processor = $processor;
+ $this->contentFactory = $contentFactory;
+ $this->imageProcessor = $imageProcessor;
+ }
+
+ /**
+ * Process Media gallery data before save product.
+ *
+ * Compare Media Gallery Entries Data with existing Media Gallery
+ * * If Media entry has not value_id set it as new
+ * * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag
+ * * Merge Existing and new media gallery
+ *
+ * @param ProductInterface $product contains only existing media gallery items.
+ * @param array $mediaGalleryEntries array which contains all media gallery items.
+ * @return void
+ * @throws InputException
+ * @throws StateException
+ * @throws LocalizedException
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+ */
+ public function processMediaGallery(ProductInterface $product, array $mediaGalleryEntries)
+ {
+ $existingMediaGallery = $product->getMediaGallery('images');
+ $newEntries = [];
+ $entriesById = [];
+ if (!empty($existingMediaGallery)) {
+ foreach ($mediaGalleryEntries as $entry) {
+ if (isset($entry['id'])) {
+ $entriesById[$entry['id']] = $entry;
+ } else {
+ $newEntries[] = $entry;
+ }
+ }
+ foreach ($existingMediaGallery as $key => &$existingEntry) {
+ if (isset($entriesById[$existingEntry['value_id']])) {
+ $updatedEntry = $entriesById[$existingEntry['value_id']];
+ if (array_key_exists('file', $updatedEntry) && $updatedEntry['file'] === null) {
+ unset($updatedEntry['file']);
+ }
+ $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry);
+ } else {
+ //set the removed flag.
+ $existingEntry['removed'] = true;
+ }
+ }
+ unset($existingEntry);
+ $product->setData('media_gallery', ["images" => $existingMediaGallery]);
+ } else {
+ $newEntries = $mediaGalleryEntries;
+ }
+
+ $this->processor->clearMediaAttribute($product, array_keys($product->getMediaAttributes()));
+ $images = $product->getMediaGallery('images');
+ if ($images) {
+ foreach ($images as $image) {
+ if (!isset($image['removed']) && !empty($image['types'])) {
+ $this->processor->setMediaAttribute($product, $image['types'], $image['file']);
+ }
+ }
+ }
+ $this->processEntries($product, $newEntries, $entriesById);
+ }
+
+ /**
+ * Convert entries into product media gallery data and set to product.
+ *
+ * @param ProductInterface $product
+ * @param array $newEntries
+ * @param array $entriesById
+ * @throws InputException
+ * @throws LocalizedException
+ * @throws StateException
+ * @return void
+ */
+ private function processEntries(ProductInterface $product, array $newEntries, array $entriesById)
+ {
+ foreach ($newEntries as $newEntry) {
+ if (!isset($newEntry['content'])) {
+ throw new InputException(__('The image content is not valid.'));
+ }
+ /** @var ImageContentInterface $contentDataObject */
+ $contentDataObject = $this->contentFactory->create()
+ ->setName($newEntry['content'][ImageContentInterface::NAME])
+ ->setBase64EncodedData($newEntry['content'][ImageContentInterface::BASE64_ENCODED_DATA])
+ ->setType($newEntry['content'][ImageContentInterface::TYPE]);
+ $newEntry['content'] = $contentDataObject;
+ $this->processNewMediaGalleryEntry($product, $newEntry);
+
+ $finalGallery = $product->getData('media_gallery');
+ $newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById));
+ if (isset($newEntry['extension_attributes'])) {
+ $this->processExtensionAttributes($newEntry, $newEntry['extension_attributes']);
+ }
+ $newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]);
+ $entriesById[$newEntryId] = $newEntry;
+ $finalGallery['images'][$newEntryId] = $newEntry;
+ $product->setData('media_gallery', $finalGallery);
+ }
+ }
+
+ /**
+ * Save gallery entry as image.
+ *
+ * @param ProductInterface $product
+ * @param array $newEntry
+ * @return void
+ * @throws InputException
+ * @throws StateException
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ private function processNewMediaGalleryEntry(
+ ProductInterface $product,
+ array $newEntry
+ ) {
+ /** @var ImageContentInterface $contentDataObject */
+ $contentDataObject = $newEntry['content'];
+
+ /** @var \Magento\Catalog\Model\Product\Media\Config $mediaConfig */
+ $mediaConfig = $product->getMediaConfig();
+ $mediaTmpPath = $mediaConfig->getBaseTmpMediaPath();
+
+ $relativeFilePath = $this->imageProcessor->processImageContent($mediaTmpPath, $contentDataObject);
+ $tmpFilePath = $mediaConfig->getTmpMediaShortUrl($relativeFilePath);
+
+ if (!$product->hasGalleryAttribute()) {
+ throw new StateException(__('Requested product does not support images.'));
+ }
+
+ $imageFileUri = $this->processor->addImage(
+ $product,
+ $tmpFilePath,
+ isset($newEntry['types']) ? $newEntry['types'] : [],
+ true,
+ isset($newEntry['disabled']) ? $newEntry['disabled'] : true
+ );
+ // Update additional fields that are still empty after addImage call.
+ $this->processor->updateImage(
+ $product,
+ $imageFileUri,
+ [
+ 'label' => $newEntry['label'],
+ 'position' => $newEntry['position'],
+ 'disabled' => $newEntry['disabled'],
+ 'media_type' => $newEntry['media_type'],
+ ]
+ );
+ }
+
+ /**
+ * Convert extension attribute for product media gallery.
+ *
+ * @param array $newEntry
+ * @param array $extensionAttributes
+ * @return void
+ */
+ private function processExtensionAttributes(array &$newEntry, array $extensionAttributes)
+ {
+ foreach ($extensionAttributes as $code => $value) {
+ if (is_array($value)) {
+ $this->processExtensionAttributes($newEntry, $value);
+ } else {
+ $newEntry[$code] = $value;
+ }
+ }
+ unset($newEntry['extension_attributes']);
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 58e8424663c83..925c9fe60855d 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -1092,7 +1092,7 @@ public function getSelectCountSql()
protected function _getSelectCountSql($select = null, $resetLeftJoins = true)
{
$this->_renderFilters();
- $countSelect = is_null($select) ? $this->_getClearSelect() : $this->_buildClearSelect($select);
+ $countSelect = $select === null ? $this->_getClearSelect() : $this->_buildClearSelect($select);
$countSelect->columns('COUNT(DISTINCT e.entity_id)');
if ($resetLeftJoins) {
$countSelect->resetJoinLeft();
@@ -1435,7 +1435,7 @@ public function getAllIdsCache($resetCache = false)
$ids = $this->_allIdsCache;
}
- if (is_null($ids)) {
+ if ($ids === null) {
$ids = $this->getAllIds();
$this->setAllIdsCache($ids);
}
@@ -1466,17 +1466,17 @@ public function addPriceData($customerGroupId = null, $websiteId = null)
{
$this->_productLimitationFilters->setUsePriceIndex(true);
- if (!isset($this->_productLimitationFilters['customer_group_id']) && is_null($customerGroupId)) {
+ if (!isset($this->_productLimitationFilters['customer_group_id']) && $customerGroupId === null) {
$customerGroupId = $this->_customerSession->getCustomerGroupId();
}
- if (!isset($this->_productLimitationFilters['website_id']) && is_null($websiteId)) {
+ if (!isset($this->_productLimitationFilters['website_id']) && $websiteId === null) {
$websiteId = $this->_storeManager->getStore($this->getStoreId())->getWebsiteId();
}
- if (!is_null($customerGroupId)) {
+ if ($customerGroupId !== null) {
$this->_productLimitationFilters['customer_group_id'] = $customerGroupId;
}
- if (!is_null($websiteId)) {
+ if ($websiteId !== null) {
$this->_productLimitationFilters['website_id'] = $websiteId;
}
@@ -2347,7 +2347,7 @@ public function setOrder($attribute, $dir = Select::SQL_DESC)
*/
public function getMaxPrice()
{
- if (is_null($this->_maxPrice)) {
+ if ($this->_maxPrice === null) {
$this->_prepareStatisticsData();
}
@@ -2361,7 +2361,7 @@ public function getMaxPrice()
*/
public function getMinPrice()
{
- if (is_null($this->_minPrice)) {
+ if ($this->_minPrice === null) {
$this->_prepareStatisticsData();
}
@@ -2375,7 +2375,7 @@ public function getMinPrice()
*/
public function getPriceStandardDeviation()
{
- if (is_null($this->_priceStandardDeviation)) {
+ if ($this->_priceStandardDeviation === null) {
$this->_prepareStatisticsData();
}
@@ -2389,7 +2389,7 @@ public function getPriceStandardDeviation()
*/
public function getPricesCount()
{
- if (is_null($this->_pricesCount)) {
+ if ($this->_pricesCount === null) {
$this->_prepareStatisticsData();
}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php
index b4f7e43387d0e..ee1df8f23424d 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php
@@ -87,6 +87,7 @@ public function build($productId)
->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId())
->where('t.customer_group_id = ?', $this->customerSession->getCustomerGroupId())
->order('t.min_price ' . Select::SQL_ASC)
+ ->order(BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS . '.' . $linkField . ' ' . Select::SQL_ASC)
->limit(1);
$priceSelect = $this->baseSelectProcessor->process($priceSelect);
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php
index f018e2b148f15..8841b6059c46f 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php
@@ -95,6 +95,7 @@ public function build($productId)
->where('t.attribute_id = ?', $priceAttribute->getAttributeId())
->where('t.value IS NOT NULL')
->order('t.value ' . Select::SQL_ASC)
+ ->order(BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS . '.' . $linkField . ' ' . Select::SQL_ASC)
->limit(1);
$priceSelect = $this->baseSelectProcessor->process($priceSelect);
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php
index b4459cd1eea07..5c47185a85bf4 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php
@@ -139,6 +139,7 @@ public function build($productId)
'special_to.value IS NULL OR ' . $connection->getDatePartSql('special_to.value') .' >= ?',
$currentDate
)->order('t.value ' . Select::SQL_ASC)
+ ->order(BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS . '.' . $linkField . ' ' . Select::SQL_ASC)
->limit(1);
$specialPrice = $this->baseSelectProcessor->process($specialPrice);
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php
index 79323e57b033e..37281193d6a1b 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php
@@ -97,6 +97,7 @@ public function build($productId)
->where('t.all_groups = 1 OR customer_group_id = ?', $this->customerSession->getCustomerGroupId())
->where('t.qty = ?', 1)
->order('t.value ' . Select::SQL_ASC)
+ ->order(BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS . '.' . $linkField . ' ' . Select::SQL_ASC)
->limit(1);
$priceSelect = $this->baseSelectProcessor->process($priceSelect);
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepository/MediaGalleryProcessorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepository/MediaGalleryProcessorTest.php
new file mode 100644
index 0000000000000..02773b2fb3d70
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepository/MediaGalleryProcessorTest.php
@@ -0,0 +1,227 @@
+product = $this->createPartialMock(
+ \Magento\Catalog\Model\Product::class,
+ [
+ 'hasGalleryAttribute',
+ 'getMediaConfig',
+ 'getMediaAttributes',
+ 'getMediaGalleryEntries',
+ ]
+ );
+ $this->product->expects($this->any())
+ ->method('hasGalleryAttribute')
+ ->willReturn(true);
+ $this->processor = $this->getMockBuilder(Processor::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->contentFactory = $this->getMockBuilder(ImageContentInterfaceFactory::class)
+ ->setMethods(['create'])
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ $this->imageProcessor = $this->getMockBuilder(ImageProcessorInterface::class)
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ $objectManager = new ObjectManager($this);
+ $this->model = $objectManager->getObject(
+ MediaGalleryProcessor::class,
+ [
+ 'processor' => $this->processor,
+ 'contentFactory' => $this->contentFactory,
+ 'imageProcessor' => $this->imageProcessor,
+ ]
+ );
+ }
+
+ /**
+ * Test add image.
+ *
+ * @return void
+ */
+ public function testProcessWithNewMediaEntry()
+ {
+ $mediaGalleryEntries = [
+ [
+ 'value_id' => null,
+ 'label' => 'label_text',
+ 'position' => 10,
+ 'disabled' => false,
+ 'types' => ['image', 'small_image'],
+ 'content' => [
+ ImageContentInterface::NAME => 'filename',
+ ImageContentInterface::TYPE => 'image/jpeg',
+ ImageContentInterface::BASE64_ENCODED_DATA => 'encoded_content',
+ ],
+ 'media_type' => 'media_type',
+ ],
+ ];
+
+ //setup media attribute backend.
+ $mediaTmpPath = '/tmp';
+ $absolutePath = '/a/b/filename.jpg';
+ $mediaConfigMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Media\Config::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mediaConfigMock->expects($this->once())
+ ->method('getTmpMediaShortUrl')
+ ->with($absolutePath)
+ ->willReturn($mediaTmpPath . $absolutePath);
+ $this->product->setData('media_gallery', ['images' => $mediaGalleryEntries]);
+ $this->product->expects($this->any())
+ ->method('getMediaAttributes')
+ ->willReturn(['image' => 'imageAttribute', 'small_image' => 'small_image_attribute']);
+ $this->product->expects($this->once())
+ ->method('getMediaConfig')
+ ->willReturn($mediaConfigMock);
+ $this->processor->expects($this->once())->method('clearMediaAttribute')
+ ->with($this->product, ['image', 'small_image']);
+
+ //verify new entries.
+ $contentDataObject = $this->getMockBuilder(\Magento\Framework\Api\ImageContent::class)
+ ->disableOriginalConstructor()
+ ->setMethods(null)
+ ->getMock();
+ $this->contentFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($contentDataObject);
+
+ $this->imageProcessor->expects($this->once())
+ ->method('processImageContent')
+ ->willReturn($absolutePath);
+
+ $imageFileUri = 'imageFileUri';
+ $this->processor->expects($this->once())->method('addImage')
+ ->with($this->product, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false)
+ ->willReturn($imageFileUri);
+ $this->processor->expects($this->once())->method('updateImage')
+ ->with(
+ $this->product,
+ $imageFileUri,
+ [
+ 'label' => 'label_text',
+ 'position' => 10,
+ 'disabled' => false,
+ 'media_type' => 'media_type',
+ ]
+ );
+
+ $this->model->processMediaGallery($this->product, $mediaGalleryEntries);
+ }
+
+ /**
+ * Test update(delete) images.
+ */
+ public function testProcessExistingWithMediaGalleryEntries()
+ {
+ //update one entry, delete one entry.
+ $newEntries = [
+ [
+ 'id' => 5,
+ 'label' => 'new_label_text',
+ 'file' => 'filename1',
+ 'position' => 10,
+ 'disabled' => false,
+ 'types' => ['image', 'small_image'],
+ ],
+ ];
+
+ $existingMediaGallery = [
+ 'images' => [
+ [
+ 'value_id' => 5,
+ 'label' => 'label_text',
+ 'file' => 'filename1',
+ 'position' => 10,
+ 'disabled' => true,
+ ],
+ [
+ 'value_id' => 6, //will be deleted.
+ 'file' => 'filename2',
+ ],
+ ],
+ ];
+
+ $expectedResult = [
+ [
+ 'value_id' => 5,
+ 'id' => 5,
+ 'label' => 'new_label_text',
+ 'file' => 'filename1',
+ 'position' => 10,
+ 'disabled' => false,
+ 'types' => ['image', 'small_image'],
+ ],
+ [
+ 'value_id' => 6, //will be deleted.
+ 'file' => 'filename2',
+ 'removed' => true,
+ ],
+ ];
+
+ $this->product->setData('media_gallery', $existingMediaGallery);
+ $this->product->expects($this->any())
+ ->method('getMediaAttributes')
+ ->willReturn(['image' => 'filename1', 'small_image' => 'filename2']);
+
+ $this->processor->expects($this->once())->method('clearMediaAttribute')
+ ->with($this->product, ['image', 'small_image']);
+ $this->processor->expects($this->once())
+ ->method('setMediaAttribute')
+ ->with($this->product, ['image', 'small_image'], 'filename1');
+ $this->model->processMediaGallery($this->product, $newEntries);
+ $this->assertEquals($expectedResult, $this->product->getMediaGallery('images'));
+ }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
index a220b9a5768fe..14c84f4781a3a 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
@@ -9,6 +9,7 @@
namespace Magento\Catalog\Test\Unit\Model;
+use Magento\Catalog\Model\ProductRepository\MediaGalleryProcessor;
use Magento\Framework\Api\Data\ImageContentInterface;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\DB\Adapter\ConnectionException;
@@ -139,7 +140,7 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase
protected $storeManagerMock;
/**
- * @var \Magento\Catalog\Model\Product\Gallery\Processor|\PHPUnit_Framework_MockObject_MockObject
+ * @var MediaGalleryProcessor|\PHPUnit_Framework_MockObject_MockObject
*/
protected $mediaGalleryProcessor;
@@ -234,7 +235,7 @@ protected function setUp()
$storeMock->expects($this->any())->method('getCode')->willReturn(\Magento\Store\Model\Store::ADMIN_CODE);
$this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock);
- $this->mediaGalleryProcessor = $this->createMock(\Magento\Catalog\Model\Product\Gallery\Processor::class);
+ $this->mediaGalleryProcessor = $this->createMock(MediaGalleryProcessor::class);
$this->collectionProcessorMock = $this->getMockBuilder(CollectionProcessorInterface::class)
->getMock();
@@ -1174,7 +1175,21 @@ public function testSaveExistingWithNewMediaGalleryEntries()
]
]
];
-
+ $expectedEntriesData = [
+ [
+ 'id' => null,
+ 'label' => "label_text",
+ 'position' => 10,
+ 'disabled' => false,
+ 'types' => ['image', 'small_image'],
+ 'content' => [
+ ImageContentInterface::NAME => 'filename',
+ ImageContentInterface::TYPE => 'image/jpeg',
+ ImageContentInterface::BASE64_ENCODED_DATA => 'encoded_content',
+ ],
+ 'media_type' => 'media_type',
+ ],
+ ];
$this->setupProductMocksForSave();
//media gallery data
$this->productData['media_gallery_entries'] = [
@@ -1198,56 +1213,8 @@ public function testSaveExistingWithNewMediaGalleryEntries()
->will($this->returnValue($this->productData));
$this->initializedProductMock->setData('media_gallery', $newEntriesData);
- $this->initializedProductMock->expects($this->any())
- ->method('getMediaAttributes')
- ->willReturn(["image" => "imageAttribute", "small_image" => "small_image_attribute"]);
-
- //setup media attribute backend
- $mediaTmpPath = '/tmp';
- $absolutePath = '/a/b/filename.jpg';
-
- $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute')
- ->with($this->initializedProductMock, ['image', 'small_image']);
-
- $mediaConfigMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Media\Config::class)
- ->disableOriginalConstructor()
- ->getMock();
- $mediaConfigMock->expects($this->once())
- ->method('getTmpMediaShortUrl')
- ->with($absolutePath)
- ->willReturn($mediaTmpPath . $absolutePath);
- $this->initializedProductMock->expects($this->once())
- ->method('getMediaConfig')
- ->willReturn($mediaConfigMock);
-
- //verify new entries
- $contentDataObject = $this->getMockBuilder(\Magento\Framework\Api\ImageContent::class)
- ->disableOriginalConstructor()
- ->setMethods(null)
- ->getMock();
- $this->contentFactoryMock->expects($this->once())
- ->method('create')
- ->willReturn($contentDataObject);
-
- $this->imageProcessorMock->expects($this->once())
- ->method('processImageContent')
- ->willReturn($absolutePath);
-
- $imageFileUri = "imageFileUri";
- $this->mediaGalleryProcessor->expects($this->once())->method('addImage')
- ->with($this->initializedProductMock, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false)
- ->willReturn($imageFileUri);
- $this->mediaGalleryProcessor->expects($this->once())->method('updateImage')
- ->with(
- $this->initializedProductMock,
- $imageFileUri,
- [
- 'label' => 'label_text',
- 'position' => 10,
- 'disabled' => false,
- 'media_type' => 'media_type',
- ]
- );
+ $this->mediaGalleryProcessor->expects($this->once())->method('processMediaGallery')
+ ->with($this->initializedProductMock, $expectedEntriesData);
$this->initializedProductMock->expects($this->once())->method('getWebsiteIds')->willReturn([]);
$this->initializedProductMock->expects($this->atLeastOnce())
->method('getSku')->willReturn($this->productData['sku']);
@@ -1325,24 +1292,6 @@ public function testSaveExistingWithMediaGalleryEntries()
],
],
];
-
- $expectedResult = [
- [
- 'value_id' => 5,
- 'id' => 5,
- "label" => "new_label_text",
- 'file' => 'filename1',
- 'position' => 10,
- 'disabled' => false,
- 'types' => ['image', 'small_image'],
- ],
- [
- 'value_id' => 6, //will be deleted
- 'file' => 'filename2',
- 'removed' => true,
- ],
- ];
-
$this->setupProductMocksForSave();
//media gallery data
$this->productData['media_gallery_entries'] = $newEntries;
@@ -1352,21 +1301,15 @@ public function testSaveExistingWithMediaGalleryEntries()
->will($this->returnValue($this->productData));
$this->initializedProductMock->setData('media_gallery', $existingMediaGallery);
- $this->initializedProductMock->expects($this->any())
- ->method('getMediaAttributes')
- ->willReturn(["image" => "filename1", "small_image" => "filename2"]);
- $this->mediaGalleryProcessor->expects($this->once())->method('clearMediaAttribute')
- ->with($this->initializedProductMock, ['image', 'small_image']);
$this->mediaGalleryProcessor->expects($this->once())
- ->method('setMediaAttribute')
- ->with($this->initializedProductMock, ['image', 'small_image'], 'filename1');
+ ->method('processMediaGallery')
+ ->with($this->initializedProductMock, $newEntries);
$this->initializedProductMock->expects($this->once())->method('getWebsiteIds')->willReturn([]);
$this->initializedProductMock->expects($this->atLeastOnce())
->method('getSku')->willReturn($this->productData['sku']);
$this->productMock->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']);
$this->productMock->expects($this->any())->method('getMediaGalleryEntries')->willReturn(null);
$this->model->save($this->productMock);
- $this->assertEquals($expectedResult, $this->initializedProductMock->getMediaGallery('images'));
}
}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/AbstractTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/AbstractTest.php
index 64416301faa06..96336d2b0706a 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/AbstractTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/AbstractTest.php
@@ -44,11 +44,13 @@ public function testWalkAttributes()
$code = 'test_attr';
$set = 10;
+ $storeId = 100;
$object = $this->createPartialMock(\Magento\Catalog\Model\Product::class, ['__wakeup']);
$object->setData('test_attr', 'test_attr');
$object->setData('attribute_set_id', $set);
+ $object->setData('store_id', $storeId);
$entityType = new \Magento\Framework\DataObject();
$entityType->setEntityTypeCode('test');
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php
index 6b908d317aa5b..cec862ee9661f 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php
@@ -84,7 +84,7 @@ public function testBuild()
$select->expects($this->any())->method('from')->willReturnSelf();
$select->expects($this->any())->method('joinInner')->willReturnSelf();
$select->expects($this->any())->method('where')->willReturnSelf();
- $select->expects($this->once())->method('order')->willReturnSelf();
+ $select->expects($this->exactly(2))->method('order')->willReturnSelf();
$select->expects($this->once())->method('limit')->willReturnSelf();
$this->resourceMock->expects($this->any())->method('getConnection')->willReturn($connection);
$this->metadataPoolMock->expects($this->once())->method('getMetadata')->willReturn($metadata);
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Attributes.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Attributes.php
index aec6549f400fc..683a96133ad30 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Attributes.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Attributes.php
@@ -182,6 +182,11 @@ private function customizeAddAttributeModal(array $meta)
. '.create_new_attribute_modal',
'actionName' => 'toggleModal',
],
+ [
+ 'targetName' => 'product_form.product_form.add_attribute_modal'
+ . '.create_new_attribute_modal.product_attribute_add_form',
+ 'actionName' => 'destroyInserted'
+ ],
[
'targetName'
=> 'product_form.product_form.add_attribute_modal'
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml
index 6ff0e193a774f..f812a27f87ad9 100644
--- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml
+++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml
@@ -57,7 +57,7 @@ $stores = $block->getStoresSortedBySortOrder();