diff --git a/app/bootstrap.php b/app/bootstrap.php index 5e693487d748d41cd2e2a0259400eaa1798124e5..ec60a1708dacc3d2238960e5b1c9e7c0da05cc92 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -11,14 +11,14 @@ error_reporting(E_ALL); #ini_set('display_errors', 1); /* PHP version validation */ -if (!defined('PHP_VERSION_ID') || !(PHP_VERSION_ID >= 50600 && PHP_VERSION_ID < 50700 || PHP_VERSION_ID === 70002 || PHP_VERSION_ID >= 70006)) { +if (!defined('PHP_VERSION_ID') || !(PHP_VERSION_ID >= 50600 && PHP_VERSION_ID < 50700 || PHP_VERSION_ID === 70002 || PHP_VERSION_ID === 70004 || PHP_VERSION_ID >= 70006)) { if (PHP_SAPI == 'cli') { - echo 'Magento supports PHP 5.6, 7.0.2, and 7.0.6 or later. ' . + echo 'Magento supports PHP 5.6, 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'; } else { echo <<<HTML <div style="font:12px/1.35em arial, helvetica, sans-serif;"> - <p>Magento supports PHP 5.6, 7.0.2, and 7.0.6 or later. Please read + <p>Magento supports PHP 5.6, 7.0.2, 7.0.4, and 7.0.6 or later. Please read <a target="_blank" href="http://devdocs.magento.com/guides/v1.0/install-gde/system-requirements.html"> Magento System Requirements</a>. </div> diff --git a/app/code/Magento/AdminNotification/composer.json b/app/code/Magento/AdminNotification/composer.json index ae24995c35935769b9a2c9812343989c383e1397..0a29908b77f6f4f6511fe5671702217ef95ceb90 100644 --- a/app/code/Magento/AdminNotification/composer.json +++ b/app/code/Magento/AdminNotification/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-admin-notification", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-media-storage": "100.2.*", diff --git a/app/code/Magento/AdvancedPricingImportExport/composer.json b/app/code/Magento/AdvancedPricingImportExport/composer.json index 8388c87e55d8fc121f72d063dfbb8cea517b80b9..65ea7524dffff5bb778aaf5ec7658302072cfaf9 100644 --- a/app/code/Magento/AdvancedPricingImportExport/composer.json +++ b/app/code/Magento/AdvancedPricingImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-advanced-pricing-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-catalog-inventory": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/Authorization/composer.json b/app/code/Magento/Authorization/composer.json index c8016abf3f743e3ed90b4150dabb33c8a0ac64ea..0ca367d4854dfe5c35811d15aee4d718c0a4edb7 100644 --- a/app/code/Magento/Authorization/composer.json +++ b/app/code/Magento/Authorization/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-authorization", "description": "Authorization module provides access to Magento ACL functionality.", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Authorizenet/composer.json b/app/code/Magento/Authorizenet/composer.json index d880b3d2c6539c22f666c5cb1a100f81fb4dbcce..0c9e9641b6076850310edd75a2a4ea5ed3ccd094 100644 --- a/app/code/Magento/Authorizenet/composer.json +++ b/app/code/Magento/Authorizenet/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-authorizenet", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-sales": "100.2.*", "magento/module-store": "100.2.*", "magento/module-quote": "100.2.*", diff --git a/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php b/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php index e7d2742d34bab58f853a3b5ef6dfcc717c35ebfd..ae13ce4a7a4523d4b761778a67cd36cc3b4af00d 100644 --- a/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php +++ b/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php @@ -13,6 +13,7 @@ use Magento\Framework\App\Config\ScopeConfigInterface; /** * "Reset to Defaults" button renderer * + * @deprecated * @author Magento Core Team <core@magentocommerce.com> */ class Reset extends \Magento\Config\Block\System\Config\Form\Field diff --git a/app/code/Magento/Backend/Test/Unit/Block/Page/System/Config/Robots/ResetTest.php b/app/code/Magento/Backend/Test/Unit/Block/Page/System/Config/Robots/ResetTest.php index 6f64d49829fb52b736108fac183e769876d0812a..1bd0a81f78163025cec29a0e214f72e8d9c15c04 100644 --- a/app/code/Magento/Backend/Test/Unit/Block/Page/System/Config/Robots/ResetTest.php +++ b/app/code/Magento/Backend/Test/Unit/Block/Page/System/Config/Robots/ResetTest.php @@ -9,6 +9,11 @@ */ namespace Magento\Backend\Test\Unit\Block\Page\System\Config\Robots; +/** + * Class ResetTest + * @deprecated + * @package Magento\Backend\Test\Unit\Block\Page\System\Config\Robots + */ class ResetTest extends \PHPUnit_Framework_TestCase { /** diff --git a/app/code/Magento/Backend/composer.json b/app/code/Magento/Backend/composer.json index 5a535b1331b0f6ed121f0333ec52701135ef4865..7d428636a1f45e23d81879c99baaeef6744518fc 100644 --- a/app/code/Magento/Backend/composer.json +++ b/app/code/Magento/Backend/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-backend", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-directory": "100.2.*", "magento/module-developer": "100.2.*", diff --git a/app/code/Magento/Backend/view/adminhtml/templates/system/shipping/applicable_country.phtml b/app/code/Magento/Backend/view/adminhtml/templates/system/shipping/applicable_country.phtml index 1ab29f9fc75994705eab61414a270eca9e953b51..6d003c6cb6810ee05982625e3b67bf6652b65ca2 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/system/shipping/applicable_country.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/system/shipping/applicable_country.phtml @@ -37,7 +37,7 @@ CountryModel.prototype = { var showMethodElement = $(applyCountryElement.id.replace(/sallowspecific/, 'showmethod')); //var specifErrMsgElement = $(applyCountryElement.id.replace(/sallowspecific/, 'specificerrmsg')); if (specifCountryElement) { - if (applyCountryElement.value == 1) { + if (applyCountryElement.value == 1 && !specifCountryElement.hasAttribute('disabled')) { //if specific country element selected specifCountryElement.enable(); if (showMethodElement) { diff --git a/app/code/Magento/Backup/composer.json b/app/code/Magento/Backup/composer.json index a912e1498806f84ef63ebb725926d144de7ae862..21ed6f1780a41348c5bb26556c14fcaa02c08770 100644 --- a/app/code/Magento/Backup/composer.json +++ b/app/code/Magento/Backup/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-backup", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-cron": "100.2.*", diff --git a/app/code/Magento/Braintree/Model/Report/Row/TransactionMap.php b/app/code/Magento/Braintree/Model/Report/Row/TransactionMap.php index 0abae8af89fbca4664c7b35568cf16e61533a3fe..c914b21893e253feb3a04fabe0f4a274d10deb1a 100644 --- a/app/code/Magento/Braintree/Model/Report/Row/TransactionMap.php +++ b/app/code/Magento/Braintree/Model/Report/Row/TransactionMap.php @@ -115,9 +115,13 @@ class TransactionMap implements DocumentInterface */ public function getCustomAttributes() { + $shouldBeLocalized = ['paymentInstrumentType', 'type', 'status']; $output = []; foreach ($this->getMappedValues() as $key => $value) { $attribute = $this->attributeValueFactory->create(); + if(in_array($key, $shouldBeLocalized)) { + $value = __($value); + } $output[] = $attribute->setAttributeCode($key)->setValue($value); } return $output; diff --git a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php index d720b748ba819cce5515d052acde4bfda50c29db..cea02f249cbed4a6da6d7426c0ab20e27fe9cbc9 100644 --- a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php +++ b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php @@ -79,7 +79,6 @@ final class ConfigProvider implements ConfigProviderInterface 'payment' => [ self::CODE => [ 'isActive' => $this->config->isActive(), - 'isSingleUse' => !$isPayPalActive, 'clientToken' => $this->getClientToken(), 'ccTypesMapper' => $this->config->getCctypesMapper(), 'sdkUrl' => $this->config->getSdkUrl(), diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionMapTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionMapTest.php index c44a67b2c61d92cabe3087e9a47f20f1b94f9250..34c607c88784d7c270d75937cadfb550216744b1 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionMapTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionMapTest.php @@ -11,6 +11,8 @@ use DateTime; use Magento\Braintree\Model\Report\Row\TransactionMap; use Magento\Framework\Api\AttributeValue; use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Phrase; +use Magento\Framework\Phrase\RendererInterface; use Magento\Store\Model\StoreManagerInterface; /** @@ -30,6 +32,16 @@ class TransactionMapTest extends \PHPUnit_Framework_TestCase */ private $attributeValueFactoryMock; + /** + * @var RendererInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $defaultRenderer; + + /** + * @var RendererInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $rendererMock; + /** * Setup */ @@ -39,6 +51,9 @@ class TransactionMapTest extends \PHPUnit_Framework_TestCase ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); + $this->defaultRenderer = Phrase::getRenderer(); + $this->rendererMock = $this->getMockBuilder(RendererInterface::class) + ->getMock(); } /** @@ -65,6 +80,8 @@ class TransactionMapTest extends \PHPUnit_Framework_TestCase $this->transactionStub ); + Phrase::setRenderer($this->rendererMock); + /** @var AttributeValue[] $result */ $result = $map->getCustomAttributes(); @@ -77,6 +94,31 @@ class TransactionMapTest extends \PHPUnit_Framework_TestCase $result[6]->getValue() ); $this->assertEquals(implode(', ', $transaction['refundIds']), $result[11]->getValue()); + $this->assertEquals($transaction['merchantAccountId'], $result[1]->getValue()); + $this->assertEquals($transaction['orderId'], $result[2]->getValue()); + $this->assertEquals($transaction['amount'], $result[7]->getValue()); + $this->assertEquals($transaction['processorSettlementResponseCode'], $result[8]->getValue()); + $this->assertEquals($transaction['processorSettlementResponseText'], $result[10]->getValue()); + $this->assertEquals($transaction['settlementBatchId'], $result[12]->getValue()); + $this->assertEquals($transaction['currencyIsoCode'], $result[13]->getValue()); + + $this->rendererMock->expects($this->at(0)) + ->method('render') + ->with([$transaction['paymentInstrumentType']]) + ->willReturn('Credit card'); + $this->assertEquals('Credit card', $result[3]->getValue()->render()); + + $this->rendererMock->expects($this->at(0)) + ->method('render') + ->with([$transaction['type']]) + ->willReturn('Sale'); + $this->assertEquals('Sale', $result[5]->getValue()->render()); + + $this->rendererMock->expects($this->at(0)) + ->method('render') + ->with([$transaction['status']]) + ->willReturn('Pending for settlement'); + $this->assertEquals('Pending for settlement', $result[9]->getValue()->render()); } /** @@ -90,9 +132,27 @@ class TransactionMapTest extends \PHPUnit_Framework_TestCase 'id' => 1, 'createdAt' => new \DateTime(), 'paypalDetails' => new PayPalDetails(['paymentId' => 10]), - 'refundIds' => [1, 2, 3, 4, 5] + 'refundIds' => [1, 2, 3, 4, 5], + 'merchantAccountId' => 'MerchantId', + 'orderId' => 1, + 'paymentInstrumentType' => 'credit_card', + 'type' => 'sale', + 'amount' => '$19.99', + 'processorSettlementResponseCode' => 1, + 'status' => 'pending_for_settlement', + 'processorSettlementResponseText' => 'sample text', + 'settlementBatchId' => 2, + 'currencyIsoCode' => 'USD' ] ] ]; } + + /** + * @return void + */ + protected function tearDown() + { + Phrase::setRenderer($this->defaultRenderer); + } } diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionsCollectionTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionsCollectionTest.php index 50488df2600c64f1267755b0da98cee69c993789..6024141280a021e443ad100eca65627a4728976c 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionsCollectionTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Report/TransactionsCollectionTest.php @@ -184,4 +184,42 @@ class TransactionsCollectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals(TransactionsCollection::TRANSACTION_MAXIMUM_COUNT, count($items)); $this->assertInstanceOf(DocumentInterface::class, $items[1]); } + + /** + * Add fields to filter + * + * @dataProvider addToFilterDataProvider + */ + public function testAddToFilter($field, $condition, $filterMapperCall, $expectedCondition) + { + $this->filterMapperMock->expects(static::exactly($filterMapperCall)) + ->method('getFilter') + ->with($field, $expectedCondition) + ->willReturn(new BraintreeSearchNodeStub()); + + $collection = new TransactionsCollection( + $this->entityFactoryMock, + $this->braintreeAdapterMock, + $this->filterMapperMock + ); + + static::assertInstanceOf( + TransactionsCollection::class, + $collection->addFieldToFilter($field, $condition) + ); + } + + /** + * addToFilter DataProvider + * + * @return array + */ + public function addToFilterDataProvider() + { + return [ + ['orderId', ['like' => 1], 1, ['like' => 1]], + ['type', 'sale', 1, ['eq' => 'sale']], + [['type', 'orderId'], [], 0, []], + ]; + } } diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php index a787111fa93aa383f6ec11b77f3cb2e623bc228e..ad9f99b39afb0820b3b0bcbdca10fbe4837e9a5e 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php @@ -154,7 +154,6 @@ class ConfigProviderTest extends \PHPUnit_Framework_TestCase 'payment' => [ ConfigProvider::CODE => [ 'isActive' => true, - 'isSingleUse' => false, 'clientToken' => self::CLIENT_TOKEN, 'ccTypesMapper' => ['visa' => 'VI', 'american-express' => 'AE'], 'sdkUrl' => self::SDK_URL, diff --git a/app/code/Magento/Braintree/Test/Unit/Ui/Component/Report/Filters/Type/DateRangeTest.php b/app/code/Magento/Braintree/Test/Unit/Ui/Component/Report/Filters/Type/DateRangeTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b81dbe2fb036f66912c35f7cdf59a88deee6dd51 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Ui/Component/Report/Filters/Type/DateRangeTest.php @@ -0,0 +1,251 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Braintree\Test\Unit\Ui\Component\Report\Filters\Type; + +use Magento\Framework\Api\Filter; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Ui\Component\Filters\FilterModifier; +use Magento\Braintree\Ui\Component\Report\Filters\Type\DateRange; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Ui\Component\Form\Element\DataType\Date as FormDate; + +/** + * Class DateRangeTest + */ +class DateRangeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $uiComponentFactory; + + /** + * @var FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $filterBuilderMock; + + /** + * @var FilterModifier|\PHPUnit_Framework_MockObject_MockObject + */ + private $filterModifierMock; + + + /** + * @var DataProviderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $dataProviderMock; + + /** + * Set up + */ + protected function setUp() + { + $this->contextMock = $this->getMockForAbstractClass(ContextInterface::class); + $processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->contextMock->expects(static::any()) + ->method('getProcessor') + ->willReturn($processor); + $this->uiComponentFactory = $this->getMockBuilder(UiComponentFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->filterBuilderMock = $this->getMockBuilder(FilterBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->filterModifierMock = $this->getMockBuilder(FilterModifier::class) + ->setMethods(['applyFilterModifier']) + ->disableOriginalConstructor() + ->getMock(); + + $this->dataProviderMock = $this->getMockForAbstractClass(DataProviderInterface::class); + } + + /** + * Run test prepare method + * + * @param string $name + * @param array $filterData + * @param array|null $expectedCondition + * @dataProvider getPrepareDataProvider + * @return void + */ + public function testPrepare($name, $filterData, $expectedCondition) + { + /** @var FormDate PHPUnit_Framework_MockObject_MockObject|$uiComponent */ + $uiComponent = $this->getMockBuilder(FormDate::class) + ->disableOriginalConstructor() + ->getMock(); + + $uiComponent->expects($this->any()) + ->method('getContext') + ->willReturn($this->contextMock); + + $this->contextMock->expects($this->any()) + ->method('getNamespace') + ->willReturn(DateRange::NAME); + $this->contextMock->expects($this->any()) + ->method('addComponentDefinition') + ->with(DateRange::NAME, ['extends' => DateRange::NAME]); + + $this->contextMock->expects($this->any()) + ->method('getFiltersParams') + ->willReturn($filterData); + + $this->contextMock->expects($this->any()) + ->method('getDataProvider') + ->willReturn($this->dataProviderMock); + + if ($expectedCondition !== null) { + if (is_string($filterData[$name])) { + $uiComponent->expects(static::once()) + ->method('convertDate') + ->with($filterData[$name]) + ->willReturn(new \DateTime($filterData[$name], new \DateTimeZone('UTC'))); + } else { + $uiComponent->method('convertDate') + ->willReturnMap([ + [ + $filterData[$name]['from'], 0, 0, 0, + new \DateTime($filterData[$name]['from'], new \DateTimeZone('UTC')) + ], + [ + $filterData[$name]['to'], 23, 59, 59, + new \DateTime($filterData[$name]['to'] . ' 23:59:00', new \DateTimeZone('UTC')) + ], + ]); + } + + $i=0; + switch (true) { + case is_string($filterData[$name]): + case isset($filterData[$name]['from']) && !isset($filterData[$name]['to']): + case !isset($filterData[$name]['from']) && isset($filterData[$name]['to']): + $filterMock = $this->getFilterMock( + $name, + $expectedCondition['type'], + $expectedCondition['date'], + $i + ); + $this->dataProviderMock->expects(static::once()) + ->method('addFilter') + ->with($filterMock); + break; + case isset($filterData[$name]['from']) && isset($filterData[$name]['to']): + $this->getFilterMock( + $name, + $expectedCondition['type_from'], + $expectedCondition['date_from'], + $i + ); + $filterMock = $this->getFilterMock( + $name, + $expectedCondition['type_to'], + $expectedCondition['date_to'], + $i + ); + $this->dataProviderMock->expects(static::exactly(2)) + ->method('addFilter') + ->with($filterMock); + break; + } + } + + $this->uiComponentFactory->expects($this->any()) + ->method('create') + ->with($name, DateRange::COMPONENT, ['context' => $this->contextMock]) + ->willReturn($uiComponent); + + $date = new DateRange( + $this->contextMock, + $this->uiComponentFactory, + $this->filterBuilderMock, + $this->filterModifierMock, + [], + ['name' => $name] + ); + $date->prepare(); + } + + /** + * Gets Filter mock + * + * @param string $name + * @param string $expectedType + * @param string $expectedDate + * @param int $i + * + * @return Filter|\PHPUnit_Framework_MockObject_MockObject + */ + private function getFilterMock($name, $expectedType, $expectedDate, &$i) + { + $this->filterBuilderMock->expects(static::at($i++)) + ->method('setConditionType') + ->with($expectedType) + ->willReturnSelf(); + $this->filterBuilderMock->expects(static::at($i++)) + ->method('setField') + ->with($name) + ->willReturnSelf(); + $this->filterBuilderMock->expects(static::at($i++)) + ->method('setValue') + ->with($expectedDate) + ->willReturnSelf(); + + $filterMock = $this->getMock(Filter::class); + $this->filterBuilderMock->expects(static::at($i++)) + ->method('create') + ->willReturn($filterMock); + + return $filterMock; + } + + /** + * @return array + */ + public function getPrepareDataProvider() + { + return [ + [ + 'test_date', + ['test_date' => ['from' => '11-05-2015', 'to' => null]], + ['date' => '2015-05-11T00:00:00+0000', 'type' => 'gteq'], + ], + [ + 'test_date', + ['test_date' => ['from' => null, 'to' => '11-05-2015']], + ['date' => '2015-05-11T23:59:00+0000', 'type' => 'lteq'], + ], + [ + 'test_date', + ['test_date' => ['from' => '11-05-2015', 'to' => '11-05-2015']], + [ + 'date_from' => '2015-05-11T00:00:00+0000', 'type_from' => 'gteq', + 'date_to' => '2015-05-11T23:59:00+0000', 'type_to' => 'lteq' + ], + ], + [ + 'test_date', + ['test_date' => '11-05-2015'], + ['date' => '2015-05-11T00:00:00+0000', 'type' => 'eq'], + ], + [ + 'test_date', + ['test_date' => ['from' => '', 'to' => '']], + null, + ] + ]; + } +} diff --git a/app/code/Magento/Braintree/Ui/Component/Report/Filters/Type/DateRange.php b/app/code/Magento/Braintree/Ui/Component/Report/Filters/Type/DateRange.php new file mode 100644 index 0000000000000000000000000000000000000000..adbb3b78cb663d92a46e7abdd9c41d70f63e3145 --- /dev/null +++ b/app/code/Magento/Braintree/Ui/Component/Report/Filters/Type/DateRange.php @@ -0,0 +1,19 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Braintree\Ui\Component\Report\Filters\Type; + +/** + * Class DateRange + */ +class DateRange extends \Magento\Ui\Component\Filters\Type\Date +{ + /** + * Braintree date format + * + * @var string + */ + protected static $dateFormat = 'Y-m-d\TH:i:00O'; +} diff --git a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/PaymentType.php b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/PaymentType.php index df6c2ffcd229dd244c8f39a8191ac0955fd936c6..489c0d4cd1e49cd5b788b6927ee09515285b049c 100644 --- a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/PaymentType.php +++ b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/PaymentType.php @@ -44,12 +44,12 @@ class PaymentType implements OptionSourceInterface private function getAvailablePaymentTypes() { return [ - PaymentInstrumentType::PAYPAL_ACCOUNT => __('Paypal account'), - PaymentInstrumentType::COINBASE_ACCOUNT => __('Coinbase account'), - PaymentInstrumentType::EUROPE_BANK_ACCOUNT => __('Europe bank account'), - PaymentInstrumentType::CREDIT_CARD => __('Credit card'), - PaymentInstrumentType::APPLE_PAY_CARD => __('Apple pay card'), - PaymentInstrumentType::ANDROID_PAY_CARD => __('Android pay card') + PaymentInstrumentType::PAYPAL_ACCOUNT => __(PaymentInstrumentType::PAYPAL_ACCOUNT), + PaymentInstrumentType::COINBASE_ACCOUNT => __(PaymentInstrumentType::COINBASE_ACCOUNT), + PaymentInstrumentType::EUROPE_BANK_ACCOUNT => __(PaymentInstrumentType::EUROPE_BANK_ACCOUNT), + PaymentInstrumentType::CREDIT_CARD => __(PaymentInstrumentType::CREDIT_CARD), + PaymentInstrumentType::APPLE_PAY_CARD => __(PaymentInstrumentType::APPLE_PAY_CARD), + PaymentInstrumentType::ANDROID_PAY_CARD => __(PaymentInstrumentType::ANDROID_PAY_CARD) ]; } } diff --git a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/Status.php b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/Status.php index f5424c6dd9b7f1a1c30b761fe6b3041017191c7e..ca6d6522990b42e11fb4e8230d2c4fee2a64d68e 100644 --- a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/Status.php +++ b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/Status.php @@ -44,20 +44,20 @@ class Status implements OptionSourceInterface private function getAvailableStatuses() { return [ - Transaction::AUTHORIZATION_EXPIRED => __('Authorization expired'), - Transaction::AUTHORIZING => __('Authorizing'), - Transaction::AUTHORIZED => __('Authorized'), - Transaction::GATEWAY_REJECTED => __('Gateway rejected'), - Transaction::FAILED => __('Failed'), - Transaction::PROCESSOR_DECLINED => __('Processor declined'), - Transaction::SETTLED => __('Settled'), - Transaction::SETTLING => __('Settling'), - Transaction::SUBMITTED_FOR_SETTLEMENT => __('Submitted for settlement'), - Transaction::VOIDED => __('Voided'), - Transaction::UNRECOGNIZED => __('Unrecognized'), - Transaction::SETTLEMENT_DECLINED => __('Settlement declined'), - Transaction::SETTLEMENT_PENDING => __('Settlement pending'), - Transaction::SETTLEMENT_CONFIRMED => __('Settlement confirmed') + Transaction::AUTHORIZATION_EXPIRED => __(Transaction::AUTHORIZATION_EXPIRED), + Transaction::AUTHORIZING => __(Transaction::AUTHORIZING), + Transaction::AUTHORIZED => __(Transaction::AUTHORIZED), + Transaction::GATEWAY_REJECTED => __(Transaction::GATEWAY_REJECTED), + Transaction::FAILED => __(Transaction::FAILED), + Transaction::PROCESSOR_DECLINED => __(Transaction::PROCESSOR_DECLINED), + Transaction::SETTLED => __(Transaction::SETTLED), + Transaction::SETTLING => __(Transaction::SETTLING), + Transaction::SUBMITTED_FOR_SETTLEMENT => __(Transaction::SUBMITTED_FOR_SETTLEMENT), + Transaction::VOIDED => __(Transaction::VOIDED), + Transaction::UNRECOGNIZED => __(Transaction::UNRECOGNIZED), + Transaction::SETTLEMENT_DECLINED => __(Transaction::SETTLEMENT_DECLINED), + Transaction::SETTLEMENT_PENDING => __(Transaction::SETTLEMENT_PENDING), + Transaction::SETTLEMENT_CONFIRMED => __(Transaction::SETTLEMENT_CONFIRMED) ]; } } diff --git a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/TransactionType.php b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/TransactionType.php index 312b2f518b4644a3ba5165b877d0bba5b0ed9e4b..0fe752d423277f58c6ee79354541d910a00eb8a8 100644 --- a/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/TransactionType.php +++ b/app/code/Magento/Braintree/Ui/Component/Report/Listing/Column/TransactionType.php @@ -44,8 +44,8 @@ class TransactionType implements OptionSourceInterface private function getAvailableTransactionTypes() { return [ - Transaction::SALE => __('Sale'), - Transaction::CREDIT => __('Credit') + Transaction::SALE => __(Transaction::SALE), + Transaction::CREDIT => __(Transaction::CREDIT) ]; } } diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json index fb833f763f370daf493eaf1a16e6791fca7e4f2a..3b77e208837a4a4341baabbae615b7e35b266500 100644 --- a/app/code/Magento/Braintree/composer.json +++ b/app/code/Magento/Braintree/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-braintree", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/magento-composer-installer": "*", "magento/module-config": "100.2.*", @@ -17,6 +17,7 @@ "magento/module-quote": "100.2.*", "magento/module-paypal": "100.2.*", "magento/module-theme": "100.2.*", + "magento/module-ui": "100.2.*", "braintree/braintree_php": "3.7.0" }, "suggest": { diff --git a/app/code/Magento/Braintree/etc/adminhtml/system.xml b/app/code/Magento/Braintree/etc/adminhtml/system.xml index 3668cd7779fdac701547ecb4d3b7471471943453..765260ce382917554a27b840c27e98ed2c1d8d6a 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/system.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/system.xml @@ -18,6 +18,7 @@ <label><![CDATA[ ]]></label> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> <attribute type="activity_path">payment/braintree/active</attribute> + <more_url>https://articles.braintreepayments.com/guides/magento/configuration</more_url> <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Enable this Solution</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -43,7 +44,7 @@ </requires> </field> <group id="braintree_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="5"> - <comment><![CDATA[<a href="https://www.braintreegateway.com/login" target="_blank">Click here to login to your existing Braintree account</a>. Or to setup a new account and accept payments on your website, <a href="https://apply.braintreegateway.com/signup/us" target="_blank">click here to signup for a Braintree account</a>.]]></comment> + <comment><![CDATA[<a href="https://www.braintreegateway.com/login" target="_blank">Click here to login to your existing Braintree account</a>. Or to setup a new account and accept payments on your website, <a href="https://apply.braintreegateway.com/signup/us" target="_blank">click here to signup for a Braintree account</a>.<br><br>Powered by <a href="https://www.braintreepayments.com/features/hosted-fields" target="_blank">Braintree v.zero with Hosted Fields</a> latest technology. Hosted Fields are small, transparent iframes that replace the sensitive credit card inputs in your checkout flow - helping you meet the latest data security requirements while ensuring your customization doesn't suffer. <a href="https://www.braintreepayments.com/features/hosted-fields" target="_blank">Find out more</a>.]]></comment> <label>Basic Braintree Settings</label> <attribute type="expanded">1</attribute> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> diff --git a/app/code/Magento/Braintree/i18n/en_US.csv b/app/code/Magento/Braintree/i18n/en_US.csv index f912e59c2edac48bc7da882f692c57802f07b54e..4cdb8538c3abf53b759b94d2adf78178654b9972 100644 --- a/app/code/Magento/Braintree/i18n/en_US.csv +++ b/app/code/Magento/Braintree/i18n/en_US.csv @@ -140,6 +140,28 @@ Debug,Debug "liabilityShifted", "Liability Shifted" "liabilityShiftPossible", "Liability Shift Possible" "riskDataId", "Risk ID" -"riskDataDecision", "Risk Decision", -"paymentId", "Payment Id", -"payerEmail", "Payer Email", +"riskDataDecision", "Risk Decision" +"paymentId", "Payment Id" +"payerEmail", "Payer Email" +"sale","Sale" +"credit","Credit" +"authorization_expired","Authorization expired" +"authorizing","Authorizing" +"authorized","Authorized" +"gateway_rejected","Gateway rejected" +"failed","Failed" +"processor_declined","Processor declined" +"settled","Settled" +"settling","Settling" +"submitted_for_settlement","Submitted for settlement" +"voided","Voided" +"unrecognized","Unrecognized" +"settlement_declined","Settlement declined" +"settlement_pending","Settlement pending" +"settlement_confirmed","Settlement confirmed" +"paypal_account","Paypal account" +"coinbase_account","Coinbase account" +"europe_bank_accout","Europe bank account" +"credit_card","Credit card" +"apple_pay_card","Apple pay card" +"android_pay_card","Android pay card" \ No newline at end of file diff --git a/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml b/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml index d1b661b2c3ecdf0d6bb92ffc03e3a235e090cb5f..031ddca7a87070ccad2a53a204aec84c67e62f1b 100644 --- a/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml +++ b/app/code/Magento/Braintree/view/adminhtml/ui_component/braintree_report.xml @@ -109,7 +109,6 @@ </item> </argument> </filterSelect> - <!-- <filterSelect name="paymentInstrumentType"> <argument name="optionsProvider" xsi:type="configurableObject"> <argument name="class" xsi:type="string">Magento\Braintree\Ui\Component\Report\Listing\Column\PaymentType</argument> @@ -126,7 +125,6 @@ </item> </argument> </filterSelect> - --> <filterInput name="paypalDetails_paymentId"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> @@ -135,6 +133,22 @@ </item> </argument> </filterInput> + <filterRange name="createdAt" class="Magento\Braintree\Ui\Component\Report\Filters\Type\DateRange"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="provider" xsi:type="string">${ $.parentName }</item> + <item name="imports" xsi:type="array"> + <item name="visible" xsi:type="string">componentType = column, index = ${ $.index }:visible</item> + </item> + <item name="parent" xsi:type="string">braintree_report.braintree_report.listing_top.listing_filters</item> + <item name="template" xsi:type="string">ui/grid/filters/elements/group</item> + <item name="component" xsi:type="string">Magento_Ui/js/grid/filters/range</item> + <item name="rangeType" xsi:type="string">date</item> + <item name="dataScope" xsi:type="string">createdAt</item> + <item name="label" xsi:type="string" translate="true">Created At</item> + </item> + </argument> + </filterRange> </filters> </container> <columns name="braintree_report_columns"> @@ -216,7 +230,6 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="sorting" xsi:type="string">desc</item> - <item name="filter" xsi:type="string">dateRange</item> <item name="dataType" xsi:type="string">date</item> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item> <item name="label" xsi:type="string" translate="true">Created At</item> diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/braintree.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/braintree.js index cb0b46a25327e10a54a771c887f60dd3b4817cc3..3cec7f1fb8ccc83ad21c7605918ffaca50d78387 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/braintree.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/braintree.js @@ -7,26 +7,19 @@ define( [ 'uiComponent', - 'uiRegistry', - 'Magento_Braintree/js/view/payment/adapter', 'Magento_Checkout/js/model/payment/renderer-list' ], function ( Component, - Registry, - Braintree, rendererList ) { 'use strict'; var config = window.checkoutConfig.payment, braintreeType = 'braintree', - payPalType = 'braintree_paypal', - path = 'checkout.steps.billing-step.payment.payments-list.', - components = []; + payPalType = 'braintree_paypal'; if (config[braintreeType].isActive) { - components.push(path + braintreeType); rendererList.push( { type: braintreeType, @@ -44,13 +37,6 @@ define( ); } - // setup Braintree SDK with merged configuration from all related components - if (components.length) { - Registry.get(components, function () { - Braintree.setup(); - }); - } - /** Add view logic here if needed */ return Component.extend({}); } 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 6990c1e2e5a0bb78c52e0075f94995bb0da390ae..af71eb29a158d9c1573eb4dca60026f567afe796 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 @@ -61,11 +61,22 @@ define( this.beforePlaceOrder(response); }, + /** + * Device data initialization + * + * @param {Object} checkout + */ + onReady: function (checkout) { + braintree.checkout = checkout; + }, + /** * Triggers on any Braintree error + * @param {Object} response */ - onError: function () { - this.paymentMethodNonce = null; + onError: function (response) { + braintree.showError($t('Payment ' + this.getTitle() + ' can\'t be initialized')); + throw response.message; }, /** @@ -90,7 +101,7 @@ define( this._super() .observe(['active']); this.validatorManager.initialize(); - this.initBraintree(); + this.initClientConfig(); return this; }, @@ -122,11 +133,11 @@ define( * @param {Boolean} isActive */ onActiveChange: function (isActive) { - if (!isActive || this.isSingleUse()) { + if (!isActive) { return; } - this.reInitBraintree(); + this.initBraintree(); }, /** @@ -146,17 +157,9 @@ define( }, /** - * Create Braintree configuration + * Init Braintree configuration */ initBraintree: function () { - this.initClientConfig(); - braintree.config = _.extend(braintree.config, this.clientConfig); - }, - - /** - * Re-init Braintree configuration - */ - reInitBraintree: function () { var intervalId = setInterval(function () { // stop loader when frame will be loaded if ($('#braintree-hosted-field-number').length) { @@ -165,6 +168,12 @@ define( } }, 500); + if (braintree.checkout) { + braintree.checkout.teardown(function () { + braintree.checkout = null; + }); + } + fullScreenLoader.startLoader(); braintree.setConfig(this.clientConfig); braintree.setup(); @@ -309,14 +318,6 @@ define( }); return false; - }, - - /** - * Check if Braintree configured without PayPal - * @returns {Boolean} - */ - isSingleUse: function () { - return window.checkoutConfig.payment[this.getCode()].isSingleUse; } }); } diff --git a/app/code/Magento/Bundle/Pricing/Price/BundleSelectionPrice.php b/app/code/Magento/Bundle/Pricing/Price/BundleSelectionPrice.php index 3239931d5df7fb9a758425583617f9cb5c8cfeb0..5222e52c1145d6e7f7282d32899937f3fdd1eab4 100644 --- a/app/code/Magento/Bundle/Pricing/Price/BundleSelectionPrice.php +++ b/app/code/Magento/Bundle/Pricing/Price/BundleSelectionPrice.php @@ -124,7 +124,7 @@ class BundleSelectionPrice extends AbstractPrice $value = $product->getData('final_price') * ($selectionPriceValue / 100); } else { // calculate price for selection type fixed - $value = $this->priceCurrency->convert($selectionPriceValue) * $this->quantity; + $value = $this->priceCurrency->convert($selectionPriceValue); } } if (!$this->useRegularPrice) { diff --git a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleSelectionPriceTest.php b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleSelectionPriceTest.php index ddb5df9b6ced1c508a4123384c80c430249b57ca..d778f64f30bdf3c02144c28e1d7be9913b923fa4 100644 --- a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleSelectionPriceTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleSelectionPriceTest.php @@ -313,6 +313,67 @@ class BundleSelectionPriceTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expectedPrice, $this->selectionPrice->getValue()); } + /** + * test for method getValue with type Fixed and selectionPriceType is empty or zero + * + * @param bool $useRegularPrice + * @dataProvider useRegularPriceDataProvider + */ + public function testFixedPriceWithMultipleQty($useRegularPrice) + { + $qty = 2; + + $selectionPrice = new \Magento\Bundle\Pricing\Price\BundleSelectionPrice( + $this->productMock, + $qty, + $this->calculatorMock, + $this->priceCurrencyMock, + $this->bundleMock, + $this->eventManagerMock, + $this->discountCalculatorMock, + $useRegularPrice + ); + + $this->setupSelectionPrice($useRegularPrice); + $regularPrice = 100.125; + $discountedPrice = 70.453; + $convertedValue = 100.247; + $actualPrice = $useRegularPrice ? $convertedValue : $discountedPrice; + $expectedPrice = $useRegularPrice ? round($convertedValue, 2) : round($discountedPrice, 2); + + $this->bundleMock->expects($this->once()) + ->method('getPriceType') + ->will($this->returnValue(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED)); + $this->productMock->expects($this->once()) + ->method('getSelectionPriceType') + ->will($this->returnValue(false)); + $this->productMock->expects($this->any()) + ->method('getSelectionPriceValue') + ->will($this->returnValue($regularPrice)); + + $this->priceCurrencyMock->expects($this->once()) + ->method('convert') + ->with($regularPrice) + ->will($this->returnValue($convertedValue)); + + if (!$useRegularPrice) { + $this->discountCalculatorMock->expects($this->once()) + ->method('calculateDiscount') + ->with( + $this->equalTo($this->bundleMock), + $this->equalTo($convertedValue) + ) + ->will($this->returnValue($discountedPrice)); + } + + $this->priceCurrencyMock->expects($this->once()) + ->method('round') + ->with($actualPrice) + ->will($this->returnValue($expectedPrice)); + + $this->assertEquals($expectedPrice, $selectionPrice->getValue()); + } + public function useRegularPriceDataProvider() { return [ diff --git a/app/code/Magento/Bundle/composer.json b/app/code/Magento/Bundle/composer.json index 77efd50f8ef2df221460ebf584993cbd60c22bbe..ab587044476db47c0a824cfdb130988f22591971 100644 --- a/app/code/Magento/Bundle/composer.json +++ b/app/code/Magento/Bundle/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-bundle", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-tax": "100.2.*", diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index c4999b0d2a8b80cde1beec512c0399ed293c6f46..1a76acf17475670674c064c8384d628b87d924a7 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -55,7 +55,6 @@ define([ this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat); priceBox.priceBox('setDefault', this.options.optionConfig.prices); } - this._applyQtyFix(); this._applyOptionNodeFix(options); options.on('change', this._onBundleOptionChanged.bind(this)); @@ -113,6 +112,7 @@ define([ * Helper to fix backend behavior: * - if default qty large than 1 then backend multiply price in config * + * @deprecated * @private */ _applyQtyFix: function applyQtyFix() { diff --git a/app/code/Magento/BundleImportExport/composer.json b/app/code/Magento/BundleImportExport/composer.json index b176f17e0dfce315e26df7f7d0247a9d38e99bd8..3d7900d98287c1eba32a5badd2d5b2fdc1857f25 100644 --- a/app/code/Magento/BundleImportExport/composer.json +++ b/app/code/Magento/BundleImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-bundle-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-import-export": "100.2.*", "magento/module-catalog-import-export": "100.2.*", diff --git a/app/code/Magento/CacheInvalidate/composer.json b/app/code/Magento/CacheInvalidate/composer.json index 6b52555f2ac534bab7967dd4e60902d07b1eb98b..cafccadb41ad869ff87ee8eb154c747f569a4c79 100644 --- a/app/code/Magento/CacheInvalidate/composer.json +++ b/app/code/Magento/CacheInvalidate/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cache-invalidate", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-page-cache": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Captcha/composer.json b/app/code/Magento/Captcha/composer.json index 50ba19c82b8a13e4a92024031143e027709cf747..9d0ce3db92aa402a7c247fed21185953c4707342 100644 --- a/app/code/Magento/Captcha/composer.json +++ b/app/code/Magento/Captcha/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-captcha", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", "magento/module-checkout": "100.2.*", diff --git a/app/code/Magento/Catalog/Pricing/Price/TierPrice.php b/app/code/Magento/Catalog/Pricing/Price/TierPrice.php index 6835453173dbd02b25e53a63b34bc4905a888d5c..6c4820d5184ac8f458b7065f80819e82fa12119c 100644 --- a/app/code/Magento/Catalog/Pricing/Price/TierPrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/TierPrice.php @@ -208,7 +208,7 @@ class TierPrice extends AbstractPrice implements TierPriceInterface, BasePricePr */ public function getSavePercent(AmountInterface $amount) { - return ceil( + return round( 100 - ((100 / $this->priceInfo->getPrice(FinalPrice::PRICE_CODE)->getValue()) * $amount->getBaseAmount()) ); diff --git a/app/code/Magento/Catalog/Setup/CategorySetup.php b/app/code/Magento/Catalog/Setup/CategorySetup.php index 14c23b0cb92bbf50dac38337f793c5e12dd22b46..a6eb9b7f19365988f220d82488d2eb2635350f34 100644 --- a/app/code/Magento/Catalog/Setup/CategorySetup.php +++ b/app/code/Magento/Catalog/Setup/CategorySetup.php @@ -24,6 +24,16 @@ class CategorySetup extends EavSetup */ private $categoryFactory; + /** + * This should be set explicitly + */ + const CATEGORY_ENTITY_TYPE_ID = 3; + + /** + * This should be set explicitly + */ + const CATALOG_PRODUCT_ENTITY_TYPE_ID = 4; + /** * Init * @@ -66,6 +76,7 @@ class CategorySetup extends EavSetup { return [ 'catalog_category' => [ + 'entity_type_id' => self::CATEGORY_ENTITY_TYPE_ID, 'entity_model' => 'Magento\Catalog\Model\ResourceModel\Category', 'attribute_model' => 'Magento\Catalog\Model\ResourceModel\Eav\Attribute', 'table' => 'catalog_category_entity', @@ -334,6 +345,7 @@ class CategorySetup extends EavSetup ], ], 'catalog_product' => [ + 'entity_type_id' => self::CATALOG_PRODUCT_ENTITY_TYPE_ID, 'entity_model' => 'Magento\Catalog\Model\ResourceModel\Product', 'attribute_model' => 'Magento\Catalog\Model\ResourceModel\Eav\Attribute', 'table' => 'catalog_product_entity', diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/TierPriceTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/TierPriceTest.php index fce88c3a3fea780d8ab05cc1fce24aba8383a1a7..ab7c62d9b5669cc33ac91f7ed20bd6b8566eafdc 100644 --- a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/TierPriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/TierPriceTest.php @@ -394,7 +394,8 @@ class TierPriceTest extends \PHPUnit_Framework_TestCase return [ ['basePrice' => '100', 'tierPrice' => '90', 'savedPercent' => '10'], ['basePrice' => '70', 'tierPrice' => '35', 'savedPercent' => '50'], - ['basePrice' => '50', 'tierPrice' => '35', 'savedPercent' => '30'] + ['basePrice' => '50', 'tierPrice' => '35', 'savedPercent' => '30'], + ['basePrice' => '20.80', 'tierPrice' => '18.72', 'savedPercent' => '10'] ]; } } diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 3c401b78b2f6d24f7f93af307a7c5d62a80dbe4c..5e638922139b3c8c4404d477fad04838fdcddb95 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-eav": "100.2.*", "magento/module-cms": "101.1.*", diff --git a/app/code/Magento/CatalogImportExport/composer.json b/app/code/Magento/CatalogImportExport/composer.json index da6745e141937858e6b1ebf8d219ac899b0613bf..7dbd7f4bc5e656ff09712549886e97a95fa3f4f1 100644 --- a/app/code/Magento/CatalogImportExport/composer.json +++ b/app/code/Magento/CatalogImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-catalog-url-rewrite": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/CatalogInventory/composer.json b/app/code/Magento/CatalogInventory/composer.json index 409492667e5dd3743270e476addbced423aab686..254d8d2db4bf9db35b338507cdd744d1aaf0e152 100644 --- a/app/code/Magento/CatalogInventory/composer.json +++ b/app/code/Magento/CatalogInventory/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-inventory", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/CatalogRule/composer.json b/app/code/Magento/CatalogRule/composer.json index e89ae17b2cb1f163b46ca16d60c72fe21e4a01f2..71eb5b49db1fc68eadb064968796cfdaf1081d60 100644 --- a/app/code/Magento/CatalogRule/composer.json +++ b/app/code/Magento/CatalogRule/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-rule", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-rule": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/CatalogRuleConfigurable/composer.json b/app/code/Magento/CatalogRuleConfigurable/composer.json index a46ef45dc4f6c7b5b67300da4cbf6740657636f0..994958bb03b8673f054081623a0785bdb7b94482 100644 --- a/app/code/Magento/CatalogRuleConfigurable/composer.json +++ b/app/code/Magento/CatalogRuleConfigurable/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-rule-configurable", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-configurable-product": "100.2.*", "magento/framework": "100.2.*", "magento/magento-composer-installer": "*" diff --git a/app/code/Magento/CatalogSearch/composer.json b/app/code/Magento/CatalogSearch/composer.json index d4fda756f4d64c005fc91d7bc0cb39c5e85cd68c..49756420bd2308cd1f8326f474395b6209c7450c 100644 --- a/app/code/Magento/CatalogSearch/composer.json +++ b/app/code/Magento/CatalogSearch/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-search", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-search": "100.2.*", diff --git a/app/code/Magento/CatalogUrlRewrite/composer.json b/app/code/Magento/CatalogUrlRewrite/composer.json index 17dde501b9c6b940a1588d803f913e5179ba413e..8fd54cbc8ee8319f625cd6320954a0c821bf9a52 100644 --- a/app/code/Magento/CatalogUrlRewrite/composer.json +++ b/app/code/Magento/CatalogUrlRewrite/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-url-rewrite", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-catalog-import-export": "100.2.*", diff --git a/app/code/Magento/CatalogWidget/composer.json b/app/code/Magento/CatalogWidget/composer.json index 3249aae83379a8273e6630bb5cb11628d33de1fb..330f46176286c70822a32762dccfed157a2ce358 100644 --- a/app/code/Magento/CatalogWidget/composer.json +++ b/app/code/Magento/CatalogWidget/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-catalog-widget", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-widget": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Checkout/composer.json b/app/code/Magento/Checkout/composer.json index 6aa7486bc5072026601eff7ff364f9db742b73da..5545e409b65abb3fb381e593c4ec34298dba6b80 100644 --- a/app/code/Magento/Checkout/composer.json +++ b/app/code/Magento/Checkout/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-checkout", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-sales": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/CheckoutAgreements/composer.json b/app/code/Magento/CheckoutAgreements/composer.json index 734922dff932299a80749f682e4b93b96b363614..afb706a39a25ec97915abfe0bc6bfcb99c95f6d4 100644 --- a/app/code/Magento/CheckoutAgreements/composer.json +++ b/app/code/Magento/CheckoutAgreements/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-checkout-agreements", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-checkout": "100.2.*", "magento/module-quote": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/Cms/composer.json b/app/code/Magento/Cms/composer.json index 897f00d3de9b7445fc96f11e34450724cd1ae444..66f6da042e6c9a77e25eb5c9e8f1595c16e3e5f6 100644 --- a/app/code/Magento/Cms/composer.json +++ b/app/code/Magento/Cms/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cms", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-theme": "100.2.*", "magento/module-widget": "100.2.*", diff --git a/app/code/Magento/CmsUrlRewrite/composer.json b/app/code/Magento/CmsUrlRewrite/composer.json index 51e21052772f9117458265fa5680d8645cda3b7e..1ae1efd9350a760846e76fcac8939ef574194361 100644 --- a/app/code/Magento/CmsUrlRewrite/composer.json +++ b/app/code/Magento/CmsUrlRewrite/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cms-url-rewrite", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-cms": "101.1.*", "magento/module-url-rewrite": "100.2.*", diff --git a/app/code/Magento/Config/composer.json b/app/code/Magento/Config/composer.json index 61ca2b198cb9dbcb7888f26fceadbe2bf3a843ed..bdd1a9c14ae6894d052f6e7e2994f2c87c6554b0 100644 --- a/app/code/Magento/Config/composer.json +++ b/app/code/Magento/Config/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-config", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-store": "100.2.*", "magento/module-cron": "100.2.*", diff --git a/app/code/Magento/Config/view/adminhtml/templates/page/system/config/robots/reset.phtml b/app/code/Magento/Config/view/adminhtml/templates/page/system/config/robots/reset.phtml index bacb94868cb603a15f7a453a8e953ce6531b27fe..3bfd63c8ee09d4e7f86f2bbe4d4b992cc904e668 100644 --- a/app/code/Magento/Config/view/adminhtml/templates/page/system/config/robots/reset.phtml +++ b/app/code/Magento/Config/view/adminhtml/templates/page/system/config/robots/reset.phtml @@ -7,6 +7,7 @@ // @codingStandardsIgnoreFile /** + * @deprecated * @var $block \Magento\Backend\Block\Page\System\Config\Robots\Reset * @var $jsonHelper \Magento\Framework\Json\Helper\Data */ diff --git a/app/code/Magento/ConfigurableImportExport/composer.json b/app/code/Magento/ConfigurableImportExport/composer.json index af59b7c8de1bbf672dd49ffed506b920a7985caf..6c249134d28626b4c8846fd7491cf25b2e278638 100644 --- a/app/code/Magento/ConfigurableImportExport/composer.json +++ b/app/code/Magento/ConfigurableImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-configurable-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-catalog-import-export": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/ConfigurableProduct/composer.json b/app/code/Magento/ConfigurableProduct/composer.json index aa2440aa43844c532d5f34f9c32659c54c48ec68..d7441f577c1db1f60381ba2c8caaf64369317542 100644 --- a/app/code/Magento/ConfigurableProduct/composer.json +++ b/app/code/Magento/ConfigurableProduct/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-configurable-product", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-catalog-inventory": "100.2.*", diff --git a/app/code/Magento/Contact/composer.json b/app/code/Magento/Contact/composer.json index 987eb0bc8c3d82456d46f2a978c442b4fc5a679b..b541f1e7a0ed3afe8f8bfb8c25b7f27282bc9372 100644 --- a/app/code/Magento/Contact/composer.json +++ b/app/code/Magento/Contact/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-contact", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Cookie/composer.json b/app/code/Magento/Cookie/composer.json index 5185ac86137bfc9819a6340cfebb12fdd58893df..5222975723385a48adf043f695c08e319f11d57b 100644 --- a/app/code/Magento/Cookie/composer.json +++ b/app/code/Magento/Cookie/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cookie", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Cron/composer.json b/app/code/Magento/Cron/composer.json index af92118f5fb58dcdc30401ba6082ab0d16c608a2..b1155c028f91971b5508269a06ebef675032e5b5 100644 --- a/app/code/Magento/Cron/composer.json +++ b/app/code/Magento/Cron/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-cron", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/CurrencySymbol/composer.json b/app/code/Magento/CurrencySymbol/composer.json index d3ce3e959beb61c1f736e662e5bc40c5bd519410..2cdfedadf7a38c7c792a27de4498772cc59c692b 100644 --- a/app/code/Magento/CurrencySymbol/composer.json +++ b/app/code/Magento/CurrencySymbol/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-currency-symbol", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-page-cache": "100.2.*", diff --git a/app/code/Magento/Customer/Controller/Account/EditPost.php b/app/code/Magento/Customer/Controller/Account/EditPost.php index 8f078e77c3b129810b58145ca79e3868fd7159d8..b2f6384524147274ae808fc32f6478bf7b66f6a1 100644 --- a/app/code/Magento/Customer/Controller/Account/EditPost.php +++ b/app/code/Magento/Customer/Controller/Account/EditPost.php @@ -7,8 +7,10 @@ namespace Magento\Customer\Controller\Account; use Magento\Customer\Model\AuthenticationInterface; +use Magento\Customer\Model\Customer\Mapper; use Magento\Customer\Model\EmailNotificationInterface; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; @@ -68,6 +70,11 @@ class EditPost extends \Magento\Customer\Controller\AbstractAccount */ private $authentication; + /** + * @var Mapper + */ + private $customerMapper; + /** * @param Context $context * @param Session $customerSession @@ -101,7 +108,7 @@ class EditPost extends \Magento\Customer\Controller\AbstractAccount { if (!($this->authentication instanceof AuthenticationInterface)) { - return \Magento\Framework\App\ObjectManager::getInstance()->get( + return ObjectManager::getInstance()->get( \Magento\Customer\Model\AuthenticationInterface::class ); } else { @@ -118,7 +125,7 @@ class EditPost extends \Magento\Customer\Controller\AbstractAccount private function getEmailNotification() { if (!($this->emailNotification instanceof EmailNotificationInterface)) { - return \Magento\Framework\App\ObjectManager::getInstance()->get( + return ObjectManager::getInstance()->get( EmailNotificationInterface::class ); } else { @@ -196,7 +203,7 @@ class EditPost extends \Magento\Customer\Controller\AbstractAccount private function getScopeConfig() { if (!($this->scopeConfig instanceof \Magento\Framework\App\Config\ScopeConfigInterface)) { - return \Magento\Framework\App\ObjectManager::getInstance()->get( + return ObjectManager::getInstance()->get( \Magento\Framework\App\Config\ScopeConfigInterface::class ); } else { @@ -241,7 +248,12 @@ class EditPost extends \Magento\Customer\Controller\AbstractAccount \Magento\Framework\App\RequestInterface $inputData, \Magento\Customer\Api\Data\CustomerInterface $currentCustomerData ) { - $customerDto = $this->customerExtractor->extract(self::FORM_DATA_EXTRACTOR_CODE, $inputData); + $attributeValues = $this->getCustomerMapper()->toFlatArray($currentCustomerData); + $customerDto = $this->customerExtractor->extract( + self::FORM_DATA_EXTRACTOR_CODE, + $inputData, + $attributeValues + ); $customerDto->setId($currentCustomerData->getId()); if (!$customerDto->getAddresses()) { $customerDto->setAddresses($currentCustomerData->getAddresses()); @@ -299,4 +311,19 @@ class EditPost extends \Magento\Customer\Controller\AbstractAccount } } } + + /** + * Get Customer Mapper instance + * + * @return Mapper + * + * @deprecated + */ + private function getCustomerMapper() + { + if ($this->customerMapper === null) { + $this->customerMapper = ObjectManager::getInstance()->get('Magento\Customer\Model\Customer\Mapper'); + } + return $this->customerMapper; + } } diff --git a/app/code/Magento/Customer/Controller/Address/FormPost.php b/app/code/Magento/Customer/Controller/Address/FormPost.php index 095fec3511c71a65f8f0df8b045053bd677585a0..2b302032f084b796bb6bee19b0c404ff441a56b2 100644 --- a/app/code/Magento/Customer/Controller/Address/FormPost.php +++ b/app/code/Magento/Customer/Controller/Address/FormPost.php @@ -9,12 +9,14 @@ use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\RegionInterface; use Magento\Customer\Api\Data\RegionInterfaceFactory; +use Magento\Customer\Model\Address\Mapper; use Magento\Customer\Model\Metadata\FormFactory; use Magento\Customer\Model\Session; use Magento\Directory\Helper\Data as HelperData; use Magento\Directory\Model\RegionFactory; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Controller\Result\ForwardFactory; use Magento\Framework\Data\Form\FormKey\Validator as FormKeyValidator; use Magento\Framework\Exception\InputException; @@ -36,6 +38,11 @@ class FormPost extends \Magento\Customer\Controller\Address */ protected $helperData; + /** + * @var Mapper + */ + private $customerAddressMapper; + /** * @param Context $context * @param Session $customerSession @@ -127,12 +134,7 @@ class FormPost extends \Magento\Customer\Controller\Address if ($existingAddress->getCustomerId() !== $this->_getSession()->getCustomerId()) { throw new \Exception(); } - $existingAddressData = $this->_dataProcessor->buildOutputDataArray( - $existingAddress, - '\Magento\Customer\Api\Data\AddressInterface' - ); - $existingAddressData['region_code'] = $existingAddress->getRegion()->getRegionCode(); - $existingAddressData['region'] = $existingAddress->getRegion()->getRegion(); + $existingAddressData = $this->getCustomerAddressMapper()->toFlatArray($existingAddress); } return $existingAddressData; } @@ -212,4 +214,19 @@ class FormPost extends \Magento\Customer\Controller\Address return $this->resultRedirectFactory->create()->setUrl($this->_redirect->error($url)); } + + /** + * Get Customer Address Mapper instance + * + * @return Mapper + * + * @deprecated + */ + private function getCustomerAddressMapper() + { + if ($this->customerAddressMapper === null) { + $this->customerAddressMapper = ObjectManager::getInstance()->get('Magento\Customer\Model\Address\Mapper'); + } + return $this->customerAddressMapper; + } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php new file mode 100644 index 0000000000000000000000000000000000000000..d7f83ba6a6380adadb3ebcd7dd3be1407741585a --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Controller\Adminhtml\File\Address; + +use Magento\Backend\App\Action; +use Magento\Backend\App\Action\Context; +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Model\FileUploader; +use Magento\Customer\Model\FileUploaderFactory; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Exception\LocalizedException; +use Psr\Log\LoggerInterface; + +class Upload extends Action +{ + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var FileUploaderFactory + */ + private $fileUploaderFactory; + + /** + * @var AddressMetadataInterface + */ + private $addressMetadataService; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Context $context + * @param FileUploaderFactory $fileUploaderFactory + * @param AddressMetadataInterface $addressMetadataService + * @param LoggerInterface $logger + */ + public function __construct( + Context $context, + FileUploaderFactory $fileUploaderFactory, + AddressMetadataInterface $addressMetadataService, + LoggerInterface $logger + ) { + $this->fileUploaderFactory = $fileUploaderFactory; + $this->addressMetadataService = $addressMetadataService; + $this->logger = $logger; + parent::__construct($context); + } + + /** + * @inheritDoc + */ + public function execute() + { + try { + if (empty($_FILES)) { + throw new \Exception('$_FILES array is empty.'); + } + + // Must be executed before any operations with $_FILES! + $this->convertFilesArray(); + + $attributeCode = key($_FILES['address']['name']); + $attributeMetadata = $this->addressMetadataService->getAttributeMetadata($attributeCode); + + /** @var FileUploader $fileUploader */ + $fileUploader = $this->fileUploaderFactory->create([ + 'attributeMetadata' => $attributeMetadata, + 'entityTypeCode' => AddressMetadataInterface::ENTITY_TYPE_ADDRESS, + 'scope' => 'address', + ]); + + $errors = $fileUploader->validate(); + if (true !== $errors) { + $errorMessage = implode('</br>', $errors); + throw new LocalizedException(__($errorMessage)); + } + + $result = $fileUploader->upload(); + } catch (LocalizedException $e) { + $result = [ + 'error' => $e->getMessage(), + 'errorcode' => $e->getCode(), + ]; + } catch (\Exception $e) { + $this->logger->critical($e); + $result = [ + 'error' => __('Something went wrong while saving file.'), + 'errorcode' => $e->getCode(), + ]; + } + + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + $resultJson->setData($result); + return $resultJson; + } + + /** + * Update global $_FILES array. Convert data to standard form + * + * NOTE: This conversion is required to use \Magento\Framework\File\Uploader::_setUploadFileId($fileId) method. + * + * @return void + */ + private function convertFilesArray() + { + foreach($_FILES['address'] as $itemKey => $item) { + foreach ($item as $value) { + if (is_array($value)) { + $_FILES['address'][$itemKey] = [ + key($value) => current($value), + ]; + } + } + } + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/File/Customer/Upload.php b/app/code/Magento/Customer/Controller/Adminhtml/File/Customer/Upload.php new file mode 100644 index 0000000000000000000000000000000000000000..47bda9f30059c7713b5cd5ee8ccd0d1cfa50b375 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/File/Customer/Upload.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Controller\Adminhtml\File\Customer; + +use Magento\Backend\App\Action; +use Magento\Backend\App\Action\Context; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\FileUploader; +use Magento\Customer\Model\FileUploaderFactory; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Exception\LocalizedException; +use Psr\Log\LoggerInterface; + +class Upload extends Action +{ + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var FileUploaderFactory + */ + private $fileUploaderFactory; + + /** + * @var CustomerMetadataInterface + */ + private $customerMetadataService; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Context $context + * @param FileUploaderFactory $fileUploaderFactory + * @param CustomerMetadataInterface $customerMetadataService + * @param LoggerInterface $logger + */ + public function __construct( + Context $context, + FileUploaderFactory $fileUploaderFactory, + CustomerMetadataInterface $customerMetadataService, + LoggerInterface $logger + ) { + $this->fileUploaderFactory = $fileUploaderFactory; + $this->customerMetadataService = $customerMetadataService; + $this->logger = $logger; + parent::__construct($context); + } + + /** + * @inheritDoc + */ + public function execute() + { + try { + if (empty($_FILES)) { + throw new \Exception('$_FILES array is empty.'); + } + + $attributeCode = key($_FILES['customer']['name']); + $attributeMetadata = $this->customerMetadataService->getAttributeMetadata($attributeCode); + + /** @var FileUploader $fileUploader */ + $fileUploader = $this->fileUploaderFactory->create([ + 'attributeMetadata' => $attributeMetadata, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + 'scope' => 'customer', + ]); + + $errors = $fileUploader->validate(); + if (true !== $errors) { + $errorMessage = implode('</br>', $errors); + throw new LocalizedException(__($errorMessage)); + } + + $result = $fileUploader->upload(); + } catch (LocalizedException $e) { + $result = [ + 'error' => $e->getMessage(), + 'errorcode' => $e->getCode(), + ]; + } catch (\Exception $e) { + $this->logger->critical($e); + $result = [ + 'error' => __('Something went wrong while saving file.'), + 'errorcode' => $e->getCode(), + ]; + } + + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + $resultJson->setData($result); + return $resultJson; + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 601c4bad9c74c240b02cde04ccedc071b7b1dfe2..d1c41545955dbab54d8e6f4af578be1c7ac0a204 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -5,21 +5,14 @@ */ namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Controller\RegistryConstants; use Magento\Customer\Model\EmailNotificationInterface; +use Magento\Customer\Model\Metadata\Form; use Magento\Framework\Exception\LocalizedException; -use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Api\AccountManagementInterface; -use Magento\Customer\Api\Data\CustomerInterfaceFactory; -use Magento\Customer\Api\Data\AddressInterfaceFactory; -use Magento\Framework\DataObjectFactory; -use Magento\Customer\Model\Address\Mapper; -use Magento\Customer\Api\AddressRepositoryInterface; -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ class Save extends \Magento\Customer\Controller\Adminhtml\Index { /** @@ -36,7 +29,7 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index { $customerData = []; if ($this->getRequest()->getPost('customer')) { - $serviceAttributes = [ + $additionalAttributes = [ CustomerInterface::DEFAULT_BILLING, CustomerInterface::DEFAULT_SHIPPING, 'confirmation', @@ -45,10 +38,9 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index ]; $customerData = $this->_extractData( - $this->getRequest(), 'adminhtml_customer', - \Magento\Customer\Api\CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, - $serviceAttributes, + CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + $additionalAttributes, 'customer' ); } @@ -66,54 +58,51 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index /** * Perform customer data filtration based on form code and form object * - * @param \Magento\Framework\App\RequestInterface $request * @param string $formCode The code of EAV form to take the list of attributes from * @param string $entityType entity type for the form * @param string[] $additionalAttributes The list of attribute codes to skip filtration for * @param string $scope scope of the request - * @param \Magento\Customer\Model\Metadata\Form $metadataForm to use for extraction - * @return array Filtered customer data + * @return array */ protected function _extractData( - \Magento\Framework\App\RequestInterface $request, $formCode, $entityType, $additionalAttributes = [], - $scope = null, - \Magento\Customer\Model\Metadata\Form $metadataForm = null + $scope = null ) { - if ($metadataForm === null) { - $metadataForm = $this->_formFactory->create( - $entityType, - $formCode, - [], - false, - \Magento\Customer\Model\Metadata\Form::DONT_IGNORE_INVISIBLE - ); - } - $filteredData = $metadataForm->extractData($request, $scope); + $metadataForm = $this->getMetadataForm($entityType, $formCode, $scope); + $formData = $metadataForm->extractData($this->getRequest(), $scope); - $object = $this->_objectFactory->create(['data' => $request->getPostValue()]); + // Initialize additional attributes + /** @var \Magento\Framework\DataObject $object */ + $object = $this->_objectFactory->create(['data' => $this->getRequest()->getPostValue()]); $requestData = $object->getData($scope); foreach ($additionalAttributes as $attributeCode) { - $filteredData[$attributeCode] = isset($requestData[$attributeCode]) ? $requestData[$attributeCode] : false; + $formData[$attributeCode] = isset($requestData[$attributeCode]) ? $requestData[$attributeCode] : false; } + $result = $metadataForm->compactData($formData); + + // Re-initialize additional attributes + $formData = array_replace($formData, $result); + + // Unset unused attributes $formAttributes = $metadataForm->getAttributes(); - /** @var \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute */ foreach ($formAttributes as $attribute) { + /** @var \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute */ $attributeCode = $attribute->getAttributeCode(); - $frontendInput = $attribute->getFrontendInput(); - if ($frontendInput != 'boolean' && $filteredData[$attributeCode] === false) { - unset($filteredData[$attributeCode]); + if ($attribute->getFrontendInput() != 'boolean' + && $formData[$attributeCode] === false + ) { + unset($formData[$attributeCode]); } } - if (empty($filteredData['extension_attributes'])) { - unset($filteredData['extension_attributes']); + if (empty($formData['extension_attributes'])) { + unset($formData['extension_attributes']); } - return $filteredData; + return $formData; } /** @@ -131,9 +120,8 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index foreach ($addressIdList as $addressId) { $scope = sprintf('address/%s', $addressId); $addressData = $this->_extractData( - $this->getRequest(), 'adminhtml_customer_address', - \Magento\Customer\Api\AddressMetadataInterface::ENTITY_TYPE_ADDRESS, + AddressMetadataInterface::ENTITY_TYPE_ADDRESS, ['default_billing', 'default_shipping'], $scope ); @@ -193,18 +181,16 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index { $returnToEdit = false; $originalRequestData = $this->getRequest()->getPostValue(); - $customerId = isset($originalRequestData['customer']['entity_id']) - ? $originalRequestData['customer']['entity_id'] - : null; + + $customerId = $this->getCurrentCustomerId(); + if ($originalRequestData) { try { // optional fields might be set in request for future processing by observers in other modules $customerData = $this->_extractCustomerData(); $addressesData = $this->_extractCustomerAddressData($customerData); - $request = $this->getRequest(); - $isExistingCustomer = (bool)$customerId; - $customer = $this->customerDataFactory->create(); - if ($isExistingCustomer) { + + if ($customerId) { $currentCustomer = $this->_customerRepository->getById($customerId); $customerData = array_merge( $this->customerMapper->toFlatArray($currentCustomer), @@ -213,6 +199,8 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index $customerData['id'] = $customerId; } + /** @var CustomerInterface $customer */ + $customer = $this->customerDataFactory->create(); $this->dataObjectHelper->populateWithArray( $customer, $customerData, @@ -237,17 +225,18 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index $this->_eventManager->dispatch( 'adminhtml_customer_prepare_save', - ['customer' => $customer, 'request' => $request] + ['customer' => $customer, 'request' => $this->getRequest()] ); $customer->setAddresses($addresses); - $customer->setStoreId($customerData['sendemail_store_id']); + if (isset($customerData['sendemail_store_id'])) { + $customer->setStoreId($customerData['sendemail_store_id']); + } // Save customer - if ($isExistingCustomer) { + if ($customerId) { $this->_customerRepository->save($customer); $this->getEmailNotification()->credentialsChanged($customer, $currentCustomer->getEmail()); - } else { $customer = $this->customerAccountManagement->createAccount($customer); $customerId = $customer->getId(); @@ -268,7 +257,7 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index // After save $this->_eventManager->dispatch( 'adminhtml_customer_save_after', - ['customer' => $customer, 'request' => $request] + ['customer' => $customer, 'request' => $this->getRequest()] ); $this->_getSession()->unsCustomerFormData(); // Done Saving customer, finish save action @@ -328,4 +317,59 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index return $this->emailNotification; } } + + /** + * Get metadata form + * + * @param string $entityType + * @param string $formCode + * @param string $scope + * @return Form + */ + private function getMetadataForm($entityType, $formCode, $scope) + { + $attributeValues = []; + + if ($entityType == CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER) { + $customerId = $this->getCurrentCustomerId(); + if ($customerId) { + $customer = $this->_customerRepository->getById($customerId); + $attributeValues = $this->customerMapper->toFlatArray($customer); + } + } + + if ($entityType == AddressMetadataInterface::ENTITY_TYPE_ADDRESS) { + $scopeData = explode('/', $scope); + if (isset($scopeData[1]) && is_numeric($scopeData[1])) { + $customerAddress = $this->addressRepository->getById($scopeData[1]); + $attributeValues = $this->addressMapper->toFlatArray($customerAddress); + } + } + + $metadataForm = $this->_formFactory->create( + $entityType, + $formCode, + $attributeValues, + false, + Form::DONT_IGNORE_INVISIBLE + ); + + return $metadataForm; + } + + /** + * Retrieve current customer ID + * + * @return int + */ + private function getCurrentCustomerId() + { + $originalRequestData = $this->getRequest()->getPostValue(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + + $customerId = isset($originalRequestData['entity_id']) + ? $originalRequestData['entity_id'] + : null; + + return $customerId; + } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php index 7c1f23881ec3d91d4dffddf111e824c4176dfc3a..2b3cbc2d2b6a0329b8aa66a0a6e6ab0ad6503f80 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php @@ -28,11 +28,7 @@ class Validate extends \Magento\Customer\Controller\Adminhtml\Index $customerForm = $this->_formFactory->create( 'customer', 'adminhtml_customer', - $this->_extensibleDataObjectConverter->toFlatArray( - $customer, - [], - '\Magento\Customer\Api\Data\CustomerInterface' - ), + [], true ); $customerForm->setInvisibleIgnored(true); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Viewfile.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Viewfile.php index af512f7fa9f91830d99605aca65a8686eb84910b..57c008445daf4d1d59101da21d2e0390714158e6 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Viewfile.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Viewfile.php @@ -7,6 +7,7 @@ namespace Magento\Customer\Controller\Adminhtml\Index; use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\CustomerInterfaceFactory; @@ -124,7 +125,7 @@ class Viewfile extends \Magento\Customer\Controller\Adminhtml\Index /** * Customer view file action * - * @return void + * @return \Magento\Framework\Controller\ResultInterface|void * @throws NotFoundException * * @SuppressWarnings(PHPMD.ExitExpression) @@ -151,7 +152,7 @@ class Viewfile extends \Magento\Customer\Controller\Adminhtml\Index /** @var \Magento\Framework\Filesystem $filesystem */ $filesystem = $this->_objectManager->get('Magento\Framework\Filesystem'); $directory = $filesystem->getDirectoryRead(DirectoryList::MEDIA); - $fileName = 'customer' . '/' . ltrim($file, '/'); + $fileName = CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER . '/' . ltrim($file, '/'); $path = $directory->getAbsolutePath($fileName); if (!$directory->isFile($fileName) && !$this->_objectManager->get('Magento\MediaStorage\Helper\File\Storage')->processStorageFile($path) @@ -175,7 +176,7 @@ class Viewfile extends \Magento\Customer\Controller\Adminhtml\Index $contentType = 'application/octet-stream'; break; } - $stat = $directory->stat($path); + $stat = $directory->stat($fileName); $contentLength = $stat['size']; $contentModify = $stat['mtime']; diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index 8c9811dfd0fedaa1cac07fb984dda328fdaf431a..8218d3be472a32ac6360766358c91890bfcea5e1 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -5,8 +5,14 @@ */ namespace Magento\Customer\Model\Customer; +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\Attribute; +use Magento\Customer\Model\FileProcessor; +use Magento\Customer\Model\FileProcessorFactory; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Type; use Magento\Customer\Model\Address; use Magento\Customer\Model\Customer; @@ -24,6 +30,11 @@ use Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool; */ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider { + /** + * Maximum file size allowed for file_uploader UI component + */ + const MAX_FILE_SIZE = 2097152; + /** * @var Collection */ @@ -81,8 +92,21 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider protected $session; /** - * Constructor + * @var FileProcessorFactory + */ + private $fileProcessorFactory; + + /** + * File types allowed for file_uploader UI component * + * @var array + */ + private $fileUploaderTypes = [ + 'image', + 'file', + ]; + + /** * @param string $name * @param string $primaryFieldName * @param string $requestFieldName @@ -90,6 +114,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * @param CustomerCollectionFactory $customerCollectionFactory * @param Config $eavConfig * @param FilterPool $filterPool + * @param FileProcessorFactory $fileProcessorFactory * @param array $meta * @param array $data */ @@ -101,6 +126,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider CustomerCollectionFactory $customerCollectionFactory, Config $eavConfig, FilterPool $filterPool, + FileProcessorFactory $fileProcessorFactory = null, array $meta = [], array $data = [] ) { @@ -110,6 +136,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider $this->collection->addAttributeToSelect('*'); $this->eavConfig = $eavConfig; $this->filterPool = $filterPool; + $this->fileProcessorFactory = $fileProcessorFactory ?: $this->getFileProcessorFactory(); $this->meta['customer']['children'] = $this->getAttributesMeta( $this->eavConfig->getEntityType('customer') ); @@ -122,6 +149,8 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * Get session object * * @return SessionManagerInterface + * + * @deprecated */ protected function getSession() { @@ -145,6 +174,9 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider /** @var Customer $customer */ foreach ($items as $customer) { $result['customer'] = $customer->getData(); + + $this->overrideFileUploaderData($customer, $result['customer']); + unset($result['address']); /** @var Address $address */ @@ -153,6 +185,8 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider $address->load($addressId); $result['address'][$addressId] = $address->getData(); $this->prepareAddressData($addressId, $result['address'], $result['customer']); + + $this->overrideFileUploaderData($address, $result['address'][$addressId]); } $this->loadedData[$customer->getId()] = $result; } @@ -167,6 +201,78 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider return $this->loadedData; } + /** + * Override file uploader UI component data + * + * Overrides data for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Customer|Address $entity + * @param array $entityData + */ + private function overrideFileUploaderData($entity, array &$entityData) + { + $attributes = $entity->getAttributes(); + foreach ($attributes as $attribute) { + /** @var Attribute $attribute */ + if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( + $entity->getEntityType(), + $attribute, + $entityData + ); + } + } + } + + /** + * Retrieve array of values required by file uploader UI component + * + * @param Type $entityType + * @param Attribute $attribute + * @param array $customerData + * @return array + */ + private function getFileUploaderData( + Type $entityType, + Attribute $attribute, + array $customerData + ) { + $attributeCode = $attribute->getAttributeCode(); + + $file = isset($customerData[$attributeCode]) + ? $customerData[$attributeCode] + : ''; + + /** @var FileProcessor $fileProcessor */ + $fileProcessor = $this->getFileProcessorFactory()->create([ + 'entityTypeCode' => $entityType->getEntityTypeCode(), + ]); + + if (!empty($file) + && $fileProcessor->isExist($file) + ) { + $stat = $fileProcessor->getStat($file); + $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); + } + + $fileName = $file; + if (strrpos($fileName, '/') !== false) { + $fileName = substr($fileName, strrpos($fileName, '/') + 1); + } + + if (!empty($file)) { + return [ + [ + 'file' => $file, + 'size' => isset($stat) ? $stat['size'] : 0, + 'url' => isset($viewUrl) ? $viewUrl : '', + 'name' => $fileName, + ], + ]; + } + return []; + } + /** * Get attributes meta * @@ -178,7 +284,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider { $meta = []; $attributes = $entityType->getAttributeCollection(); - /* @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */ + /* @var AbstractAttribute $attribute */ foreach ($attributes as $attribute) { $this->processFrontendInput($attribute, $meta); @@ -203,10 +309,100 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider $meta[$code]['arguments']['data']['config']['validation'] = $rules; } $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME; + + $this->overrideFileUploaderMetadata($entityType, $attribute, $meta[$code]['arguments']['data']['config']); } return $meta; } + /** + * Override file uploader UI component metadata + * + * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Type $entityType + * @param AbstractAttribute $attribute + * @param array $config + */ + private function overrideFileUploaderMetadata( + Type $entityType, + AbstractAttribute $attribute, + array &$config + ) { + if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $maxFileSize = self::MAX_FILE_SIZE; + + if (isset($config['validation']['max_file_size'])) { + $maxFileSize = (int)$config['validation']['max_file_size']; + } + + $allowedExtensions = []; + + if (isset($config['validation']['file_extensions'])) { + $allowedExtensions = explode(',', $config['validation']['file_extensions']); + array_walk($allowedExtensions, function(&$value) { $value = strtolower(trim($value)); }); + } + + $allowedExtensions = implode(' ', $allowedExtensions); + + $entityTypeCode = $entityType->getEntityTypeCode(); + $url = $this->getFileUploadUrl($entityTypeCode); + + $config = [ + 'formElement' => 'fileUploader', + 'componentType' => 'fileUploader', + 'maxFileSize' => $maxFileSize, + 'allowedExtensions' => $allowedExtensions, + 'uploaderConfig' => [ + 'url' => $url, + ], + 'label' => $this->getMetadataValue($config, 'label'), + 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), + 'required' => $this->getMetadataValue($config, 'required'), + 'visible' => $this->getMetadataValue($config, 'visible'), + 'validation' => $this->getMetadataValue($config, 'validation'), + ]; + } + } + + /** + * Retrieve metadata value + * + * @param array $config + * @param string $name + * @param mixed $default + * @return mixed + */ + private function getMetadataValue($config, $name, $default = null) + { + $value = isset($config[$name]) ? $config[$name] : $default; + return $value; + } + + /** + * Retrieve URL to file upload + * + * @param string $entityTypeCode + * @return string + */ + private function getFileUploadUrl($entityTypeCode) + { + switch ($entityTypeCode) { + case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: + $url = 'customer/file/customer_upload'; + break; + + case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: + $url = 'customer/file/address_upload'; + break; + + default: + $url = ''; + break; + } + return $url; + } + /** * Process attributes by frontend input type * @@ -250,4 +446,20 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); } } + + /** + * Get FileProcessorFactory instance + * + * @return FileProcessorFactory + * + * @deprecated + */ + private function getFileProcessorFactory() + { + if ($this->fileProcessorFactory === null) { + $this->fileProcessorFactory = ObjectManager::getInstance() + ->get('Magento\Customer\Model\FileProcessorFactory'); + } + return $this->fileProcessorFactory; + } } diff --git a/app/code/Magento/Customer/Model/CustomerExtractor.php b/app/code/Magento/Customer/Model/CustomerExtractor.php index 56e127def199e470a5f939e59f6b8e6e442e544c..197d75943c8dd989e98336e021fc30b7e38b44f8 100644 --- a/app/code/Magento/Customer/Model/CustomerExtractor.php +++ b/app/code/Magento/Customer/Model/CustomerExtractor.php @@ -6,6 +6,8 @@ */ namespace Magento\Customer\Model; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Api\GroupManagementInterface; use Magento\Framework\App\RequestInterface; @@ -60,12 +62,23 @@ class CustomerExtractor /** * @param string $formCode * @param RequestInterface $request - * @return \Magento\Customer\Api\Data\CustomerInterface + * @param array $attributeValues + * @return CustomerInterface */ - public function extract($formCode, RequestInterface $request) - { - $customerForm = $this->formFactory->create('customer', $formCode); + public function extract( + $formCode, + RequestInterface $request, + array $attributeValues = [] + ) { + $customerForm = $this->formFactory->create( + CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + $formCode, + $attributeValues + ); + $customerData = $customerForm->extractData($request); + $customerData = $customerForm->compactData($customerData); + $allowedAttributes = $customerForm->getAllowedAttributes(); $isGroupIdEmpty = isset($allowedAttributes['group_id']); diff --git a/app/code/Magento/Customer/Model/FileProcessor.php b/app/code/Magento/Customer/Model/FileProcessor.php new file mode 100644 index 0000000000000000000000000000000000000000..c98a85cc18463306b3360982b336a945ed63de3c --- /dev/null +++ b/app/code/Magento/Customer/Model/FileProcessor.php @@ -0,0 +1,237 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model; + +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Url\EncoderInterface; +use Magento\Framework\UrlInterface; +use Magento\MediaStorage\Model\File\Uploader; +use Magento\MediaStorage\Model\File\UploaderFactory; + +class FileProcessor +{ + /** + * Temporary directory name + */ + const TMP_DIR = 'tmp'; + + /** + * @var WriteInterface + */ + private $mediaDirectory; + + /** + * @var UploaderFactory + */ + private $uploaderFactory; + + /** + * @var UrlInterface + */ + private $urlBuilder; + + /** + * @var EncoderInterface + */ + private $urlEncoder; + + /** + * @var string + */ + private $entityTypeCode; + + /** + * @var array + */ + private $allowedExtensions = []; + + /** + * @param Filesystem $filesystem + * @param UploaderFactory $uploaderFactory + * @param UrlInterface $urlBuilder + * @param EncoderInterface $urlEncoder + * @param string $entityTypeCode + * @param array $allowedExtensions + */ + public function __construct( + Filesystem $filesystem, + UploaderFactory $uploaderFactory, + UrlInterface $urlBuilder, + EncoderInterface $urlEncoder, + $entityTypeCode, + array $allowedExtensions = [] + ) { + $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->uploaderFactory = $uploaderFactory; + $this->urlBuilder = $urlBuilder; + $this->urlEncoder = $urlEncoder; + $this->entityTypeCode = $entityTypeCode; + $this->allowedExtensions = $allowedExtensions; + } + + /** + * Retrieve base64 encoded file content + * + * @param string $fileName + * @return string + */ + public function getBase64EncodedData($fileName) + { + $filePath = $this->entityTypeCode . '/' . ltrim($fileName, '/'); + + $fileContent = $this->mediaDirectory->readFile($filePath); + + $encodedContent = base64_encode($fileContent); + return $encodedContent; + } + + /** + * Get file statistics data + * + * @param string $fileName + * @return array + */ + public function getStat($fileName) + { + $filePath = $this->entityTypeCode . '/' . ltrim($fileName, '/'); + + $result = $this->mediaDirectory->stat($filePath); + return $result; + } + + /** + * Check if the file exists + * + * @param string $fileName + * @return bool + */ + public function isExist($fileName) + { + $filePath = $this->entityTypeCode . '/' . ltrim($fileName, '/'); + + $result = $this->mediaDirectory->isExist($filePath); + return $result; + } + + /** + * Retrieve customer/index/viewfile action URL + * + * @param string $filePath + * @param string $type + * @return string + */ + public function getViewUrl($filePath, $type) + { + $viewUrl = ''; + + if ($this->entityTypeCode == AddressMetadataInterface::ENTITY_TYPE_ADDRESS) { + $filePath = $this->entityTypeCode . '/' . ltrim($filePath, '/'); + $viewUrl = $this->urlBuilder->getBaseUrl(['_type' => UrlInterface::URL_TYPE_MEDIA]) + . $this->mediaDirectory->getRelativePath($filePath); + } + + if ($this->entityTypeCode == CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER) { + $viewUrl = $this->urlBuilder->getUrl( + 'customer/index/viewfile', + [$type => $this->urlEncoder->encode(ltrim($filePath, '/'))] + ); + } + + return $viewUrl; + } + + /** + * Save uploaded file to temporary directory + * + * @param string $fileId + * @return \string[] + * @throws LocalizedException + */ + public function saveTemporaryFile($fileId) + { + /** @var Uploader $uploader */ + $uploader = $this->uploaderFactory->create(['fileId' => $fileId]); + $uploader->setFilesDispersion(false); + $uploader->setFilenamesCaseSensitivity(false); + $uploader->setAllowRenameFiles(true); + $uploader->setAllowedExtensions($this->allowedExtensions); + + $path = $this->mediaDirectory->getAbsolutePath( + $this->entityTypeCode . '/' . self::TMP_DIR + ); + + $result = $uploader->save($path); + if (!$result) { + throw new LocalizedException(__('File can not be saved to the destination folder.')); + } + + return $result; + } + + /** + * Move file from temporary directory into base directory + * + * @param string $fileName + * @return string + * @throws LocalizedException + */ + public function moveTemporaryFile($fileName) + { + $fileName = ltrim($fileName, '/'); + + $dispersionPath = Uploader::getDispretionPath($fileName); + $destinationPath = $this->entityTypeCode . $dispersionPath; + + if (!$this->mediaDirectory->create($destinationPath)) { + throw new LocalizedException( + __('Unable to create directory %1.', $destinationPath) + ); + } + + if (!$this->mediaDirectory->isWritable($destinationPath)) { + throw new LocalizedException( + __('Destination folder is not writable or does not exists.') + ); + } + + $destinationFileName = Uploader::getNewFileName( + $this->mediaDirectory->getAbsolutePath($destinationPath) . '/' . $fileName + ); + + try { + $this->mediaDirectory->renameFile( + $this->entityTypeCode . '/' . self::TMP_DIR . '/' . $fileName, + $destinationPath . '/' . $destinationFileName + ); + } catch (\Exception $e) { + throw new LocalizedException( + __('Something went wrong while saving the file.') + ); + } + + $fileName = $dispersionPath . '/' . $fileName; + return $fileName; + } + + /** + * Remove uploaded file + * + * @param string $fileName + * @return bool + */ + public function removeUploadedFile($fileName) + { + $filePath = $this->entityTypeCode . '/' . ltrim($fileName, '/'); + + $result = $this->mediaDirectory->delete($filePath); + return $result; + } +} diff --git a/app/code/Magento/Customer/Model/FileUploader.php b/app/code/Magento/Customer/Model/FileUploader.php new file mode 100644 index 0000000000000000000000000000000000000000..97dfdafdf75754f47f52ee318e7bac64429193dd --- /dev/null +++ b/app/code/Magento/Customer/Model/FileUploader.php @@ -0,0 +1,172 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model; + +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Api\Data\AttributeMetadataInterface; +use Magento\Customer\Model\FileProcessorFactory; +use Magento\Customer\Model\Metadata\ElementFactory; +use Magento\Framework\Exception\LocalizedException; + +class FileUploader +{ + /** + * @var CustomerMetadataInterface + */ + private $customerMetadataService; + + /** + * @var AddressMetadataInterface + */ + private $addressMetadataService; + + /** + * @var ElementFactory + */ + private $elementFactory; + + /** + * @var FileProcessorFactory + */ + private $fileProcessorFactory; + + /** + * @var AttributeMetadataInterface + */ + private $attributeMetadata; + + /** + * @var string + */ + private $entityTypeCode; + + /** + * @var string + */ + private $scope; + + /** + * @param CustomerMetadataInterface $customerMetadataService + * @param AddressMetadataInterface $addressMetadataService + * @param ElementFactory $elementFactory + * @param FileProcessorFactory $fileProcessorFactory + * @param AttributeMetadataInterface $attributeMetadata + * @param string $entityTypeCode + * @param string $scope + */ + public function __construct( + CustomerMetadataInterface $customerMetadataService, + AddressMetadataInterface $addressMetadataService, + ElementFactory $elementFactory, + FileProcessorFactory $fileProcessorFactory, + AttributeMetadataInterface $attributeMetadata, + $entityTypeCode, + $scope + ) { + $this->customerMetadataService = $customerMetadataService; + $this->addressMetadataService = $addressMetadataService; + $this->elementFactory = $elementFactory; + $this->fileProcessorFactory = $fileProcessorFactory; + $this->attributeMetadata = $attributeMetadata; + $this->entityTypeCode = $entityTypeCode; + $this->scope = $scope; + } + + /** + * Validate uploaded file + * + * @return array|bool + */ + public function validate() + { + $formElement = $this->elementFactory->create( + $this->attributeMetadata, + null, + $this->entityTypeCode + ); + + $errors = $formElement->validateValue($this->getData()); + return $errors; + } + + /** + * Execute file uploading + * + * @return \string[] + * @throws LocalizedException + */ + public function upload() + { + /** @var FileProcessor $fileProcessor */ + $fileProcessor = $this->fileProcessorFactory->create([ + 'entityTypeCode' => $this->entityTypeCode, + 'allowedExtensions' => $this->getAllowedExtensions(), + ]); + + $result = $fileProcessor->saveTemporaryFile($this->scope . '[' . $this->getAttributeCode() . ']'); + + // Update tmp_name param. Required for attribute validation! + $result['tmp_name'] = $result['path'] . '/' . ltrim($result['file'], '/'); + + $result['url'] = $fileProcessor->getViewUrl( + FileProcessor::TMP_DIR . '/' . ltrim($result['name'], '/'), + $this->attributeMetadata->getFrontendInput() + ); + + return $result; + } + + /** + * Get attribute code + * + * @return string + */ + private function getAttributeCode() + { + return key($_FILES[$this->scope]['name']); + } + + /** + * Retrieve data from global $_FILES array + * + * @return array + */ + private function getData() + { + $data = []; + + $fileAttributes = $_FILES[$this->scope]; + foreach ($fileAttributes as $attributeName => $attributeValue) { + $data[$attributeName] = $attributeValue[$this->getAttributeCode()]; + } + + return $data; + } + + /** + * Get allowed extensions + * + * @return array + */ + private function getAllowedExtensions() + { + $allowedExtensions = []; + + $validationRules = $this->attributeMetadata->getValidationRules(); + foreach ($validationRules as $validationRule) { + if ($validationRule->getName() == 'file_extensions') { + $allowedExtensions = explode(',', $validationRule->getValue()); + array_walk($allowedExtensions, function (&$value) { + $value = strtolower(trim($value)); + }); + break; + } + } + + return $allowedExtensions; + } +} diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index 09a369bd50bf8d2cfa21a2185da72ca4f4d24ad7..69b19bc37373d13f95d5080b3e69960c65593662 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -7,6 +7,10 @@ */ namespace Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Model\FileProcessor; +use Magento\Customer\Model\FileProcessorFactory; +use Magento\Framework\Api\Data\ImageContentInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\File\UploaderFactory; use Magento\Framework\Filesystem; use Magento\Framework\App\Filesystem\DirectoryList; @@ -46,6 +50,16 @@ class File extends AbstractData */ private $uploaderFactory; + /** + * @var FileProcessor + */ + protected $fileProcessor; + + /** + * @var FileProcessorFactory + */ + protected $fileProcessorFactory; + /** * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Psr\Log\LoggerInterface $logger @@ -86,10 +100,6 @@ class File extends AbstractData */ public function extractValue(\Magento\Framework\App\RequestInterface $request) { - if ($this->getIsAjaxRequest()) { - return false; - } - $extend = $this->_getRequestValue($request); $attrCode = $this->getAttribute()->getAttributeCode(); @@ -117,6 +127,14 @@ class File extends AbstractData $value[$fileKey] = $scopeData[$attrCode]; } } + } else if (isset($extend[0]['file']) && !empty($extend[0]['file'])) { + /** + * This case is required by file uploader UI component + * + * $extend[0]['file'] - uses for AJAX validation + * $extend[0] - uses for POST request + */ + $value = $this->getIsAjaxRequest() ? $extend[0]['file'] : $extend[0]; } else { $value = []; } @@ -194,7 +212,17 @@ class File extends AbstractData */ protected function _isUploadedFile($filename) { - return is_uploaded_file($filename); + if (is_uploaded_file($filename)) { + return true; + } + + // This case is required for file uploader UI component + $temporaryFile = FileProcessor::TMP_DIR . '/' . pathinfo($filename)['basename']; + if ($this->getFileProcessor()->isExist($temporaryFile)) { + return true; + } + + return false; } /** @@ -241,7 +269,7 @@ class File extends AbstractData /** * {@inheritdoc} * - * @return $this|string + * @return ImageContentInterface|array|string|null */ public function compactValue($value) { @@ -249,11 +277,49 @@ class File extends AbstractData return $this; } - $attribute = $this->getAttribute(); - $original = $this->_value; + // Remove outdated file (in the case of file uploader UI component) + if (empty($value) && !empty($this->_value)) { + $this->getFileProcessor()->removeUploadedFile($this->_value); + return $value; + } + + if (isset($value['file']) && !empty($value['file'])) { + if ($value['file'] == $this->_value) { + return $this->_value; + } + $result = $this->processUiComponentValue($value); + } else { + $result = $this->processInputFieldValue($value); + } + + return $result; + } + + /** + * Process file uploader UI component data + * + * @param array $value + * @return string|null + */ + protected function processUiComponentValue(array $value) + { + $result = $this->getFileProcessor()->moveTemporaryFile($value['file']); + return $result; + } + + /** + * Process input type=file component data + * + * @param string $value + * @return bool|int|string + */ + protected function processInputFieldValue($value) + { $toDelete = false; - if ($original) { - if (!$attribute->isRequired() && !empty($value['delete'])) { + if ($this->_value) { + if (!$this->getAttribute()->isRequired() + && !empty($value['delete']) + ) { $toDelete = true; } if (!empty($value['tmp_name'])) { @@ -262,11 +328,11 @@ class File extends AbstractData } $mediaDir = $this->_fileSystem->getDirectoryWrite(DirectoryList::MEDIA); - $result = $original; - // unlink entity file + $result = $this->_value; + if ($toDelete) { + $mediaDir->delete($this->_entityTypeCode . '/' . ltrim($this->_value, '/')); $result = ''; - $mediaDir->delete($this->_entityTypeCode . $original); } if (!empty($value['tmp_name'])) { @@ -309,4 +375,37 @@ class File extends AbstractData return $output; } + + /** + * Get FileProcessor instance + * + * @return FileProcessor + * + * @deprecated + */ + protected function getFileProcessor() + { + if ($this->fileProcessor === null) { + $this->fileProcessor = $this->getFileProcessorFactory()->create([ + 'entityTypeCode' => $this->_entityTypeCode, + ]); + } + return $this->fileProcessor; + } + + /** + * Get FileProcessorFactory instance + * + * @return FileProcessorFactory + * + * @deprecated + */ + protected function getFileProcessorFactory() + { + if ($this->fileProcessorFactory === null) { + $this->fileProcessorFactory = ObjectManager::getInstance() + ->get('Magento\Customer\Model\FileProcessorFactory'); + } + return $this->fileProcessorFactory; + } } diff --git a/app/code/Magento/Customer/Model/Metadata/Form/Image.php b/app/code/Magento/Customer/Model/Metadata/Form/Image.php index b50a2d519f4e0e8a0c6d7ed1b89b9bcf345fe470..993cd83826627fbd55bb2284ab45be0c4936c4a4 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/Image.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/Image.php @@ -7,10 +7,21 @@ */ namespace Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\FileProcessor; use Magento\Framework\Api\ArrayObjectSearch; +use Magento\Framework\Api\Data\ImageContentInterface; +use Magento\Framework\Api\Data\ImageContentInterfaceFactory; +use Magento\Framework\App\ObjectManager; class Image extends File { + /** + * @var ImageContentInterfaceFactory + */ + private $imageContentFactory; + /** * Validate file by attribute validate rules * Return array of errors @@ -68,7 +79,7 @@ class Image extends File $maxImageHeight = ArrayObjectSearch::getArrayElementByName( $rules, - 'max_image_height' + 'max_image_heght' ); if ($maxImageHeight !== null) { if ($maxImageHeight < $imageProp[1]) { @@ -79,4 +90,81 @@ class Image extends File return $errors; } + + /** + * Process file uploader UI component data + * + * @param array $value + * @return bool|int|ImageContentInterface|string + */ + protected function processUiComponentValue(array $value) + { + if ($this->_entityTypeCode == AddressMetadataInterface::ENTITY_TYPE_ADDRESS) { + $result = $this->processCustomerAddressValue($value); + return $result; + } + + if ($this->_entityTypeCode == CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER) { + $result = $this->processCustomerValue($value); + return $result; + } + + return $this->_value; + } + + /** + * Process file uploader UI component data for customer_address entity + * + * @param array $value + * @return string + */ + protected function processCustomerAddressValue(array $value) + { + $result = $this->getFileProcessor()->moveTemporaryFile($value['file']); + return $result; + } + + /** + * Process file uploader UI component data for customer entity + * + * @param array $value + * @return bool|int|ImageContentInterface|string + */ + protected function processCustomerValue(array $value) + { + $temporaryFile = FileProcessor::TMP_DIR . '/' . ltrim($value['file'], '/'); + + if ($this->getFileProcessor()->isExist($temporaryFile)) { + $base64EncodedData = $this->getFileProcessor()->getBase64EncodedData($temporaryFile); + + /** @var ImageContentInterface $imageContentDataObject */ + $imageContentDataObject = $this->getImageContentFactory()->create() + ->setName($value['name']) + ->setBase64EncodedData($base64EncodedData) + ->setType($value['type']); + + // Remove temporary file + $this->getFileProcessor()->removeUploadedFile($temporaryFile); + + return $imageContentDataObject; + } + + return $this->_value; + } + + /** + * Get ImageContentInterfaceFactory instance + * + * @return ImageContentInterfaceFactory + * + * @deprecated + */ + private function getImageContentFactory() + { + if ($this->imageContentFactory === null) { + $this->imageContentFactory = ObjectManager::getInstance() + ->get('Magento\Framework\Api\Data\ImageContentInterfaceFactory'); + } + return $this->imageContentFactory; + } } diff --git a/app/code/Magento/Customer/Model/ResourceModel/AddressRepository.php b/app/code/Magento/Customer/Model/ResourceModel/AddressRepository.php index fb2b2d13c9c50e2ac24036a046ed65ae8dea3c64..891863d64051d64763a1b7a4620f028a357d48fc 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/AddressRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/AddressRepository.php @@ -107,6 +107,7 @@ class AddressRepository implements \Magento\Customer\Api\AddressRepositoryInterf } if ($addressModel === null) { + /** @var \Magento\Customer\Model\Address $addressModel */ $addressModel = $this->addressFactory->create(); $addressModel->updateData($address); $addressModel->setCustomer($customerModel); diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 47ea0843ded2e5caf307c5fef1f98cdd46e307be..1dfdfb0c388cab31b46e38a640e60082693bb0be 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -168,20 +168,7 @@ class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInte \Magento\Customer\Api\CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER ); } - // Populate model with secure data - if ($customer->getId()) { - $customerSecure = $this->customerRegistry->retrieveSecureData($customer->getId()); - $customerModel->setRpToken($customerSecure->getRpToken()); - $customerModel->setRpTokenCreatedAt($customerSecure->getRpTokenCreatedAt()); - $customerModel->setPasswordHash($customerSecure->getPasswordHash()); - $customerModel->setFailuresNum($customerSecure->getFailuresNum()); - $customerModel->setFirstFailure($customerSecure->getFirstFailure()); - $customerModel->setLockExpires($customerSecure->getLockExpires()); - } else { - if ($passwordHash) { - $customerModel->setPasswordHash($passwordHash); - } - } + $this->populateCustomerWithSecureData($customerModel, $passwordHash); // If customer email was changed, reset RpToken info if ($prevCustomerData @@ -229,6 +216,34 @@ class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInte return $savedCustomer; } + /** + * Set secure data to customer model + * + * @param \Magento\Customer\Model\Customer $customerModel + * @param string|null $passwordHash + * @return void + */ + private function populateCustomerWithSecureData($customerModel, $passwordHash = null) + { + if ($customerModel->getId()) { + $customerSecure = $this->customerRegistry->retrieveSecureData($customerModel->getId()); + + $customerModel->setRpToken($passwordHash ? null : $customerSecure->getRpToken()); + $customerModel->setRpTokenCreatedAt($passwordHash ? null : $customerSecure->getRpTokenCreatedAt()); + $customerModel->setPasswordHash($passwordHash ?: $customerSecure->getPasswordHash()); + + $customerModel->setFailuresNum($customerSecure->getFailuresNum()); + $customerModel->setFirstFailure($customerSecure->getFirstFailure()); + $customerModel->setLockExpires($customerSecure->getLockExpires()); + } elseif ($passwordHash) { + $customerModel->setPasswordHash($passwordHash); + } + + if ($passwordHash && $customerModel->getId()) { + $this->customerRegistry->remove($customerModel->getId()); + } + } + /** * {@inheritdoc} */ diff --git a/app/code/Magento/Customer/Setup/CustomerSetup.php b/app/code/Magento/Customer/Setup/CustomerSetup.php index dc99278f670c41e529d48ab30e00c5d16c7e09fb..57d003407c21cf58bc15a5dcccf28932912b476d 100644 --- a/app/code/Magento/Customer/Setup/CustomerSetup.php +++ b/app/code/Magento/Customer/Setup/CustomerSetup.php @@ -126,6 +126,7 @@ class CustomerSetup extends EavSetup { $entities = [ 'customer' => [ + 'entity_type_id' => \Magento\Customer\Api\CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER, 'entity_model' => 'Magento\Customer\Model\ResourceModel\Customer', 'attribute_model' => 'Magento\Customer\Model\Attribute', 'table' => 'customer_entity', @@ -338,6 +339,7 @@ class CustomerSetup extends EavSetup ], ], 'customer_address' => [ + 'entity_type_id' => \Magento\Customer\Api\AddressMetadataInterface::ATTRIBUTE_SET_ID_ADDRESS, 'entity_model' => 'Magento\Customer\Model\ResourceModel\Address', 'attribute_model' => 'Magento\Customer\Model\Attribute', 'table' => 'customer_address_entity', diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php index cdad131d06453375f844cbc4ad7abf45ccde2a7a..84c0184c18cf3eca56a5a9f190009bfc83f86e5e 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php @@ -94,6 +94,11 @@ class EditPostTest extends \PHPUnit_Framework_TestCase */ protected $authenticationMock; + /** + * @var \Magento\Customer\Model\Customer\Mapper|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerMapperMock; + protected function setUp() { $this->prepareContext(); @@ -133,6 +138,10 @@ class EditPostTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->customerMapperMock = $this->getMockBuilder('Magento\Customer\Model\Customer\Mapper') + ->disableOriginalConstructor() + ->getMock(); + $this->model = new EditPost( $this->context, $this->customerSession, @@ -141,16 +150,28 @@ class EditPostTest extends \PHPUnit_Framework_TestCase $this->validator, $this->customerExtractor ); - $reflection = new \ReflectionClass(get_class($this->model)); - $reflectionProperty = $reflection->getProperty('emailNotification'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($this->model, $this->emailNotification); - $reflectionProperty2 = $reflection->getProperty('scopeConfig'); - $reflectionProperty2->setAccessible(true); - $reflectionProperty2->setValue($this->model, $scopeConfigMock); - $reflectionProperty3 = $reflection->getProperty('authentication'); - $reflectionProperty3->setAccessible(true); - $reflectionProperty3->setValue($this->model, $this->authenticationMock); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager->setBackwardCompatibleProperty( + $this->model, + 'emailNotification', + $this->emailNotification + ); + $objectManager->setBackwardCompatibleProperty( + $this->model, + 'scopeConfig', + $scopeConfigMock + ); + $objectManager->setBackwardCompatibleProperty( + $this->model, + 'authentication', + $this->authenticationMock + ); + $objectManager->setBackwardCompatibleProperty( + $this->model, + 'customerMapper', + $this->customerMapperMock + ); } public function testInvalidFormKey() @@ -203,6 +224,11 @@ class EditPostTest extends \PHPUnit_Framework_TestCase ->method('getEmail') ->willReturn($customerEmail); + $this->customerMapperMock->expects($this->once()) + ->method('toFlatArray') + ->with($currentCustomerMock) + ->willReturn([]); + $this->customerSession->expects($this->once()) ->method('getCustomerId') ->willReturn($customerId); @@ -301,6 +327,11 @@ class EditPostTest extends \PHPUnit_Framework_TestCase $currentCustomerMock = $this->getCurrentCustomerMock($customerId, $address); $newCustomerMock = $this->getNewCustomerMock($customerId, $address); + $this->customerMapperMock->expects($this->once()) + ->method('toFlatArray') + ->with($currentCustomerMock) + ->willReturn([]); + $this->customerExtractor->expects($this->once()) ->method('extract') ->with('customer_account_edit', $this->request) @@ -409,6 +440,11 @@ class EditPostTest extends \PHPUnit_Framework_TestCase $currentCustomerMock = $this->getCurrentCustomerMock($customerId, $address); $newCustomerMock = $this->getNewCustomerMock($customerId, $address); + $this->customerMapperMock->expects($this->once()) + ->method('toFlatArray') + ->with($currentCustomerMock) + ->willReturn([]); + $this->customerSession->expects($this->once()) ->method('getCustomerId') ->willReturn($customerId); @@ -559,6 +595,11 @@ class EditPostTest extends \PHPUnit_Framework_TestCase $currentCustomerMock = $this->getCurrentCustomerMock($customerId, $address); $newCustomerMock = $this->getNewCustomerMock($customerId, $address); + $this->customerMapperMock->expects($this->once()) + ->method('toFlatArray') + ->with($currentCustomerMock) + ->willReturn([]); + $exception = new $exception(__($message)); $this->validator->expects($this->once()) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php index dea40e2769e0f23b472ccc72e85a51f77c138aae..86559c1c0e0e670a863ac2424ce374e1b6dacc6d 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php @@ -157,6 +157,11 @@ class FormPostTest extends \PHPUnit_Framework_TestCase */ protected $messageManager; + /** + * @var \Magento\Customer\Model\Address\Mapper|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerAddressMapper; + protected function setUp() { $this->prepareContext(); @@ -197,6 +202,10 @@ class FormPostTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->customerAddressMapper = $this->getMockBuilder('Magento\Customer\Model\Address\Mapper') + ->disableOriginalConstructor() + ->getMock(); + $this->model = new FormPost( $this->context, $this->session, @@ -212,6 +221,13 @@ class FormPostTest extends \PHPUnit_Framework_TestCase $this->regionFactory, $this->helperData ); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager->setBackwardCompatibleProperty( + $this->model, + 'customerAddressMapper', + $this->customerAddressMapper + ); } protected function prepareContext() @@ -458,22 +474,11 @@ class FormPostTest extends \PHPUnit_Framework_TestCase ->with($this->addressData) ->willReturnSelf(); - $this->dataProcessor->expects($this->once()) - ->method('buildOutputDataArray') - ->with($this->addressData, '\Magento\Customer\Api\Data\AddressInterface') + $this->customerAddressMapper->expects($this->once()) + ->method('toFlatArray') + ->with($this->addressData) ->willReturn($existingAddressData); - $this->addressData->expects($this->any()) - ->method('getRegion') - ->willReturn($this->regionData); - - $this->regionData->expects($this->once()) - ->method('getRegionCode') - ->willReturn($regionCode); - $this->regionData->expects($this->once()) - ->method('getRegion') - ->willReturn($region); - $this->formFactory->expects($this->once()) ->method('create') ->with('customer_address', 'customer_address_edit', $existingAddressData) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2853a39982367d0f992854e6f7ef707e822807ab --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php @@ -0,0 +1,234 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Controller\Adminhtml\File\Address; + +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Controller\Adminhtml\File\Address\Upload; +use Magento\Framework\Controller\ResultFactory; + +class UploadTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Upload + */ + private $controller; + + /** + * @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $context; + + /** + * @var \Magento\Customer\Model\FileUploaderFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileUploaderFactory; + + /** + * @var ResultFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultFactory; + + /** + * @var AddressMetadataInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressMetadataService; + + /** + * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $logger; + + protected function setUp() + { + $this->resultFactory = $this->getMockBuilder('Magento\Framework\Controller\ResultFactory') + ->disableOriginalConstructor() + ->getMock(); + + $this->context = $this->getMockBuilder('Magento\Backend\App\Action\Context') + ->disableOriginalConstructor() + ->getMock(); + + $this->context->expects($this->once()) + ->method('getResultFactory') + ->willReturn($this->resultFactory); + + $this->fileUploaderFactory = $this->getMockBuilder('Magento\Customer\Model\FileUploaderFactory') + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->addressMetadataService = $this->getMockBuilder('Magento\Customer\Api\AddressMetadataInterface') + ->getMockForAbstractClass(); + + $this->logger = $this->getMockBuilder('Psr\Log\LoggerInterface') + ->getMockForAbstractClass(); + + $this->controller = new Upload( + $this->context, + $this->fileUploaderFactory, + $this->addressMetadataService, + $this->logger + ); + } + + public function testExecuteEmptyFiles() + { + $exception = new \Exception('$_FILES array is empty.'); + $this->logger->expects($this->once()) + ->method('critical') + ->with($exception) + ->willReturnSelf(); + + $resultJson = $this->getMockBuilder('Magento\Framework\Controller\Result\Json') + ->disableOriginalConstructor() + ->getMock(); + $resultJson->expects($this->once()) + ->method('setData') + ->with([ + 'error' => __('Something went wrong while saving file.'), + 'errorcode' => 0, + ]) + ->willReturnSelf(); + + $this->resultFactory->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_JSON) + ->willReturn($resultJson); + + $this->assertInstanceOf('Magento\Framework\Controller\Result\Json', $this->controller->execute()); + } + + public function testExecute() + { + $attributeCode = 'attribute_code'; + + $_FILES = [ + 'address' => [ + 'name' => [ + 'new_0' => [ + $attributeCode => 'filename', + ], + ], + ], + ]; + + $resultFileName = '/filename.ext1'; + $resultFilePath = 'filepath'; + $resultFileUrl = 'viewFileUrl'; + + $result = [ + 'name' => $resultFileName, + 'file' => $resultFileName, + 'path' => $resultFilePath, + 'tmp_name' => $resultFilePath . $resultFileName, + 'url' => $resultFileUrl, + ]; + + $attributeMetadataMock = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') + ->getMockForAbstractClass(); + + $this->addressMetadataService->expects($this->once()) + ->method('getAttributeMetadata') + ->with($attributeCode) + ->willReturn($attributeMetadataMock); + + $fileUploader = $this->getMockBuilder('Magento\Customer\Model\FileUploader') + ->disableOriginalConstructor() + ->getMock(); + $fileUploader->expects($this->once()) + ->method('validate') + ->willReturn(true); + $fileUploader->expects($this->once()) + ->method('upload') + ->willReturn($result); + + $this->fileUploaderFactory->expects($this->once()) + ->method('create') + ->with([ + 'attributeMetadata' => $attributeMetadataMock, + 'entityTypeCode' => AddressMetadataInterface::ENTITY_TYPE_ADDRESS, + 'scope' => 'address', + ]) + ->willReturn($fileUploader); + + $resultJson = $this->getMockBuilder('Magento\Framework\Controller\Result\Json') + ->disableOriginalConstructor() + ->getMock(); + $resultJson->expects($this->once()) + ->method('setData') + ->with($result) + ->willReturnSelf(); + + $this->resultFactory->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_JSON) + ->willReturn($resultJson); + + $this->assertInstanceOf('Magento\Framework\Controller\Result\Json', $this->controller->execute()); + } + + public function testExecuteWithErrors() + { + $attributeCode = 'attribute_code'; + + $_FILES = [ + 'address' => [ + 'name' => [ + 'new_0' => [ + $attributeCode => 'filename', + ], + ], + ], + ]; + + $errors = [ + 'error1', + 'error2', + ]; + + $attributeMetadataMock = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') + ->getMockForAbstractClass(); + + $this->addressMetadataService->expects($this->once()) + ->method('getAttributeMetadata') + ->with($attributeCode) + ->willReturn($attributeMetadataMock); + + $fileUploader = $this->getMockBuilder('Magento\Customer\Model\FileUploader') + ->disableOriginalConstructor() + ->getMock(); + $fileUploader->expects($this->once()) + ->method('validate') + ->willReturn($errors); + + $this->fileUploaderFactory->expects($this->once()) + ->method('create') + ->with([ + 'attributeMetadata' => $attributeMetadataMock, + 'entityTypeCode' => AddressMetadataInterface::ENTITY_TYPE_ADDRESS, + 'scope' => 'address', + ]) + ->willReturn($fileUploader); + + $resultJson = $this->getMockBuilder('Magento\Framework\Controller\Result\Json') + ->disableOriginalConstructor() + ->getMock(); + $resultJson->expects($this->once()) + ->method('setData') + ->with([ + 'error' => implode('</br>', $errors), + 'errorcode' => 0, + ]) + ->willReturnSelf(); + + $this->resultFactory->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_JSON) + ->willReturn($resultJson); + + $this->assertInstanceOf('Magento\Framework\Controller\Result\Json', $this->controller->execute()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Customer/UploadTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Customer/UploadTest.php new file mode 100644 index 0000000000000000000000000000000000000000..29ac801bb58852cabc50b16dbd21f8be46515edf --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Customer/UploadTest.php @@ -0,0 +1,230 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Controller\Adminhtml\File\Customer; + +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Controller\Adminhtml\File\Customer\Upload; +use Magento\Framework\Controller\ResultFactory; + +class UploadTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Upload + */ + private $controller; + + /** + * @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $context; + + /** + * @var \Magento\Customer\Model\FileUploaderFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileUploaderFactory; + + /** + * @var ResultFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultFactory; + + /** + * @var CustomerMetadataInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerMetadataService; + + /** + * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $logger; + + protected function setUp() + { + $this->resultFactory = $this->getMockBuilder('Magento\Framework\Controller\ResultFactory') + ->disableOriginalConstructor() + ->getMock(); + + $this->context = $this->getMockBuilder('Magento\Backend\App\Action\Context') + ->disableOriginalConstructor() + ->getMock(); + + $this->context->expects($this->once()) + ->method('getResultFactory') + ->willReturn($this->resultFactory); + + $this->fileUploaderFactory = $this->getMockBuilder('Magento\Customer\Model\FileUploaderFactory') + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->customerMetadataService = $this->getMockBuilder('Magento\Customer\Api\CustomerMetadataInterface') + ->getMockForAbstractClass(); + + $this->logger = $this->getMockBuilder('Psr\Log\LoggerInterface') + ->getMockForAbstractClass(); + + $this->controller = new Upload( + $this->context, + $this->fileUploaderFactory, + $this->customerMetadataService, + $this->logger + ); + } + + public function testExecuteEmptyFiles() + { + $exception = new \Exception('$_FILES array is empty.'); + $this->logger->expects($this->once()) + ->method('critical') + ->with($exception) + ->willReturnSelf(); + + $resultJson = $this->getMockBuilder('Magento\Framework\Controller\Result\Json') + ->disableOriginalConstructor() + ->getMock(); + $resultJson->expects($this->once()) + ->method('setData') + ->with([ + 'error' => __('Something went wrong while saving file.'), + 'errorcode' => 0, + ]) + ->willReturnSelf(); + + $this->resultFactory->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_JSON) + ->willReturn($resultJson); + + $this->assertInstanceOf('Magento\Framework\Controller\Result\Json', $this->controller->execute()); + } + + public function testExecute() + { + $attributeCode = 'attribute_code'; + + $_FILES = [ + 'customer' => [ + 'name' => [ + $attributeCode => 'filename', + ], + ], + ]; + + $resultFileName = '/filename.ext1'; + $resultFilePath = 'filepath'; + $resultFileUrl = 'viewFileUrl'; + + $result = [ + 'name' => $resultFileName, + 'file' => $resultFileName, + 'path' => $resultFilePath, + 'tmp_name' => $resultFilePath . $resultFileName, + 'url' => $resultFileUrl, + ]; + + $attributeMetadataMock = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') + ->getMockForAbstractClass(); + + $this->customerMetadataService->expects($this->once()) + ->method('getAttributeMetadata') + ->with($attributeCode) + ->willReturn($attributeMetadataMock); + + $fileUploader = $this->getMockBuilder('Magento\Customer\Model\FileUploader') + ->disableOriginalConstructor() + ->getMock(); + $fileUploader->expects($this->once()) + ->method('validate') + ->willReturn(true); + $fileUploader->expects($this->once()) + ->method('upload') + ->willReturn($result); + + $this->fileUploaderFactory->expects($this->once()) + ->method('create') + ->with([ + 'attributeMetadata' => $attributeMetadataMock, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + 'scope' => 'customer', + ]) + ->willReturn($fileUploader); + + $resultJson = $this->getMockBuilder('Magento\Framework\Controller\Result\Json') + ->disableOriginalConstructor() + ->getMock(); + $resultJson->expects($this->once()) + ->method('setData') + ->with($result) + ->willReturnSelf(); + + $this->resultFactory->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_JSON) + ->willReturn($resultJson); + + $this->assertInstanceOf('Magento\Framework\Controller\Result\Json', $this->controller->execute()); + } + + public function testExecuteWithErrors() + { + $attributeCode = 'attribute_code'; + + $_FILES = [ + 'customer' => [ + 'name' => [ + $attributeCode => 'filename', + ], + ], + ]; + + $errors = [ + 'error1', + 'error2', + ]; + + $attributeMetadataMock = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') + ->getMockForAbstractClass(); + + $this->customerMetadataService->expects($this->once()) + ->method('getAttributeMetadata') + ->with($attributeCode) + ->willReturn($attributeMetadataMock); + + $fileUploader = $this->getMockBuilder('Magento\Customer\Model\FileUploader') + ->disableOriginalConstructor() + ->getMock(); + $fileUploader->expects($this->once()) + ->method('validate') + ->willReturn($errors); + + $this->fileUploaderFactory->expects($this->once()) + ->method('create') + ->with([ + 'attributeMetadata' => $attributeMetadataMock, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + 'scope' => 'customer', + ]) + ->willReturn($fileUploader); + + $resultJson = $this->getMockBuilder('Magento\Framework\Controller\Result\Json') + ->disableOriginalConstructor() + ->getMock(); + $resultJson->expects($this->once()) + ->method('setData') + ->with([ + 'error' => implode('</br>', $errors), + 'errorcode' => 0, + ]) + ->willReturnSelf(); + + $this->resultFactory->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_JSON) + ->willReturn($resultJson); + + $this->assertInstanceOf('Magento\Framework\Controller\Result\Json', $this->controller->execute()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 574f54cea931cfe7f5e3f98bd80106ba532ae613..7813690cea78044864f4b565488baec7a0da4786 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -5,9 +5,13 @@ */ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\Data\AttributeMetadataInterface; -use Magento\Customer\Controller\Adminhtml\Index\Save; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Controller\RegistryConstants; use Magento\Customer\Model\EmailNotificationInterface; +use Magento\Customer\Model\Metadata\Form; use Magento\Framework\Controller\Result\Redirect; /** @@ -137,6 +141,16 @@ class SaveTest extends \PHPUnit_Framework_TestCase */ protected $emailNotificationMock; + /** + * @var \Magento\Customer\Model\Address\Mapper|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerAddressMapperMock; + + /** + * @var \Magento\Customer\Api\AddressRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerAddressRepositoryMock; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -183,9 +197,15 @@ class SaveTest extends \PHPUnit_Framework_TestCase $this->customerRepositoryMock = $this->getMockBuilder('Magento\Customer\Api\CustomerRepositoryInterface') ->disableOriginalConstructor() ->getMock(); + $this->customerAddressRepositoryMock = $this->getMockBuilder('Magento\Customer\Api\AddressRepositoryInterface') + ->disableOriginalConstructor() + ->getMock(); $this->customerMapperMock = $this->getMockBuilder('Magento\Customer\Model\Customer\Mapper') ->disableOriginalConstructor() ->getMock(); + $this->customerAddressMapperMock = $this->getMockBuilder('Magento\Customer\Model\Address\Mapper') + ->disableOriginalConstructor() + ->getMock(); $this->dataHelperMock = $this->getMockBuilder('Magento\Framework\Api\DataObjectHelper') ->disableOriginalConstructor() ->getMock(); @@ -240,13 +260,16 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'authorization' => $this->authorizationMock, 'messageManager' => $this->messageManagerMock, 'resultRedirectFactory' => $this->redirectFactoryMock, + 'addressRepository' => $this->customerAddressRepositoryMock, + 'addressMapper' => $this->customerAddressMapperMock, ] ); - $refClass = new \ReflectionClass(Save::class); - $property = $refClass->getProperty('emailNotification'); - $property->setAccessible(true); - $property->setValue($this->model, $this->emailNotificationMock); + $objectManager->setBackwardCompatibleProperty( + $this->model, + 'emailNotification', + $this->emailNotificationMock + ); } /** @@ -285,6 +308,17 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'coolness' => false, 'disable_auto_group_change' => 'false', ]; + $dataToCompact = [ + 'entity_id' => $customerId, + 'code' => 'value', + 'coolness' => false, + 'disable_auto_group_change' => 'false', + CustomerInterface::DEFAULT_BILLING => false, + CustomerInterface::DEFAULT_SHIPPING => false, + 'confirmation' => false, + 'sendemail_store_id' => false, + 'extension_attributes' => false, + ]; $addressFilteredData = [ 'entity_id' => $addressId, 'default_billing' => 'true', @@ -294,12 +328,26 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'region' => 'region', 'region_id' => 'region_id', ]; + $addressDataToCompact = [ + 'entity_id' => $addressId, + 'default_billing' => 'true', + 'default_shipping' => 'true', + 'code' => 'value', + 'coolness' => false, + 'region' => 'region', + 'region_id' => 'region_id', + ]; $savedData = [ 'entity_id' => $customerId, 'darkness' => true, 'name' => 'Name', - \Magento\Customer\Api\Data\CustomerInterface::DEFAULT_BILLING => false, - \Magento\Customer\Api\Data\CustomerInterface::DEFAULT_SHIPPING => false, + CustomerInterface::DEFAULT_BILLING => false, + CustomerInterface::DEFAULT_SHIPPING => false, + ]; + $savedAddressData = [ + 'entity_id' => $addressId, + 'default_billing' => true, + 'default_shipping' => true, ]; $mergedData = [ 'entity_id' => $customerId, @@ -307,8 +355,8 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'name' => 'Name', 'code' => 'value', 'disable_auto_group_change' => 0, - \Magento\Customer\Api\Data\CustomerInterface::DEFAULT_BILLING => $addressId, - \Magento\Customer\Api\Data\CustomerInterface::DEFAULT_SHIPPING => $addressId, + CustomerInterface::DEFAULT_BILLING => $addressId, + CustomerInterface::DEFAULT_SHIPPING => $addressId, 'confirmation' => false, 'sendemail_store_id' => '1', 'id' => $customerId, @@ -327,7 +375,7 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'id' => $addressId, ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $formMock */ + /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') ->disableOriginalConstructor() ->getMock(); @@ -339,9 +387,13 @@ class SaveTest extends \PHPUnit_Framework_TestCase ->willReturn('int'); $attributes = [$attributeMock]; - $this->requestMock->expects($this->exactly(3)) + $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturn($postValue); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ['address/' . $addressId, null, $postValue['address'][$addressId]], + ]); $this->requestMock->expects($this->exactly(3)) ->method('getPost') ->willReturnMap( @@ -352,69 +404,80 @@ class SaveTest extends \PHPUnit_Framework_TestCase ] ); - /** @var \Magento\Customer\Model\Metadata\Form|\PHPUnit_Framework_MockObject_MockObject $formMock */ - $formMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') + /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ + $objectMock = $this->getMockBuilder('Magento\Framework\DataObject') + ->disableOriginalConstructor() + ->getMock(); + $objectMock->expects($this->exactly(2)) + ->method('getData') + ->willReturnMap( + [ + ['customer', null, $postValue['customer']], + ['address/' . $addressId, null, $postValue['address'][$addressId]], + ] + ); + + $this->objectFactoryMock->expects($this->exactly(2)) + ->method('create') + ->with(['data' => $postValue]) + ->willReturn($objectMock); + + $customerFormMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') ->disableOriginalConstructor() ->getMock(); + $customerFormMock->expects($this->once()) + ->method('extractData') + ->with($this->requestMock, 'customer') + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) + ->method('compactData') + ->with($dataToCompact) + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) + ->method('getAttributes') + ->willReturn($attributes); + + $customerAddressFormMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') + ->disableOriginalConstructor() + ->getMock(); + $customerAddressFormMock->expects($this->once()) + ->method('extractData') + ->with($this->requestMock, 'address/' . $addressId) + ->willReturn($addressFilteredData); + $customerAddressFormMock->expects($this->once()) + ->method('compactData') + ->with($addressDataToCompact) + ->willReturn($addressFilteredData); + $customerAddressFormMock->expects($this->once()) + ->method('getAttributes') + ->willReturn($attributes); $this->formFactoryMock->expects($this->exactly(2)) ->method('create') ->willReturnMap( [ [ - \Magento\Customer\Api\CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'adminhtml_customer', - [], + $savedData, false, - \Magento\Customer\Model\Metadata\Form::DONT_IGNORE_INVISIBLE, + Form::DONT_IGNORE_INVISIBLE, [], - $formMock + $customerFormMock ], [ - \Magento\Customer\Api\AddressMetadataInterface::ENTITY_TYPE_ADDRESS, + AddressMetadataInterface::ENTITY_TYPE_ADDRESS, 'adminhtml_customer_address', - [], + $savedAddressData, false, - \Magento\Customer\Model\Metadata\Form::DONT_IGNORE_INVISIBLE, + Form::DONT_IGNORE_INVISIBLE, [], - $formMock + $customerAddressFormMock ], ] ); - $formMock->expects($this->exactly(2)) - ->method('extractData') - ->willReturnMap( - [ - [$this->requestMock, 'customer', true, $filteredData], - [$this->requestMock, 'address/' . $addressId, true, $addressFilteredData], - ] - ); - - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder('Magento\Framework\DataObject') - ->disableOriginalConstructor() - ->getMock(); - - $this->objectFactoryMock->expects($this->exactly(2)) - ->method('create') - ->with(['data' => $postValue]) - ->willReturn($objectMock); - - $objectMock->expects($this->exactly(2)) - ->method('getData') - ->willReturnMap( - [ - ['customer', null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], - ] - ); - - $formMock->expects($this->exactly(2)) - ->method('getAttributes') - ->willReturn($attributes); - - /** @var \Magento\Customer\Api\Data\CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ + /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ $customerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface') ->disableOriginalConstructor() ->getMock(); @@ -423,12 +486,12 @@ class SaveTest extends \PHPUnit_Framework_TestCase ->method('create') ->willReturn($customerMock); - $this->customerRepositoryMock->expects($this->once()) + $this->customerRepositoryMock->expects($this->exactly(2)) ->method('getById') ->with($customerId) ->willReturn($customerMock); - $this->customerMapperMock->expects($this->once()) + $this->customerMapperMock->expects($this->exactly(2)) ->method('toFlatArray') ->with($customerMock) ->willReturn($savedData); @@ -437,6 +500,16 @@ class SaveTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->customerAddressRepositoryMock->expects($this->once()) + ->method('getById') + ->with($addressId) + ->willReturn($addressMock); + + $this->customerAddressMapperMock->expects($this->once()) + ->method('toFlatArray') + ->with($addressMock) + ->willReturn($savedAddressData); + $this->addressDataFactoryMock->expects($this->once()) ->method('create') ->willReturn($addressMock); @@ -504,7 +577,7 @@ class SaveTest extends \PHPUnit_Framework_TestCase $this->registryMock->expects($this->once()) ->method('register') - ->with(\Magento\Customer\Controller\RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); + ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); $this->messageManagerMock->expects($this->once()) ->method('addSuccess') @@ -566,6 +639,15 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'coolness' => false, 'disable_auto_group_change' => 'false', ]; + $dataToCompact = [ + 'coolness' => false, + 'disable_auto_group_change' => 'false', + CustomerInterface::DEFAULT_BILLING => false, + CustomerInterface::DEFAULT_SHIPPING => false, + 'confirmation' => false, + 'sendemail_store_id' => false, + 'extension_attributes' => false, + ]; $addressFilteredData = [ 'entity_id' => $addressId, 'default_billing' => 'false', @@ -575,10 +657,19 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'region' => 'region', 'region_id' => 'region_id', ]; + $addressDataToCompact = [ + 'entity_id' => $addressId, + 'default_billing' => 'false', + 'default_shipping' => 'false', + 'code' => 'value', + 'coolness' => false, + 'region' => 'region', + 'region_id' => 'region_id', + ]; $mergedData = [ 'disable_auto_group_change' => 0, - \Magento\Customer\Api\Data\CustomerInterface::DEFAULT_BILLING => null, - \Magento\Customer\Api\Data\CustomerInterface::DEFAULT_SHIPPING => null, + CustomerInterface::DEFAULT_BILLING => null, + CustomerInterface::DEFAULT_SHIPPING => null, 'confirmation' => false, ]; $mergedAddressData = [ @@ -595,7 +686,7 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'id' => $addressId, ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $formMock */ + /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') ->disableOriginalConstructor() ->getMock(); @@ -607,9 +698,13 @@ class SaveTest extends \PHPUnit_Framework_TestCase ->willReturn('int'); $attributes = [$attributeMock]; - $this->requestMock->expects($this->exactly(3)) + $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturn($postValue); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ['address/' . $addressId, null, $postValue['address'][$addressId]], + ]); $this->requestMock->expects($this->exactly(3)) ->method('getPost') ->willReturnMap( @@ -620,69 +715,80 @@ class SaveTest extends \PHPUnit_Framework_TestCase ] ); - /** @var \Magento\Customer\Model\Metadata\Form|\PHPUnit_Framework_MockObject_MockObject $formMock */ - $formMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') + /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ + $objectMock = $this->getMockBuilder('Magento\Framework\DataObject') + ->disableOriginalConstructor() + ->getMock(); + $objectMock->expects($this->exactly(2)) + ->method('getData') + ->willReturnMap( + [ + ['customer', null, $postValue['customer']], + ['address/' . $addressId, null, $postValue['address'][$addressId]], + ] + ); + + $this->objectFactoryMock->expects($this->exactly(2)) + ->method('create') + ->with(['data' => $postValue]) + ->willReturn($objectMock); + + $customerFormMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') ->disableOriginalConstructor() ->getMock(); + $customerFormMock->expects($this->once()) + ->method('extractData') + ->with($this->requestMock, 'customer') + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) + ->method('compactData') + ->with($dataToCompact) + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) + ->method('getAttributes') + ->willReturn($attributes); + + $customerAddressFormMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') + ->disableOriginalConstructor() + ->getMock(); + $customerAddressFormMock->expects($this->once()) + ->method('extractData') + ->with($this->requestMock, 'address/' . $addressId) + ->willReturn($addressFilteredData); + $customerAddressFormMock->expects($this->once()) + ->method('compactData') + ->with($addressDataToCompact) + ->willReturn($addressFilteredData); + $customerAddressFormMock->expects($this->once()) + ->method('getAttributes') + ->willReturn($attributes); $this->formFactoryMock->expects($this->exactly(2)) ->method('create') ->willReturnMap( [ [ - \Magento\Customer\Api\CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'adminhtml_customer', [], false, - \Magento\Customer\Model\Metadata\Form::DONT_IGNORE_INVISIBLE, + Form::DONT_IGNORE_INVISIBLE, [], - $formMock + $customerFormMock ], [ - \Magento\Customer\Api\AddressMetadataInterface::ENTITY_TYPE_ADDRESS, + AddressMetadataInterface::ENTITY_TYPE_ADDRESS, 'adminhtml_customer_address', [], false, - \Magento\Customer\Model\Metadata\Form::DONT_IGNORE_INVISIBLE, + Form::DONT_IGNORE_INVISIBLE, [], - $formMock + $customerAddressFormMock ], ] ); - $formMock->expects($this->exactly(2)) - ->method('extractData') - ->willReturnMap( - [ - [$this->requestMock, 'customer', true, $filteredData], - [$this->requestMock, 'address/' . $addressId, true, $addressFilteredData], - ] - ); - - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder('Magento\Framework\DataObject') - ->disableOriginalConstructor() - ->getMock(); - - $this->objectFactoryMock->expects($this->exactly(2)) - ->method('create') - ->with(['data' => $postValue]) - ->willReturn($objectMock); - - $objectMock->expects($this->exactly(2)) - ->method('getData') - ->willReturnMap( - [ - ['customer', null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], - ] - ); - - $formMock->expects($this->exactly(2)) - ->method('getAttributes') - ->willReturn($attributes); - - /** @var \Magento\Customer\Api\Data\CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ + /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ $customerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface') ->disableOriginalConstructor() ->getMock(); @@ -699,6 +805,16 @@ class SaveTest extends \PHPUnit_Framework_TestCase ->method('create') ->willReturn($addressMock); + $this->customerAddressRepositoryMock->expects($this->once()) + ->method('getById') + ->with($addressId) + ->willReturn($addressMock); + + $this->customerAddressMapperMock->expects($this->once()) + ->method('toFlatArray') + ->with($addressMock) + ->willReturn([]); + $this->dataHelperMock->expects($this->exactly(2)) ->method('populateWithArray') ->willReturnMap( @@ -753,7 +869,7 @@ class SaveTest extends \PHPUnit_Framework_TestCase $this->registryMock->expects($this->once()) ->method('register') - ->with(\Magento\Customer\Controller\RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); + ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); $this->messageManagerMock->expects($this->once()) ->method('addSuccess') @@ -800,8 +916,17 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'coolness' => false, 'disable_auto_group_change' => 'false', ]; + $dataToCompact = [ + 'coolness' => false, + 'disable_auto_group_change' => 'false', + CustomerInterface::DEFAULT_BILLING => false, + CustomerInterface::DEFAULT_SHIPPING => false, + 'confirmation' => false, + 'sendemail_store_id' => false, + 'extension_attributes' => false, + ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $formMock */ + /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') ->disableOriginalConstructor() ->getMock(); @@ -813,9 +938,12 @@ class SaveTest extends \PHPUnit_Framework_TestCase ->willReturn('int'); $attributes = [$attributeMock]; - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturn($postValue); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ]); $this->requestMock->expects($this->exactly(2)) ->method('getPost') ->willReturnMap( @@ -825,46 +953,46 @@ class SaveTest extends \PHPUnit_Framework_TestCase ] ); - /** @var \Magento\Customer\Model\Metadata\Form|\PHPUnit_Framework_MockObject_MockObject $formMock */ - $formMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') - ->disableOriginalConstructor() - ->getMock(); - - $this->formFactoryMock->expects($this->once()) - ->method('create') - ->with( - \Magento\Customer\Api\CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, - 'adminhtml_customer', - [], - false, - \Magento\Customer\Model\Metadata\Form::DONT_IGNORE_INVISIBLE - )->willReturn($formMock); - - $formMock->expects($this->once()) - ->method('extractData') - ->with($this->requestMock, 'customer') - ->willReturn($filteredData); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ $objectMock = $this->getMockBuilder('Magento\Framework\DataObject') ->disableOriginalConstructor() ->getMock(); + $objectMock->expects($this->once()) + ->method('getData') + ->with('customer') + ->willReturn($postValue['customer']); $this->objectFactoryMock->expects($this->once()) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); - $objectMock->expects($this->once()) - ->method('getData') - ->with('customer') - ->willReturn($postValue['customer']); - - $formMock->expects($this->once()) + $customerFormMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') + ->disableOriginalConstructor() + ->getMock(); + $customerFormMock->expects($this->once()) + ->method('extractData') + ->with($this->requestMock, 'customer') + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) + ->method('compactData') + ->with($dataToCompact) + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) ->method('getAttributes') ->willReturn($attributes); - /** @var \Magento\Customer\Api\Data\CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ + $this->formFactoryMock->expects($this->once()) + ->method('create') + ->with( + CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + 'adminhtml_customer', + [], + false, + Form::DONT_IGNORE_INVISIBLE + )->willReturn($customerFormMock); + + /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ $customerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface') ->disableOriginalConstructor() ->getMock(); @@ -940,8 +1068,17 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'coolness' => false, 'disable_auto_group_change' => 'false', ]; + $dataToCompact = [ + 'coolness' => false, + 'disable_auto_group_change' => 'false', + CustomerInterface::DEFAULT_BILLING => false, + CustomerInterface::DEFAULT_SHIPPING => false, + 'confirmation' => false, + 'sendemail_store_id' => false, + 'extension_attributes' => false, + ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $formMock */ + /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') ->disableOriginalConstructor() ->getMock(); @@ -953,9 +1090,12 @@ class SaveTest extends \PHPUnit_Framework_TestCase ->willReturn('int'); $attributes = [$attributeMock]; - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturn($postValue); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ]); $this->requestMock->expects($this->exactly(2)) ->method('getPost') ->willReturnMap( @@ -965,46 +1105,46 @@ class SaveTest extends \PHPUnit_Framework_TestCase ] ); - /** @var \Magento\Customer\Model\Metadata\Form|\PHPUnit_Framework_MockObject_MockObject $formMock */ - $formMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') - ->disableOriginalConstructor() - ->getMock(); - - $this->formFactoryMock->expects($this->once()) - ->method('create') - ->with( - \Magento\Customer\Api\CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, - 'adminhtml_customer', - [], - false, - \Magento\Customer\Model\Metadata\Form::DONT_IGNORE_INVISIBLE - )->willReturn($formMock); - - $formMock->expects($this->once()) - ->method('extractData') - ->with($this->requestMock, 'customer') - ->willReturn($filteredData); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ $objectMock = $this->getMockBuilder('Magento\Framework\DataObject') ->disableOriginalConstructor() ->getMock(); + $objectMock->expects($this->once()) + ->method('getData') + ->with('customer') + ->willReturn($postValue['customer']); $this->objectFactoryMock->expects($this->once()) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); - $objectMock->expects($this->once()) - ->method('getData') - ->with('customer') - ->willReturn($postValue['customer']); - - $formMock->expects($this->once()) + /** @var Form|\PHPUnit_Framework_MockObject_MockObject $formMock */ + $customerFormMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') + ->disableOriginalConstructor() + ->getMock(); + $customerFormMock->expects($this->once()) + ->method('extractData') + ->with($this->requestMock, 'customer') + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) + ->method('compactData') + ->with($dataToCompact) + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) ->method('getAttributes') ->willReturn($attributes); - /** @var \Magento\Customer\Api\Data\CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ + $this->formFactoryMock->expects($this->once()) + ->method('create') + ->with( + CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + 'adminhtml_customer', + [], + false, + Form::DONT_IGNORE_INVISIBLE + )->willReturn($customerFormMock); + $customerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface') ->disableOriginalConstructor() ->getMock(); @@ -1080,8 +1220,17 @@ class SaveTest extends \PHPUnit_Framework_TestCase 'coolness' => false, 'disable_auto_group_change' => 'false', ]; + $dataToCompact = [ + 'coolness' => false, + 'disable_auto_group_change' => 'false', + CustomerInterface::DEFAULT_BILLING => false, + CustomerInterface::DEFAULT_SHIPPING => false, + 'confirmation' => false, + 'sendemail_store_id' => false, + 'extension_attributes' => false, + ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $formMock */ + /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') ->disableOriginalConstructor() ->getMock(); @@ -1093,9 +1242,12 @@ class SaveTest extends \PHPUnit_Framework_TestCase ->willReturn('int'); $attributes = [$attributeMock]; - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturn($postValue); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ]); $this->requestMock->expects($this->exactly(2)) ->method('getPost') ->willReturnMap( @@ -1105,46 +1257,46 @@ class SaveTest extends \PHPUnit_Framework_TestCase ] ); - /** @var \Magento\Customer\Model\Metadata\Form|\PHPUnit_Framework_MockObject_MockObject $formMock */ - $formMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') - ->disableOriginalConstructor() - ->getMock(); - - $this->formFactoryMock->expects($this->once()) - ->method('create') - ->with( - \Magento\Customer\Api\CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, - 'adminhtml_customer', - [], - false, - \Magento\Customer\Model\Metadata\Form::DONT_IGNORE_INVISIBLE - )->willReturn($formMock); - - $formMock->expects($this->once()) - ->method('extractData') - ->with($this->requestMock, 'customer') - ->willReturn($filteredData); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ $objectMock = $this->getMockBuilder('Magento\Framework\DataObject') ->disableOriginalConstructor() ->getMock(); + $objectMock->expects($this->once()) + ->method('getData') + ->with('customer') + ->willReturn($postValue['customer']); $this->objectFactoryMock->expects($this->once()) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); - $objectMock->expects($this->once()) - ->method('getData') - ->with('customer') - ->willReturn($postValue['customer']); - - $formMock->expects($this->once()) + $customerFormMock = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form') + ->disableOriginalConstructor() + ->getMock(); + $customerFormMock->expects($this->once()) + ->method('extractData') + ->with($this->requestMock, 'customer') + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) + ->method('compactData') + ->with($dataToCompact) + ->willReturn($filteredData); + $customerFormMock->expects($this->once()) ->method('getAttributes') ->willReturn($attributes); - /** @var \Magento\Customer\Api\Data\CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ + $this->formFactoryMock->expects($this->once()) + ->method('create') + ->with( + CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + 'adminhtml_customer', + [], + false, + Form::DONT_IGNORE_INVISIBLE + )->willReturn($customerFormMock); + + /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ $customerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface') ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php index 979d41c13bfc50663e051438b337c3adfdce03e9..8ac75a637b8f87dc859c49c2e88419495379b469 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php @@ -169,10 +169,6 @@ class ValidateTest extends \PHPUnit_Framework_TestCase ->method('validateData') ->willReturn([$error]); - $this->extensibleDataObjectConverter->expects($this->once()) - ->method('toFlatArray') - ->willReturn([]); - $validationResult = $this->getMockForAbstractClass( 'Magento\Customer\Api\Data\ValidationResultsInterface', [], @@ -208,10 +204,6 @@ class ValidateTest extends \PHPUnit_Framework_TestCase ->method('validateData') ->willReturn([$error]); - $this->extensibleDataObjectConverter->expects($this->once()) - ->method('toFlatArray') - ->willReturn([]); - $validationResult = $this->getMockForAbstractClass( 'Magento\Customer\Api\Data\ValidationResultsInterface', [], @@ -245,10 +237,6 @@ class ValidateTest extends \PHPUnit_Framework_TestCase $this->form->expects($this->never()) ->method('validateData'); - $this->extensibleDataObjectConverter->expects($this->once()) - ->method('toFlatArray') - ->willReturn([]); - $validationResult = $this->getMockForAbstractClass( 'Magento\Customer\Api\Data\ValidationResultsInterface', [], diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ViewfileTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ViewfileTest.php index 3586651d269d0821264af27191e4e4a5517ea79d..0cc2ca730379753f6b96046656f422d258ed485f 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ViewfileTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ViewfileTest.php @@ -169,7 +169,7 @@ class ViewfileTest extends \PHPUnit_Framework_TestCase ->willReturnMap([['file', null, null], ['image', null, $decodedFile]]); $this->directoryMock->expects($this->once())->method('getAbsolutePath')->with($fileName)->willReturn($path); - $this->directoryMock->expects($this->once())->method('stat')->with($path)->willReturn($stat); + $this->directoryMock->expects($this->once())->method('stat')->with($fileName)->willReturn($stat); $this->fileSystemMock->expects($this->once())->method('getDirectoryRead') ->with(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA) diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php index 8627a4737d614ae5e5472837a707da058a93b6fc..8a9e48bd889d0c04ec91e54c231dcfa8d83f3854 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php @@ -5,12 +5,12 @@ */ namespace Magento\Customer\Test\Unit\Model\Customer; +use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Type; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Ui\Component\Form\Field; use Magento\Ui\DataProvider\EavValidationRules; -use Magento\Customer\Model\Customer\DataProvider; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; @@ -44,6 +44,16 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase */ protected $sessionMock; + /** + * @var \Magento\Customer\Model\FileProcessorFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $fileProcessorFactory; + + /** + * @var \Magento\Customer\Model\FileProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $fileProcessor; + /** * Set up * @@ -69,6 +79,15 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase ->getMockBuilder('Magento\Framework\Session\SessionManagerInterface') ->setMethods(['getCustomerFormData', 'unsCustomerFormData']) ->getMockForAbstractClass(); + + $this->fileProcessor = $this->getMockBuilder('Magento\Customer\Model\FileProcessor') + ->disableOriginalConstructor() + ->getMock(); + + $this->fileProcessorFactory = $this->getMockBuilder('Magento\Customer\Model\FileProcessorFactory') + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); } /** @@ -94,6 +113,12 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase ] ); + $helper->setBackwardCompatibleProperty( + $dataProvider, + 'fileProcessorFactory', + $this->fileProcessorFactory + ); + $meta = $dataProvider->getMeta(); $this->assertNotEmpty($meta); $this->assertEquals($expected, $meta); @@ -318,7 +343,7 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase $attributeBooleanMock->expects($this->exactly(2)) ->method('getAttributeCode') ->willReturn('test-code-boolean'); - $attributeBooleanMock->expects($this->once()) + $attributeBooleanMock->expects($this->any()) ->method('getFrontendInput') ->willReturn('boolean'); $attributeBooleanMock->expects($this->any()) @@ -375,6 +400,10 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase $customer->expects($this->once()) ->method('getAddresses') ->willReturn([$address]); + $customer->expects($this->once()) + ->method('getAttributes') + ->willReturn([]); + $address->expects($this->atLeastOnce()) ->method('getId') ->willReturn(2); @@ -389,6 +418,9 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase 'lastname' => 'lastname', 'street' => "street\nstreet", ]); + $address->expects($this->once()) + ->method('getAttributes') + ->willReturn([]); $helper = new ObjectManager($this); $dataProvider = $helper->getObject( @@ -412,6 +444,12 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase ->method('getCustomerFormData') ->willReturn(null); + $helper->setBackwardCompatibleProperty( + $dataProvider, + 'fileProcessorFactory', + $this->fileProcessorFactory + ); + $this->assertEquals( [ '' => [ @@ -496,6 +534,10 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase $customer->expects($this->once()) ->method('getAddresses') ->willReturn([$address]); + $customer->expects($this->once()) + ->method('getAttributes') + ->willReturn([]); + $address->expects($this->atLeastOnce()) ->method('getId') ->willReturn(2); @@ -510,6 +552,9 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase 'lastname' => 'lastname', 'street' => "street\nstreet", ]); + $address->expects($this->once()) + ->method('getAttributes') + ->willReturn([]); $helper = new ObjectManager($this); $dataProvider = $helper->getObject( @@ -535,6 +580,378 @@ class DataProviderTest extends \PHPUnit_Framework_TestCase $this->sessionMock->expects($this->once()) ->method('unsCustomerFormData'); + $helper->setBackwardCompatibleProperty( + $dataProvider, + 'fileProcessorFactory', + $this->fileProcessorFactory + ); + $this->assertEquals([$customerId => $customerFormData], $dataProvider->getData()); } + + public function testGetDataWithCustomAttributeImage() + { + $customerId = 1; + $customerEmail = 'user1@example.com'; + + $filename = '/filename.ext1'; + $viewUrl = 'viewUrl'; + + $expectedData = [ + $customerId => [ + 'customer' => [ + 'email' => $customerEmail, + 'img1' => [ + [ + 'file' => $filename, + 'size' => 1, + 'url' => $viewUrl, + 'name' => 'filename.ext1', + ], + ], + ], + ], + ]; + + $attributeMock = $this->getMockBuilder('Magento\Customer\Model\Attribute') + ->disableOriginalConstructor() + ->getMock(); + $attributeMock->expects($this->exactly(2)) + ->method('getFrontendInput') + ->willReturn('image'); + $attributeMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn('img1'); + + $entityTypeMock = $this->getMockBuilder('Magento\Eav\Model\Entity\Type') + ->disableOriginalConstructor() + ->getMock(); + $entityTypeMock->expects($this->once()) + ->method('getEntityTypeCode') + ->willReturn(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + + $customerMock = $this->getMockBuilder('Magento\Customer\Model\Customer') + ->disableOriginalConstructor() + ->getMock(); + $customerMock->expects($this->once()) + ->method('getData') + ->willReturn([ + 'email' => $customerEmail, + 'img1' => $filename, + ]); + $customerMock->expects($this->once()) + ->method('getAddresses') + ->willReturn([]); + $customerMock->expects($this->once()) + ->method('getId') + ->willReturn($customerId); + $customerMock->expects($this->once()) + ->method('getAttributes') + ->willReturn([$attributeMock]); + $customerMock->expects($this->once()) + ->method('getEntityType') + ->willReturn($entityTypeMock); + + $collectionMock = $this->getMockBuilder('Magento\Customer\Model\ResourceModel\Customer\Collection') + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$customerMock]); + + $this->customerCollectionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($collectionMock); + + $this->sessionMock->expects($this->once()) + ->method('getCustomerFormData') + ->willReturn([]); + + $this->fileProcessorFactory->expects($this->any()) + ->method('create') + ->with([ + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]) + ->willReturn($this->fileProcessor); + + $this->fileProcessor->expects($this->once()) + ->method('isExist') + ->with($filename) + ->willReturn(true); + $this->fileProcessor->expects($this->once()) + ->method('getStat') + ->with($filename) + ->willReturn(['size' => 1]); + $this->fileProcessor->expects($this->once()) + ->method('getViewUrl') + ->with('/filename.ext1', 'image') + ->willReturn($viewUrl); + + $objectManager = new ObjectManager($this); + $dataProvider = $objectManager->getObject( + '\Magento\Customer\Model\Customer\DataProvider', + [ + 'name' => 'test-name', + 'primaryFieldName' => 'primary-field-name', + 'requestFieldName' => 'request-field-name', + 'eavValidationRules' => $this->eavValidationRulesMock, + 'customerCollectionFactory' => $this->customerCollectionFactoryMock, + 'eavConfig' => $this->getEavConfigMock() + ] + ); + + $objectManager->setBackwardCompatibleProperty( + $dataProvider, + 'session', + $this->sessionMock + ); + + $objectManager->setBackwardCompatibleProperty( + $dataProvider, + 'fileProcessorFactory', + $this->fileProcessorFactory + ); + + $this->assertEquals($expectedData, $dataProvider->getData()); + } + + public function testGetDataWithCustomAttributeImageNoData() + { + $customerId = 1; + $customerEmail = 'user1@example.com'; + + $expectedData = [ + $customerId => [ + 'customer' => [ + 'email' => $customerEmail, + 'img1' => [], + ], + ], + ]; + + $attributeMock = $this->getMockBuilder('Magento\Customer\Model\Attribute') + ->disableOriginalConstructor() + ->getMock(); + $attributeMock->expects($this->once()) + ->method('getFrontendInput') + ->willReturn('image'); + $attributeMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn('img1'); + + $entityTypeMock = $this->getMockBuilder('Magento\Eav\Model\Entity\Type') + ->disableOriginalConstructor() + ->getMock(); + $entityTypeMock->expects($this->once()) + ->method('getEntityTypeCode') + ->willReturn(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + + $customerMock = $this->getMockBuilder('Magento\Customer\Model\Customer') + ->disableOriginalConstructor() + ->getMock(); + $customerMock->expects($this->once()) + ->method('getData') + ->willReturn([ + 'email' => $customerEmail, + ]); + $customerMock->expects($this->once()) + ->method('getAddresses') + ->willReturn([]); + $customerMock->expects($this->once()) + ->method('getId') + ->willReturn($customerId); + $customerMock->expects($this->once()) + ->method('getAttributes') + ->willReturn([$attributeMock]); + $customerMock->expects($this->once()) + ->method('getEntityType') + ->willReturn($entityTypeMock); + + $collectionMock = $this->getMockBuilder('Magento\Customer\Model\ResourceModel\Customer\Collection') + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$customerMock]); + + $this->customerCollectionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($collectionMock); + + $this->sessionMock->expects($this->once()) + ->method('getCustomerFormData') + ->willReturn([]); + + $objectManager = new ObjectManager($this); + $dataProvider = $objectManager->getObject( + '\Magento\Customer\Model\Customer\DataProvider', + [ + 'name' => 'test-name', + 'primaryFieldName' => 'primary-field-name', + 'requestFieldName' => 'request-field-name', + 'eavValidationRules' => $this->eavValidationRulesMock, + 'customerCollectionFactory' => $this->customerCollectionFactoryMock, + 'eavConfig' => $this->getEavConfigMock() + ] + ); + + $objectManager->setBackwardCompatibleProperty( + $dataProvider, + 'session', + $this->sessionMock + ); + + $objectManager->setBackwardCompatibleProperty( + $dataProvider, + 'fileProcessorFactory', + $this->fileProcessorFactory + ); + + $this->assertEquals($expectedData, $dataProvider->getData()); + } + + public function testGetAttributesMetaWithCustomAttributeImage() + { + $maxFileSize = 1000; + $allowedExtension = 'ext1 ext2'; + + $attributeCode = 'img1'; + + $collectionMock = $this->getMockBuilder('Magento\Customer\Model\ResourceModel\Customer\Collection') + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('addAttributeToSelect') + ->with('*'); + + $this->customerCollectionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($collectionMock); + + $attributeMock = $this->getMockBuilder('Magento\Eav\Model\Entity\Attribute\AbstractAttribute') + ->setMethods([ + 'getAttributeCode', + 'getFrontendInput', + 'getDataUsingMethod', + ]) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $attributeMock->expects($this->any()) + ->method('getAttributeCode') + ->willReturn($attributeCode); + $attributeMock->expects($this->any()) + ->method('getFrontendInput') + ->willReturn('image'); + $attributeMock->expects($this->any()) + ->method('getDataUsingMethod') + ->willReturnCallback( + function ($origName) { + return $origName; + } + ); + + $typeCustomerMock = $this->getMockBuilder('Magento\Eav\Model\Entity\Type') + ->disableOriginalConstructor() + ->getMock(); + $typeCustomerMock->expects($this->once()) + ->method('getAttributeCollection') + ->willReturn([$attributeMock]); + $typeCustomerMock->expects($this->once()) + ->method('getEntityTypeCode') + ->willReturn(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + + $typeAddressMock = $this->getMockBuilder('Magento\Eav\Model\Entity\Type') + ->disableOriginalConstructor() + ->getMock(); + $typeAddressMock->expects($this->once()) + ->method('getAttributeCollection') + ->willReturn([]); + + $this->eavConfigMock->expects($this->at(0)) + ->method('getEntityType') + ->with('customer') + ->willReturn($typeCustomerMock); + $this->eavConfigMock->expects($this->at(1)) + ->method('getEntityType') + ->with('customer_address') + ->willReturn($typeAddressMock); + + $this->eavValidationRulesMock->expects($this->once()) + ->method('build') + ->with($attributeMock, [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => 'is_visible', + 'required' => 'is_required', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'label' => __('frontend_label'), + ]) + ->willReturn([ + 'max_file_size' => $maxFileSize, + 'file_extensions' => 'ext1, eXt2 ', // Added spaces and upper-cases + ]); + + $this->fileProcessorFactory->expects($this->any()) + ->method('create') + ->with([ + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]) + ->willReturn($this->fileProcessor); + + $objectManager = new ObjectManager($this); + $dataProvider = $objectManager->getObject( + '\Magento\Customer\Model\Customer\DataProvider', + [ + 'name' => 'test-name', + 'primaryFieldName' => 'primary-field-name', + 'requestFieldName' => 'request-field-name', + 'eavValidationRules' => $this->eavValidationRulesMock, + 'customerCollectionFactory' => $this->customerCollectionFactoryMock, + 'eavConfig' => $this->eavConfigMock, + 'fileProcessorFactory' => $this->fileProcessorFactory, + ] + ); + + $result = $dataProvider->getMeta(); + + $this->assertNotEmpty($result); + + $expected = [ + 'customer' => [ + 'children' => [ + $attributeCode => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'formElement' => 'fileUploader', + 'componentType' => 'fileUploader', + 'maxFileSize' => $maxFileSize, + 'allowedExtensions' => $allowedExtension, + 'uploaderConfig' => [ + 'url' => 'customer/file/customer_upload', + ], + 'sortOrder' => 'sort_order', + 'required' => 'is_required', + 'visible' => 'is_visible', + 'validation' => [ + 'max_file_size' => $maxFileSize, + 'file_extensions' => 'ext1, eXt2 ', + ], + 'label' => __('frontend_label'), + ], + ], + ], + ], + ], + ], + 'address' => [ + 'children' => [], + ], + ]; + + $this->assertEquals($expected, $result); + } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/CustomerExtractorTest.php b/app/code/Magento/Customer/Test/Unit/Model/CustomerExtractorTest.php index c414d648b21728edf5a49314edaa5f2397a12c61..5f112188d4387e43bf5e14f09f1c48a94c081964 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/CustomerExtractorTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/CustomerExtractorTest.php @@ -120,6 +120,10 @@ class CustomerExtractorTest extends \PHPUnit_Framework_TestCase ->method('extractData') ->with($this->request) ->willReturn($customerData); + $this->customerForm->expects($this->once()) + ->method('compactData') + ->with($customerData) + ->willReturn($customerData); $this->customerForm->expects($this->once()) ->method('getAllowedAttributes') ->willReturn(['group_id' => 'attribute object']); diff --git a/app/code/Magento/Customer/Test/Unit/Model/FileProcessorTest.php b/app/code/Magento/Customer/Test/Unit/Model/FileProcessorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b25aa7c4dc89fd319e809cabf1d82726230eec03 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Model/FileProcessorTest.php @@ -0,0 +1,380 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Model; + +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\FileProcessor; +use Magento\Framework\App\Filesystem\DirectoryList; + +class FileProcessorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + */ + private $filesystem; + + /** + * @var \Magento\MediaStorage\Model\File\UploaderFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $uploaderFactory; + + /** + * @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlBuilder; + + /** + * @var \Magento\Framework\Url\EncoderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlEncoder; + + /** + * @var \Magento\Framework\Filesystem\Directory\WriteInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $mediaDirectory; + + protected function setUp() + { + $this->mediaDirectory = $this->getMockBuilder('Magento\Framework\Filesystem\Directory\WriteInterface') + ->getMockForAbstractClass(); + + $this->filesystem = $this->getMockBuilder('Magento\Framework\Filesystem') + ->disableOriginalConstructor() + ->getMock(); + $this->filesystem->expects($this->any()) + ->method('getDirectoryWrite') + ->with(DirectoryList::MEDIA) + ->willReturn($this->mediaDirectory); + + $this->uploaderFactory = $this->getMockBuilder('Magento\MediaStorage\Model\File\UploaderFactory') + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + + $this->urlBuilder = $this->getMockBuilder('Magento\Framework\UrlInterface') + ->getMockForAbstractClass(); + + $this->urlEncoder = $this->getMockBuilder('Magento\Framework\Url\EncoderInterface') + ->getMockForAbstractClass(); + } + + private function getModel($entityTypeCode, array $allowedExtensions = []) + { + $model = new FileProcessor( + $this->filesystem, + $this->uploaderFactory, + $this->urlBuilder, + $this->urlEncoder, + $entityTypeCode, + $allowedExtensions + ); + return $model; + } + + + public function testGetStat() + { + $fileName = '/filename.ext1'; + + $this->mediaDirectory->expects($this->once()) + ->method('stat') + ->with(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER . $fileName) + ->willReturn(['size' => 1]); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + $result = $model->getStat($fileName); + + $this->assertTrue(is_array($result)); + $this->assertArrayHasKey('size', $result); + $this->assertEquals(1, $result['size']); + } + + public function testIsExist() + { + $fileName = '/filename.ext1'; + + $this->mediaDirectory->expects($this->once()) + ->method('isExist') + ->with(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER . $fileName) + ->willReturn(true); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + $this->assertTrue($model->isExist($fileName)); + } + + public function testGetViewUrlCustomer() + { + $filePath = 'filename.ext1'; + $encodedFilePath = 'encodedfilenameext1'; + + $fileUrl = 'fileUrl'; + + $this->urlEncoder->expects($this->once()) + ->method('encode') + ->with($filePath) + ->willReturn($encodedFilePath); + + $this->urlBuilder->expects($this->once()) + ->method('getUrl') + ->with('customer/index/viewfile', ['image' => $encodedFilePath]) + ->willReturn($fileUrl); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + $this->assertEquals($fileUrl, $model->getViewUrl($filePath, 'image')); + } + + public function testGetViewUrlCustomerAddress() + { + $filePath = 'filename.ext1'; + + $baseUrl = 'baseUrl'; + $relativeUrl = 'relativeUrl'; + + $this->urlBuilder->expects($this->once()) + ->method('getBaseUrl') + ->with(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]) + ->willReturn($baseUrl); + + $this->mediaDirectory->expects($this->once()) + ->method('getRelativePath') + ->with(AddressMetadataInterface::ENTITY_TYPE_ADDRESS . '/' . $filePath) + ->willReturn($relativeUrl); + + $model = $this->getModel(AddressMetadataInterface::ENTITY_TYPE_ADDRESS); + $this->assertEquals($baseUrl . $relativeUrl, $model->getViewUrl($filePath, 'image')); + } + + public function testRemoveUploadedFile() + { + $fileName = '/filename.ext1'; + + $this->mediaDirectory->expects($this->once()) + ->method('delete') + ->with(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER . $fileName) + ->willReturn(true); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + $this->assertTrue($model->removeUploadedFile($fileName)); + } + + public function testSaveTemporaryFile() + { + $attributeCode = 'img1'; + + $allowedExtensions = [ + 'ext1', + 'ext2', + ]; + + $absolutePath = '/absolute/filepath'; + + $expectedResult = [ + 'file' => 'filename.ext1', + 'path' => 'filepath', + ]; + + $uploaderMock = $this->getMockBuilder('Magento\MediaStorage\Model\File\Uploader') + ->disableOriginalConstructor() + ->getMock(); + $uploaderMock->expects($this->once()) + ->method('setFilesDispersion') + ->with(false) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('setFilenamesCaseSensitivity') + ->with(false) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('setAllowRenameFiles') + ->with(true) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('setAllowedExtensions') + ->with($allowedExtensions) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('save') + ->with($absolutePath) + ->willReturn($expectedResult); + + $this->uploaderFactory->expects($this->once()) + ->method('create') + ->with(['fileId' => 'customer[' . $attributeCode . ']']) + ->willReturn($uploaderMock); + + $this->mediaDirectory->expects($this->once()) + ->method('getAbsolutePath') + ->with(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER . '/' . FileProcessor::TMP_DIR) + ->willReturn($absolutePath); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, $allowedExtensions); + $result = $model->saveTemporaryFile('customer[' . $attributeCode . ']'); + + $this->assertEquals($expectedResult, $result); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage File can not be saved to the destination folder. + */ + public function testSaveTemporaryFileWithError() + { + $attributeCode = 'img1'; + + $allowedExtensions = [ + 'ext1', + 'ext2', + ]; + + $absolutePath = '/absolute/filepath'; + + $uploaderMock = $this->getMockBuilder('Magento\MediaStorage\Model\File\Uploader') + ->disableOriginalConstructor() + ->getMock(); + $uploaderMock->expects($this->once()) + ->method('setFilesDispersion') + ->with(false) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('setFilenamesCaseSensitivity') + ->with(false) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('setAllowRenameFiles') + ->with(true) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('setAllowedExtensions') + ->with($allowedExtensions) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('save') + ->with($absolutePath) + ->willReturn(false); + + $this->uploaderFactory->expects($this->once()) + ->method('create') + ->with(['fileId' => 'customer[' . $attributeCode . ']']) + ->willReturn($uploaderMock); + + $this->mediaDirectory->expects($this->once()) + ->method('getAbsolutePath') + ->with(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER . '/' . FileProcessor::TMP_DIR) + ->willReturn($absolutePath); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, $allowedExtensions); + $model->saveTemporaryFile('customer[' . $attributeCode . ']'); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Unable to create directory customer/f/i + */ + public function testMoveTemporaryFileUnableToCreateDirectory() + { + $filePath = '/filename.ext1'; + + $destinationPath = 'customer/f/i'; + + $this->mediaDirectory->expects($this->once()) + ->method('create') + ->with($destinationPath) + ->willReturn(false); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + $model->moveTemporaryFile($filePath); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Destination folder is not writable or does not exists + */ + public function testMoveTemporaryFileDestinationFolderDoesNotExists() + { + $filePath = '/filename.ext1'; + + $destinationPath = 'customer/f/i'; + + $this->mediaDirectory->expects($this->once()) + ->method('create') + ->with($destinationPath) + ->willReturn(true); + $this->mediaDirectory->expects($this->once()) + ->method('isWritable') + ->with($destinationPath) + ->willReturn(false); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + $model->moveTemporaryFile($filePath); + } + + public function testMoveTemporaryFile() + { + $filePath = '/filename.ext1'; + + $destinationPath = 'customer/f/i'; + + $this->mediaDirectory->expects($this->once()) + ->method('create') + ->with($destinationPath) + ->willReturn(true); + $this->mediaDirectory->expects($this->once()) + ->method('isWritable') + ->with($destinationPath) + ->willReturn(true); + $this->mediaDirectory->expects($this->once()) + ->method('getAbsolutePath') + ->with($destinationPath) + ->willReturn('/' . $destinationPath); + + $path = CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER . '/' . FileProcessor::TMP_DIR . $filePath; + $newPath = $destinationPath . $filePath; + + $this->mediaDirectory->expects($this->once()) + ->method('renameFile') + ->with($path, $newPath) + ->willReturn(true); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + $this->assertEquals('/f/i' . $filePath, $model->moveTemporaryFile($filePath)); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Something went wrong while saving the file + */ + public function testMoveTemporaryFileWithException() + { + $filePath = '/filename.ext1'; + + $destinationPath = 'customer/f/i'; + + $this->mediaDirectory->expects($this->once()) + ->method('create') + ->with($destinationPath) + ->willReturn(true); + $this->mediaDirectory->expects($this->once()) + ->method('isWritable') + ->with($destinationPath) + ->willReturn(true); + $this->mediaDirectory->expects($this->once()) + ->method('getAbsolutePath') + ->with($destinationPath) + ->willReturn('/' . $destinationPath); + + $path = CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER . '/' . FileProcessor::TMP_DIR . $filePath; + $newPath = $destinationPath . $filePath; + + $this->mediaDirectory->expects($this->once()) + ->method('renameFile') + ->with($path, $newPath) + ->willThrowException(new \Exception('Exception.')); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + $model->moveTemporaryFile($filePath); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/FileUploaderTest.php b/app/code/Magento/Customer/Test/Unit/Model/FileUploaderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a902151860f8f81c93f76b6dd617b1b6a7349f75 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Model/FileUploaderTest.php @@ -0,0 +1,186 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Model; + +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\FileProcessor; +use Magento\Customer\Model\FileUploader; + +class FileUploaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var CustomerMetadataInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerMetadataService; + + /** + * @var \Magento\Customer\Api\AddressMetadataInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressMetadataService; + + /** + * @var \Magento\Customer\Model\Metadata\ElementFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $elementFactory; + + /** + * @var \Magento\Customer\Model\FileProcessorFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileProcessorFactory; + + /** + * @var \Magento\Customer\Api\Data\AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeMetadata; + + protected function setUp() + { + $this->customerMetadataService = $this->getMockBuilder('Magento\Customer\Api\CustomerMetadataInterface') + ->getMockForAbstractClass(); + + $this->addressMetadataService = $this->getMockBuilder('Magento\Customer\Api\AddressMetadataInterface') + ->getMockForAbstractClass(); + + $this->elementFactory = $this->getMockBuilder('Magento\Customer\Model\Metadata\ElementFactory') + ->disableOriginalConstructor() + ->getMock(); + + $this->fileProcessorFactory = $this->getMockBuilder('Magento\Customer\Model\FileProcessorFactory') + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->attributeMetadata = $this->getMockBuilder('Magento\Customer\Api\Data\AttributeMetadataInterface') + ->getMockForAbstractClass(); + } + + /** + * @param string $entityTypeCode + * @param string $scope + * @return FileUploader + */ + private function getModel($entityTypeCode, $scope) + { + $model = new FileUploader( + $this->customerMetadataService, + $this->addressMetadataService, + $this->elementFactory, + $this->fileProcessorFactory, + $this->attributeMetadata, + $entityTypeCode, + $scope + ); + return $model; + } + + public function testValidate() + { + $attributeCode = 'attribute_code'; + + $filename = 'filename.ext1'; + + $_FILES = [ + 'customer' => [ + 'name' => [ + $attributeCode => $filename, + ], + ], + ]; + + $formElement = $this->getMockBuilder('Magento\Customer\Model\Metadata\Form\Image') + ->disableOriginalConstructor() + ->getMock(); + $formElement->expects($this->once()) + ->method('validateValue') + ->with(['name' => $filename]) + ->willReturn(true); + + $this->elementFactory->expects($this->once()) + ->method('create') + ->with($this->attributeMetadata, null, CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER) + ->willReturn($formElement); + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'customer'); + $this->assertTrue($model->validate()); + } + + public function testUpload() + { + $attributeCode = 'attribute_code'; + $attributeFrontendInput = 'image'; + + $resultFileName = '/filename.ext1'; + $resultFilePath = 'filepath'; + $resultFileUrl = 'viewFileUrl'; + + $allowedExtensions = 'ext1,EXT2 , eXt3'; // Added spaces, commas and upper-cases + $expectedAllowedExtensions = [ + 'ext1', + 'ext2', + 'ext3', + ]; + + $_FILES = [ + 'customer' => [ + 'name' => [ + $attributeCode => 'filename', + ], + ], + ]; + + $expectedResult = [ + 'name' => $resultFileName, + 'file' => $resultFileName, + 'path' => $resultFilePath, + 'tmp_name' => $resultFilePath . $resultFileName, + 'url' => $resultFileUrl, + ]; + + $fileProcessor = $this->getMockBuilder('Magento\Customer\Model\FileProcessor') + ->disableOriginalConstructor() + ->getMock(); + $fileProcessor->expects($this->once()) + ->method('saveTemporaryFile') + ->with('customer[' . $attributeCode . ']') + ->willReturn([ + 'name' => $resultFileName, + 'path' => $resultFilePath, + 'file' => $resultFileName, + ]); + $fileProcessor->expects($this->once()) + ->method('getViewUrl') + ->with(FileProcessor::TMP_DIR . '/filename.ext1', $attributeFrontendInput) + ->willReturn($resultFileUrl); + + $this->fileProcessorFactory->expects($this->once()) + ->method('create') + ->with([ + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + 'allowedExtensions' => $expectedAllowedExtensions, + ]) + ->willReturn($fileProcessor); + + $validationRuleMock = $this->getMockBuilder('Magento\Customer\Api\Data\ValidationRuleInterface') + ->getMockForAbstractClass(); + $validationRuleMock->expects($this->once()) + ->method('getName') + ->willReturn('file_extensions'); + $validationRuleMock->expects($this->once()) + ->method('getValue') + ->willReturn($allowedExtensions); + + $this->attributeMetadata->expects($this->once()) + ->method('getFrontendInput') + ->willReturn($attributeFrontendInput); + $this->attributeMetadata->expects($this->once()) + ->method('getValidationRules') + ->willReturn([$validationRuleMock]); + + + $model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'customer'); + $this->assertEquals($expectedResult, $model->upload()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/FileTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/FileTest.php index 7a5b08fa531cb16f63197c6e90d012b14013370e..8bdd14daf53e63343d7ce2bf19a4356cf48a0912 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/FileTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/FileTest.php @@ -8,6 +8,7 @@ namespace Magento\Customer\Test\Unit\Model\Metadata\Form; use Magento\Customer\Model\Metadata\ElementFactory; +use Magento\Framework\App\Filesystem\DirectoryList; use Magento\MediaStorage\Model\File\Validator\NotProtectedExtension; class FileTest extends AbstractFormTestCase @@ -30,6 +31,11 @@ class FileTest extends AbstractFormTestCase */ protected $uploaderFactoryMock; + /** + * @var \Magento\Customer\Model\FileProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileProcessorMock; + protected function setUp() { parent::setUp(); @@ -43,6 +49,10 @@ class FileTest extends AbstractFormTestCase $this->requestMock = $this->getMockBuilder('Magento\Framework\App\Request\Http') ->disableOriginalConstructor()->getMock(); $this->uploaderFactoryMock = $this->getMock('Magento\Framework\File\UploaderFactory', [], [], '', false); + + $this->fileProcessorMock = $this->getMockBuilder('Magento\Customer\Model\FileProcessor') + ->disableOriginalConstructor() + ->getMock(); } /** @@ -52,10 +62,9 @@ class FileTest extends AbstractFormTestCase * @param string $delete * @dataProvider extractValueNoRequestScopeDataProvider */ - public function testExtractValueNoRequestScope($expected, $attributeCode = '', $isAjax = false, $delete = '') + public function testExtractValueNoRequestScope($expected, $attributeCode = '', $delete = '') { $value = 'value'; - $fileForm = $this->getClass($value, $isAjax); $this->requestMock->expects( $this->any() @@ -75,7 +84,14 @@ class FileTest extends AbstractFormTestCase if (!empty($attributeCode)) { $_FILES[$attributeCode] = ['attributeCodeValue']; } - $this->assertEquals($expected, $fileForm->extractValue($this->requestMock)); + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertEquals($expected, $model->extractValue($this->requestMock)); if (!empty($attributeCode)) { unset($_FILES[$attributeCode]); } @@ -84,11 +100,10 @@ class FileTest extends AbstractFormTestCase public function extractValueNoRequestScopeDataProvider() { return [ - 'ajax' => [false, '', true], 'no_file' => [[]], - 'delete' => [['delete' => true], '', false, true], - 'file_delete' => [['attributeCodeValue', 'delete' => true], 'attributeCode', false, true], - 'file_!delete' => [['attributeCodeValue'], 'attributeCode', false, false] + 'delete' => [['delete' => true], '', true], + 'file_delete' => [['attributeCodeValue', 'delete' => true], 'attributeCode', true], + 'file_!delete' => [['attributeCodeValue'], 'attributeCode', false] ]; } @@ -101,7 +116,6 @@ class FileTest extends AbstractFormTestCase public function testExtractValueWithRequestScope($expected, $requestScope, $mainScope = false) { $value = 'value'; - $fileForm = $this->getClass($value, false); $this->requestMock->expects( $this->any() @@ -126,12 +140,18 @@ class FileTest extends AbstractFormTestCase $this->returnValue('attributeCode') ); - $fileForm->setRequestScope($requestScope); + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $model->setRequestScope($requestScope); if ($mainScope) { $_FILES['mainScope'] = $mainScope; } - $this->assertEquals($expected, $fileForm->extractValue($this->requestMock)); + $this->assertEquals($expected, $model->extractValue($this->requestMock)); if ($mainScope) { unset($_FILES['mainScope']); } @@ -163,7 +183,6 @@ class FileTest extends AbstractFormTestCase */ public function testValidateValueNotToUpload($expected, $value, $isAjax = false, $isRequired = true) { - $fileForm = $this->getClass($value, $isAjax); $this->attributeMetadataMock->expects( $this->any() )->method( @@ -179,7 +198,13 @@ class FileTest extends AbstractFormTestCase $this->returnValue('attributeLabel') ); - $this->assertEquals($expected, $fileForm->validateValue($value)); + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => $isAjax, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertEquals($expected, $model->validateValue($value)); } public function validateValueNotToUploadDataProvider() @@ -201,8 +226,7 @@ class FileTest extends AbstractFormTestCase public function testValidateValueToUpload($expected, $value, $parameters = []) { $parameters = array_merge(['uploaded' => true, 'valid' => true], $parameters); - $fileForm = $this->getClass($value, false); - $fileForm->expects($this->any())->method('_isUploadedFile')->will($this->returnValue($parameters['uploaded'])); + $this->attributeMetadataMock->expects($this->any())->method('isRequired')->will($this->returnValue(false)); $this->attributeMetadataMock->expects( $this->any() @@ -226,7 +250,18 @@ class FileTest extends AbstractFormTestCase )->will( $this->returnValue($parameters['valid']) ); - $this->assertEquals($expected, $fileForm->validateValue($value)); + + $this->fileProcessorMock->expects($this->any()) + ->method('isExist') + ->willReturn($parameters['uploaded']); + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertEquals($expected, $model->validateValue($value)); } public function validateValueToUploadDataProvider() @@ -248,30 +283,54 @@ class FileTest extends AbstractFormTestCase public function testCompactValueIsAjax() { - $fileForm = $this->getClass('value', true); - $this->assertSame($fileForm, $fileForm->compactValue('aValue')); + $model = $this->initialize([ + 'value' => 'value', + 'isAjax' => true, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertSame($model, $model->compactValue('aValue')); } public function testCompactValueNoDelete() { - $fileForm = $this->getClass('value', false); $this->attributeMetadataMock->expects($this->any())->method('isRequired')->will($this->returnValue(false)); - $this->assertSame('value', $fileForm->compactValue([])); + + $model = $this->initialize([ + 'value' => 'value', + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->fileProcessorMock->expects($this->once()) + ->method('removeUploadedFile') + ->with('value') + ->willReturnSelf(); + + $this->assertSame([], $model->compactValue([])); } public function testCompactValueDelete() { - $fileForm = $this->getClass('value', false); $this->attributeMetadataMock->expects($this->any())->method('isRequired')->will($this->returnValue(false)); + $mediaDirMock = $this->getMockForAbstractClass('\Magento\Framework\Filesystem\Directory\WriteInterface'); $mediaDirMock->expects($this->once()) ->method('delete') - ->with(self::ENTITY_TYPE . 'value'); + ->with(self::ENTITY_TYPE . '/' . 'value'); + $this->fileSystemMock->expects($this->once()) ->method('getDirectoryWrite') - ->with(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA) + ->with(DirectoryList::MEDIA) ->will($this->returnValue($mediaDirMock)); - $this->assertSame('', $fileForm->compactValue(['delete' => true])); + + $model = $this->initialize([ + 'value' => 'value', + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertSame('', $model->compactValue(['delete' => true])); } public function testCompactValueTmpFile() @@ -279,11 +338,10 @@ class FileTest extends AbstractFormTestCase $value = ['tmp_name' => 'tmp.file', 'name' => 'new.file']; $expected = 'saved.file'; - $fileForm = $this->getClass(null, false); $mediaDirMock = $this->getMockForAbstractClass('\Magento\Framework\Filesystem\Directory\WriteInterface'); $this->fileSystemMock->expects($this->once()) ->method('getDirectoryWrite') - ->with(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA) + ->with(DirectoryList::MEDIA) ->will($this->returnValue($mediaDirMock)); $mediaDirMock->expects($this->any()) ->method('getAbsolutePath') @@ -309,14 +367,26 @@ class FileTest extends AbstractFormTestCase ->method('getUploadedFileName') ->will($this->returnValue($expected)); - $this->assertSame($expected, $fileForm->compactValue($value)); + $model = $this->initialize([ + 'value' => null, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertSame($expected, $model->compactValue($value)); } public function testRestoreValue() { $value = 'value'; - $fileForm = $this->getClass($value, false); - $this->assertEquals($value, $fileForm->restoreValue('aValue')); + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertEquals($value, $model->restoreValue('aValue')); } /** @@ -325,8 +395,13 @@ class FileTest extends AbstractFormTestCase */ public function testOutputValueNonJson($format) { - $fileForm = $this->getClass('value', false); - $this->assertSame('', $fileForm->outputValue($format)); + $model = $this->initialize([ + 'value' => 'value', + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertSame('', $model->outputValue($format)); } public function outputValueDataProvider() @@ -344,7 +419,7 @@ class FileTest extends AbstractFormTestCase { $value = 'value'; $urlKey = 'url_key'; - $fileForm = $this->getClass($value, false); + $this->urlEncode->expects( $this->once() )->method( @@ -354,36 +429,229 @@ class FileTest extends AbstractFormTestCase )->will( $this->returnValue($urlKey) ); + $expected = ['value' => $value, 'url_key' => $urlKey]; - $this->assertSame($expected, $fileForm->outputValue(ElementFactory::OUTPUT_FORMAT_JSON)); + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertSame($expected, $model->outputValue(ElementFactory::OUTPUT_FORMAT_JSON)); } /** - * Helper for creating the unit under test. - * - * @param string|int|bool|null $value The value undergoing testing by a given test - * @param bool $isAjax - * @return \PHPUnit_Framework_MockObject_MockObject | File + * @param array $data + * @return \Magento\Customer\Model\Metadata\Form\File */ - protected function getClass($value, $isAjax) + private function initialize(array $data) { - $fileForm = $this->getMock( - 'Magento\Customer\Model\Metadata\Form\File', - ['_isUploadedFile'], - [ - $this->localeMock, - $this->loggerMock, - $this->attributeMetadataMock, - $this->localeResolverMock, - $value, - self::ENTITY_TYPE, - $isAjax, - $this->urlEncode, - $this->fileValidatorMock, - $this->fileSystemMock, - $this->uploaderFactoryMock - ] + $model = new \Magento\Customer\Model\Metadata\Form\File( + $this->localeMock, + $this->loggerMock, + $this->attributeMetadataMock, + $this->localeResolverMock, + $data['value'], + $data['entityTypeCode'], + $data['isAjax'], + $this->urlEncode, + $this->fileValidatorMock, + $this->fileSystemMock, + $this->uploaderFactoryMock ); - return $fileForm; + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager->setBackwardCompatibleProperty( + $model, + 'fileProcessor', + $this->fileProcessorMock + ); + + return $model; + } + + public function testExtractValueFileUploaderUIComponent() + { + $attributeCode = 'img1'; + $requestScope = 'customer'; + $fileName = 'filename.ext1'; + + $this->attributeMetadataMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn($attributeCode); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with($requestScope) + ->willReturn([ + $attributeCode => [ + [ + 'file' => $fileName, + ], + ], + ]); + + $model = $this->initialize([ + 'value' => 'value', + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $model->setRequestScope($requestScope); + $result = $model->extractValue($this->requestMock); + + $this->assertEquals(['file' => $fileName], $result); + } + + public function testCompactValueRemoveUiComponentValue() + { + $value = 'value'; + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->fileProcessorMock->expects($this->once()) + ->method('removeUploadedFile') + ->with($value) + ->willReturnSelf(); + + $this->assertEquals([], $model->compactValue([])); + } + + public function testCompactValueNoAction() + { + $value = 'value'; + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertEquals($value, $model->compactValue($value)); + } + + public function testCompactValueUiComponent() + { + $value = [ + 'file' => 'filename', + ]; + + $model = $this->initialize([ + 'value' => null, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->fileProcessorMock->expects($this->once()) + ->method('moveTemporaryFile') + ->with($value['file']) + ->willReturn(true); + + $this->assertTrue($model->compactValue($value)); + } + + public function testCompactValueInputField() + { + $value = [ + 'name' => 'filename.ext1', + 'tmp_name' => 'tmpfilename.ext1', + ]; + + $absolutePath = 'absolute_path'; + $uploadedFilename = 'filename.ext1'; + + $mediaDirectoryMock = $this->getMockBuilder('Magento\Framework\Filesystem\Directory\WriteInterface') + ->getMockForAbstractClass(); + $mediaDirectoryMock->expects($this->once()) + ->method('getAbsolutePath') + ->with(self::ENTITY_TYPE) + ->willReturn($absolutePath); + + $this->fileSystemMock->expects($this->once()) + ->method('getDirectoryWrite') + ->with(DirectoryList::MEDIA) + ->willReturn($mediaDirectoryMock); + + $uploaderMock = $this->getMockBuilder('Magento\Framework\File\Uploader') + ->disableOriginalConstructor() + ->getMock(); + $uploaderMock->expects($this->once()) + ->method('setFilesDispersion') + ->with(true) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('setFilenamesCaseSensitivity') + ->with(false) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('setAllowRenameFiles') + ->with(true) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('save') + ->with($absolutePath, $value['name']) + ->willReturnSelf(); + $uploaderMock->expects($this->once()) + ->method('getUploadedFileName') + ->willReturn($uploadedFilename); + + $this->uploaderFactoryMock->expects($this->once()) + ->method('create') + ->with(['fileId' => $value]) + ->willReturn($uploaderMock); + + $model = $this->initialize([ + 'value' => null, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertEquals($uploadedFilename, $model->compactValue($value)); + } + + public function testCompactValueInputFieldWithException() + { + $value = [ + 'name' => 'filename.ext1', + 'tmp_name' => 'tmpfilename.ext1', + ]; + + $originValue = 'origin'; + + $mediaDirectoryMock = $this->getMockBuilder('Magento\Framework\Filesystem\Directory\WriteInterface') + ->getMockForAbstractClass(); + $mediaDirectoryMock->expects($this->once()) + ->method('delete') + ->with(self::ENTITY_TYPE . '/' . $originValue); + + $this->fileSystemMock->expects($this->once()) + ->method('getDirectoryWrite') + ->with(DirectoryList::MEDIA) + ->willReturn($mediaDirectoryMock); + + $exception = new \Exception('Error'); + + $this->uploaderFactoryMock->expects($this->once()) + ->method('create') + ->with(['fileId' => $value]) + ->willThrowException($exception); + + $this->loggerMock->expects($this->once()) + ->method('critical') + ->with($exception) + ->willReturnSelf(); + + $model = $this->initialize([ + 'value' => $originValue, + 'isAjax' => false, + 'entityTypeCode' => self::ENTITY_TYPE, + ]); + + $this->assertEquals('', $model->compactValue($value)); } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/ImageTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/ImageTest.php index f002a5cb1e4a5d8cd23ddf2a2ea16f0911be2a3a..e7b602ff241fbf7bd06ab036638e441059ed5edd 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/ImageTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/ImageTest.php @@ -5,49 +5,399 @@ */ namespace Magento\Customer\Test\Unit\Model\Metadata\Form; -use Magento\Customer\Model\Metadata\Form\Image; +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\FileProcessor; -class ImageTest extends FileTest +class ImageTest extends AbstractFormTestCase { /** - * Create an instance of the class that is being tested - * - * @param string|int|bool|null $value - * @param bool $isAjax - * @return Image - */ - protected function getClass($value, $isAjax) - { - $imageForm = $this->getMock( - 'Magento\Customer\Model\Metadata\Form\Image', - ['_isUploadedFile'], - [ - $this->localeMock, - $this->loggerMock, - $this->attributeMetadataMock, - $this->localeResolverMock, - $value, - 0, - $isAjax, - $this->urlEncode, - $this->fileValidatorMock, - $this->fileSystemMock, - $this->uploaderFactoryMock - ] + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Url\EncoderInterface + */ + protected $urlEncode; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\MediaStorage\Model\File\Validator\NotProtectedExtension + */ + protected $fileValidatorMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Filesystem + */ + protected $fileSystemMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\Request\Http + */ + protected $requestMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\File\UploaderFactory + */ + protected $uploaderFactoryMock; + + /** + * @var FileProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileProcessorMock; + + /** + * @var \Magento\Framework\Api\Data\ImageContentInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $imageContentFactory; + + protected function setUp() + { + parent::setUp(); + + $this->urlEncode = $this->getMockBuilder('Magento\Framework\Url\EncoderInterface') + ->disableOriginalConstructor() + ->getMock(); + + $this->fileValidatorMock = $this->getMockBuilder( + 'Magento\MediaStorage\Model\File\Validator\NotProtectedExtension' + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileSystemMock = $this->getMockBuilder('Magento\Framework\Filesystem') + ->disableOriginalConstructor() + ->getMock(); + + $this->requestMock = $this->getMockBuilder('Magento\Framework\App\Request\Http') + ->disableOriginalConstructor() + ->getMock(); + + $this->uploaderFactoryMock = $this->getMockBuilder('Magento\Framework\File\UploaderFactory') + ->disableOriginalConstructor() + ->getMock(); + + $this->fileProcessorMock = $this->getMockBuilder('Magento\Customer\Model\FileProcessor') + ->disableOriginalConstructor() + ->getMock(); + + $this->imageContentFactory = $this->getMockBuilder('Magento\Framework\Api\Data\ImageContentInterfaceFactory') + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + } + + /** + * @param array $data + * @return \Magento\Customer\Model\Metadata\Form\File + */ + private function initialize(array $data) + { + $model = new \Magento\Customer\Model\Metadata\Form\Image( + $this->localeMock, + $this->loggerMock, + $this->attributeMetadataMock, + $this->localeResolverMock, + $data['value'], + $data['entityTypeCode'], + $data['isAjax'], + $this->urlEncode, + $this->fileValidatorMock, + $this->fileSystemMock, + $this->uploaderFactoryMock + ); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager->setBackwardCompatibleProperty( + $model, + 'fileProcessor', + $this->fileProcessorMock + ); + $objectManager->setBackwardCompatibleProperty( + $model, + 'imageContentFactory', + $this->imageContentFactory ); - return $imageForm; + + return $model; + } + + public function testValidateIsNotValidFile() + { + $value = [ + 'tmp_name' => 'tmp_file', + 'name' => 'realFileName', + ]; + + $this->attributeMetadataMock->expects($this->once()) + ->method('getStoreLabel') + ->willReturn('File Input Field Label'); + + $this->fileProcessorMock->expects($this->once()) + ->method('isExist') + ->with(FileProcessor::TMP_DIR . '/' . $value['tmp_name']) + ->willReturn(true); + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]); + + $this->assertEquals(['"realFileName" is not a valid file.'], $model->validateValue($value)); + } + + public function testValidate() + { + $value = [ + 'tmp_name' => __DIR__ . '/_files/logo.gif', + 'name' => 'logo.gif', + ]; + + $this->attributeMetadataMock->expects($this->once()) + ->method('getStoreLabel') + ->willReturn('File Input Field Label'); + + $this->fileProcessorMock->expects($this->once()) + ->method('isExist') + ->with(FileProcessor::TMP_DIR . '/' . $value['name']) + ->willReturn(true); + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]); + + $this->assertTrue($model->validateValue($value)); } - public function validateValueToUploadDataProvider() + public function testValidateMaxFileSize() { - $imagePath = __DIR__ . '/_files/logo.gif'; - return [ - [ - ['"realFileName" is not a valid file.'], - ['tmp_name' => 'tmp_file', 'name' => 'realFileName'], - ['valid' => false], - ], - [true, ['tmp_name' => $imagePath, 'name' => 'logo.gif']] + $value = [ + 'tmp_name' => __DIR__ . '/_files/logo.gif', + 'name' => 'logo.gif', + 'size' => 2, ]; + + $maxFileSize = 1; + + $validationRuleMock = $this->getMockBuilder('Magento\Customer\Api\Data\ValidationRuleInterface') + ->getMockForAbstractClass(); + $validationRuleMock->expects($this->any()) + ->method('getName') + ->willReturn('max_file_size'); + $validationRuleMock->expects($this->any()) + ->method('getValue') + ->willReturn($maxFileSize); + + $this->attributeMetadataMock->expects($this->once()) + ->method('getStoreLabel') + ->willReturn('File Input Field Label'); + $this->attributeMetadataMock->expects($this->once()) + ->method('getValidationRules') + ->willReturn([$validationRuleMock]); + + $this->fileProcessorMock->expects($this->once()) + ->method('isExist') + ->with(FileProcessor::TMP_DIR . '/' . $value['name']) + ->willReturn(true); + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]); + + $this->assertEquals(['"logo.gif" exceeds the allowed file size.'], $model->validateValue($value)); + } + + public function testValidateMaxImageWidth() + { + $value = [ + 'tmp_name' => __DIR__ . '/_files/logo.gif', + 'name' => 'logo.gif', + ]; + + $maxImageWidth = 1; + + $validationRuleMock = $this->getMockBuilder('Magento\Customer\Api\Data\ValidationRuleInterface') + ->getMockForAbstractClass(); + $validationRuleMock->expects($this->any()) + ->method('getName') + ->willReturn('max_image_width'); + $validationRuleMock->expects($this->any()) + ->method('getValue') + ->willReturn($maxImageWidth); + + $this->attributeMetadataMock->expects($this->once()) + ->method('getStoreLabel') + ->willReturn('File Input Field Label'); + $this->attributeMetadataMock->expects($this->once()) + ->method('getValidationRules') + ->willReturn([$validationRuleMock]); + + $this->fileProcessorMock->expects($this->once()) + ->method('isExist') + ->with(FileProcessor::TMP_DIR . '/' . $value['name']) + ->willReturn(true); + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]); + + $this->assertEquals(['"logo.gif" width exceeds allowed value of 1 px.'], $model->validateValue($value)); + } + + public function testValidateMaxImageHeight() + { + $value = [ + 'tmp_name' => __DIR__ . '/_files/logo.gif', + 'name' => 'logo.gif', + ]; + + $maxImageHeight = 1; + + $validationRuleMock = $this->getMockBuilder('Magento\Customer\Api\Data\ValidationRuleInterface') + ->getMockForAbstractClass(); + $validationRuleMock->expects($this->any()) + ->method('getName') + ->willReturn('max_image_heght'); + $validationRuleMock->expects($this->any()) + ->method('getValue') + ->willReturn($maxImageHeight); + + $this->attributeMetadataMock->expects($this->once()) + ->method('getStoreLabel') + ->willReturn('File Input Field Label'); + $this->attributeMetadataMock->expects($this->once()) + ->method('getValidationRules') + ->willReturn([$validationRuleMock]); + + $this->fileProcessorMock->expects($this->once()) + ->method('isExist') + ->with(FileProcessor::TMP_DIR . '/' . $value['name']) + ->willReturn(true); + + $model = $this->initialize([ + 'value' => $value, + 'isAjax' => false, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]); + + $this->assertEquals(['"logo.gif" height exceeds allowed value of 1 px.'], $model->validateValue($value)); + } + + public function testCompactValueNoChanges() + { + $originValue = 'filename.ext1'; + + $value = [ + 'file' => $originValue, + ]; + + $model = $this->initialize([ + 'value' => $originValue, + 'isAjax' => false, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]); + + $this->assertEquals($originValue, $model->compactValue($value)); + } + + public function testCompactValueUiComponentAddress() + { + $originValue = 'filename.ext1'; + + $value = [ + 'file' => 'filename.ext2', + ]; + + $this->fileProcessorMock->expects($this->once()) + ->method('moveTemporaryFile') + ->with($value['file']) + ->willReturn(true); + + $model = $this->initialize([ + 'value' => $originValue, + 'isAjax' => false, + 'entityTypeCode' => AddressMetadataInterface::ENTITY_TYPE_ADDRESS, + ]); + + $this->assertTrue($model->compactValue($value)); + } + + public function testCompactValueUiComponentCustomer() + { + $originValue = 'filename.ext1'; + + $value = [ + 'file' => 'filename.ext2', + 'name' => 'filename.ext2', + 'type' => 'image', + ]; + + $base64EncodedData = 'encoded_data'; + + $this->fileProcessorMock->expects($this->once()) + ->method('isExist') + ->with(FileProcessor::TMP_DIR . '/' . $value['file']) + ->willReturn(true); + $this->fileProcessorMock->expects($this->once()) + ->method('getBase64EncodedData') + ->with(FileProcessor::TMP_DIR . '/' . $value['file']) + ->willReturn($base64EncodedData); + $this->fileProcessorMock->expects($this->once()) + ->method('removeUploadedFile') + ->with(FileProcessor::TMP_DIR . '/' . $value['file']) + ->willReturnSelf(); + + $imageContentMock = $this->getMockBuilder('Magento\Framework\Api\Data\ImageContentInterface') + ->getMockForAbstractClass(); + $imageContentMock->expects($this->once()) + ->method('setName') + ->with($value['name']) + ->willReturnSelf(); + $imageContentMock->expects($this->once()) + ->method('setBase64EncodedData') + ->with($base64EncodedData) + ->willReturnSelf(); + $imageContentMock->expects($this->once()) + ->method('setType') + ->with($value['type']) + ->willReturnSelf(); + + $this->imageContentFactory->expects($this->once()) + ->method('create') + ->willReturn($imageContentMock); + + $model = $this->initialize([ + 'value' => $originValue, + 'isAjax' => false, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]); + + $this->assertEquals($imageContentMock, $model->compactValue($value)); + } + + public function testCompactValueUiComponentCustomerNotExists() + { + $originValue = 'filename.ext1'; + + $value = [ + 'file' => 'filename.ext2', + 'name' => 'filename.ext2', + 'type' => 'image', + ]; + + $this->fileProcessorMock->expects($this->once()) + ->method('isExist') + ->with(FileProcessor::TMP_DIR . '/' . $value['file']) + ->willReturn(false); + + $model = $this->initialize([ + 'value' => $originValue, + 'isAjax' => false, + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]); + + $this->assertEquals($originValue, $model->compactValue($value)); } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index 2fe13090e544ed226b5aaca3f7e6ba7a3b38d985..d823fb9dfc00ba8f85398a219eb825496c7a7561 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -428,6 +428,20 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase $storeId = 2; $passwordHash = 'ukfa4sdfa56s5df02asdf4rt'; + $customerSecureData = $this->getMock( + 'Magento\Customer\Model\Data\CustomerSecure', + [ + 'getRpToken', + 'getRpTokenCreatedAt', + 'getPasswordHash', + 'getFailuresNum', + 'getFirstFailure', + 'getLockExpires', + ], + [], + '', + false + ); $region = $this->getMockForAbstractClass('Magento\Customer\Api\Data\RegionInterface', [], '', false); $address = $this->getMockForAbstractClass( 'Magento\Customer\Api\Data\AddressInterface', @@ -491,6 +505,42 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase 'setAddresses' ] ); + $customerModel->expects($this->atLeastOnce()) + ->method('setRpToken') + ->with(null); + $customerModel->expects($this->atLeastOnce()) + ->method('setRpTokenCreatedAt') + ->with(null); + $customerModel->expects($this->atLeastOnce()) + ->method('setPasswordHash') + ->with($passwordHash); + $this->customerRegistry->expects($this->once()) + ->method('remove') + ->with($customerId); + + $this->customerRegistry->expects($this->once()) + ->method('retrieveSecureData') + ->with($customerId) + ->willReturn($customerSecureData); + $customerSecureData->expects($this->never()) + ->method('getRpToken') + ->willReturn('rpToken'); + $customerSecureData->expects($this->never()) + ->method('getRpTokenCreatedAt') + ->willReturn('rpTokenCreatedAt'); + $customerSecureData->expects($this->never()) + ->method('getPasswordHash') + ->willReturn('passwordHash'); + $customerSecureData->expects($this->once()) + ->method('getFailuresNum') + ->willReturn('failuresNum'); + $customerSecureData->expects($this->once()) + ->method('getFirstFailure') + ->willReturn('firstFailure'); + $customerSecureData->expects($this->once()) + ->method('getLockExpires') + ->willReturn('lockExpires'); + $this->customer->expects($this->atLeastOnce()) ->method('getId') ->willReturn($customerId); @@ -527,6 +577,10 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase $customerAttributesMetaData->expects($this->at(2)) ->method('setAddresses') ->with([$address]); + $customerAttributesMetaData + ->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn($customerId); $this->extensibleDataObjectConverter->expects($this->once()) ->method('toNestedArray') ->with($customerAttributesMetaData, [], '\Magento\Customer\Api\Data\CustomerInterface') @@ -551,16 +605,13 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase ->with($storeId); $customerModel->expects($this->once()) ->method('setId') - ->with(null); + ->with($customerId); $customerModel->expects($this->once()) ->method('getAttributeSetId') ->willReturn(null); $customerModel->expects($this->once()) ->method('setAttributeSetId') ->with(\Magento\Customer\Api\CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER); - $customerModel->expects($this->once()) - ->method('setPasswordHash') - ->with($passwordHash); $customerModel->expects($this->atLeastOnce()) ->method('getId') ->willReturn($customerId); diff --git a/app/code/Magento/Customer/composer.json b/app/code/Magento/Customer/composer.json index e845476287b7954bfdadbee714f06317c5a23e7c..74f0ebc2b5e0026acbdc5061949c10ad04aa8d9c 100644 --- a/app/code/Magento/Customer/composer.json +++ b/app/code/Magento/Customer/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-customer", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-eav": "100.2.*", "magento/module-directory": "100.2.*", diff --git a/app/code/Magento/Customer/view/frontend/templates/widget/name.phtml b/app/code/Magento/Customer/view/frontend/templates/widget/name.phtml index f46d111b5b0587d36329fb4ff515da437e9e0ecd..f1f1416c2f4e4bedfd54db3fe4afdb5c56c5317d 100644 --- a/app/code/Magento/Customer/view/frontend/templates/widget/name.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/widget/name.phtml @@ -30,7 +30,7 @@ $middle = $block->showMiddlename(); $suffix = $block->showSuffix(); ?> -<?php if ($prefix || $middle || $suffix && !$block->getNoWrap()): ?> +<?php if (($prefix || $middle || $suffix) && !$block->getNoWrap()): ?> <div class="field required fullname <?php /* @escapeNotVerified */ echo $block->getContainerClassName() ?>"> <label for="<?php /* @escapeNotVerified */ echo $block->getFieldId('firstname') ?>" class="label"> <span><?php /* @escapeNotVerified */ echo __('Name') ?></span> @@ -139,7 +139,7 @@ $suffix = $block->showSuffix(); </div> <?php endif; ?> - <?php if ($prefix || $middle || $suffix && !$block->getNoWrap()): ?> + <?php if (($prefix || $middle || $suffix) && !$block->getNoWrap()): ?> </div> </fieldset> </div> diff --git a/app/code/Magento/CustomerImportExport/composer.json b/app/code/Magento/CustomerImportExport/composer.json index 8aee70d0a240be1d0e2498ee5501d40768ea79bc..533f40c7688e4aa8269bedfe84f5331a772b0b2a 100644 --- a/app/code/Magento/CustomerImportExport/composer.json +++ b/app/code/Magento/CustomerImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-customer-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Deploy/composer.json b/app/code/Magento/Deploy/composer.json index 3d69f9864698a8e6ea74c2b3105d2c6a84a15714..05db53f039027e2ca8e62f490005a782e0dfc088 100644 --- a/app/code/Magento/Deploy/composer.json +++ b/app/code/Magento/Deploy/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-deploy", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-store": "100.2.*", "magento/module-theme": "100.2.*", diff --git a/app/code/Magento/Developer/composer.json b/app/code/Magento/Developer/composer.json index be0a0d1c19464f6321a80bd9cea5129c0ed6cf5b..23f6d20c2aab74dc0f23c03f5727aa99379fbf54 100644 --- a/app/code/Magento/Developer/composer.json +++ b/app/code/Magento/Developer/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-developer", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/framework": "100.2.*", "magento/module-config": "100.2.*" diff --git a/app/code/Magento/Dhl/composer.json b/app/code/Magento/Dhl/composer.json index 46ebc809ff8d4dff823815b2a505ff1e4e2e2c21..e88cd535952d628628bbc984137430d5370a6ecd 100644 --- a/app/code/Magento/Dhl/composer.json +++ b/app/code/Magento/Dhl/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-dhl", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-shipping": "100.2.*", diff --git a/app/code/Magento/Directory/composer.json b/app/code/Magento/Directory/composer.json index a59ee693785dab5a39079399db56d8c1bb914112..9b3b253c26a35ed14c4de51c77624a10750ce5f2 100644 --- a/app/code/Magento/Directory/composer.json +++ b/app/code/Magento/Directory/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-directory", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Downloadable/composer.json b/app/code/Magento/Downloadable/composer.json index 413bbed80e5be85d60619061b32bbee27f10c124..8d8814740ccd13248b1e7ab903052ca429567ead 100644 --- a/app/code/Magento/Downloadable/composer.json +++ b/app/code/Magento/Downloadable/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-downloadable", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/DownloadableImportExport/composer.json b/app/code/Magento/DownloadableImportExport/composer.json index 4718b8c13e952f4a85ea8bf477ca40d5155f1f92..c99a0d6d05b822e792e24a27a69f68f240f8b002 100644 --- a/app/code/Magento/DownloadableImportExport/composer.json +++ b/app/code/Magento/DownloadableImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-downloadable-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-import-export": "100.2.*", "magento/module-catalog-import-export": "100.2.*", diff --git a/app/code/Magento/Eav/Setup/EavSetup.php b/app/code/Magento/Eav/Setup/EavSetup.php index a53ed607747492e63b41532fabe907301ad36c4c..9ed8ffcdc93751c4f008276faa99322c29a20167 100644 --- a/app/code/Magento/Eav/Setup/EavSetup.php +++ b/app/code/Magento/Eav/Setup/EavSetup.php @@ -192,6 +192,9 @@ class EavSetup 'additional_attribute_table' => $this->_getValue($params, 'additional_attribute_table'), 'entity_attribute_collection' => $this->_getValue($params, 'entity_attribute_collection'), ]; + if (isset($params['entity_type_id'])) { + $data['entity_type_id'] = $params['entity_type_id']; + } if ($this->getEntityType($code, 'entity_type_id')) { $this->updateEntityType($code, $data); @@ -199,7 +202,11 @@ class EavSetup $this->setup->getConnection()->insert($this->setup->getTable('eav_entity_type'), $data); } - $this->addAttributeSet($code, $this->_defaultAttributeSetName); + if (isset($params['entity_type_id'])) { + $this->addAttributeSet($code, $this->_defaultAttributeSetName, null, $params['entity_type_id']); + } else { + $this->addAttributeSet($code, $this->_defaultAttributeSetName); + } $this->addAttributeGroup($code, $this->_defaultGroupName, $this->_generalGroupName); return $this; @@ -310,9 +317,10 @@ class EavSetup * @param int|string $entityTypeId * @param string $name * @param int $sortOrder + * @param int $setId * @return $this */ - public function addAttributeSet($entityTypeId, $name, $sortOrder = null) + public function addAttributeSet($entityTypeId, $name, $sortOrder = null, $setId = null) { $data = [ 'entity_type_id' => $this->getEntityTypeId($entityTypeId), @@ -320,6 +328,10 @@ class EavSetup 'sort_order' => $this->getAttributeSetSortOrder($entityTypeId, $sortOrder), ]; + if ($setId !== null) { + $data['attribute_set_id'] = $setId; + } + $setId = $this->getAttributeSet($entityTypeId, $name, 'attribute_set_id'); if ($setId) { $this->updateAttributeSet($entityTypeId, $setId, $data); diff --git a/app/code/Magento/Eav/composer.json b/app/code/Magento/Eav/composer.json index 6fb9fb0cb2cf3cc55edc01232c73284c6919a0a6..16e62a6434a26aa358a3491d697ce7cd3bb54ad7 100644 --- a/app/code/Magento/Eav/composer.json +++ b/app/code/Magento/Eav/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-eav", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/Email/composer.json b/app/code/Magento/Email/composer.json index 081b94e4a17d182c87d565ed924c74a7d7022889..1b03ad7705cc66310d47125ba87cc5e0f4986cef 100644 --- a/app/code/Magento/Email/composer.json +++ b/app/code/Magento/Email/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-email", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-theme": "100.2.*", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/EncryptionKey/composer.json b/app/code/Magento/EncryptionKey/composer.json index 5fcb0c936288d98b4393b6411ff0aafc215342bf..6d7ff5558734ab1952b59b2e02db4cc5093f9984 100644 --- a/app/code/Magento/EncryptionKey/composer.json +++ b/app/code/Magento/EncryptionKey/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-encryption-key", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/Fedex/composer.json b/app/code/Magento/Fedex/composer.json index 5934776c81081afb8847b47559db427cef8bfb50..d27d5c1c60d407ce4d1d5b3781b0209c5955a0d4 100644 --- a/app/code/Magento/Fedex/composer.json +++ b/app/code/Magento/Fedex/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-fedex", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-shipping": "100.2.*", "magento/module-directory": "100.2.*", diff --git a/app/code/Magento/GiftMessage/composer.json b/app/code/Magento/GiftMessage/composer.json index ace05fc1d084b4aa5e2d49dc7996464a45e3d404..04c620dc36db5829f9c8c6aa8a6aec620d211e95 100644 --- a/app/code/Magento/GiftMessage/composer.json +++ b/app/code/Magento/GiftMessage/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-gift-message", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-checkout": "100.2.*", diff --git a/app/code/Magento/GoogleAdwords/composer.json b/app/code/Magento/GoogleAdwords/composer.json index 0ec802f83756878704a533ce68680d8eeb58ac96..ad3df5c0b8b2621f395acbfe81bfde3c6b7bae27 100644 --- a/app/code/Magento/GoogleAdwords/composer.json +++ b/app/code/Magento/GoogleAdwords/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-google-adwords", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-sales": "100.2.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/GoogleAnalytics/composer.json b/app/code/Magento/GoogleAnalytics/composer.json index 7fdf4395f3e68e055a29d9020c87c94db584cc69..4cccc7bd0cbff69369ae852138045ed06a0dbf4a 100644 --- a/app/code/Magento/GoogleAnalytics/composer.json +++ b/app/code/Magento/GoogleAnalytics/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-google-analytics", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-sales": "100.2.*", "magento/framework": "100.2.*", diff --git a/app/code/Magento/GoogleOptimizer/composer.json b/app/code/Magento/GoogleOptimizer/composer.json index f269aad3dff69346c8580d00444dc7379ddc370c..20a48fc78060b53fb144aa5da405b343ce696063 100644 --- a/app/code/Magento/GoogleOptimizer/composer.json +++ b/app/code/Magento/GoogleOptimizer/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-google-optimizer", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-google-analytics": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/GroupedImportExport/composer.json b/app/code/Magento/GroupedImportExport/composer.json index 9af72d5e959cd1af514bf39ab44d8771a14322d0..b036a8d5d528bd3959ea66b294d7b51736f7ba2c 100644 --- a/app/code/Magento/GroupedImportExport/composer.json +++ b/app/code/Magento/GroupedImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-grouped-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-import-export": "100.2.*", "magento/module-catalog-import-export": "100.2.*", diff --git a/app/code/Magento/GroupedProduct/composer.json b/app/code/Magento/GroupedProduct/composer.json index 944b45c7d584e6ab584cbf6b093aca089b16054a..69aed214284454722708703068de37e3134393f0 100644 --- a/app/code/Magento/GroupedProduct/composer.json +++ b/app/code/Magento/GroupedProduct/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-grouped-product", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-catalog-inventory": "100.2.*", diff --git a/app/code/Magento/ImportExport/composer.json b/app/code/Magento/ImportExport/composer.json index 642c653dc9750e2417694420cd5979d96c55cf1a..35a088cbd9c91907c3f9f2c434f16cfb86ed57a8 100644 --- a/app/code/Magento/ImportExport/composer.json +++ b/app/code/Magento/ImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/Indexer/composer.json b/app/code/Magento/Indexer/composer.json index ffb0a76b34ffe54195abcfd6f358ab7f33fab91e..cbaab12f6ee285ac1207f8c01d5ed5ff5af42c07 100644 --- a/app/code/Magento/Indexer/composer.json +++ b/app/code/Magento/Indexer/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-indexer", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Integration/composer.json b/app/code/Magento/Integration/composer.json index 17f0f616a915c95453dab499078b3e8d3d3bd140..028e56472268abc0e0fcc612107a5bd8072da806 100644 --- a/app/code/Magento/Integration/composer.json +++ b/app/code/Magento/Integration/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-integration", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/LayeredNavigation/composer.json b/app/code/Magento/LayeredNavigation/composer.json index 46995fbe03d6eb13581281ba0fab08383296d777..99299c3e4a32cccd465af4131bc549b033142545 100644 --- a/app/code/Magento/LayeredNavigation/composer.json +++ b/app/code/Magento/LayeredNavigation/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-layered-navigation", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-catalog": "101.1.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/Marketplace/composer.json b/app/code/Magento/Marketplace/composer.json index 6f2a4cfcb47a9f617d18556a8af34038dc33a8eb..2916ff94544bcc46cd59a9e48e9385e89ea6283b 100644 --- a/app/code/Magento/Marketplace/composer.json +++ b/app/code/Magento/Marketplace/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-marketplace", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-backend": "100.2.*" }, diff --git a/app/code/Magento/MediaStorage/composer.json b/app/code/Magento/MediaStorage/composer.json index ff180cfbc6804bd04399e7a1165df9e489b4e373..fa7858d5b0aff32de7d37f2756aca1891cb15cf7 100644 --- a/app/code/Magento/MediaStorage/composer.json +++ b/app/code/Magento/MediaStorage/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-media-storage", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-config": "100.2.*", diff --git a/app/code/Magento/Msrp/composer.json b/app/code/Magento/Msrp/composer.json index e1edf0d8a8dded668df4ddb863593757d30ccd92..b86374e6be2779d14fd3d2b3e8a4d5b46b1b881f 100644 --- a/app/code/Magento/Msrp/composer.json +++ b/app/code/Magento/Msrp/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-msrp", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-downloadable": "100.2.*", diff --git a/app/code/Magento/Multishipping/composer.json b/app/code/Magento/Multishipping/composer.json index b97ebd181de740c13edbfbee7d6a0daebd8bdc95..8d9ac91cd05a07b78c19588cbcd34ce3a110cc9f 100644 --- a/app/code/Magento/Multishipping/composer.json +++ b/app/code/Magento/Multishipping/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-multishipping", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-checkout": "100.2.*", "magento/module-sales": "100.2.*", diff --git a/app/code/Magento/NewRelicReporting/composer.json b/app/code/Magento/NewRelicReporting/composer.json index 9673353984d0a53ec43ddbe1ac57219e959aed8f..9faa17d454e5072f7dc9ec5473057bfad1533dce 100644 --- a/app/code/Magento/NewRelicReporting/composer.json +++ b/app/code/Magento/NewRelicReporting/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-new-relic-reporting", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Newsletter/composer.json b/app/code/Magento/Newsletter/composer.json index a206b86fc37aef9cbe49531a24ff4362eef87899..d6746f48dcbe54b44a15723b9a144f9cf77447c7 100644 --- a/app/code/Magento/Newsletter/composer.json +++ b/app/code/Magento/Newsletter/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-newsletter", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", "magento/module-widget": "100.2.*", diff --git a/app/code/Magento/OfflinePayments/composer.json b/app/code/Magento/OfflinePayments/composer.json index 707e657004e3d3a79cd82f109394ca74b4766cca..52deb08ba3d904a824faa762391ddf67ae29b026 100644 --- a/app/code/Magento/OfflinePayments/composer.json +++ b/app/code/Magento/OfflinePayments/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-offline-payments", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-checkout": "100.2.*", "magento/module-payment": "100.2.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/OfflineShipping/composer.json b/app/code/Magento/OfflineShipping/composer.json index 69d3f7b1eb0534b622c9f2bc3becfee9ba564044..e9a759057d322074de433b3460162c3a883f3ebf 100644 --- a/app/code/Magento/OfflineShipping/composer.json +++ b/app/code/Magento/OfflineShipping/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-offline-shipping", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/PageCache/composer.json b/app/code/Magento/PageCache/composer.json index 6eaf0c9731202678d387040594c017e41aa1b425..5c29e4fc3c97081626ae4a77243ccd6ac9843d38 100644 --- a/app/code/Magento/PageCache/composer.json +++ b/app/code/Magento/PageCache/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-page-cache", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Payment/Model/MethodList.php b/app/code/Magento/Payment/Model/MethodList.php index df0ddb9ed4b0c5490db8813f028998f2d69e6a4f..d547fe42f332ca4c49f0ff51ac16c0b59e538556 100644 --- a/app/code/Magento/Payment/Model/MethodList.php +++ b/app/code/Magento/Payment/Model/MethodList.php @@ -61,6 +61,7 @@ class MethodList { return $this->methodSpecificationFactory->create( [ + AbstractMethod::CHECK_USE_CHECKOUT, AbstractMethod::CHECK_USE_FOR_COUNTRY, AbstractMethod::CHECK_USE_FOR_CURRENCY, AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX, diff --git a/app/code/Magento/Payment/Test/Unit/Model/MethodListTest.php b/app/code/Magento/Payment/Test/Unit/Model/MethodListTest.php index b1e5bedb5a250e4daf48d8107eb421c305397d69..241f3fc4fcf532ff283d0a2afcdee833beb6e211 100644 --- a/app/code/Magento/Payment/Test/Unit/Model/MethodListTest.php +++ b/app/code/Magento/Payment/Test/Unit/Model/MethodListTest.php @@ -8,8 +8,8 @@ namespace Magento\Payment\Test\Unit\Model; -use Magento\Payment\Model\Method\Free; -use \Magento\Payment\Model\MethodList; +use Magento\Payment\Model\MethodList; +use Magento\Payment\Model\Method\AbstractMethod; class MethodListTest extends \PHPUnit_Framework_TestCase { @@ -68,6 +68,12 @@ class MethodListTest extends \PHPUnit_Framework_TestCase $this->specificationFactoryMock->expects($this->atLeastOnce()) ->method('create') + ->with([ + AbstractMethod::CHECK_USE_CHECKOUT, + AbstractMethod::CHECK_USE_FOR_COUNTRY, + AbstractMethod::CHECK_USE_FOR_CURRENCY, + AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX + ]) ->will($this->returnValue($compositeMock)); $storeMethods = [$methodMock]; diff --git a/app/code/Magento/Payment/composer.json b/app/code/Magento/Payment/composer.json index a156849e243131bc4c93c58d6fdefac8b85215c5..35ff326d916bd43f8ae2366d9b50f5b77fe46dc8 100644 --- a/app/code/Magento/Payment/composer.json +++ b/app/code/Magento/Payment/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-payment", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-sales": "100.2.*", diff --git a/app/code/Magento/Paypal/Setup/InstallSchema.php b/app/code/Magento/Paypal/Setup/InstallSchema.php index d55782be8627915e7458874c5e6cc9fddd60224c..d0a07242ad75e03b334d7d23f289e7e9847546bd 100644 --- a/app/code/Magento/Paypal/Setup/InstallSchema.php +++ b/app/code/Magento/Paypal/Setup/InstallSchema.php @@ -165,7 +165,7 @@ class InstallSchema implements InstallSchemaInterface 'Report Id' )->addColumn( 'report_date', - \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP, + \Magento\Framework\DB\Ddl\Table::TYPE_DATE, null, [], 'Report Date' diff --git a/app/code/Magento/Paypal/composer.json b/app/code/Magento/Paypal/composer.json index b0bdb0d62f03ec8e10d68fd0e1379815bfc5db64..bddc5d91a94587f4d26532246d8c83f8de6ba7f7 100644 --- a/app/code/Magento/Paypal/composer.json +++ b/app/code/Magento/Paypal/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-paypal", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-checkout": "100.2.*", diff --git a/app/code/Magento/Paypal/etc/adminhtml/rules/payment_ca.xml b/app/code/Magento/Paypal/etc/adminhtml/rules/payment_ca.xml index fb2b7456fb7c591431fd89af3b8973110d4c8eca..fc0ca6ebb6e9da5170061678910a1ac620d7fc03 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/rules/payment_ca.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/rules/payment_ca.xml @@ -143,9 +143,13 @@ <rule type="paypalExpressDisable" event="activate-rule"/> <rule type="paypalExpressLockConfiguration" event="activate-rule"/> <rule type="paypalExpressMarkDisable" event="deactivate-rule"> + <argument name="wpp_ca">wpp_ca</argument> + <argument name="paypal_payflowpro_ca">paypal_payflowpro_ca</argument> <argument name="payflow_link_ca">payflow_link_ca</argument> </rule> <rule type="paypalExpressUnlockConfiguration" event="deactivate-rule"> + <argument name="wpp_ca">wpp_ca</argument> + <argument name="paypal_payflowpro_ca">paypal_payflowpro_ca</argument> <argument name="payflow_link_ca">payflow_link_ca</argument> </rule> </relation> diff --git a/app/code/Magento/Paypal/etc/adminhtml/rules/payment_us.xml b/app/code/Magento/Paypal/etc/adminhtml/rules/payment_us.xml index dfbb68283daa971c0db56b80252816bdc3b919c3..554b459d3c45a0db6cfe4c11bba36528ae4361d5 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/rules/payment_us.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/rules/payment_us.xml @@ -351,6 +351,7 @@ <rule type="payflowExpressEnable" event="activate-rule"/> <rule type="payflowBmlEnable" event="activate-rule"/> <rule type="payflowShowSortOrder" event="activate-rule"/> + <rule type="payflowShowSortOrder" event="activate-express"/> <rule type="simpleDisable" event="deactivate-rule"/> <rule type="payflowExpressEnableConditional" event="deactivate-rule"> <argument name="payflow_advanced">payflow_advanced</argument> @@ -363,6 +364,7 @@ <argument name="paypal_payflowpro_with_express_checkout">paypal_payflowpro_with_express_checkout</argument> </rule> <rule type="payflowHideSortOrder" event="deactivate-rule"/> + <rule type="payflowHideSortOrder" event="deactivate-express"/> <rule type="payflowBmlDisableConditionalExpress" event="deactivate-express"/> <rule type="payflowBmlDisableConditionalExpress" event=":load"/> <rule type="payflowBmlEnable" event="activate-express"/> diff --git a/app/code/Magento/Persistent/composer.json b/app/code/Magento/Persistent/composer.json index d165344851ab24e87b210be8bd6ca43b1a536d3f..25cc9d76acd768fe970363628abad60945e418d9 100644 --- a/app/code/Magento/Persistent/composer.json +++ b/app/code/Magento/Persistent/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-persistent", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-checkout": "100.2.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/ProductAlert/composer.json b/app/code/Magento/ProductAlert/composer.json index 6e143b5fb7e5948441e6d65b9a64d9f36bfd499a..a43a52209740318e0f5adcc8bea03a8860c04037 100644 --- a/app/code/Magento/ProductAlert/composer.json +++ b/app/code/Magento/ProductAlert/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-product-alert", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/ProductVideo/composer.json b/app/code/Magento/ProductVideo/composer.json index 3ba8aef446d0caeea0775e0443723e24b374b8c0..362fd006d2690c9f9b60b7be3abb71d5ce0b4da7 100644 --- a/app/code/Magento/ProductVideo/composer.json +++ b/app/code/Magento/ProductVideo/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-product-video", "description": "Add Video to Products", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-backend": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php index 2ff39f99e412e4d9d84d921da0111e5a04b476bb..cd8c6dabb532f3a4631b8eff5700b8096725df0a 100644 --- a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php +++ b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php @@ -9,6 +9,16 @@ use Magento\Framework\Event\ObserverInterface; class CollectTotalsObserver implements ObserverInterface { + /** + * @var \Magento\Customer\Api\AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var \Magento\Customer\Model\Session + */ + private $customerSession; + /** * @var \Magento\Customer\Helper\Address */ @@ -50,13 +60,17 @@ class CollectTotalsObserver implements ObserverInterface \Magento\Customer\Model\Vat $customerVat, VatValidator $vatValidator, \Magento\Customer\Api\Data\CustomerInterfaceFactory $customerDataFactory, - \Magento\Customer\Api\GroupManagementInterface $groupManagement + \Magento\Customer\Api\GroupManagementInterface $groupManagement, + \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, + \Magento\Customer\Model\Session $customerSession ) { $this->customerVat = $customerVat; $this->customerAddressHelper = $customerAddressHelper; $this->vatValidator = $vatValidator; $this->customerDataFactory = $customerDataFactory; $this->groupManagement = $groupManagement; + $this->addressRepository = $addressRepository; + $this->customerSession = $customerSession; } /** @@ -84,6 +98,15 @@ class CollectTotalsObserver implements ObserverInterface } $customerCountryCode = $address->getCountryId(); $customerVatNumber = $address->getVatId(); + + /** try to get data from customer if quote address needed data is empty */ + if (empty($customerCountryCode) && empty($customerVatNumber) && $customer->getDefaultShipping()) { + $customerAddress = $this->addressRepository->getById($customer->getDefaultShipping()); + + $customerCountryCode = $customerAddress->getCountryId(); + $customerVatNumber = $customerAddress->getVatId(); + } + $groupId = null; if (empty($customerVatNumber) || false == $this->customerVat->isCountryInEU($customerCountryCode)) { $groupId = $customer->getId() ? $this->groupManagement->getDefaultGroup( @@ -101,6 +124,7 @@ class CollectTotalsObserver implements ObserverInterface if ($groupId) { $address->setPrevQuoteCustomerGroupId($quote->getCustomerGroupId()); $quote->setCustomerGroupId($groupId); + $this->customerSession->setCustomerGroupId($groupId); $customer->setGroupId($groupId); $quote->setCustomer($customer); } diff --git a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php index fedcd43bfdd50304651736f3e4e3de244c2d9cbd..1233535f20e107a1436d13a1094203229254e026 100644 --- a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php +++ b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php @@ -14,7 +14,7 @@ namespace Magento\Quote\Test\Unit\Observer\Frontend\Quote\Address; class CollectTotalsObserverTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver; + * @var \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver */ protected $model; @@ -23,11 +23,21 @@ class CollectTotalsObserverTest extends \PHPUnit_Framework_TestCase */ protected $customerAddressMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $customerSession; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ protected $customerVatMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $addressRepository; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -172,6 +182,11 @@ class CollectTotalsObserverTest extends \PHPUnit_Framework_TestCase ->method('getCustomer') ->will($this->returnValue($this->customerMock)); + $this->addressRepository = $this->getMock(\Magento\Customer\Api\AddressRepositoryInterface::class); + $this->customerSession = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerMock->expects($this->any())->method('getStoreId')->will($this->returnValue($this->storeId)); $this->model = new \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver( @@ -179,7 +194,9 @@ class CollectTotalsObserverTest extends \PHPUnit_Framework_TestCase $this->customerVatMock, $this->vatValidatorMock, $this->customerDataFactoryMock, - $this->groupManagementMock + $this->groupManagementMock, + $this->addressRepository, + $this->customerSession ); } @@ -319,4 +336,75 @@ class CollectTotalsObserverTest extends \PHPUnit_Framework_TestCase ->willReturn($this->customerMock); $this->model->execute($this->observerMock); } + + public function testDispatchWithEmptyShippingAddress() + { + $customerCountryCode = "DE"; + $customerVat = "123123123"; + $defaultShipping = 1; + + $customerAddress = $this->getMock(\Magento\Customer\Api\Data\AddressInterface::class); + $customerAddress->expects($this->once()) + ->method("getCountryId") + ->willReturn($customerCountryCode); + + $customerAddress->expects($this->once()) + ->method("getVatId") + ->willReturn($customerVat); + $this->addressRepository->expects($this->once()) + ->method("getById") + ->with($defaultShipping) + ->willReturn($customerAddress); + + $this->customerMock->expects($this->atLeastOnce()) + ->method("getDefaultShipping") + ->willReturn($defaultShipping); + + $this->vatValidatorMock->expects($this->once()) + ->method('isEnabled') + ->with($this->quoteAddressMock, $this->storeId) + ->will($this->returnValue(true)); + + $this->quoteAddressMock->expects($this->once()) + ->method('getCountryId') + ->will($this->returnValue(null)); + $this->quoteAddressMock->expects($this->once()) + ->method('getVatId') + ->will($this->returnValue(null)); + + $this->customerVatMock->expects($this->once()) + ->method('isCountryInEU') + ->with($customerCountryCode) + ->willReturn(true); + + $this->quoteMock->expects($this->once()) + ->method('getCustomerGroupId') + ->will($this->returnValue('customerGroupId')); + $validationResult = ['some' => 'result']; + $this->customerVatMock->expects($this->once()) + ->method('getCustomerGroupIdBasedOnVatNumber') + ->with($customerCountryCode, $validationResult, $this->storeId) + ->will($this->returnValue('customerGroupId')); + $this->customerSession->expects($this->once()) + ->method("setCustomerGroupId") + ->with('customerGroupId'); + + $this->vatValidatorMock->expects($this->once()) + ->method('validate') + ->with($this->quoteAddressMock, $this->storeId) + ->will($this->returnValue($validationResult)); + + /** Assertions */ + $this->quoteAddressMock->expects($this->once()) + ->method('setPrevQuoteCustomerGroupId') + ->with('customerGroupId'); + + $this->quoteMock->expects($this->once())->method('setCustomerGroupId')->with('customerGroupId'); + $this->quoteMock->expects($this->once())->method('setCustomer')->with($this->customerMock); + $this->customerDataFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->customerMock); + $this->model->execute($this->observerMock); + + } } diff --git a/app/code/Magento/Quote/composer.json b/app/code/Magento/Quote/composer.json index bab6a636b5b5bc0d727fb8f540548ed818890e4e..3d6fb04674bba5da6713a55dbc7ac448bf0c89e9 100644 --- a/app/code/Magento/Quote/composer.json +++ b/app/code/Magento/Quote/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-quote", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Reports/composer.json b/app/code/Magento/Reports/composer.json index f6687eba4e68bc30b3618d7be0945f57f1eec734..0bb989cb54ac8cb84e3da943a231634b6707f256 100644 --- a/app/code/Magento/Reports/composer.json +++ b/app/code/Magento/Reports/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-reports", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/RequireJs/composer.json b/app/code/Magento/RequireJs/composer.json index 7948aeecebb057cc47052a9915a8ce298dbca812..8c9fed33ab730dd2431b222b2edd04aa2252e12f 100644 --- a/app/code/Magento/RequireJs/composer.json +++ b/app/code/Magento/RequireJs/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-require-js", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-module", diff --git a/app/code/Magento/Review/composer.json b/app/code/Magento/Review/composer.json index 29eb5cf1e568f7bfe07614d0d029d47c44446af7..09f5529feabdd97512839be2dacd00f791083c3c 100644 --- a/app/code/Magento/Review/composer.json +++ b/app/code/Magento/Review/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-review", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Rss/composer.json b/app/code/Magento/Rss/composer.json index 29731136602bd74973753787e15546580595c072..d35f0ee6ebecbf75af87566d776f172ab3d22c0d 100644 --- a/app/code/Magento/Rss/composer.json +++ b/app/code/Magento/Rss/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-rss", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*", diff --git a/app/code/Magento/Rule/composer.json b/app/code/Magento/Rule/composer.json index a4bc7c0d3c7adc9bdac4daa4489eb8c289a2aaff..95b8e6ffec2a682895b3459ca7bdcc7b4f001669 100644 --- a/app/code/Magento/Rule/composer.json +++ b/app/code/Magento/Rule/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-rule", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-eav": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/Sales/Block/Order/History.php b/app/code/Magento/Sales/Block/Order/History.php index 683234666262a7d2cc86b70a8d2c64d4b824b48a..9b212d40bfa6af7937f14aa31746b84a7ab507db 100644 --- a/app/code/Magento/Sales/Block/Order/History.php +++ b/app/code/Magento/Sales/Block/Order/History.php @@ -5,6 +5,9 @@ */ namespace Magento\Sales\Block\Order; +use \Magento\Framework\App\ObjectManager; +use \Magento\Sales\Model\ResourceModel\Order\CollectionFactoryInterface; + /** * Sales order history block */ @@ -33,6 +36,11 @@ class History extends \Magento\Framework\View\Element\Template /** @var \Magento\Sales\Model\ResourceModel\Order\Collection */ protected $orders; + /** + * @var CollectionFactoryInterface + */ + private $orderCollectionFactory; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Sales\Model\ResourceModel\Order\CollectionFactory $orderCollectionFactory @@ -62,6 +70,19 @@ class History extends \Magento\Framework\View\Element\Template $this->pageConfig->getTitle()->set(__('My Orders')); } + /** + * @return CollectionFactoryInterface + * + * @deprecated + */ + private function getOrderCollectionFactory() + { + if ($this->orderCollectionFactory === null) { + $this->orderCollectionFactory = ObjectManager::getInstance()->get(CollectionFactoryInterface::class); + } + return $this->orderCollectionFactory; + } + /** * @return bool|\Magento\Sales\Model\ResourceModel\Order\Collection */ @@ -71,11 +92,8 @@ class History extends \Magento\Framework\View\Element\Template return false; } if (!$this->orders) { - $this->orders = $this->_orderCollectionFactory->create()->addFieldToSelect( + $this->orders = $this->getOrderCollectionFactory()->create($customerId)->addFieldToSelect( '*' - )->addFieldToFilter( - 'customer_id', - $customerId )->addFieldToFilter( 'status', ['in' => $this->_orderConfig->getVisibleOnFrontStatuses()] diff --git a/app/code/Magento/Sales/Block/Order/History/Container.php b/app/code/Magento/Sales/Block/Order/History/Container.php new file mode 100644 index 0000000000000000000000000000000000000000..cc20f20ea84c085d62588bca6dfd265e8de224c8 --- /dev/null +++ b/app/code/Magento/Sales/Block/Order/History/Container.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Block\Order\History; + +/** + * Sales order history extra container block + */ +class Container extends \Magento\Framework\View\Element\Template +{ + /** + * @var \Magento\Sales\Api\Data\OrderInterface + */ + private $order; + + /** + * Set order + * + * @param \Magento\Sales\Api\Data\OrderInterface $order + * @return $this + */ + public function setOrder(\Magento\Sales\Api\Data\OrderInterface $order) + { + $this->order = $order; + return $this; + } + + /** + * Get order + * + * @return \Magento\Sales\Api\Data\OrderInterface + */ + private function getOrder() + { + return $this->order; + } + + /** + * Here we set an order for children during retrieving their HTML + * + * @param string $alias + * @param bool $useCache + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getChildHtml($alias = '', $useCache = false) + { + $layout = $this->getLayout(); + if ($layout) { + $name = $this->getNameInLayout(); + foreach ($layout->getChildBlocks($name) as $child) { + $child->setOrder($this->getOrder()); + } + } + return parent::getChildHtml($alias, $useCache); + } +} diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/CollectionFactory.php b/app/code/Magento/Sales/Model/ResourceModel/Order/CollectionFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c2b9886e3fa52f54c0ba5f6b1456d5d493631313 --- /dev/null +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/CollectionFactory.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\Model\ResourceModel\Order; + +/** + * Class CollectionFactory + */ +class CollectionFactory implements CollectionFactoryInterface +{ + /** + * Object Manager instance + * + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager = null; + + /** + * Instance name to create + * + * @var string + */ + private $instanceName = null; + + /** + * Factory constructor + * + * @param \Magento\Framework\ObjectManagerInterface $objectManager + * @param string $instanceName + */ + public function __construct( + \Magento\Framework\ObjectManagerInterface $objectManager, + $instanceName = '\\Magento\\Sales\\Model\\ResourceModel\\Order\\Collection' + ) { + $this->objectManager = $objectManager; + $this->instanceName = $instanceName; + } + + /** + * {@inheritdoc} + */ + public function create($customerId = null) + { + /** @var \Magento\Sales\Model\ResourceModel\Order\Collection $collection */ + $collection = $this->objectManager->create($this->instanceName); + + if ($customerId) { + $collection->addFieldToFilter('customer_id', $customerId); + } + + return $collection; + } +} diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/CollectionFactoryInterface.php b/app/code/Magento/Sales/Model/ResourceModel/Order/CollectionFactoryInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..27a9a640906b214705833c9f53ef25b157f89802 --- /dev/null +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/CollectionFactoryInterface.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\Model\ResourceModel\Order; + +/** + * Class CollectionFactoryInterface + */ +interface CollectionFactoryInterface +{ + /** + * Create class instance with specified parameters + * + * @param int $customerId + * @return \Magento\Sales\Model\ResourceModel\Order\Collection + */ + public function create($customerId = null); +} diff --git a/app/code/Magento/Sales/Setup/SalesSetup.php b/app/code/Magento/Sales/Setup/SalesSetup.php index d69147ba925f02f169c74931194578c05f723faa..3c72ce093d87c5b75a5ce60f55fc83aa7b086dae 100644 --- a/app/code/Magento/Sales/Setup/SalesSetup.php +++ b/app/code/Magento/Sales/Setup/SalesSetup.php @@ -18,6 +18,26 @@ use Magento\Framework\Setup\ModuleDataSetupInterface; */ class SalesSetup extends \Magento\Eav\Setup\EavSetup { + /** + * This should be set explicitly + */ + const ORDER_ENTITY_TYPE_ID = 5; + + /** + * This should be set explicitly + */ + const INVOICE_PRODUCT_ENTITY_TYPE_ID = 6; + + /** + * This should be set explicitly + */ + const CREDITMEMO_PRODUCT_ENTITY_TYPE_ID = 7; + + /** + * This should be set explicitly + */ + const SHIPMENT_PRODUCT_ENTITY_TYPE_ID = 8; + /** * @var ScopeConfigInterface */ @@ -214,6 +234,7 @@ class SalesSetup extends \Magento\Eav\Setup\EavSetup { $entities = [ 'order' => [ + 'entity_type_id' => self::ORDER_ENTITY_TYPE_ID, 'entity_model' => 'Magento\Sales\Model\ResourceModel\Order', 'table' => 'sales_order', 'increment_model' => 'Magento\Eav\Model\Entity\Increment\NumericValue', @@ -221,6 +242,7 @@ class SalesSetup extends \Magento\Eav\Setup\EavSetup 'attributes' => [], ], 'invoice' => [ + 'entity_type_id' => self::INVOICE_PRODUCT_ENTITY_TYPE_ID, 'entity_model' => 'Magento\Sales\Model\ResourceModel\Order\Invoice', 'table' => 'sales_invoice', 'increment_model' => 'Magento\Eav\Model\Entity\Increment\NumericValue', @@ -228,6 +250,7 @@ class SalesSetup extends \Magento\Eav\Setup\EavSetup 'attributes' => [], ], 'creditmemo' => [ + 'entity_type_id' => self::CREDITMEMO_PRODUCT_ENTITY_TYPE_ID, 'entity_model' => 'Magento\Sales\Model\ResourceModel\Order\Creditmemo', 'table' => 'sales_creditmemo', 'increment_model' => 'Magento\Eav\Model\Entity\Increment\NumericValue', @@ -235,6 +258,7 @@ class SalesSetup extends \Magento\Eav\Setup\EavSetup 'attributes' => [], ], 'shipment' => [ + 'entity_type_id' => self::SHIPMENT_PRODUCT_ENTITY_TYPE_ID, 'entity_model' => 'Magento\Sales\Model\ResourceModel\Order\Shipment', 'table' => 'sales_shipment', 'increment_model' => 'Magento\Eav\Model\Entity\Increment\NumericValue', diff --git a/app/code/Magento/Sales/Test/Unit/Block/Order/HistoryTest.php b/app/code/Magento/Sales/Test/Unit/Block/Order/HistoryTest.php index 9e8f325857b6bb551f996de278169272c3c01e39..a4709ed7ccc98695e3a4701b7067646bb4b957e6 100644 --- a/app/code/Magento/Sales/Test/Unit/Block/Order/HistoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Block/Order/HistoryTest.php @@ -22,6 +22,16 @@ class HistoryTest extends \PHPUnit_Framework_TestCase */ protected $orderCollectionFactory; + /** + * @var \Magento\Sales\Model\ResourceModel\Order\CollectionFactoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $orderCollectionFactoryInterface; + + /** + * @var \Magento\Framework\App\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + */ + private $objectManager; + /** * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject */ @@ -48,6 +58,14 @@ class HistoryTest extends \PHPUnit_Framework_TestCase $this->orderCollectionFactory = $this->getMockBuilder('Magento\Sales\Model\ResourceModel\Order\CollectionFactory') ->disableOriginalConstructor()->setMethods(['create'])->getMock(); + $this->orderCollectionFactoryInterface = + $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\CollectionFactoryInterface::class) + ->disableOriginalConstructor()->setMethods(['create'])->getMock(); + $this->objectManager = $this->getMock(\Magento\Framework\ObjectManagerInterface::class, [], [], '', false); + $this->objectManager->expects($this->any()) + ->method('get') + ->will($this->returnValue($this->orderCollectionFactoryInterface)); + \Magento\Framework\App\ObjectManager::setInstance($this->objectManager); $this->customerSession = $this->getMockBuilder('Magento\Customer\Model\Session') ->setMethods(['getCustomerId'])->disableOriginalConstructor()->getMock(); @@ -94,18 +112,14 @@ class HistoryTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo('*')) ->will($this->returnSelf()); $orderCollection->expects($this->at(1)) - ->method('addFieldToFilter') - ->with('customer_id', $this->equalTo($customerId)) - ->will($this->returnSelf()); - $orderCollection->expects($this->at(2)) ->method('addFieldToFilter') ->with('status', $this->equalTo(['in' => $statuses])) ->will($this->returnSelf()); - $orderCollection->expects($this->at(3)) + $orderCollection->expects($this->at(2)) ->method('setOrder') ->with('created_at', 'desc') ->will($this->returnSelf()); - $this->orderCollectionFactory->expects($this->atLeastOnce()) + $this->orderCollectionFactoryInterface->expects($this->atLeastOnce()) ->method('create') ->will($this->returnValue($orderCollection)); $this->pageConfig->expects($this->atLeastOnce()) diff --git a/app/code/Magento/Sales/composer.json b/app/code/Magento/Sales/composer.json index d6e19280565f87d5a1d03db4b3b2081a48f4b9a5..09d1a9d6b7dd41ea95a09b26c0f72276327fe245 100644 --- a/app/code/Magento/Sales/composer.json +++ b/app/code/Magento/Sales/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sales", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 209e46c3240b675f32622f3547331856bf87a1eb..78dd17256703ad32b852262899a3b3a6b97545b6 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -80,6 +80,7 @@ <preference for="Magento\Sales\Model\Spi\ShipmentResourceInterface" type="Magento\Sales\Model\ResourceModel\Order\Shipment"/> <preference for="Magento\Sales\Model\Spi\ShipmentTrackResourceInterface" type="Magento\Sales\Model\ResourceModel\Order\Shipment\Track"/> <preference for="Magento\Sales\Model\Spi\TransactionResourceInterface" type="Magento\Sales\Model\ResourceModel\Order\Payment\Transaction"/> + <preference for="Magento\Sales\Model\ResourceModel\Order\CollectionFactoryInterface" type="Magento\Sales\Model\ResourceModel\Order\CollectionFactory"/> <type name="Magento\Sales\Model\ResourceModel\Report" shared="false"/> <type name="Magento\Sales\Model\Order\Pdf\Config\Reader"> <arguments> diff --git a/app/code/Magento/Sales/view/frontend/layout/sales_order_history.xml b/app/code/Magento/Sales/view/frontend/layout/sales_order_history.xml index 85b8a83a198f54facd9d931a684881056e12625c..c4c93b9655aa148ed31fdac6aa94c352faf13e9e 100644 --- a/app/code/Magento/Sales/view/frontend/layout/sales_order_history.xml +++ b/app/code/Magento/Sales/view/frontend/layout/sales_order_history.xml @@ -11,6 +11,12 @@ <referenceContainer name="content"> <block class="Magento\Sales\Block\Order\History" name="sales.order.history" cacheable="false"> <container name="sales.order.history.info" as="info" label="Order History Info"/> + <container name="sales.order.history.extra.column.header" as="extra.column.header" label="Order History Extra Column Header"/> + <block class="Magento\Sales\Block\Order\History\Container" + name="sales.order.history.extra.container" as="extra.container"> + <block class="Magento\Framework\View\Element\Template" + name="sales.order.history.extra.container.data" as="extra.container.data"/> + </block> </block> <block class="Magento\Customer\Block\Account\Dashboard" name="customer.account.link.back" template="account/link/back.phtml" cacheable="false"/> </referenceContainer> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml index b3723d56ac28dc7b85c9fa2fe1a8aa978b780b9a..a57c68d17258fddb1bd0a4d3f2b6da3758ae690b 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml @@ -17,6 +17,7 @@ <tr> <th scope="col" class="col id"><?php /* @escapeNotVerified */ echo __('Order #') ?></th> <th scope="col" class="col date"><?php /* @escapeNotVerified */ echo __('Date') ?></th> + <?php /* @noEscape */ echo $block->getChildHtml('extra.column.header');?> <th scope="col" class="col shipping"><?php /* @escapeNotVerified */ echo __('Ship To') ?></th> <th scope="col" class="col total"><?php /* @escapeNotVerified */ echo __('Order Total') ?></th> <th scope="col" class="col status"><?php /* @escapeNotVerified */ echo __('Status') ?></th> @@ -28,6 +29,11 @@ <tr> <td data-th="<?php echo $block->escapeHtml(__('Order #')) ?>" class="col id"><?php /* @escapeNotVerified */ echo $_order->getRealOrderId() ?></td> <td data-th="<?php echo $block->escapeHtml(__('Date')) ?>" class="col date"><?php /* @escapeNotVerified */ echo $block->formatDate($_order->getCreatedAt()) ?></td> + <?php $extra = $block->getChildBlock('extra.container'); ?> + <?php if ($extra): ?> + <?php $extra->setOrder($_order); ?> + <?php /* @noEscape */ echo $extra->getChildHtml() ?> + <?php endif; ?> <td data-th="<?php echo $block->escapeHtml(__('Ship To')) ?>" class="col shipping"><?php echo $_order->getShippingAddress() ? $block->escapeHtml($_order->getShippingAddress()->getName()) : ' ' ?></td> <td data-th="<?php echo $block->escapeHtml(__('Order Total')) ?>" class="col total"><?php /* @escapeNotVerified */ echo $_order->formatPrice($_order->getGrandTotal()) ?></td> <td data-th="<?php echo $block->escapeHtml(__('Status')) ?>" class="col status"><?php /* @escapeNotVerified */ echo $_order->getStatusLabel() ?></td> diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php index 62fcb8e4b3b6243d007e4d7c9dcbdb8b0985af63..3366ba4cd1f6e87903445e5f0c5e2a8954539a58 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php @@ -61,7 +61,6 @@ class Address extends \Magento\Rule\Model\Condition\AbstractCondition 'base_subtotal' => __('Subtotal'), 'total_qty' => __('Total Items Quantity'), 'weight' => __('Total Weight'), - 'payment_method' => __('Payment Method'), 'shipping_method' => __('Shipping Method'), 'postcode' => __('Shipping Postcode'), 'region' => __('Shipping Region'), diff --git a/app/code/Magento/SalesRule/composer.json b/app/code/Magento/SalesRule/composer.json index 80a0802f2f557199c7f5ce2116cc72bccfb03407..fabef581762a84cac8844bf7e7099b0bb8b6b6c0 100644 --- a/app/code/Magento/SalesRule/composer.json +++ b/app/code/Magento/SalesRule/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sales-rule", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-rule": "100.2.*", diff --git a/app/code/Magento/SalesSequence/composer.json b/app/code/Magento/SalesSequence/composer.json index 5689a019806d23984af8e95a8caa4ce46da61506..eeda2be05e68f86cb5552e6f48ca502dd96cb9be 100644 --- a/app/code/Magento/SalesSequence/composer.json +++ b/app/code/Magento/SalesSequence/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sales-sequence", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-module", diff --git a/app/code/Magento/SampleData/composer.json b/app/code/Magento/SampleData/composer.json index 3fda6ff798d6964e9b2bba22e0da2a13f9b5fc89..936bbf0d6be3a7399babc2e835282e7c71bea4c2 100644 --- a/app/code/Magento/SampleData/composer.json +++ b/app/code/Magento/SampleData/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sample-data", "description": "Sample Data fixtures", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "suggest": { diff --git a/app/code/Magento/Search/composer.json b/app/code/Magento/Search/composer.json index a4c1fa890239086b6f2275a0cb9da8b1c03e0538..cc95e6784526f33bb65246b599a4147e8a1afa1d 100644 --- a/app/code/Magento/Search/composer.json +++ b/app/code/Magento/Search/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-search", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-catalog-search": "100.2.*", diff --git a/app/code/Magento/Security/composer.json b/app/code/Magento/Security/composer.json index fd2feb9e50f2a5d1e44852c39d60850980bcba01..f64c0f0b29d282b0feaefb24ebb3963086f0e083 100644 --- a/app/code/Magento/Security/composer.json +++ b/app/code/Magento/Security/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-security", "description": "Security management module", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-store": "100.2.*", "magento/framework": "100.2.*" diff --git a/app/code/Magento/SendFriend/composer.json b/app/code/Magento/SendFriend/composer.json index a18523b95b4560451b5a3f8d93817e73dc6d1190..de9dbf7a84576022d8f6b1a38324f902565870df 100644 --- a/app/code/Magento/SendFriend/composer.json +++ b/app/code/Magento/SendFriend/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-send-friend", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-customer": "100.2.*", diff --git a/app/code/Magento/Shipping/composer.json b/app/code/Magento/Shipping/composer.json index de12c13c7cab0b5a10404040abf242fb98fd08f5..dc9ea808c0f7b19e396a7c73f2b416221729a34b 100644 --- a/app/code/Magento/Shipping/composer.json +++ b/app/code/Magento/Shipping/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-shipping", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-sales": "100.2.*", diff --git a/app/code/Magento/Shipping/view/adminhtml/web/order/packaging.js b/app/code/Magento/Shipping/view/adminhtml/web/order/packaging.js index 2ad2ff596a7077f92c91623fef98d374449480b1..b3a7ea2e2495657efcf2de1fcfc5a00eb8939f7b 100644 --- a/app/code/Magento/Shipping/view/adminhtml/web/order/packaging.js +++ b/app/code/Magento/Shipping/view/adminhtml/web/order/packaging.js @@ -353,7 +353,7 @@ Packaging.prototype = { var response = transport.responseText; if (response) { packagePrapareGrid.update(response); - this._processPackagePrapare(packagePrapareGrid); + this.processPackagePrepare(packagePrapareGrid); if (packagePrapareGrid.select('.grid tbody tr').length) { packageBlock.select('[data-action=package-add-items]')[0].hide(); packageBlock.select('[data-action=package-save-items]')[0].show(); @@ -692,22 +692,27 @@ Packaging.prototype = { } }, - _processPackagePrapare: function(packagePrapare) { - var itemsAll = []; - packagePrapare.select('.grid tbody tr').each(function(item) { - var qty = item.select('[name="qty"]')[0]; - var itemId = item.select('[type="checkbox"]')[0].value; - var qtyValue = 0; + processPackagePrepare: function(packagePrepare) { + var itemsAll = [], + qty, + itemId, + qtyValue = 0, + value = 1; + + packagePrepare.select('.grid tbody tr').each(function(item) { + qty = item.select('[name="qty"]')[0], + itemId = item.select('[type="checkbox"]')[0].value, + qtyValue = parseFloat(qty.value); + if (Object.isFunction(this.itemQtyCallback)) { - var value = this.itemQtyCallback(itemId); - qtyValue = ((typeof value == 'string') && (value.length == 0)) ? 0 : parseFloat(value); - if (isNaN(qtyValue) || qtyValue < 0) { - qtyValue = 1; + value = this.itemQtyCallback(itemId); + if (typeof value !== 'undefined') { + qtyValue = parseFloat(value); + qtyValue = this.validateItemQty(itemId, qtyValue); + qty.value = qtyValue; } - qtyValue = this.validateItemQty(itemId, qtyValue); - qty.value = qtyValue; } else { - var value = item.select('[name="qty"]')[0].value; + value = item.select('[name="qty"]')[0].value; qtyValue = ((typeof value == 'string') && (value.length == 0)) ? 0 : parseFloat(value); if (isNaN(qtyValue) || qtyValue < 0) { qtyValue = 1; @@ -737,10 +742,10 @@ Packaging.prototype = { this.itemsAll = itemsAll; } - packagePrapare.select('tbody input[type="checkbox"]').each(function(item){ + packagePrepare.select('tbody input[type="checkbox"]').each(function(item){ $(item).observe('change', this._observeQty); this._observeQty.call(item); - }.bind(this)) + }.bind(this)); }, _observeQty: function() { diff --git a/app/code/Magento/Sitemap/composer.json b/app/code/Magento/Sitemap/composer.json index 419d95a07159eb89533f0367289c868b1a7c7d04..be553a4da194f4dbf64fb4237fd92bb27e4e17ff 100644 --- a/app/code/Magento/Sitemap/composer.json +++ b/app/code/Magento/Sitemap/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-sitemap", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/Store/composer.json b/app/code/Magento/Store/composer.json index 50d54966a9b4c5143e46d1a87de7fb9a9246a415..b9f76f6c2c3137aa21d8f4a6cf97880fdc3e72f6 100644 --- a/app/code/Magento/Store/composer.json +++ b/app/code/Magento/Store/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-store", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-directory": "100.2.*", "magento/module-ui": "100.2.*", diff --git a/app/code/Magento/Swagger/composer.json b/app/code/Magento/Swagger/composer.json index f30fa606c6d777d6a56a003f57455dff6cb68533..dc2d1cbee003de647cf72f7312ad7f7fcc222e3b 100644 --- a/app/code/Magento/Swagger/composer.json +++ b/app/code/Magento/Swagger/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-swagger", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-module", diff --git a/app/code/Magento/Swatches/composer.json b/app/code/Magento/Swatches/composer.json index db3da5c4f27f4c30a33b649f945dfd904cadbfb4..f87fdb635cb73386b40fec6ac932eaee3f9cd398 100644 --- a/app/code/Magento/Swatches/composer.json +++ b/app/code/Magento/Swatches/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-swatches", "description": "Add Swatches to Products", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-configurable-product": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/SwatchesLayeredNavigation/composer.json b/app/code/Magento/SwatchesLayeredNavigation/composer.json index f8d7fac97dc132e20adcb3c6adb050ea73eb9b66..9b658d0a42e61e0b556f1aa5aed6e12ed8cfdbe7 100644 --- a/app/code/Magento/SwatchesLayeredNavigation/composer.json +++ b/app/code/Magento/SwatchesLayeredNavigation/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-swatches-layered-navigation", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/magento-composer-installer": "*" }, diff --git a/app/code/Magento/Tax/composer.json b/app/code/Magento/Tax/composer.json index 9063d4f0395543b0c22647177f1b6f786c07b0e6..4553fdbb383323732b667df9be3e140aca011744 100644 --- a/app/code/Magento/Tax/composer.json +++ b/app/code/Magento/Tax/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-tax", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-config": "100.2.*", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/code/Magento/TaxImportExport/composer.json b/app/code/Magento/TaxImportExport/composer.json index faa66cbaaf906f64dfb4bc6042ca0e4472e3c7ad..c3d09e4926e02d67a98c0e15c050d67997ae1ecd 100644 --- a/app/code/Magento/TaxImportExport/composer.json +++ b/app/code/Magento/TaxImportExport/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-tax-import-export", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-tax": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-directory": "100.2.*", diff --git a/app/code/Magento/Theme/Test/Unit/Ui/Component/Design/Config/SearchRobots/ResetButtonTest.php b/app/code/Magento/Theme/Test/Unit/Ui/Component/Design/Config/SearchRobots/ResetButtonTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b1c7095c0f7c9215abc4c330e209a423221cfcb6 --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Ui/Component/Design/Config/SearchRobots/ResetButtonTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Theme\Test\Unit\Ui\Component\Design\Config\SearchRobots; + +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Theme\Ui\Component\Design\Config\SearchRobots\ResetButton; +use Magento\Framework\View\Element\UiComponent\Processor; +use Magento\Ui\Component\Form\Field; + +class ResetButtonTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject | ContextInterface + */ + private $contextMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | UiComponentFactory + */ + private $componentFactoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | ScopeConfigInterface + */ + private $scopeConfigMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | + */ + private $processorMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | + */ + private $wrappingComponentMock; + + /** + * @var ResetButton + */ + private $resetButton; + + public function setUp() + { + $this->contextMock = $this->getMockBuilder(ContextInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->componentFactoryMock = $this->getMockBuilder(UiComponentFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->processorMock = $this->getMockBuilder(Processor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->contextMock->expects($this->atLeastOnce()) + ->method("getProcessor") + ->willReturn($this->processorMock); + $this->wrappingComponentMock = $this->getMockBuilder(Field::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resetButton = new ResetButton( + $this->contextMock, + $this->componentFactoryMock, + [], + [ + 'config' => [ + 'formElement' => 'button' + ] + ], + $this->scopeConfigMock + ); + } + + public function testPrepare() + { + $robotsContent = "Content"; + + $this->componentFactoryMock->expects($this->once()) + ->method("create") + ->willReturn($this->wrappingComponentMock); + $this->wrappingComponentMock->expects($this->once()) + ->method("getContext") + ->willReturn($this->contextMock); + $this->scopeConfigMock->expects($this->once()) + ->method("getValue") + ->willReturn($robotsContent); + + $this->resetButton->prepare(); + $actions = $this->resetButton->getData("config/actions"); + $this->assertEquals(json_encode($robotsContent), $actions[0]["params"][0]); + } +} diff --git a/app/code/Magento/Theme/Ui/Component/Design/Config/SearchRobots/ResetButton.php b/app/code/Magento/Theme/Ui/Component/Design/Config/SearchRobots/ResetButton.php new file mode 100644 index 0000000000000000000000000000000000000000..d57c10fd08f4a284ae01b9e42f3fbf22d1c41343 --- /dev/null +++ b/app/code/Magento/Theme/Ui/Component/Design/Config/SearchRobots/ResetButton.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Theme\Ui\Component\Design\Config\SearchRobots; + +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Ui\Component\Form\Field; +use Magento\Framework\App\Config\ScopeConfigInterface; + +class ResetButton extends Field +{ + /** + * Page robots default instructions + */ + const XML_PATH_ROBOTS_DEFAULT_CUSTOM_INSTRUCTIONS = 'design/search_engine_robots/default_custom_instructions'; + + /** @var ScopeConfigInterface */ + private $scopeConfig; + + /** + * ResetButton constructor. + * @param ContextInterface $context + * @param UiComponentFactory $uiComponentFactory + * @param array|\Magento\Framework\View\Element\UiComponentInterface[] $components + * @param array $data + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + ContextInterface $context, + UiComponentFactory $uiComponentFactory, + $components, + array $data, + ScopeConfigInterface $scopeConfig + ) { + $this->scopeConfig = $scopeConfig; + parent::__construct($context, $uiComponentFactory, $components, $data); + } + + /** + * Get robots.txt custom instruction default value + * + * @return string + */ + private function getRobotsDefaultCustomInstructions() + { + return trim((string)$this->scopeConfig->getValue( + self::XML_PATH_ROBOTS_DEFAULT_CUSTOM_INSTRUCTIONS, ScopeConfigInterface::SCOPE_TYPE_DEFAULT + )); + } + + /** + * Add js listener to reset button + * + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function prepare() + { + parent::prepare(); + + $this->_data['config']['actions'] = [ + [ + 'actionName' => 'reset', + 'targetName' => '${ $.name }', + 'params' => [ + json_encode($this->getRobotsDefaultCustomInstructions()) + ] + ] + ]; + } +} \ No newline at end of file diff --git a/app/code/Magento/Theme/composer.json b/app/code/Magento/Theme/composer.json index dad8bac7c254b292c34538a6b7f890500e1d4f87..49382f9642fa5d2b88d7d2f4cea79c07d5306cb1 100644 --- a/app/code/Magento/Theme/composer.json +++ b/app/code/Magento/Theme/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-theme", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Theme/etc/adminhtml/system.xml b/app/code/Magento/Theme/etc/adminhtml/system.xml deleted file mode 100644 index 84773112f504e400641d002e98fcc0d8a6d8d045..0000000000000000000000000000000000000000 --- a/app/code/Magento/Theme/etc/adminhtml/system.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> - <system> - <section id="design"> - <group id="search_engine_robots" translate="label" type="text" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Search Engine Robots</label> - <field id="default_robots" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> - <label>Default Robots</label> - <comment>This will be included before head closing tag in page HTML.</comment> - <source_model>Magento\Config\Model\Config\Source\Design\Robots</source_model> - </field> - <field id="custom_instructions" translate="label comment" type="textarea" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Edit custom instruction of robots.txt File</label> - <backend_model>Magento\Config\Model\Config\Backend\Admin\Robots</backend_model> - </field> - <field id="reset_to_defaults" translate="label comment" type="button" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Reset to Defaults</label> - <frontend_model>Magento\Backend\Block\Page\System\Config\Robots\Reset</frontend_model> - <comment>This action will delete your custom instructions and reset robots.txt file to system's default settings.</comment> - </field> - </group> - </section> - </system> -</config> diff --git a/app/code/Magento/Theme/etc/di.xml b/app/code/Magento/Theme/etc/di.xml index 9cb17d93ab652e1ce5ef32aac0605e9a5046690b..cee9fc816914b55f31970995b64fa404ccbfcff8 100644 --- a/app/code/Magento/Theme/etc/di.xml +++ b/app/code/Magento/Theme/etc/di.xml @@ -211,6 +211,15 @@ <item name="path" xsi:type="string">design/footer/absolute_footer</item> <item name="fieldset" xsi:type="string">other_settings/footer</item> </item> + <item name="default_robots" xsi:type="array"> + <item name="path" xsi:type="string">design/search_engine_robots/default_robots</item> + <item name="fieldset" xsi:type="string">other_settings/search_engine_robots</item> + </item> + <item name="custom_instructions" xsi:type="array"> + <item name="path" xsi:type="string">design/search_engine_robots/custom_instructions</item> + <item name="backend_model" xsi:type="string">Magento\Config\Model\Config\Backend\Admin\Robots</item> + <item name="fieldset" xsi:type="string">other_settings/search_engine_robots</item> + </item> </argument> </arguments> </type> diff --git a/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml index ace2babc100d35fca9c4aabce774c41ca5afb295..8ff8430220abea0d25bc4f9dd3ea1cbd470aa3c4 100644 --- a/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml +++ b/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml @@ -271,5 +271,49 @@ </argument> </field> </fieldset> + <fieldset name="search_engine_robots"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="label" xsi:type="string">Search Engine Robots</item> + <item name="sortOrder" xsi:type="string">120</item> + <item name="collapsible" xsi:type="boolean">true</item> + <item name="level" xsi:type="number">1</item> + </item> + </argument> + <field name="default_robots"> + <argument name="data" xsi:type="array"> + <item name="options" xsi:type="object">Magento\Config\Model\Config\Source\Design\Robots</item> + <item name="config" xsi:type="array"> + <item name="label" xsi:type="string" translate="true">Default Robots</item> + <item name="dataType" xsi:type="string">text</item> + <item name="formElement" xsi:type="string">select</item> + <item name="dataScope" xsi:type="string">default_robots</item> + <item name="notice" xsi:type="string" translate="true">This will be included before head closing tag in page HTML.</item> + </item> + </argument> + </field> + <field name="custom_instructions"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="label" xsi:type="string" translate="true">Edit custom instruction of robots.txt File</item> + <item name="dataType" xsi:type="string">text</item> + <item name="formElement" xsi:type="string">textarea</item> + <item name="dataScope" xsi:type="string">custom_instructions</item> + </item> + </argument> + </field> + <field name="reset_to_defaults" class="Magento\Theme\Ui\Component\Design\Config\SearchRobots\ResetButton"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="title" xsi:type="string">Reset To Defaults</item> + <item name="formElement" xsi:type="string">button</item> + <item name="dataScope" xsi:type="string">reset_to_defaults</item> + <item name="component" xsi:type="string">Magento_Theme/js/form/component/robots-reset-button</item> + <item name="template" xsi:type="string">Magento_Theme/form/button-field</item> + <item name="notice" xsi:type="string" translate="true">This action will delete your custom instructions and reset robots.txt file to system's default settings.</item> + </item> + </argument> + </field> + </fieldset> </fieldset> </form> diff --git a/app/code/Magento/Theme/view/adminhtml/web/js/form/component/robots-reset-button.js b/app/code/Magento/Theme/view/adminhtml/web/js/form/component/robots-reset-button.js new file mode 100644 index 0000000000000000000000000000000000000000..e483632734d4568ec14b702ba075a9484d255010 --- /dev/null +++ b/app/code/Magento/Theme/view/adminhtml/web/js/form/component/robots-reset-button.js @@ -0,0 +1,39 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/components/button', + 'uiRegistry' +], function (Button, registry) { + 'use strict'; + + return Button.extend({ + defaults: { + customInstructionField: '${ $.parentName }.custom_instructions', + label: '', + buttonTpl: 'Magento_Theme/form/element/button' + }, + + /** + * @private + * @param {String} json + * @return {String} + */ + _parseJson: function (json) { + return JSON.parse(json); + }, + + /** + * @param {String} defaultRobotsTxt + */ + reset: function (defaultRobotsTxt) { + var customInstructions = registry.get(this.customInstructionField); + + if (customInstructions) { + customInstructions.set('value', this._parseJson(defaultRobotsTxt)); + } + } + }); +}); diff --git a/app/code/Magento/Theme/view/adminhtml/web/template/form/button-field.html b/app/code/Magento/Theme/view/adminhtml/web/template/form/button-field.html new file mode 100644 index 0000000000000000000000000000000000000000..fa37c9bdf7e0fa44de49d136a0b326fa20d7b3e7 --- /dev/null +++ b/app/code/Magento/Theme/view/adminhtml/web/template/form/button-field.html @@ -0,0 +1,23 @@ +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div class="admin__field" + visible="visible" + css="$data.additionalClasses" + attr="'data-index': index"> + <label class="admin__field-label"> + <span text="label"/> + </label> + + <div class="admin__field-control" + css="'_with-tooltip': $data.tooltip"> + <render args="tooltipTpl" if="$data.tooltip"/> + <render args="buttonTpl" /> + <div class="admin__field-note" if="$data.notice"> + <span text="notice"/> + </div> + </div> +</div> \ No newline at end of file diff --git a/app/code/Magento/Theme/view/adminhtml/web/template/form/element/button.html b/app/code/Magento/Theme/view/adminhtml/web/template/form/element/button.html new file mode 100644 index 0000000000000000000000000000000000000000..4f9164baa07d02ec1f22ef9dfdbf15fb937292dc --- /dev/null +++ b/app/code/Magento/Theme/view/adminhtml/web/template/form/element/button.html @@ -0,0 +1,13 @@ +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<button type="button" + click="action" + disable="disabled" + attr="'data-index': index"> + <span text="title"> + </span> +</button> \ No newline at end of file diff --git a/app/code/Magento/Translation/composer.json b/app/code/Magento/Translation/composer.json index 67a0582986f68c1ef3af8f474a7606023c3d0e9f..9ea00544d805094927499672f98fb72ad12ae97a 100644 --- a/app/code/Magento/Translation/composer.json +++ b/app/code/Magento/Translation/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-translation", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-developer": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/Ui/Component/Filters/Type/Date.php b/app/code/Magento/Ui/Component/Filters/Type/Date.php index f670f826e4e20965c1312db80d14f4e9c52e7e09..f5d392ea5136565797ae7306b870c07399043d68 100644 --- a/app/code/Magento/Ui/Component/Filters/Type/Date.php +++ b/app/code/Magento/Ui/Component/Filters/Type/Date.php @@ -23,6 +23,13 @@ class Date extends AbstractFilter */ protected $wrappedComponent; + /** + * Date format + * + * @var string + */ + protected static $dateFormat = 'Y-m-d H:i:s'; + /** * Prepare component configuration * @@ -96,7 +103,7 @@ class Date extends AbstractFilter if (!empty($value)) { $filter = $this->filterBuilder->setConditionType($type) ->setField($this->getName()) - ->setValue($value->format('Y-m-d H:i:s')) + ->setValue($value->format(static::$dateFormat)) ->create(); $this->getContext()->getDataProvider()->addFilter($filter); diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php index 7000d9e091139a08199a6d23059f4bcd3220ff21..af53b71f18414f8c0f16f27e32b13ba354d29567 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php @@ -5,9 +5,11 @@ */ namespace Magento\Ui\Test\Unit\Component\Filters\Type; -use Magento\Framework\View\Element\UiComponent\ContextInterface as UiContext; +use Magento\Framework\Api\Filter; +use Magento\Framework\Api\FilterBuilder; use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Ui\Component\Filters\FilterModifier; use Magento\Ui\Component\Filters\Type\Date; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Component\Form\Element\DataType\Date as FormDate; @@ -20,61 +22,55 @@ class DateTest extends \PHPUnit_Framework_TestCase /** * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $contextMock; + private $contextMock; /** * @var UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $uiComponentFactory; + private $uiComponentFactory; /** - * @var \Magento\Framework\Api\FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var FilterBuilder|\PHPUnit_Framework_MockObject_MockObject */ - protected $filterBuilderMock; + private $filterBuilderMock; /** - * @var \Magento\Ui\Component\Filters\FilterModifier|\PHPUnit_Framework_MockObject_MockObject + * @var FilterModifier|\PHPUnit_Framework_MockObject_MockObject */ - protected $filterModifierMock; + private $filterModifierMock; + + + /** + * @var DataProviderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $dataProviderMock; /** * Set up */ protected function setUp() { - $this->contextMock = $this->getMockForAbstractClass( - 'Magento\Framework\View\Element\UiComponent\ContextInterface', - [], - '', - false - ); - $processor = $this->getMockBuilder('Magento\Framework\View\Element\UiComponent\Processor') + $this->contextMock = $this->getMockForAbstractClass(ContextInterface::class); + $processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->contextMock->expects(static::any()) + ->method('getProcessor') + ->willReturn($processor); + $this->uiComponentFactory = $this->getMockBuilder(UiComponentFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->filterBuilderMock = $this->getMockBuilder(FilterBuilder::class) ->disableOriginalConstructor() ->getMock(); - $this->contextMock->expects($this->any())->method('getProcessor')->willReturn($processor); - $this->uiComponentFactory = $this->getMock( - 'Magento\Framework\View\Element\UiComponentFactory', - ['create'], - [], - '', - false - ); - $this->filterBuilderMock = $this->getMock( - 'Magento\Framework\Api\FilterBuilder', - [], - [], - '', - false - ); - - $this->filterModifierMock = $this->getMock( - 'Magento\Ui\Component\Filters\FilterModifier', - ['applyFilterModifier'], - [], - '', - false - ); + $this->filterModifierMock = $this->getMockBuilder(FilterModifier::class) + ->setMethods(['applyFilterModifier']) + ->disableOriginalConstructor() + ->getMock(); + + $this->dataProviderMock = $this->getMockForAbstractClass(DataProviderInterface::class); } /** @@ -92,7 +88,7 @@ class DateTest extends \PHPUnit_Framework_TestCase [] ); - $this->assertTrue($date->getComponentName() === Date::NAME); + static::assertTrue($date->getComponentName() === Date::NAME); } /** @@ -107,13 +103,9 @@ class DateTest extends \PHPUnit_Framework_TestCase public function testPrepare($name, $filterData, $expectedCondition) { /** @var FormDate $uiComponent */ - $uiComponent = $this->getMock( - 'Magento\Ui\Component\Form\Element\DataType\Date', - [], - [], - '', - false - ); + $uiComponent = $this->getMockBuilder(FormDate::class) + ->disableOriginalConstructor() + ->getMock(); $uiComponent->expects($this->any()) ->method('getContext') @@ -125,30 +117,62 @@ class DateTest extends \PHPUnit_Framework_TestCase $this->contextMock->expects($this->any()) ->method('addComponentDefinition') ->with(Date::NAME, ['extends' => Date::NAME]); + $this->contextMock->expects($this->any()) - ->method('getRequestParam') - ->with(UiContext::FILTER_VAR) + ->method('getFiltersParams') ->willReturn($filterData); - $dataProvider = $this->getMockForAbstractClass( - 'Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface', - [], - '', - false - ); + $this->contextMock->expects($this->any()) ->method('getDataProvider') - ->willReturn($dataProvider); + ->willReturn($this->dataProviderMock); + if ($expectedCondition !== null) { - $dataProvider->expects($this->any()) - ->method('addFilter') - ->with($expectedCondition, $name); - - $uiComponent->expects($this->any()) - ->method('getLocale') - ->willReturn($expectedCondition['locale']); - $uiComponent->expects($this->any()) - ->method('convertDate') - ->willReturnArgument(0); + if (is_string($filterData[$name])) { + $uiComponent->expects(static::once()) + ->method('convertDate') + ->with($filterData[$name]) + ->willReturn(new \DateTime($filterData[$name])); + } else { + $uiComponent->method('convertDate') + ->willReturnMap([ + [$filterData[$name]['from'], 0, 0, 0, new \DateTime($filterData[$name]['from'])], + [$filterData[$name]['to'], 23, 59, 59, new \DateTime($filterData[$name]['to'] . ' 23:59:59')], + ]); + } + + $i=0; + switch (true) { + case is_string($filterData[$name]): + case isset($filterData[$name]['from']) && !isset($filterData[$name]['to']): + case !isset($filterData[$name]['from']) && isset($filterData[$name]['to']): + $filterMock = $this->getFilterMock( + $name, + $expectedCondition['type'], + $expectedCondition['date'], + $i + ); + $this->dataProviderMock->expects(static::once()) + ->method('addFilter') + ->with($filterMock); + break; + case isset($filterData[$name]['from']) && isset($filterData[$name]['to']): + $this->getFilterMock( + $name, + $expectedCondition['type_from'], + $expectedCondition['date_from'], + $i + ); + $filterMock = $this->getFilterMock( + $name, + $expectedCondition['type_to'], + $expectedCondition['date_to'], + $i + ); + $this->dataProviderMock->expects(static::exactly(2)) + ->method('addFilter') + ->with($filterMock); + break; + } } $this->uiComponentFactory->expects($this->any()) @@ -167,6 +191,39 @@ class DateTest extends \PHPUnit_Framework_TestCase $date->prepare(); } + /** + * Gets Filter mock + * + * @param string $name + * @param string $expectedType + * @param string $expectedDate + * @param int $i + * + * @return Filter|\PHPUnit_Framework_MockObject_MockObject + */ + private function getFilterMock($name, $expectedType, $expectedDate, &$i) + { + $this->filterBuilderMock->expects(static::at($i++)) + ->method('setConditionType') + ->with($expectedType) + ->willReturnSelf(); + $this->filterBuilderMock->expects(static::at($i++)) + ->method('setField') + ->with($name) + ->willReturnSelf(); + $this->filterBuilderMock->expects(static::at($i++)) + ->method('setValue') + ->with($expectedDate) + ->willReturnSelf(); + + $filterMock = $this->getMock(Filter::class); + $this->filterBuilderMock->expects(static::at($i++)) + ->method('create') + ->willReturn($filterMock); + + return $filterMock; + } + /** * @return array */ @@ -175,26 +232,27 @@ class DateTest extends \PHPUnit_Framework_TestCase return [ [ 'test_date', - ['test_date' => ['from' => '11-05-2015', 'to' => '']], - ['from' => '11-05-2015', 'orig_from' => '11-05-2015', 'datetime' => true, 'locale' => 'en_US'], + ['test_date' => ['from' => '11-05-2015', 'to' => null]], + ['date' => '2015-05-11 00:00:00', 'type' => 'gteq'], ], [ 'test_date', - ['test_date' => ['from' => '', 'to' => '11-05-2015']], - ['to' => '11-05-2015', 'orig_to' => '11-05-2015', 'datetime' => true, 'locale' => 'en_US'], + ['test_date' => ['from' => null, 'to' => '11-05-2015']], + ['date' => '2015-05-11 23:59:59', 'type' => 'lteq'], ], [ 'test_date', - ['test_date' => ['from' => '10-05-2015', 'to' => '11-05-2015']], + ['test_date' => ['from' => '11-05-2015', 'to' => '11-05-2015']], [ - 'from' => '10-05-2015', - 'orig_from' => '10-05-2015', - 'to' => '11-05-2015', - 'orig_to' => '11-05-2015', - 'datetime' => true, - 'locale' => 'en_US' + 'date_from' => '2015-05-11 00:00:00', 'type_from' => 'gteq', + 'date_to' => '2015-05-11 23:59:59', 'type_to' => 'lteq' ], ], + [ + 'test_date', + ['test_date' => '11-05-2015'], + ['date' => '2015-05-11 00:00:00', 'type' => 'eq'], + ], [ 'test_date', ['test_date' => ['from' => '', 'to' => '']], diff --git a/app/code/Magento/Ui/composer.json b/app/code/Magento/Ui/composer.json index cea9711ddda7b0b059e2724681bcc077ac6a693b..43c7b7fa3011fd7c6386fcde17eb45b6c5e02695 100644 --- a/app/code/Magento/Ui/composer.json +++ b/app/code/Magento/Ui/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-ui", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/framework": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/button.js b/app/code/Magento/Ui/view/base/web/js/form/components/button.js index 855b9ac5da177d5391ff28a8cfd57be4df54c823..cdcf2e9bb07895e7bdf3810fac7124368267de00 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/button.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/button.js @@ -58,7 +58,7 @@ define([ */ applyAction: function (action) { var targetName = action.targetName, - params = action.params || [], + params = utils.copy(action.params) || [], actionName = action.actionName, target; diff --git a/app/code/Magento/Ups/composer.json b/app/code/Magento/Ups/composer.json index 1ddbfa2a0cb9f07e70f6278c7884043afff28cbd..ade4e738a68efe0499041830892bb6b61f5a3156 100644 --- a/app/code/Magento/Ups/composer.json +++ b/app/code/Magento/Ups/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-ups", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-sales": "100.2.*", diff --git a/app/code/Magento/UrlRewrite/composer.json b/app/code/Magento/UrlRewrite/composer.json index 87e4f357f4abd631ed6bdab0ac63e4eae1015259..ac75ecdf61b8fd0531f2b14f76d2aec7d72581be 100644 --- a/app/code/Magento/UrlRewrite/composer.json +++ b/app/code/Magento/UrlRewrite/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-url-rewrite", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-catalog": "101.1.*", "magento/module-store": "100.2.*", "magento/framework": "100.2.*", diff --git a/app/code/Magento/User/composer.json b/app/code/Magento/User/composer.json index 4cdf24ff7513df294d1d31506097f0f385122767..755c47ac1147612e1e55b589e72afa705b39b0ce 100644 --- a/app/code/Magento/User/composer.json +++ b/app/code/Magento/User/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-user", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-authorization": "100.2.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Usps/composer.json b/app/code/Magento/Usps/composer.json index d9e575675841cd308f60398d9af12d0c2047b079..5baccddf4884307f4ebdeed9199fedb00debf4bc 100644 --- a/app/code/Magento/Usps/composer.json +++ b/app/code/Magento/Usps/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-usps", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-shipping": "100.2.*", "magento/module-directory": "100.2.*", diff --git a/app/code/Magento/Variable/composer.json b/app/code/Magento/Variable/composer.json index 4cedfca248c9868d2e82160083ff16e727274a4f..21f3ec1dd467f240c609d5931ffa5cbde8dc3e15 100644 --- a/app/code/Magento/Variable/composer.json +++ b/app/code/Magento/Variable/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-variable", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-backend": "100.2.*", "magento/module-email": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/Vault/Model/Method/Vault.php b/app/code/Magento/Vault/Model/Method/Vault.php index 3457bc6a0a61f52adf2c86f68752e9df653ab2f0..aa0bead2327eabfb0414fcdcd21d46121553d9ff 100644 --- a/app/code/Magento/Vault/Model/Method/Vault.php +++ b/app/code/Magento/Vault/Model/Method/Vault.php @@ -576,7 +576,8 @@ final class Vault implements VaultPaymentInterface */ public function isAvailable(\Magento\Quote\Api\Data\CartInterface $quote = null) { - return $this->getVaultProvider()->isAvailable($quote); + return $this->getVaultProvider()->isAvailable($quote) + && $this->config->getValue(self::$activeKey, $this->getStore() ?: $quote->getStoreId()); } /** diff --git a/app/code/Magento/Vault/Test/Unit/Model/Method/VaultTest.php b/app/code/Magento/Vault/Test/Unit/Model/Method/VaultTest.php index 2dcece98e770a4958f15eac057acf24df3a96531..32e783278e1301f3bf45a085edb7b81d72801cd7 100644 --- a/app/code/Magento/Vault/Test/Unit/Model/Method/VaultTest.php +++ b/app/code/Magento/Vault/Test/Unit/Model/Method/VaultTest.php @@ -8,8 +8,10 @@ namespace Magento\Vault\Test\Unit\Model\Method; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Payment\Gateway\Command\CommandManagerInterface; use Magento\Payment\Gateway\Command\CommandManagerPoolInterface; +use Magento\Payment\Gateway\ConfigInterface; use Magento\Payment\Model\InfoInterface; use Magento\Payment\Model\MethodInterface; +use Magento\Quote\Api\Data\CartInterface; use Magento\Sales\Api\Data\OrderPaymentExtensionInterface; use Magento\Sales\Api\Data\TransactionInterface; use Magento\Sales\Model\Order\Payment; @@ -18,6 +20,10 @@ use Magento\Vault\Api\PaymentTokenManagementInterface; use Magento\Vault\Model\Method\Vault; use Magento\Vault\Model\VaultPaymentInterface; +/** + * Class VaultTest + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class VaultTest extends \PHPUnit_Framework_TestCase { /** @@ -209,4 +215,52 @@ class VaultTest extends \PHPUnit_Framework_TestCase $model = $this->objectManager->getObject(Vault::class); $model->capture($paymentModel, 0); } + + /** + * @covers \Magento\Vault\Model\Method\Vault::isAvailable + * @dataProvider isAvailableDataProvider + */ + public function testIsAvailable($isAvailableProvider, $isActive, $expected) + { + $storeId = 1; + $quote = $this->getMockForAbstractClass(CartInterface::class); + $vaultProvider = $this->getMockForAbstractClass(MethodInterface::class); + $config = $this->getMockForAbstractClass(ConfigInterface::class); + + $vaultProvider->expects(static::once()) + ->method('isAvailable') + ->with($quote) + ->willReturn($isAvailableProvider); + + $config->expects(static::any()) + ->method('getValue') + ->with('active', $storeId) + ->willReturn($isActive); + + $quote->expects(static::any()) + ->method('getStoreId') + ->willReturn($storeId); + + /** @var Vault $model */ + $model = $this->objectManager->getObject(Vault::class, [ + 'config' => $config, + 'vaultProvider' => $vaultProvider + ]); + $actual = $model->isAvailable($quote); + static::assertEquals($expected, $actual); + } + + /** + * List of variations for testing isAvailable method + * @return array + */ + public function isAvailableDataProvider() + { + return [ + ['isAvailableProvider' => true, 'isActiveVault' => false, 'expected' => false], + ['isAvailableProvider' => false, 'isActiveVault' => false, 'expected' => false], + ['isAvailableProvider' => false, 'isActiveVault' => true, 'expected' => false], + ['isAvailableProvider' => true, 'isActiveVault' => true, 'expected' => true], + ]; + } } diff --git a/app/code/Magento/Vault/composer.json b/app/code/Magento/Vault/composer.json index f3db74c215f26b294a0611b6d9da361ff9e92d20..74dd568e3b45ee4e3e7f9e235d3389191b7a93e4 100644 --- a/app/code/Magento/Vault/composer.json +++ b/app/code/Magento/Vault/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-vault", "description": "", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*", "magento/module-sales": "100.2.*", "magento/module-store": "100.2.*", diff --git a/app/code/Magento/Version/composer.json b/app/code/Magento/Version/composer.json index ca8a0ef98d80827fd2c3a7e835c3c91fb59a5b31..3a972432101154fa2c4f167473fc6f4d6aa113a0 100644 --- a/app/code/Magento/Version/composer.json +++ b/app/code/Magento/Version/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-version", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-module", diff --git a/app/code/Magento/Webapi/composer.json b/app/code/Magento/Webapi/composer.json index 4cf2eb86adee05d6d7b084ba47ca12c61e812366..f2796df70a8fae522a34d0c94e079afd9f525f9c 100644 --- a/app/code/Magento/Webapi/composer.json +++ b/app/code/Magento/Webapi/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-webapi", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-authorization": "100.2.*", "magento/module-integration": "100.2.*", diff --git a/app/code/Magento/WebapiSecurity/composer.json b/app/code/Magento/WebapiSecurity/composer.json index f3e02f82511d470bbbff0642e87fe1b2508cb12f..506975f1aa188d4f0d90f8d2f0484153998f2c39 100644 --- a/app/code/Magento/WebapiSecurity/composer.json +++ b/app/code/Magento/WebapiSecurity/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-webapi-security", "description": "WebapiSecurity module provides option to loosen security on some webapi resources.", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-webapi": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/app/code/Magento/Weee/composer.json b/app/code/Magento/Weee/composer.json index baa70fa352d7a4b62114ac43fe6b886cf96328f2..20a43510937d270933dc0f9b65deacba7ffb843f 100644 --- a/app/code/Magento/Weee/composer.json +++ b/app/code/Magento/Weee/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-weee", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-catalog": "101.1.*", "magento/module-tax": "100.2.*", diff --git a/app/code/Magento/Widget/composer.json b/app/code/Magento/Widget/composer.json index f4885500b77e10171beceace975679e81fa2c2d7..f59c4180eb2bb56522ae20f3615da92dd0c60370 100644 --- a/app/code/Magento/Widget/composer.json +++ b/app/code/Magento/Widget/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-widget", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-cms": "101.1.*", "magento/module-backend": "100.2.*", diff --git a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php index a80849133c53898d22b32738315d7cbb313626fa..ff74604d6fccd21880d7971533698c979ab89a26 100644 --- a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php +++ b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php @@ -291,6 +291,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab 'thumbnail', 'links_purchased_separately', 'links_title', + 'price_type' ]; $productCollection->addPriceData() diff --git a/app/code/Magento/Wishlist/composer.json b/app/code/Magento/Wishlist/composer.json index 68fbc5a30c8ed999fa80e46e366b3dce3e64c662..3faee8912493c96092f70d7fc3ca0e77164b39bc 100644 --- a/app/code/Magento/Wishlist/composer.json +++ b/app/code/Magento/Wishlist/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-wishlist", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/module-store": "100.2.*", "magento/module-customer": "100.2.*", "magento/module-catalog": "101.1.*", diff --git a/app/design/adminhtml/Magento/backend/composer.json b/app/design/adminhtml/Magento/backend/composer.json index 7686e3c3ef855cd7c61ef0c5d869c5e6cb273019..29569db622d2ccc9fad9e9b87b3ad4f7ae4757d2 100644 --- a/app/design/adminhtml/Magento/backend/composer.json +++ b/app/design/adminhtml/Magento/backend/composer.json @@ -2,7 +2,7 @@ "name": "magento/theme-adminhtml-backend", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-theme", diff --git a/app/design/frontend/Magento/blank/composer.json b/app/design/frontend/Magento/blank/composer.json index 0297d02a4ef949d5139c1d8cd9a3fe515f40a938..27ed40860dd5b065dc51e82f487722eb519ba215 100644 --- a/app/design/frontend/Magento/blank/composer.json +++ b/app/design/frontend/Magento/blank/composer.json @@ -2,7 +2,7 @@ "name": "magento/theme-frontend-blank", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "100.2.*" }, "type": "magento2-theme", diff --git a/app/design/frontend/Magento/luma/composer.json b/app/design/frontend/Magento/luma/composer.json index 507ed10117d698a88b87c9b9b1ed083047c69527..98fe0ebf8061c23fa37d30a9551f05d5c41bce1b 100644 --- a/app/design/frontend/Magento/luma/composer.json +++ b/app/design/frontend/Magento/luma/composer.json @@ -2,7 +2,7 @@ "name": "magento/theme-frontend-luma", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/theme-frontend-blank": "100.2.*", "magento/framework": "100.2.*" }, diff --git a/composer.json b/composer.json index b90db7519b5aa6fabc7839e13918e7d88d8d7c0f..76fbf6a25a125a1104730bfa8805b62861cf8303 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "AFL-3.0" ], "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "zendframework/zend-stdlib": "~2.4.6", "zendframework/zend-code": "~2.4.6", "zendframework/zend-server": "~2.4.6", diff --git a/composer.lock b/composer.lock index 897f4db750e35c4ad1f6ffb4cb9687520c5b36b7..b5ad3f09ea4f0cfc17d40ad49b026af795d884be 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "e447a3cde1c69182cd539976df5284d2", - "content-hash": "77f475ee4b3a11984e7cebfd321a1081", + "hash": "9b461c73cd2e4f9b44405f2661f9d04b", + "content-hash": "2371141b038d4d1fb3753d78db86fa26", "packages": [ { "name": "braintree/braintree_php", @@ -4454,7 +4454,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "lib-libxml": "*", "ext-ctype": "*", "ext-gd": "*", diff --git a/dev/tests/api-functional/_files/Magento/TestModuleIntegrationFromConfig/composer.json b/dev/tests/api-functional/_files/Magento/TestModuleIntegrationFromConfig/composer.json index d6b725ffd896aadba6ee73c84c9a790b984c64bd..629afd442a46e7549206c1520b1b6c75f024dcb1 100644 --- a/dev/tests/api-functional/_files/Magento/TestModuleIntegrationFromConfig/composer.json +++ b/dev/tests/api-functional/_files/Magento/TestModuleIntegrationFromConfig/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-test-module-integration-from-config", "description": "test integration create from config", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.42.0-beta8", "magento/module-integration": "0.42.0-beta8" }, diff --git a/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/composer.json b/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/composer.json index 28b53da6cd8fa193da5c54a72768f438555756c9..1e2d9c0fa13dd80fcfe69374a7e02700f6b570ea 100644 --- a/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/composer.json +++ b/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-test-join-directives", "description": "test integration for join directives", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.42.0-beta8", "magento/module-sales": "0.42.0-beta8" }, diff --git a/dev/tests/functional/composer.json b/dev/tests/functional/composer.json index f90a14e5491b5468aa786ee77b472b19e5001d5f..c8db587759f5366738b8c6d869f6918157961d69 100644 --- a/dev/tests/functional/composer.json +++ b/dev/tests/functional/composer.json @@ -1,7 +1,7 @@ { "require": { "magento/mtf": "1.0.0-rc43", - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "phpunit/phpunit": "4.1.0", "phpunit/phpunit-selenium": ">=1.2" }, diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutTest.xml index 2ea4873f454a3cbdada0b94c96b90f6c5271b91c..c8cbd35427d288174ce64f0ea5df6474219444c4 100644 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutTest.xml @@ -26,8 +26,7 @@ <item name="isClosed" xsi:type="string">No</item> <item name="transactionType" xsi:type="string">Authorization</item> </data> - <data name="tag" xsi:type="string">test_type:3rd_party_test_deprecated</data> - <constraint name="Magento\Sales\Test\Constraint\AssertTransactionDetails" /> + <data name="tag" xsi:type="string">test_type:3rd_party_test</data> <constraint name="Magento\Checkout\Test\Constraint\AssertOrderSuccessPlacedMessage" /> <constraint name="Magento\Checkout\Test\Constraint\AssertMinicartEmpty" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/FormPageActions.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/FormPageActions.php old mode 100644 new mode 100755 index ecb59b6351a21eb68156216a9fecc0163e74c0e8..801000227c0d7fd7364be743ddad9ef36b240e49 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/FormPageActions.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/FormPageActions.php @@ -85,7 +85,7 @@ class FormPageActions extends PageActions */ public function reset() { - $this->waitBeforeClick(); + $this->waitForElementVisible($this->resetButton); $this->_rootElement->find($this->resetButton)->click(); } @@ -94,7 +94,7 @@ class FormPageActions extends PageActions */ public function saveAndContinue() { - $this->waitBeforeClick(); + $this->waitForElementVisible($this->saveAndContinueButton); $this->_rootElement->find($this->saveAndContinueButton)->click(); $this->waitForElementNotVisible('.popup popup-loading'); $this->waitForElementNotVisible('.loader'); @@ -105,7 +105,7 @@ class FormPageActions extends PageActions */ public function save() { - $this->waitBeforeClick(); + $this->waitForElementVisible($this->saveButton); $this->_rootElement->find($this->saveButton)->click(); $this->waitForElementNotVisible($this->spinner); $this->waitForElementNotVisible($this->loader, Locator::SELECTOR_XPATH); @@ -117,7 +117,10 @@ class FormPageActions extends PageActions */ public function delete() { - $this->waitBeforeClick(); + $this->waitForElementNotVisible($this->spinner); + $this->waitForElementNotVisible($this->loader, Locator::SELECTOR_XPATH); + $this->waitForElementNotVisible($this->loaderOld, Locator::SELECTOR_XPATH); + $this->waitForElementVisible($this->deleteButton); $this->_rootElement->find($this->deleteButton)->click(); } @@ -130,16 +133,4 @@ class FormPageActions extends PageActions { return $this->_rootElement->find($this->deleteButton)->isVisible(); } - - /** - * Wait for User before clicking any Button which calls JS validation on correspondent form. - * See details in MAGETWO-31121. - * - * @return void - */ - protected function waitBeforeClick() - { - time_nanosleep(0, 600000000); - usleep(1000000); - } } diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php old mode 100644 new mode 100755 index 45f4e3427554b4c2f8569ff08ae689a5915854bf..02f19ce4bce4c3c4e490704ae4a44d17f0117158 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php @@ -467,6 +467,7 @@ abstract class Grid extends Block */ public function openFirstRow() { + $this->waitLoader(); $this->_rootElement->find($this->firstRowSelector, Locator::SELECTOR_XPATH)->click(); } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/History.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/History.php index b3b0f4eafa74cc44467414cb127b08d099b7d7dd..2235122955a723ccc0cafcafc51d24934405e896 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/History.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/History.php @@ -21,6 +21,13 @@ class History extends Block */ protected $commentHistory = '.note-list-comment'; + /** + * Authorized Amount. + * + * @var string + */ + protected $authorizedAmount = '//div[@class="note-list-comment"][contains(text(), "Authorized amount of")]'; + /** * Captured Amount from IPN. * @@ -53,6 +60,17 @@ class History extends Block return $this->_rootElement->find($this->commentHistory, Locator::SELECTOR_CSS)->getText(); } + /** + * Get the authorized amount from the comments history. + * + * @return string + */ + public function getAuthorizedAmount() + { + $this->waitCommentsHistory(); + return $this->_rootElement->find($this->authorizedAmount, Locator::SELECTOR_XPATH)->getText(); + } + /** * Get the captured amount from the comments history. * diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertAuthorizationInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertAuthorizationInCommentsHistory.php index 7495855d5351fac31be919a13d3bb604c486d8bb..a6b8e8983d2b617953b93490dc4c3a3e1079cec2 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertAuthorizationInCommentsHistory.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertAuthorizationInCommentsHistory.php @@ -38,7 +38,7 @@ class AssertAuthorizationInCommentsHistory extends AbstractConstraint $salesOrder->open(); $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - $actualAuthorizedAmount = $salesOrderView->getOrderHistoryBlock()->getCommentsHistory(); + $actualAuthorizedAmount = $salesOrderView->getOrderHistoryBlock()->getAuthorizedAmount(); \PHPUnit_Framework_Assert::assertContains( self::AUTHORIZED_AMOUNT . $prices['grandTotal'], diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertTransactionDetails.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertTransactionDetails.php deleted file mode 100644 index f793d57df1108988f5a629fd90f5a20bea6935fa..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertTransactionDetails.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Sales\Test\Constraint; - -use Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Transactions\Grid; -use Magento\Sales\Test\Fixture\OrderInjectable; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; -use Magento\Mtf\Constraint\AbstractConstraint; - -/** - * Assert that transaction is present in the Transactions tab of the order with corresponding status - */ -class AssertTransactionDetails extends AbstractConstraint -{ - /** - * Message about authorized amount in order. - */ - const AUTHORIZED_AMOUNT = 'Authorized amount of $'; - - /** - * Assert that comment about authorized amount exist in Comments History section on order page in Admin. - * - * @param SalesOrderView $salesOrderView - * @param OrderIndex $salesOrder - * @param string $orderId - * @param array $transactionDetails - * @throws \Exception - */ - public function processAssert( - SalesOrderView $salesOrderView, - OrderIndex $salesOrder, - $orderId, - array $transactionDetails - ) { - $transactionId = ''; - $salesOrder->open(); - $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - $comment = $salesOrderView->getOrderHistoryBlock()->getCommentsHistory(); - preg_match('/(ID: ")(\w+-*\w+)(")/', $comment, $matches); - if (!empty($matches[2])) { - $transactionId = $matches[2]; - } - \PHPUnit_Framework_Assert::assertNotEmpty($transactionId); - $orderForm = $salesOrderView->getOrderForm()->openTab('transactions'); - /** @var Grid $grid */ - $grid = $orderForm->getTab('transactions')->getGridBlock(); - $actualTxnIds = $grid->getIds(); - \PHPUnit_Framework_Assert::assertEquals( - $transactionDetails, - $actualTxnIds[$transactionId], - 'Incorrect transaction details for the order #' . $orderId - ); - } - - /** - * Returns string representation of successful assertion. - * - * @return string - */ - public function toString() - { - return "Message about transaction details are present in Transactions tab."; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/di.xml index 9a8f26a0505ba37a65bed0d0728b9594d6f29185..ea687be19d3c34b0eeb5093690e54243731543ff 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/di.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/di.xml @@ -36,9 +36,4 @@ <argument name="severity" xsi:type="string">high</argument> </arguments> </type> - <type name="Magento\Sales\Test\Constraint\AssertTransactionDetails"> - <arguments> - <argument name="severity" xsi:type="string">high</argument> - </arguments> - </type> </config> diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestStep/CreateSalesRuleStep.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestStep/CreateSalesRuleStep.php index d919ecc88eae2d141912a72daa6544291c7d20f3..cdceb7404e20bd22b3093655e7a8acd452dc870c 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestStep/CreateSalesRuleStep.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestStep/CreateSalesRuleStep.php @@ -59,7 +59,6 @@ class CreateSalesRuleStep implements TestStepInterface { $result['salesRule'] = null; if ($this->salesRule !== null) { - $this->deleteAllSalesRule->run(); $salesRule = $this->fixtureFactory->createByCode( 'salesRule', ['dataset' => $this->salesRule] diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AbstractAssertOrderTaxOnBackend.php b/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AbstractAssertOrderTaxOnBackend.php old mode 100644 new mode 100755 index c85ef8ea9a07920572d574828eb40e34bb8382b7..4c6c01b28d9d4ff12fcd0e5f1b2c45d2b4136c49 --- a/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AbstractAssertOrderTaxOnBackend.php +++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Constraint/AbstractAssertOrderTaxOnBackend.php @@ -93,7 +93,6 @@ abstract class AbstractAssertOrderTaxOnBackend extends AbstractConstraint $this->orderInvoiceNew = $orderInvoiceNew; $this->orderCreditMemoNew = $orderCreditMemoNew; $orderIndex->open(); - $this->waitBeforeClick(); $orderIndex->getSalesOrderGrid()->openFirstRow(); //Check prices on order page $actualPrices = []; @@ -223,15 +222,4 @@ abstract class AbstractAssertOrderTaxOnBackend extends AbstractConstraint { return 'Prices on backend after order creation is correct.'; } - - /** - * Wait for User before click - * - * @return void - */ - protected function waitBeforeClick() - { - time_nanosleep(0, 600000000); - usleep(1000000); - } } diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/TestStep/CreateTaxRuleStep.php b/dev/tests/functional/tests/app/Magento/Tax/Test/TestStep/CreateTaxRuleStep.php index 4ddae5cc0da6742fcd42e073809152c975d6793d..37113ef0cc225e0d5f3fb51e626c6bcd499bbfb2 100644 --- a/dev/tests/functional/tests/app/Magento/Tax/Test/TestStep/CreateTaxRuleStep.php +++ b/dev/tests/functional/tests/app/Magento/Tax/Test/TestStep/CreateTaxRuleStep.php @@ -59,7 +59,6 @@ class CreateTaxRuleStep implements TestStepInterface { $result['taxRule'] = null; if ($this->taxRule !== null) { - $this->deleteAllTaxRule->run(); $taxRuleDataSets = explode(',', $this->taxRule); foreach ($taxRuleDataSets as $taxRuleDataSet) { $taxRule = $this->fixtureFactory->createByCode( diff --git a/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance_unstable.xml b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance_unstable.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac37fc159b40d5e8574117b3e347bd730cbec983 --- /dev/null +++ b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance_unstable.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../vendor/magento/mtf/Magento/Mtf/TestRunner/etc/testRunner.xsd"> + <rule scope="testcase"> + <allow> + <tag group="test_type" value="acceptance_test" /> + </allow> + </rule> + <rule scope="variation"> + <allow> + <tag group="test_type" value="acceptance_test" /> + <tag group="stable" value="no" /> + </allow> + </rule> +</config> diff --git a/dev/tests/integration/framework/Magento/TestFramework/Db/Mysql.php b/dev/tests/integration/framework/Magento/TestFramework/Db/Mysql.php index 0ac0438f69ab7627924214d8ff615000cc64d841..2acfcaa96f2ac2a8269123bb1ec4213b990d3c66 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Db/Mysql.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Db/Mysql.php @@ -13,6 +13,11 @@ namespace Magento\TestFramework\Db; class Mysql extends \Magento\TestFramework\Db\AbstractDb { + /** + * Default port + */ + const DEFAULT_PORT = 3306; + /** * Defaults extra file name */ @@ -32,12 +37,25 @@ class Mysql extends \Magento\TestFramework\Db\AbstractDb */ private $_defaultsExtraFile; + /** + * Port number for connection + * + * @var integer + */ + private $_port; + /** * {@inheritdoc} */ public function __construct($host, $user, $password, $schema, $varPath, \Magento\Framework\Shell $shell) { parent::__construct($host, $user, $password, $schema, $varPath, $shell); + $this->_port = self::DEFAULT_PORT; + if (strpos($this->_host, ':') !== false) { + list($host, $port) = explode(':', $this->_host); + $this->_host = $host; + $this->_port = (int) $port; + } $this->_dbDumpFile = $this->_varPath . '/setup_dump_' . $this->_schema . '.sql'; $this->_defaultsExtraFile = rtrim($this->_varPath, '\\/') . '/' . self::DEFAULTS_EXTRA_FILE_NAME; } @@ -49,10 +67,11 @@ class Mysql extends \Magento\TestFramework\Db\AbstractDb { $this->ensureDefaultsExtraFile(); $this->_shell->execute( - 'mysql --defaults-file=%s --host=%s %s -e %s', + 'mysql --defaults-file=%s --host=%s --port=%s %s -e %s', [ $this->_defaultsExtraFile, $this->_host, + $this->_port, $this->_schema, "DROP DATABASE `{$this->_schema}`; CREATE DATABASE `{$this->_schema}`" ] @@ -86,8 +105,8 @@ class Mysql extends \Magento\TestFramework\Db\AbstractDb { $this->ensureDefaultsExtraFile(); $this->_shell->execute( - 'mysqldump --defaults-file=%s --host=%s %s > %s', - [$this->_defaultsExtraFile, $this->_host, $this->_schema, $this->getSetupDbDumpFilename()] + 'mysqldump --defaults-file=%s --host=%s --port=%s %s > %s', + [$this->_defaultsExtraFile, $this->_host, $this->_port, $this->_schema, $this->getSetupDbDumpFilename()] ); } @@ -102,8 +121,8 @@ class Mysql extends \Magento\TestFramework\Db\AbstractDb throw new \LogicException("DB dump file does not exist: " . $this->getSetupDbDumpFilename()); } $this->_shell->execute( - 'mysql --defaults-file=%s --host=%s %s < %s', - [$this->_defaultsExtraFile, $this->_host, $this->_schema, $this->getSetupDbDumpFilename()] + 'mysql --defaults-file=%s --host=%s --port=%s %s < %s', + [$this->_defaultsExtraFile, $this->_host, $this->_port, $this->_schema, $this->getSetupDbDumpFilename()] ); } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index e3df4d34aabecbc083a17e93f2dfab2416561643..683c8669f73eff0f526cd5eb194f39b6d9118e0a 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -339,7 +339,12 @@ class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle $this->assertEquals(1, $subscriber->getStatus()); $post = [ - 'customer' => ['entity_id' => $customerId], + 'customer' => [ + 'entity_id' => $customerId, + 'email' => 'customer@example.com', + 'firstname' => 'test firstname', + 'lastname' => 'test lastname', + ], 'subscription' => 'false' ]; $this->getRequest()->setPostValue($post); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php index 250059158bea36881aa9e48fd753a1fe2d850291..1dc2da7750eb49cbe815cc2609e81aaf2c64c5f2 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php @@ -40,6 +40,12 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Framework\Api\DataObjectHelper */ protected $dataObjectHelper; + /** @var \Magento\Framework\Encryption\EncryptorInterface */ + protected $encryptor; + + /** @var \Magento\Customer\Model\CustomerRegistry */ + protected $customerRegistry; + protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); @@ -50,6 +56,9 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase $this->accountManagement = $this->objectManager->create('Magento\Customer\Api\AccountManagementInterface'); $this->converter = $this->objectManager->create('Magento\Framework\Api\ExtensibleDataObjectConverter'); $this->dataObjectHelper = $this->objectManager->create('Magento\Framework\Api\DataObjectHelper'); + $this->encryptor = $this->objectManager->create(\Magento\Framework\Encryption\EncryptorInterface::class); + $this->customerRegistry = $this->objectManager->create(\Magento\Customer\Model\CustomerRegistry::class); + /** @var \Magento\Framework\Config\CacheInterface $cache */ $cache = $this->objectManager->create('Magento\Framework\Config\CacheInterface'); $cache->remove('extension_attributes_config'); @@ -137,6 +146,8 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase $email = 'savecustomer@example.com'; $firstName = 'Firstsave'; $lastName = 'Lastsave'; + $newPassword = 'newPassword123'; + $newPasswordHash = $this->encryptor->getHash($newPassword, true); $customerBefore = $this->customerRepository->getById($existingCustomerId); $customerData = array_merge($customerBefore->__toArray(), [ 'id' => 1, @@ -154,7 +165,7 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase $customerData, '\Magento\Customer\Api\Data\CustomerInterface' ); - $this->customerRepository->save($customerDetails); + $this->customerRepository->save($customerDetails, $newPasswordHash); $customerAfter = $this->customerRepository->getById($existingCustomerId); $this->assertEquals($email, $customerAfter->getEmail()); $this->assertEquals($firstName, $customerAfter->getFirstname()); @@ -167,8 +178,7 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase $defaultShipping ); $this->assertEquals('Admin', $customerAfter->getCreatedIn()); - $passwordFromFixture = 'password'; - $this->accountManagement->authenticate($customerAfter->getEmail(), $passwordFromFixture); + $this->accountManagement->authenticate($customerAfter->getEmail(), $newPassword); $attributesBefore = $this->converter->toFlatArray( $customerBefore, [], diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/composer.json b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/composer.json index 5bc2a54a586106f7f3eb9a5268d35eeb48f42787..222e5ff822b043185e44719804e0af75d8809c6e 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/composer.json +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/A/composer.json @@ -1,7 +1,7 @@ { "name": "magento/module-a", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.1", "magento/module-b": "0.1" }, diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/composer.json b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/composer.json index 5020e9e09f61fcdf76c8ae1495e3e845e1222221..57944758267fd668f541b34586a8eb9f7acd9c98 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/composer.json +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code/Magento/B/composer.json @@ -1,7 +1,7 @@ { "name": "magento/module-b", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.74.0-beta6", "magento/module-a": "0.1" }, diff --git a/dev/tests/integration/testsuite/Magento/Widget/_files/design/adminhtml/magento_basic/composer.json b/dev/tests/integration/testsuite/Magento/Widget/_files/design/adminhtml/magento_basic/composer.json index c4eaac5c94c1717701c0e5cae395bdd3ae61304d..920dc1289b0cfa00cd696b35fb495434830850f6 100644 --- a/dev/tests/integration/testsuite/Magento/Widget/_files/design/adminhtml/magento_basic/composer.json +++ b/dev/tests/integration/testsuite/Magento/Widget/_files/design/adminhtml/magento_basic/composer.json @@ -2,7 +2,7 @@ "name": "magento/admin-Magento_Catalog", "description": "N/A", "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "magento/framework": "0.1.0-alpha103" }, "type": "magento2-theme", diff --git a/lib/internal/Magento/Framework/Api/ImageProcessor.php b/lib/internal/Magento/Framework/Api/ImageProcessor.php index 768c8d8bdd4cd161c1f67326a9a62183174f6d26..d0cb13d995d7061ac117bbfafeeb6da743598bc6 100644 --- a/lib/internal/Magento/Framework/Api/ImageProcessor.php +++ b/lib/internal/Magento/Framework/Api/ImageProcessor.php @@ -123,7 +123,7 @@ class ImageProcessor implements ImageProcessorInterface ); if ($previousImageAttribute) { $previousImagePath = $previousImageAttribute->getValue(); - if (!empty($previousImagePath)) { + if (!empty($previousImagePath) && ($previousImagePath != $filename)) { @unlink($this->mediaDirectory->getAbsolutePath() . $entityType . $previousImagePath); } } diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index d183a0f436ffb88af347d417cb43578132b878ac..fb555b30fc9e80fc3c0b7c612923564d384ad8d0 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -329,6 +329,12 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface throw new \Zend_Db_Adapter_Exception('No host configured to connect'); } + if (isset($this->_config['port'])) { + throw new \Zend_Db_Adapter_Exception('Port must be configured within host parameter (like localhost:3306'); + } + + unset($this->_config['port']); + if (strpos($this->_config['host'], '/') !== false) { $this->_config['unix_socket'] = $this->_config['host']; unset($this->_config['host']); diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php index 20a8ec9b8330782acbd9889b5782dd86bef5dbdb..feff4276cfc09cc038eb896215320700ef39cc5c 100644 --- a/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php +++ b/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php @@ -14,6 +14,8 @@ namespace Magento\Framework\DB\Test\Unit\Adapter\Pdo; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; use Magento\Framework\DB\Select\SelectRenderer; +use Magento\Framework\Model\ResourceModel\Type\Db\Pdo\Mysql; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class MysqlTest extends \PHPUnit_Framework_TestCase { @@ -523,4 +525,28 @@ class MysqlTest extends \PHPUnit_Framework_TestCase ['short_table_name', ['field1', 'field2'], '', 'SHORT_TABLE_NAME_FIELD1_FIELD2'], ]; } + + public function testConfigValidation() + { + $subject = (new ObjectManager($this))->getObject( + Mysql::class, + [ + 'config' => ['host' => 'localhost'], + ] + ); + + $this->assertInstanceOf(Mysql::class, $subject); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Port must be configured within host (like 'localhost:33390') parameter, not within port + */ + public function testConfigValidationByPortWithException() + { + (new ObjectManager($this))->getObject( + Mysql::class, + ['config' => ['host' => 'localhost', 'port' => '33390']] + ); + } } diff --git a/lib/internal/Magento/Framework/HTTP/Client/Curl.php b/lib/internal/Magento/Framework/HTTP/Client/Curl.php index ee19ce7d961619348eff6e099f7e4f662ff004fb..67525f62f6336cf7de844e866a153be223dc0b5c 100644 --- a/lib/internal/Magento/Framework/HTTP/Client/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Client/Curl.php @@ -12,6 +12,12 @@ namespace Magento\Framework\HTTP\Client; */ class Curl implements \Magento\Framework\HTTP\ClientInterface { + /** + * Max supported protocol by curl CURL_SSLVERSION_TLSv1_2 + * @var int + */ + private static $sslVersion = 6; + /** * Hostname * @var string @@ -374,6 +380,7 @@ class Curl implements \Magento\Framework\HTTP\ClientInterface //$this->curlOption(CURLOPT_HEADER, 1); $this->curlOption(CURLOPT_RETURNTRANSFER, 1); $this->curlOption(CURLOPT_HEADERFUNCTION, [$this, 'parseHeaders']); + $this->curlOption(CURLOPT_SSLVERSION, self::$sslVersion); if (count($this->_curlUserOptions)) { foreach ($this->_curlUserOptions as $k => $v) { diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Type/Db/Pdo/Mysql.php b/lib/internal/Magento/Framework/Model/ResourceModel/Type/Db/Pdo/Mysql.php index e2cc95603f4761995a859fb83e68643fae353c41..8b6a2c23386ff3f7a4a83070d7ac268a0eb3849d 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Type/Db/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Type/Db/Pdo/Mysql.php @@ -114,6 +114,12 @@ class Mysql extends \Magento\Framework\Model\ResourceModel\Type\Db implements Co } } + if (isset($config['port'])) { + throw new \InvalidArgumentException( + "Port must be configured within host (like '$config[host]:$config[port]') parameter, not within port" + ); + } + $config['active'] = !( $config['active'] === 'false' || $config['active'] === false diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index 43bdd86e15659ed3430e475b8e7755712b2b0220..e86bed1c03fafc40369900f092fc0a6b59d5d108 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -8,7 +8,7 @@ "AFL-3.0" ], "require": { - "php": "~5.6.0|7.0.2|~7.0.6", + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", "ext-spl": "*", "ext-dom": "*", "ext-simplexml": "*", diff --git a/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php b/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php index 577e787bdd5f286dc238fdce41f1a016a98051b2..bbd0a44254e89ce2e84d3bbe7e043ac80c76a435 100644 --- a/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php +++ b/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php @@ -7,7 +7,6 @@ namespace Magento\Setup\Console\Command; use Magento\Framework\Setup\ConsoleLogger; use Magento\Setup\Model\InstallerFactory; -use Magento\Setup\Model\ObjectManagerProvider; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -29,21 +28,14 @@ class UpgradeCommand extends AbstractSetupCommand */ private $installerFactory; - /** - * @var \Magento\Setup\Model\ObjectManagerProvider; - */ - private $objectManagerProvider; - /** * Constructor * * @param InstallerFactory $installerFactory - * @param ObjectManagerProvider $objectManagerProvider */ - public function __construct(InstallerFactory $installerFactory, ObjectManagerProvider $objectManagerProvider) + public function __construct(InstallerFactory $installerFactory) { $this->installerFactory = $installerFactory; - $this->objectManagerProvider = $objectManagerProvider; parent::__construct(); } @@ -73,16 +65,6 @@ class UpgradeCommand extends AbstractSetupCommand */ protected function execute(InputInterface $input, OutputInterface $output) { - $areaCode = 'setup'; - /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ - $objectManager = $this->objectManagerProvider->get(); - /** @var \Magento\Framework\App\State $appState */ - $appState = $objectManager->get('Magento\Framework\App\State'); - $appState->setAreaCode($areaCode); - /** @var \Magento\Framework\ObjectManager\ConfigLoaderInterface $configLoader */ - $configLoader = $objectManager->get('Magento\Framework\ObjectManager\ConfigLoaderInterface'); - $objectManager->configure($configLoader->load($areaCode)); - $keepGenerated = $input->getOption(self::INPUT_KEY_KEEP_GENERATED); $installer = $this->installerFactory->create(new ConsoleLogger($output)); $installer->updateModulesSequence($keepGenerated); diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php index 17d78718e4bf4bcdc2978c946fe98223b8169011..7c1f72a4edd27fb111ded76cc0b16a52f95d4865 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php @@ -7,35 +7,19 @@ namespace Magento\Setup\Test\Unit\Console\Command; use Magento\Setup\Console\Command\UpgradeCommand; use Symfony\Component\Console\Tester\CommandTester; +use Magento\Framework\Console\Cli; class UpgradeCommandTest extends \PHPUnit_Framework_TestCase { public function testExecute() { $installerFactory = $this->getMock('Magento\Setup\Model\InstallerFactory', [], [], '', false); - $objectManagerProvider = $this->getMock('\Magento\Setup\Model\ObjectManagerProvider', [], [], '', false); - $objectManager = $this->getMockForAbstractClass('Magento\Framework\ObjectManagerInterface'); - $configLoader = $this->getMockForAbstractClass('Magento\Framework\ObjectManager\ConfigLoaderInterface'); - $configLoader->expects($this->once())->method('load')->willReturn(['some_key' => 'some_value']); - $state = $this->getMock('Magento\Framework\App\State', [], [], '', false); - $state->expects($this->once())->method('setAreaCode')->with('setup'); - $objectManagerProvider->expects($this->once())->method('get')->willReturn($objectManager); - $objectManager->expects($this->once())->method('configure'); - $state->expects($this->once())->method('setAreaCode')->with('setup'); $installer = $this->getMock('Magento\Setup\Model\Installer', [], [], '', false); $installer->expects($this->at(0))->method('updateModulesSequence'); $installer->expects($this->at(1))->method('installSchema'); $installer->expects($this->at(2))->method('installDataFixtures'); $installerFactory->expects($this->once())->method('create')->willReturn($installer); - - $objectManager->expects($this->exactly(2)) - ->method('get') - ->will($this->returnValueMap([ - ['Magento\Framework\App\State', $state], - ['Magento\Framework\ObjectManager\ConfigLoaderInterface', $configLoader] - ])); - - $commandTester = new CommandTester(new UpgradeCommand($installerFactory, $objectManagerProvider)); - $commandTester->execute([]); + $commandTester = new CommandTester(new UpgradeCommand($installerFactory)); + $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->execute([])); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php b/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php index 5b076e122a6e11057928132344baf8357f2d0a6f..af60706fc438e41865aee39fa3a3746ce25eccb9 100644 --- a/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Validator/DbValidatorTest.php @@ -38,7 +38,7 @@ class DbValidatorTest extends \PHPUnit_Framework_TestCase public function testCheckDatabaseConnection() { $this->connection - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('fetchOne') ->with('SELECT version()') ->willReturn('5.6.0-0ubuntu0.12.04.1'); @@ -79,6 +79,7 @@ class DbValidatorTest extends \PHPUnit_Framework_TestCase ] ); $this->assertEquals(true, $this->dbValidator->checkDatabaseConnection('name', 'host', 'user', 'password')); + $this->assertEquals(true, $this->dbValidator->checkDatabaseConnection('name', 'host:3339', 'user', 'password')); } /**