diff --git a/app/bootstrap.php b/app/bootstrap.php index 0b47e6303cf3f..44726d993a0ea 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -35,6 +35,17 @@ $mask = file_exists($umaskFile) ? octdec(file_get_contents($umaskFile)) : 002; umask($mask); +if (empty($_SERVER['ENABLE_IIS_REWRITES']) || ($_SERVER['ENABLE_IIS_REWRITES'] != 1)) { + /* + * Unset headers used by IIS URL rewrites. + */ + unset($_SERVER['HTTP_X_REWRITE_URL']); + unset($_SERVER['HTTP_X_ORIGINAL_URL']); + unset($_SERVER['IIS_WasUrlRewritten']); + unset($_SERVER['UNENCODED_URL']); + unset($_SERVER['ORIG_PATH_INFO']); +} + if (!empty($_SERVER['MAGE_PROFILER']) && isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'text/html') !== false diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php index 92f2d82ef2736..2fb2e6f7c71a5 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php @@ -52,12 +52,19 @@ public function execute() $user->setPassword($password); $user->setPasswordConfirmation($passwordConfirmation); } - $user->save(); - /** Send password reset email notification only when password was changed */ - if ($password !== '') { - $user->sendPasswordResetNotificationEmail(); + $errors = $user->validate(); + if ($errors !== true && !empty($errors)) { + foreach ($errors as $error) { + $this->messageManager->addError($error); + } + } else { + $user->save(); + /** Send password reset email notification only when password was changed */ + if ($password !== '') { + $user->sendPasswordResetNotificationEmail(); + } + $this->messageManager->addSuccess(__('You saved the account.')); } - $this->messageManager->addSuccess(__('You saved the account.')); } catch (ValidatorException $e) { $this->messageManager->addMessages($e->getMessages()); if ($e->getMessage()) { diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php index 6bfbdd8c3a234..71014ac05c5d4 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php @@ -76,7 +76,10 @@ protected function setUp() $this->_userMock = $this->getMockBuilder('Magento\User\Model\User') ->disableOriginalConstructor() ->setMethods( - ['load', 'save', 'sendPasswordResetNotificationEmail', 'verifyIdentity', '__sleep', '__wakeup'] + [ + 'load', 'save', 'sendPasswordResetNotificationEmail', + 'verifyIdentity', 'validate', '__sleep', '__wakeup' + ] ) ->getMock(); @@ -193,6 +196,7 @@ public function testSaveAction() $this->_userMock->expects($this->once())->method('save'); $this->_userMock->expects($this->once())->method('verifyIdentity')->will($this->returnValue(true)); + $this->_userMock->expects($this->once())->method('validate')->willReturn(true); $this->_userMock->expects($this->once())->method('sendPasswordResetNotificationEmail'); $this->_requestMock->setParams($requestParams); diff --git a/app/code/Magento/Backend/composer.json b/app/code/Magento/Backend/composer.json index 3f4eaf3be7103..839824ac0b5f4 100644 --- a/app/code/Magento/Backend/composer.json +++ b/app/code/Magento/Backend/composer.json @@ -21,7 +21,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.7", + "version": "100.0.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json index acbf6f227bdec..7d0abd2619bca 100644 --- a/app/code/Magento/Braintree/composer.json +++ b/app/code/Magento/Braintree/composer.json @@ -22,7 +22,7 @@ "magento/module-checkout-agreements": "100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "proprietary" ], diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index abe72e14dd310..f878b89e14fc4 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -132,7 +132,7 @@ public function execute() $attributesData[$attributeCode] = $value; } elseif ($attribute->getFrontendInput() == 'multiselect') { // Check if 'Change' checkbox has been checked by admin for this attribute - $isChanged = (bool)$this->getRequest()->getPost($attributeCode . '_checkbox'); + $isChanged = (bool)$this->getRequest()->getPost('toggle_' . $attributeCode); if (!$isChanged) { unset($attributesData[$attributeCode]); continue; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index 95eb7b4cd7983..27eef1a6e499b 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -73,7 +73,6 @@ public function execute() $productTypeId = $this->getRequest()->getParam('type'); if ($data) { try { - $data = $this->unserializeData($data); $product = $this->initializationHelper->initialize($this->productBuilder->build($this->getRequest())); $this->productTypeManager->processProduct($product); @@ -158,29 +157,6 @@ public function execute() return $resultRedirect; } - /** - * @param array $data - * @return array - */ - private function unserializeData($data) - { - if (isset($data["configurable-matrix-serialized"])) { - $configurableMatrixSerialized = $data["configurable-matrix-serialized"]; - if ($configurableMatrixSerialized != null && !empty($configurableMatrixSerialized)) { - $data["variations-matrix"] = json_decode($configurableMatrixSerialized, true); - unset($data["configurable-matrix-serialized"]); - } - } - if (isset($data["associated_product_ids_serialized"])) { - $associatedProductIdsSerialized = $data["associated_product_ids_serialized"]; - if ($associatedProductIdsSerialized != null && !empty($associatedProductIdsSerialized)) { - $data["associated_product_ids"] = json_decode($associatedProductIdsSerialized, true); - unset($data["associated_product_ids_serialized"]); - } - } - return $data; - } - /** * Notify customer when image was not deleted in specific case. * TODO: temporary workaround must be eliminated in MAGETWO-45306 diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index bf19a151ce288..70cd26b73728f 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -34,7 +34,7 @@ "magento/module-catalog-sample-data": "Sample Data version:100.0.*" }, "type": "magento2-module", - "version": "100.0.9", + "version": "100.0.10", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php b/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php index 8f85fe6881c7d..202f82c54bddc 100644 --- a/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php +++ b/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php @@ -13,7 +13,13 @@ use Magento\Framework\Json\Helper\Data; use Magento\Framework\View\Result\PageFactory; use Psr\Log\LoggerInterface; +use Magento\Framework\Data\Form\FormKey\Validator; +use Magento\Framework\App\ObjectManager; +/** + * Class RemoveItem + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class RemoveItem extends Action { /** @@ -36,6 +42,11 @@ class RemoveItem extends Action */ protected $resultPageFactory; + /** + * @var Validator + */ + private $formKeyValidator; + /** * @param Context $context * @param Sidebar $sidebar @@ -59,12 +70,17 @@ public function __construct( } /** + * Executes the main action of the controller + * * @return $this */ public function execute() { $itemId = (int)$this->getRequest()->getParam('item_id'); try { + if (!$this->getFormKeyValidator()->validate($this->getRequest())) { + throw new LocalizedException(__('We can\'t remove the item.')); + } $this->sidebar->checkQuoteItem($itemId); $this->sidebar->removeQuoteItem($itemId); return $this->jsonResponse(); @@ -90,4 +106,18 @@ protected function jsonResponse($error = '') $this->jsonHelper->jsonEncode($response) ); } + + /** + * Getter for FormKeyValidator + * + * @deprecated + * @return Validator + */ + private function getFormKeyValidator() + { + if ($this->formKeyValidator === null) { + $this->formKeyValidator = ObjectManager::getInstance()->get(Validator::class); + } + return $this->formKeyValidator; + } } diff --git a/app/code/Magento/Checkout/Controller/Sidebar/UpdateItemQty.php b/app/code/Magento/Checkout/Controller/Sidebar/UpdateItemQty.php index ef1cdd614ee8a..b0403f469dbf9 100644 --- a/app/code/Magento/Checkout/Controller/Sidebar/UpdateItemQty.php +++ b/app/code/Magento/Checkout/Controller/Sidebar/UpdateItemQty.php @@ -12,7 +12,13 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Json\Helper\Data; use Psr\Log\LoggerInterface; +use Magento\Framework\Data\Form\FormKey\Validator; +use \Magento\Framework\App\ObjectManager; +/** + * Class UpdateItemQty + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class UpdateItemQty extends Action { /** @@ -30,6 +36,11 @@ class UpdateItemQty extends Action */ protected $jsonHelper; + /** + * @var Validator + */ + private $formKeyValidator; + /** * @param Context $context * @param Sidebar $sidebar @@ -50,6 +61,8 @@ public function __construct( } /** + * Executes the main action of the controller + * * @return $this */ public function execute() @@ -58,6 +71,9 @@ public function execute() $itemQty = (int)$this->getRequest()->getParam('item_qty'); try { + if (!$this->getFormKeyValidator()->validate($this->getRequest())) { + throw new LocalizedException(__('We can\'t update the shopping cart.')); + } $this->sidebar->checkQuoteItem($itemId); $this->sidebar->updateQuoteItem($itemId, $itemQty); return $this->jsonResponse(); @@ -81,4 +97,18 @@ protected function jsonResponse($error = '') $this->jsonHelper->jsonEncode($this->sidebar->getResponseData($error)) ); } + + /** + * Getter for FormKeyValidator + * + * @deprecated + * @return Validator + */ + private function getFormKeyValidator() + { + if ($this->formKeyValidator === null) { + $this->formKeyValidator = ObjectManager::getInstance()->get(Validator::class); + } + return $this->formKeyValidator; + } } diff --git a/app/code/Magento/Checkout/Model/Cart.php b/app/code/Magento/Checkout/Model/Cart.php index 585a1ed5336cf..759ce84ce8fc3 100644 --- a/app/code/Magento/Checkout/Model/Cart.php +++ b/app/code/Magento/Checkout/Model/Cart.php @@ -8,8 +8,8 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; use Magento\Checkout\Model\Cart\CartInterface; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\DataObject; +use Magento\Framework\Exception\NoSuchEntityException; /** * Shopping cart model @@ -90,6 +90,11 @@ class Cart extends DataObject implements CartInterface */ protected $productRepository; + /** + * @var \Magento\Checkout\Model\Cart\RequestInfoFilterInterface + */ + private $requestInfoFilter; + /** * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig @@ -310,6 +315,7 @@ protected function _getProduct($productInfo) * * @param \Magento\Framework\DataObject|int|array $requestInfo * @return \Magento\Framework\DataObject + * @throws \Magento\Framework\Exception\LocalizedException */ protected function _getProductRequest($requestInfo) { @@ -317,15 +323,19 @@ protected function _getProductRequest($requestInfo) $request = $requestInfo; } elseif (is_numeric($requestInfo)) { $request = new \Magento\Framework\DataObject(['qty' => $requestInfo]); - } else { + } elseif (is_array($requestInfo)) { $request = new \Magento\Framework\DataObject($requestInfo); + } else { + throw new \Magento\Framework\Exception\LocalizedException( + __('We found an invalid request for adding product to quote.') + ); } if (!$request->hasQty()) { $request->setQty(1); } - !$request->hasFormKey() ?: $request->unsFormKey(); + $this->getRequestInfoFilter()->filter($request); return $request; } @@ -720,4 +730,19 @@ public function updateItem($itemId, $requestInfo = null, $updatingParams = null) $this->_checkoutSession->setLastAddedProductId($productId); return $result; } + + /** + * Getter for RequestInfoFilter + * + * @deprecated + * @return \Magento\Checkout\Model\Cart\RequestInfoFilterInterface + */ + private function getRequestInfoFilter() + { + if ($this->requestInfoFilter === null) { + $this->requestInfoFilter = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Checkout\Model\Cart\RequestInfoFilterInterface::class); + } + return $this->requestInfoFilter; + } } diff --git a/app/code/Magento/Checkout/Model/Cart/RequestInfoFilter.php b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilter.php new file mode 100644 index 0000000000000..10f3b81386b8f --- /dev/null +++ b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilter.php @@ -0,0 +1,44 @@ +filterList = $filterList; + } + + /** + * Filters the data with values from filterList + * + * @param \Magento\Framework\DataObject $params + * @return $this + */ + public function filter(\Magento\Framework\DataObject $params) + { + foreach ($this->filterList as $filterKey) { + /** @var string $filterKey */ + if ($params->hasData($filterKey)) { + $params->unsetData($filterKey); + } + } + return $this; + } +} diff --git a/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterComposite.php b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterComposite.php new file mode 100644 index 0000000000000..2ef24c0a5f28a --- /dev/null +++ b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterComposite.php @@ -0,0 +1,41 @@ +filters = $filters; + } + + /** + * Loops through all leafs of the composite and calls filter method + * + * @param \Magento\Framework\DataObject $params + * @return $this + */ + public function filter(\Magento\Framework\DataObject $params) + { + foreach ($this->filters as $filter) { + $filter->filter($params); + } + return $this; + } +} diff --git a/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterInterface.php b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterInterface.php new file mode 100644 index 0000000000000..4bd268f6c896e --- /dev/null +++ b/app/code/Magento/Checkout/Model/Cart/RequestInfoFilterInterface.php @@ -0,0 +1,21 @@ +sidebarMock = $this->getMock('Magento\Checkout\Model\Sidebar', [], [], '', false); - $this->loggerMock = $this->getMock('Psr\Log\LoggerInterface'); - $this->jsonHelperMock = $this->getMock('Magento\Framework\Json\Helper\Data', [], [], '', false); - $this->requestMock = $this->getMock('Magento\Framework\App\RequestInterface'); + $this->sidebarMock = $this->getMock(Sidebar::class, [], [], '', false); + $this->loggerMock = $this->getMock(LoggerInterface::class); + $this->jsonHelperMock = $this->getMock(Data::class, [], [], '', false); + $this->requestMock = $this->getMock(RequestInterface::class); $this->responseMock = $this->getMockForAbstractClass( - 'Magento\Framework\App\ResponseInterface', + ResponseInterface::class, [], '', false, @@ -49,11 +65,12 @@ protected function setUp() true, ['representJson'] ); - $this->resultPageFactoryMock = $this->getMock('Magento\Framework\View\Result\PageFactory', [], [], '', false); + $this->resultPageFactoryMock = + $this->getMock(PageFactory::class, [], [], '', false); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->removeItem = $this->objectManagerHelper->getObject( - 'Magento\Checkout\Controller\Sidebar\RemoveItem', + RemoveItem::class, [ 'sidebar' => $this->sidebarMock, 'logger' => $this->loggerMock, @@ -63,6 +80,15 @@ protected function setUp() 'resultPageFactory' => $this->resultPageFactoryMock, ] ); + + $this->formKeyValidatorMock = + $this->getMock(Validator::class, [], [], '', false); + + $this->objectManagerHelper->setBackwardCompatibleProperty( + $this->removeItem, + 'formKeyValidator', + $this->formKeyValidatorMock + ); } public function testExecute() @@ -72,6 +98,10 @@ public function testExecute() ->with('item_id', null) ->willReturn('1'); + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $this->sidebarMock->expects($this->once()) ->method('checkQuoteItem') ->with(1) @@ -116,6 +146,49 @@ public function testExecute() $this->assertEquals('json represented', $this->removeItem->execute()); } + public function testExecuteWithValidationLocalizedException() + { + $exceptionMessage = 'We can\'t remove the item.'; + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('item_id', null) + ->willReturn('1'); + + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(false); + + $this->sidebarMock->expects($this->never()) + ->method('checkQuoteItem'); + + $this->sidebarMock->expects($this->once()) + ->method('getResponseData') + ->with($exceptionMessage) + ->willReturn( + [ + 'success' => false, + 'error_message' => $exceptionMessage, + ] + ); + + $this->jsonHelperMock->expects($this->once()) + ->method('jsonEncode') + ->with( + [ + 'success' => false, + 'error_message' => $exceptionMessage, + ] + ) + ->willReturn('json encoded'); + + $this->responseMock->expects($this->once()) + ->method('representJson') + ->with('json encoded') + ->willReturn('json represented'); + + $this->assertEquals('json represented', $this->removeItem->execute()); + } + public function testExecuteWithLocalizedException() { $this->requestMock->expects($this->once()) @@ -123,6 +196,10 @@ public function testExecuteWithLocalizedException() ->with('item_id', null) ->willReturn('1'); + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $this->sidebarMock->expects($this->once()) ->method('checkQuoteItem') ->with(1) @@ -163,6 +240,10 @@ public function testExecuteWithException() ->with('item_id', null) ->willReturn('1'); + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $exception = new \Exception('Error message!'); $this->sidebarMock->expects($this->once()) diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/UpdateItemQtyTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/UpdateItemQtyTest.php index c067287cb9030..6c97682849692 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/UpdateItemQtyTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/UpdateItemQtyTest.php @@ -5,40 +5,54 @@ */ namespace Magento\Checkout\Test\Unit\Controller\Sidebar; +use Magento\Checkout\Controller\Sidebar\UpdateItemQty; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Checkout\Model\Sidebar; +use Psr\Log\LoggerInterface; +use Magento\Framework\Json\Helper\Data; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Data\Form\FormKey\Validator; +/** + * Class UpdateItemQtyTest + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class UpdateItemQtyTest extends \PHPUnit_Framework_TestCase { - /** @var \Magento\Checkout\Controller\Sidebar\UpdateItemQty */ + /** @var UpdateItemQty */ protected $updateItemQty; /** @var ObjectManagerHelper */ protected $objectManagerHelper; - /** @var \Magento\Checkout\Model\Sidebar|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Sidebar|\PHPUnit_Framework_MockObject_MockObject */ protected $sidebarMock; - /** @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $loggerMock; - /** @var \Magento\Framework\Json\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Data|\PHPUnit_Framework_MockObject_MockObject */ protected $jsonHelperMock; - /** @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $requestMock; - /** @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ResponseInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $responseMock; + /** @var Validator|\PHPUnit_Framework_MockObject_MockObject */ + private $formKeyValidatorMock; + protected function setUp() { - $this->sidebarMock = $this->getMock('Magento\Checkout\Model\Sidebar', [], [], '', false); - $this->loggerMock = $this->getMock('Psr\Log\LoggerInterface'); - $this->jsonHelperMock = $this->getMock('Magento\Framework\Json\Helper\Data', [], [], '', false); - $this->requestMock = $this->getMock('Magento\Framework\App\RequestInterface'); + $this->sidebarMock = $this->getMock(Sidebar::class, [], [], '', false); + $this->loggerMock = $this->getMock(LoggerInterface::class); + $this->jsonHelperMock = $this->getMock(Data::class, [], [], '', false); + $this->requestMock = $this->getMock(RequestInterface::class); $this->responseMock = $this->getMockForAbstractClass( - 'Magento\Framework\App\ResponseInterface', + ResponseInterface::class, [], '', false, @@ -49,7 +63,7 @@ protected function setUp() $this->objectManagerHelper = new ObjectManagerHelper($this); $this->updateItemQty = $this->objectManagerHelper->getObject( - 'Magento\Checkout\Controller\Sidebar\UpdateItemQty', + UpdateItemQty::class, [ 'sidebar' => $this->sidebarMock, 'logger' => $this->loggerMock, @@ -58,6 +72,15 @@ protected function setUp() 'response' => $this->responseMock, ] ); + + $this->formKeyValidatorMock = + $this->getMock(Validator::class, [], [], '', false); + + $this->objectManagerHelper->setBackwardCompatibleProperty( + $this->updateItemQty, + 'formKeyValidator', + $this->formKeyValidatorMock + ); } public function testExecute() @@ -71,6 +94,10 @@ public function testExecute() ->with('item_qty', null) ->willReturn('2'); + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $this->sidebarMock->expects($this->once()) ->method('checkQuoteItem') ->with(1) @@ -113,6 +140,54 @@ public function testExecute() $this->assertEquals('json represented', $this->updateItemQty->execute()); } + + public function testExecuteWithValidateLocalizedException() + { + $exceptionMessage = 'We can\'t update the shopping cart.'; + $this->requestMock->expects($this->at(0)) + ->method('getParam') + ->with('item_id', null) + ->willReturn('1'); + $this->requestMock->expects($this->at(1)) + ->method('getParam') + ->with('item_qty', null) + ->willReturn('2'); + + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(false); + + $this->sidebarMock->expects($this->never()) + ->method('checkQuoteItem'); + + $this->sidebarMock->expects($this->once()) + ->method('getResponseData') + ->with($exceptionMessage) + ->willReturn( + [ + 'success' => false, + 'error_message' => $exceptionMessage, + ] + ); + + $this->jsonHelperMock->expects($this->once()) + ->method('jsonEncode') + ->with( + [ + 'success' => false, + 'error_message' => $exceptionMessage, + ] + ) + ->willReturn('json encoded'); + + $this->responseMock->expects($this->once()) + ->method('representJson') + ->with('json encoded') + ->willReturn('json represented'); + + $this->assertEquals('json represented', $this->updateItemQty->execute()); + } + public function testExecuteWithLocalizedException() { $this->requestMock->expects($this->at(0)) @@ -124,6 +199,10 @@ public function testExecuteWithLocalizedException() ->with('item_qty', null) ->willReturn('2'); + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $this->sidebarMock->expects($this->once()) ->method('checkQuoteItem') ->with(1) @@ -168,6 +247,10 @@ public function testExecuteWithException() ->with('item_qty', null) ->willReturn('2'); + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $exception = new \Exception('Error!'); $this->sidebarMock->expects($this->once()) diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterCompositeTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterCompositeTest.php new file mode 100644 index 0000000000000..6c758cf4661fd --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterCompositeTest.php @@ -0,0 +1,73 @@ +objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $requestInfoFilterMock1 = $this->getMock( + \Magento\Checkout\Model\Cart\RequestInfoFilter::class, + ['filter'], + [], + '', + false + ); + $requestInfoFilterMock2 = $this->getMock( + \Magento\Checkout\Model\Cart\RequestInfoFilter::class, + ['filter'], + [], + '', + false + ); + + $requestInfoFilterMock1->expects($this->atLeastOnce()) + ->method('filter'); + $requestInfoFilterMock2->expects($this->atLeastOnce()) + ->method('filter'); + + $filterList = [ $requestInfoFilterMock1, $requestInfoFilterMock2]; + + $this->model = $this->objectManager->getObject( + \Magento\Checkout\Model\Cart\RequestInfoFilterComposite::class, + [ + 'filters' => $filterList, + ] + ); + } + + /** + * Test Filter method + */ + public function testFilter() + { + /** @var \Magento\Framework\DataObject $params */ + $params = $this->objectManager->getObject( + \Magento\Framework\DataObject::class, + ['data' => ['abc' => 1, 'efg' => 1, 'xyz' => 1]] + ); + $result = $this->model->filter($params); + $this->assertEquals($this->model, $result); + } +} diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterTest.php new file mode 100644 index 0000000000000..cd6ca330da1b2 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/RequestInfoFilterTest.php @@ -0,0 +1,52 @@ +objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->model = $this->objectManager->getObject( + \Magento\Checkout\Model\Cart\RequestInfoFilter::class, + [ + 'filterList' => ['efg', 'xyz'], + ] + ); + } + + /** + * Test Filter method + */ + public function testFilter() + { + /** @var \Magento\Framework\DataObject $params */ + $params = $this->objectManager->getObject( + \Magento\Framework\DataObject::class, + ['data' => ['abc' => 1, 'efg' => 1, 'xyz' => 1]] + ); + $result = $this->model->filter($params); + $this->assertEquals($this->model, $result); + $this->assertEquals(['abc' => 1], $params->convertToArray()); + } +} diff --git a/app/code/Magento/Checkout/Test/Unit/Model/CartTest.php b/app/code/Magento/Checkout/Test/Unit/Model/CartTest.php index 90e0fddd03373..55e070b15b860 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/CartTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/CartTest.php @@ -7,9 +7,12 @@ namespace Magento\Checkout\Test\Unit\Model; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Catalog\Model\Product; /** * Class CartTest + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CartTest extends \PHPUnit_Framework_TestCase { @@ -27,7 +30,7 @@ class CartTest extends \PHPUnit_Framework_TestCase */ protected $customerSessionMock; - /** @var \Magento\CatalogInventory\Api\StockItem|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \PHPUnit_Framework_MockObject_MockObject */ protected $stockItemMock; /** @@ -55,6 +58,26 @@ class CartTest extends \PHPUnit_Framework_TestCase */ protected $stockState; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $storeMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $productRepository; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $requestInfoFilterMock; + protected function setUp() { $this->checkoutSessionMock = $this->getMock('Magento\Checkout\Model\Session', [], [], '', false); @@ -81,11 +104,41 @@ protected function setUp() false ); + $this->storeManagerMock = $this->getMockForAbstractClass( + \Magento\Store\Model\StoreManagerInterface::class, + [], + '', + false + ); + $this->productRepository = $this->getMockForAbstractClass( + \Magento\Catalog\Api\ProductRepositoryInterface::class, + ['getById'], + '', + false + ); $this->stockRegistry->expects($this->any()) ->method('getStockItem') ->will($this->returnValue($this->stockItemMock)); $this->quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); $this->eventManagerMock = $this->getMock('Magento\Framework\Event\ManagerInterface'); + $this->storeMock = $this->getMock( + \Magento\Store\Model\Store::class, + ['getWebsiteId', 'getId', '__wakeup'], + [], + '', + false + ); + $this->requestInfoFilterMock = $this->getMock(\Magento\Checkout\Model\Cart\RequestInfoFilterInterface::class); + + $this->storeMock->expects($this->any()) + ->method('getWebsiteId') + ->will($this->returnValue(10)); + $this->storeMock->expects($this->any()) + ->method('getId') + ->will($this->returnValue(10)); + $this->storeManagerMock->expects($this->any()) + ->method('getStore') + ->will($this->returnValue($this->storeMock)); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->cart = $this->objectManagerHelper->getObject( @@ -96,9 +149,17 @@ protected function setUp() 'stockRegistry' => $this->stockRegistry, 'stockState' => $this->stockState, 'customerSession' => $this->customerSessionMock, - 'eventManager' => $this->eventManagerMock + 'eventManager' => $this->eventManagerMock, + 'storeManager' => $this->storeManagerMock, + 'productRepository' => $this->productRepository ] ); + + $this->objectManagerHelper->setBackwardCompatibleProperty( + $this->cart, + 'requestInfoFilter', + $this->requestInfoFilterMock + ); } public function testSuggestItemsQty() @@ -250,4 +311,162 @@ public function useQtyDataProvider() ['useQty' => false] ]; } + + /** + * Test successful scenarios for AddProduct + * + * @param int|Product $productInfo + * @param \Magento\Framework\DataObject|int|array $requestInfo + * @dataProvider addProductDataProvider + */ + public function testAddProduct($productInfo, $requestInfo) + { + $product = $this->getMock( + \Magento\Catalog\Model\Product::class, + ['getStore', 'getWebsiteIds', 'getProductUrl', 'getId', '__wakeup'], + [], + '', + false + ); + $product->expects($this->any()) + ->method('getId') + ->will($this->returnValue(4)); + $product->expects($this->once()) + ->method('getStore') + ->will($this->returnValue($this->storeMock)); + $product->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue([10])); + $product->expects($this->any()) + ->method('getProductUrl') + ->will($this->returnValue('url')); + $this->productRepository->expects($this->any()) + ->method('getById') + ->will($this->returnValue($product)); + $this->quoteMock->expects($this->once()) + ->method('addProduct') + ->will($this->returnValue(1)); + $this->checkoutSessionMock->expects($this->once()) + ->method('getQuote') + ->will($this->returnValue($this->quoteMock)); + + $this->eventManagerMock->expects($this->at(0))->method('dispatch')->with( + 'checkout_cart_product_add_after', + ['quote_item' => 1, 'product' => $product] + ); + + if (!$productInfo) { + $productInfo = $product; + } + $result = $this->cart->addProduct($productInfo, $requestInfo); + $this->assertSame($this->cart, $result); + } + + /** + * Test exception on adding product for AddProduct + * + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testAddProductException() + { + $product = $this->getMock( + \Magento\Catalog\Model\Product::class, + ['getStore', 'getWebsiteIds', 'getProductUrl', 'getId', '__wakeup'], + [], + '', + false + ); + $product->expects($this->any()) + ->method('getId') + ->will($this->returnValue(4)); + $product->expects($this->once()) + ->method('getStore') + ->will($this->returnValue($this->storeMock)); + $product->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue([10])); + $product->expects($this->any()) + ->method('getProductUrl') + ->will($this->returnValue('url')); + $this->productRepository->expects($this->any()) + ->method('getById') + ->will($this->returnValue($product)); + $this->quoteMock->expects($this->once()) + ->method('addProduct') + ->will($this->returnValue('error')); + $this->checkoutSessionMock->expects($this->once()) + ->method('getQuote') + ->will($this->returnValue($this->quoteMock)); + + $this->eventManagerMock->expects($this->never())->method('dispatch')->with( + 'checkout_cart_product_add_after', + ['quote_item' => 1, 'product' => $product] + ); + $this->setExpectedException(\Magento\Framework\Exception\LocalizedException::class); + $this->cart->addProduct(4, 4); + } + + /** + * Test bad parameters on adding product for AddProduct + * + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testAddProductExceptionBadParams() + { + $product = $this->getMock( + \Magento\Catalog\Model\Product::class, + ['getWebsiteIds', 'getId', '__wakeup'], + [], + '', + false + ); + $product->expects($this->any()) + ->method('getId') + ->will($this->returnValue(4)); + $product->expects($this->any()) + ->method('getWebsiteIds') + ->will($this->returnValue([10])); + $this->productRepository->expects($this->any()) + ->method('getById') + ->will($this->returnValue($product)); + + $this->eventManagerMock->expects($this->never())->method('dispatch')->with( + 'checkout_cart_product_add_after', + ['quote_item' => 1, 'product' => $product] + ); + $this->setExpectedException(\Magento\Framework\Exception\LocalizedException::class); + $this->cart->addProduct(4, 'bad'); + } + + /** + * Data provider for testAddProduct + * + * @return array + */ + public function addProductDataProvider() + { + $obj = new ObjectManagerHelper($this) ; + $data = ['qty' => 5.5, 'sku' => 'prod']; + + return [ + 'prod_int_info_int' => [4, 4], + 'prod_int_info_array' => [ 4, $data], + 'prod_int_info_object' => [ + 4, + $obj->getObject( + \Magento\Framework\DataObject::class, + ['data' => $data] + ) + ], + 'prod_obj_info_int' => [null, 4], + 'prod_obj_info_array' => [ null, $data], + 'prod_obj_info_object' => [ + null, + $obj->getObject( + \Magento\Framework\DataObject::class, + ['data' => $data] + ) + ] + ]; + } } diff --git a/app/code/Magento/Checkout/composer.json b/app/code/Magento/Checkout/composer.json index e3d69a6a2f5a1..40c0274172580 100644 --- a/app/code/Magento/Checkout/composer.json +++ b/app/code/Magento/Checkout/composer.json @@ -27,7 +27,7 @@ "magento/module-cookie": "100.0.*" }, "type": "magento2-module", - "version": "100.0.8", + "version": "100.0.9", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Checkout/etc/di.xml b/app/code/Magento/Checkout/etc/di.xml index c55598fdff5e7..8b99a4fc43fad 100644 --- a/app/code/Magento/Checkout/etc/di.xml +++ b/app/code/Magento/Checkout/etc/di.xml @@ -26,4 +26,19 @@ + + + + + form_key + + + + + + + Magento\Checkout\Model\Cart\RequestInfoFilter + + + diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml index bccf81bcb6ee8..6fb9058c3b768 100644 --- a/app/code/Magento/Checkout/etc/frontend/di.xml +++ b/app/code/Magento/Checkout/etc/frontend/di.xml @@ -72,4 +72,12 @@ + + + + form_key + custom_price + + + diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js b/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js index d1bf7e1d0830d..59bf955a335ab 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js @@ -116,10 +116,25 @@ define( addressToEstimationAddress: function (address) { var estimatedAddressData = { - country_id: address.countryId, - region: address.region, - region_id: address.regionId, - postcode: address.postcode + 'street': address.street, + 'city': address.city, + 'region_id': address.regionId, + 'region': address.region, + 'country_id': address.countryId, + 'postcode': address.postcode, + 'email': address.email, + 'customer_id': address.customerId, + 'firstname': address.firstname, + 'lastname': address.lastname, + 'middlename': address.middlename, + 'prefix': address.prefix, + 'suffix': address.suffix, + 'vat_id': address.vatId, + 'company': address.company, + 'telephone': address.telephone, + 'fax': address.fax, + 'custom_attributes': address.customAttributes + }; return this.formAddressDataToQuoteAddress(estimatedAddressData); } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rate-processor/new-address.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rate-processor/new-address.js index 9e776288fe784..0e8fb8c889f4d 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rate-processor/new-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rate-processor/new-address.js @@ -25,10 +25,25 @@ define( serviceUrl = resourceUrlManager.getUrlForEstimationShippingMethodsForNewAddress(quote), payload = JSON.stringify({ address: { - country_id: address.countryId, - region_id: address.regionId, - region: address.region, - postcode: address.postcode + 'street': address.street, + 'city': address.city, + 'region_id': address.regionId, + 'region': address.region, + 'country_id': address.countryId, + 'postcode': address.postcode, + 'email': address.email, + 'customer_id': address.customerId, + 'firstname': address.firstname, + 'lastname': address.lastname, + 'middlename': address.middlename, + 'prefix': address.prefix, + 'suffix': address.suffix, + 'vat_id': address.vatId, + 'company': address.company, + 'telephone': address.telephone, + 'fax': address.fax, + 'custom_attributes': address.customAttributes, + 'save_in_address_book': address.saveInAddressBook } } ); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index 569391405500c..7a1fb1dcc385a 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -11,7 +11,8 @@ define([ 'Magento_Ui/js/modal/alert', 'Magento_Ui/js/modal/confirm', "jquery/ui", - "mage/decorate" + "mage/decorate", + 'mage/cookies' ], function($, authenticationPopup, customerData, alert, confirm){ $.widget('mage.sidebar', { @@ -195,6 +196,9 @@ define([ * @param callback - callback method to execute after AJAX success */ _ajax: function(url, data, elem, callback) { + $.extend(data, { + 'form_key': $.mage.cookies.get('form_key') + }); $.ajax({ url: url, data: data, diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php index ab1cd30b7b41e..a8049ba28b606 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php @@ -55,14 +55,8 @@ public function afterInitialize( $this->productType->setUsedProductAttributeIds($attributes, $product); $product->setNewVariationsAttributeSetId($setId); - $associatedProductIds = $this->request->getPost('associated_product_ids_serialized', '[]'); - if ($associatedProductIds !== null && !empty($associatedProductIds)) { - $associatedProductIds = json_decode($associatedProductIds, true); - } - $variationsMatrix = $this->request->getParam('configurable-matrix-serialized', '[]'); - if ($variationsMatrix !== null && !empty($variationsMatrix)) { - $variationsMatrix = json_decode($variationsMatrix, true); - } + $associatedProductIds = $this->request->getPost('associated_product_ids', []); + $variationsMatrix = $this->request->getParam('variations-matrix', []); if (!empty($variationsMatrix)) { $generatedProductIds = $this->variationHandler->generateSimpleProducts($product, $variationsMatrix); $associatedProductIds = array_merge($associatedProductIds, $generatedProductIds); diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php index a94d8a8889bf9..3037057f5c5f8 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php @@ -47,9 +47,6 @@ public function afterInitialize( \Magento\Catalog\Model\Product $configurableProduct ) { $configurations = $this->request->getParam('configurations', []); - if ($this->request->getParam('configurations_serialized')) { - $configurations = json_decode($this->request->getParam('configurations_serialized'), true); - } $configurations = $this->variationHandler->duplicateImagesForVariations($configurations); foreach ($configurations as $productId => $productData) { /** @var \Magento\Catalog\Model\Product $product */ diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php index dacc30e38dc57..5fa72c9af4131 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/ConfigurableTest.php @@ -79,23 +79,19 @@ public function testAfterInitializeIfAttributesNotEmptyAndActionNameNotGenerateV { $this->productMock->expects($this->once())->method('getTypeId')->willReturn(ConfigurableProduct::TYPE_CODE); $associatedProductIds = ['key' => 'value']; - $associatedProductIdsSerialized = json_encode($associatedProductIds); $generatedProductIds = ['key_one' => 'value_one']; $expectedArray = ['key' => 'value', 'key_one' => 'value_one']; $attributes = ['key' => 'value']; $postValue = 'postValue'; - $variationsMatrix = ['variationKey' => 'variationValue']; - $variationsMatrixSerialized = json_encode($variationsMatrix); - $postValueMap = [ ['new-variations-attribute-set-id', null, $postValue], - ['associated_product_ids_serialized', '[]', $associatedProductIdsSerialized], + ['associated_product_ids', [], $associatedProductIds], ['affect_configurable_product_attributes', null, $postValue], ]; $this->requestMock->expects($this->any())->method('getPost')->will($this->returnValueMap($postValueMap)); $paramValueMap = [ - ['configurable-matrix-serialized', '[]', $variationsMatrixSerialized], + ['variations-matrix', [], $postValue], ['attributes', null, $attributes], ]; $this->requestMock->expects($this->any())->method('getParam')->will($this->returnValueMap($paramValueMap)); @@ -114,7 +110,7 @@ public function testAfterInitializeIfAttributesNotEmptyAndActionNameNotGenerateV 'generateSimpleProducts' )->with( $this->productMock, - $variationsMatrix + $postValue )->will( $this->returnValue($generatedProductIds) ); @@ -127,17 +123,16 @@ public function testAfterInitializeIfAttributesNotEmptyAndActionNameGenerateVari { $this->productMock->expects($this->once())->method('getTypeId')->willReturn(ConfigurableProduct::TYPE_CODE); $associatedProductIds = ['key' => 'value']; - $associatedProductIdsSerialized = json_encode($associatedProductIds); $attributes = ['key' => 'value']; $postValue = 'postValue'; $valueMap = [ ['new-variations-attribute-set-id', null, $postValue], - ['associated_product_ids_serialized', '[]', $associatedProductIdsSerialized], + ['associated_product_ids', [], $associatedProductIds], ['affect_configurable_product_attributes', null, $postValue], ]; $this->requestMock->expects($this->any())->method('getPost')->will($this->returnValueMap($valueMap)); $paramValueMap = [ - ['variations-matrix', '[]', '[]'], + ['variations-matrix', [], []], ['attributes', null, $attributes], ]; $this->requestMock->expects($this->any())->method('getParam')->will($this->returnValueMap($paramValueMap)); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml index a6e24876833aa..5865ec97fea58 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml @@ -13,33 +13,30 @@
- + - + - +
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml index d1eb5a009d1b2..bb6195f9e8496 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml @@ -42,13 +42,6 @@ $currencySymbol = $block->getCurrencySymbol(); -
-
-
- -
-
-
@@ -83,12 +76,12 @@ $currencySymbol = $block->getCurrencySymbol(); + attr: { 'data-row-number': $index() }"> @@ -254,9 +227,6 @@ $currencySymbol = $block->getCurrencySymbol(); - - -
- @@ -126,39 +119,27 @@ $currencySymbol = $block->getCurrencySymbol(); + attr: {id: $parent.getRowId(variation, 'name'), + name: $parent.getVariationRowName(variation, 'name'), + value: variation.sku}"/> + attr: {id: $parent.getRowId(variation, 'configurable_attribute'), + name: $parent.getVariationRowName(variation, 'configurable_attribute'), + value: variation.attribute}"/> + attr: {name: $parent.getVariationRowName(variation, 'status'), + value: variation.status}"/>
- + @@ -167,16 +148,12 @@ $currencySymbol = $block->getCurrencySymbol();
- +
@@ -203,13 +178,11 @@ $currencySymbol = $block->getCurrencySymbol(); - +
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/paging/sizes.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/paging/sizes.js deleted file mode 100644 index 92a83f1bbdd49..0000000000000 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/paging/sizes.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -define([ - 'Magento_Ui/js/grid/paging/sizes' -], function (Sizes) { - 'use strict'; - - return Sizes.extend({ - defaults: { - excludedOptions: ['100', '200'] - }, - - /** - * @override - */ - initialize: function () { - this._super(); - - this.excludedOptions.forEach(function (excludedOption) { - delete this.options[excludedOption]; - }, this); - this.updateArray(); - - return this; - } - }); -}); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/summary.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/summary.js index b67d670475b3a..b5526b1cccc6c 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/summary.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/summary.js @@ -8,9 +8,8 @@ define([ 'jquery', 'ko', 'underscore', - 'Magento_Ui/js/grid/paging/paging', 'mage/translate' -], function (Component, $, ko, _, paging) { +], function (Component, $, ko, _) { 'use strict'; return Component.extend({ @@ -25,61 +24,22 @@ define([ gridExisting: [], gridNew: [], gridDeleted: [], - variationsExisting: [], - variationsNew: [], - variationsDeleted: [], - pagingExisting: paging({ - name: 'configurableWizard.pagingExisting', - sizesConfig: { - component: 'Magento_ConfigurableProduct/js/variations/paging/sizes', - name: 'configurableWizard.pagingExisting_sizes' - } - }), - pagingNew: paging({ - name: 'configurableWizard.pagingNew', - sizesConfig: { - component: 'Magento_ConfigurableProduct/js/variations/paging/sizes', - name: 'configurableWizard.pagingNew_sizes' - } - }), - pagingDeleted: paging({ - name: 'configurableWizard.pagingDeleted', - sizesConfig: { - component: 'Magento_ConfigurableProduct/js/variations/paging/sizes', - name: 'configurableWizard.pagingDeleted_sizes' - } - }), attributes: [], attributesName: [$.mage.__('Images'), $.mage.__('SKU'), $.mage.__('Quantity'), $.mage.__('Price')], sections: [], gridTemplate: 'Magento_ConfigurableProduct/variations/steps/summary-grid' }, initObservable: function () { - var pagingObservables = { - currentNew: ko.getObservable(this.pagingNew, 'current'), - currentExisting: ko.getObservable(this.pagingExisting, 'current'), - currentDeleted: ko.getObservable(this.pagingDeleted, 'current'), - pageSizeNew: ko.getObservable(this.pagingNew, 'pageSize'), - pageSizeExisting: ko.getObservable(this.pagingExisting, 'pageSize'), - pageSizeDeleted: ko.getObservable(this.pagingDeleted, 'pageSize') - }; - this._super().observe('gridExisting gridNew gridDeleted attributes sections'); this.gridExisting.columns = ko.observableArray(); this.gridNew.columns = ko.observableArray(); this.gridDeleted.columns = ko.observableArray(); - _.each(pagingObservables, function (observable) { - observable.subscribe(function () { - this.generateGrid(); - }, this); - }, this); - return this; }, nextLabelText: $.mage.__('Generate Products'), variations: [], - calculate: function (variations, getSectionValue) { + generateGrid: function (variations, getSectionValue) { var productSku = this.variationsComponent().getProductValue('sku'), productPrice = this.variationsComponent().getProductValue('price'), productWeight = this.variationsComponent().getProductValue('weight'), @@ -87,8 +47,8 @@ define([ gridExisting = [], gridNew = [], gridDeleted = []; - this.variations = []; + _.each(variations, function (options) { var product, images, sku, quantity, price, variation, productId = this.variationsComponent().getProductIdByOptions(options); @@ -138,6 +98,14 @@ define([ variationsKeys.push(this.variationsComponent().getVariationKey(options)); }, this); + this.gridExisting(gridExisting); + this.gridExisting.columns(this.getColumnsName(this.wizard.data.attributes)); + + if (gridNew.length > 0) { + this.gridNew(gridNew); + this.gridNew.columns(this.getColumnsName(this.wizard.data.attributes)); + } + _.each(_.omit(this.variationsComponent().productAttributesMap, variationsKeys), function (productId) { gridDeleted.push(this.prepareRowForGrid( _.findWhere(this.variationsComponent().variations, { @@ -146,28 +114,13 @@ define([ )); }.bind(this)); - this.variationsExisting = gridExisting; - this.variationsNew = gridNew; - this.variationsDeleted = gridDeleted; - - }, - generateGrid: function () { - var pageExisting = this.pagingExisting.pageSize * this.pagingExisting.current, - pageNew = this.pagingNew.pageSize * this.pagingNew.current, - pageDeleted = this.pagingDeleted.pageSize * this.pagingDeleted.current; - - this.pagingExisting.totalRecords = this.variationsExisting.length; - this.gridExisting(this.variationsExisting.slice(pageExisting - this.pagingExisting.pageSize, pageExisting)); - - this.pagingNew.totalRecords = this.variationsNew.length; - this.gridNew(this.variationsNew.slice(pageNew - this.pagingNew.pageSize, pageNew)); - - this.pagingDeleted.totalRecords = this.variationsDeleted.length; - this.gridDeleted(this.variationsDeleted.slice(pageDeleted - this.pagingDeleted.pageSize, pageDeleted)); + if (gridDeleted.length > 0) { + this.gridDeleted(gridDeleted); + this.gridDeleted.columns(this.getColumnsName(this.variationsComponent().productAttributes)); + } }, prepareRowForGrid: function (variation) { var row = []; - row.push(_.extend({ images: [] }, variation.images)); @@ -202,11 +155,7 @@ define([ this.gridNew([]); this.gridExisting([]); this.gridDeleted([]); - this.gridExisting.columns(this.getColumnsName(this.wizard.data.attributes)); - this.gridNew.columns(this.getColumnsName(this.wizard.data.attributes)); - this.gridDeleted.columns(this.getColumnsName(this.variationsComponent().productAttributes)); - this.calculate(wizard.data.variations, wizard.data.sectionHelper); - this.generateGrid(); + this.generateGrid(wizard.data.variations, wizard.data.sectionHelper); }, force: function () { this.variationsComponent().render(this.variations, this.attributes()); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js index f65c210ea3e75..40ca39e36f53d 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js @@ -1,21 +1,18 @@ +// jscs:disable requireDotNotation /** * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ +// jscs:disable jsDoc define([ 'uiComponent', 'jquery', 'ko', 'underscore', - 'Magento_Ui/js/modal/alert', - 'Magento_Ui/js/grid/paging/paging' -], function (Component, $, ko, _, alert, paging) { + 'Magento_Ui/js/modal/alert' +], function (Component, $, ko, _, alert) { 'use strict'; - /** - * @param {String} message - * @constructor - */ function UserException(message) { this.message = message; this.name = 'UserException'; @@ -27,11 +24,6 @@ define([ opened: false, attributes: [], productMatrix: [], - productMatrixSerialized: ko.observable(''), - associatedProducts: [], - associatedProductsSerialized: ko.observable(''), - configurations: [], - configurationsSerialized: ko.observable(''), variations: [], productAttributes: [], fullAttributes: [], @@ -39,19 +31,8 @@ define([ productAttributesMap: null, modules: { associatedProductGrid: '${ $.configurableProductGrid }' - }, - paging: paging({ - name: 'configurableProductVariationsGrid.paging', - sizesConfig: { - component: 'Magento_ConfigurableProduct/js/variations/paging/sizes', - name: 'configurableProductVariationsGrid_sizes' - } - }) + } }, - - /** - * @override - */ initialize: function () { this._super(); @@ -60,61 +41,11 @@ define([ } this.initProductAttributesMap(); }, - - /** - * @override - */ initObservable: function () { - var pagingObservables = { - current: ko.getObservable(this.paging, 'current'), - pageSize: ko.getObservable(this.paging, 'pageSize') - }; - this._super().observe('actions opened attributes productMatrix'); - this.paging.totalRecords = this.variations.length; - - _.each(pagingObservables, function (observable) { - observable.subscribe(function () { - if (this.variations.length > 0) { - this.render(this.variations); - } - }, this); - }, this); - - $('#save-split-button, #save-split-button .item').click(function () { - var variations = this.prepareVariations(), - validationError = this.validateVariationPrices(this.variations) || false; - - if (validationError) { - pagingObservables.current( - Math.floor(this.variations.indexOf(validationError) / pagingObservables.pageSize() + 1) - ); - $('[data-form="edit-product"]').validation('isValid'); - - return; - } - - this.productMatrixSerialized(JSON.stringify(variations)); - this.associatedProductsSerialized(JSON.stringify(this.associatedProducts)); - this.configurationsSerialized(JSON.stringify(this.configurations)); - }.bind(this)); return this; }, - - /** - * Validate variations data. - * @param {Array} variations - */ - validateVariationPrices: function (variations) { - return _.find(variations, function (variation) { - return variation.hasOwnProperty('price') && variation.price === ''; - }); - }, - - /** - * @param {String} rowIndex - */ showGrid: function (rowIndex) { var product = this.productMatrix()[rowIndex], attributes = JSON.parse(product.attribute), @@ -122,8 +53,7 @@ define([ 'entity_id': { 'condition_type': 'neq', value: product.productId } - } : {}; - + } : {}; this.rowIndexToEdit = rowIndex; filterModifier = _.extend(filterModifier, _.mapObject(attributes, function (value) { @@ -141,35 +71,21 @@ define([ false ); }, - - /** - * @param {Array} newProducts - */ changeProduct: function (newProducts) { - var oldProduct = this.variations[this.rowIndexToEdit], + var oldProduct = this.productMatrix()[this.rowIndexToEdit], newProduct = this._makeProduct(_.extend(oldProduct, newProducts[0])); - this.productAttributesMap[this.getVariationKey(newProduct.options)] = newProduct.productId; - this.variations.splice(this.rowIndexToEdit, 1, newProduct); - this.render(this.variations); + this.productMatrix.splice(this.rowIndexToEdit, 1, newProduct); }, - - /** - * @param {Array} newProducts - */ appendProducts: function (newProducts) { - var newProduct = {}; - - this.variations.push.apply( - this.variations, + this.productMatrix.push.apply( + this.productMatrix, _.map( newProducts, _.wrap( this._makeProduct.bind(this), function (func, product) { - product.associatedProductId = product.productId; - newProduct = func(product); - + var newProduct = func(product); this.productAttributesMap[this.getVariationKey(newProduct.options)] = newProduct.productId; return newProduct; @@ -177,14 +93,7 @@ define([ ) ) ); - this.associatedProducts.push(newProduct.productId); - this.render(this.variations); }, - - /** - * @param {Object} product - * @returns {Object} - */ _makeProduct: function (product) { var productId = product['entity_id'] || product.productId || null, attributes = _.pick(product, this.attributes.pluck('code')), @@ -217,7 +126,6 @@ define([ options: options, price: parseFloat(product.price.replace(/[^\d.]+/g, '')).toFixed(4), productId: productId, - associatedProductId: product.associatedProductId || null, productUrl: this.buildProductUrl(productId), quantity: product.quantity || null, sku: product.sku, @@ -226,33 +134,14 @@ define([ weight: product.weight || null }; }, - - /** - * @param {String} name - * @see use in matrix.phtml - * @returns {jQuery} - */ getProductValue: function (name) { return $('[name="product[' + name.split('/').join('][') + ']"]', this.productForm).val(); }, - - /** - * @param {Object} data - * @param {String} field - * @see use in matrix.phtml - * @returns {String} - */ getRowId: function (data, field) { var key = data.variationKey; return 'variations-matrix-' + key + '-' + field; }, - - /** - * @param {Object} variation - * @param {String} field - * @returns {String} - */ getVariationRowName: function (variation, field) { var result; @@ -264,157 +153,49 @@ define([ return result; }, - - /** - * @param {Object} attribute - * @param {String} field - * @see use in matrix.phtml - * @returns {String} - */ getAttributeRowName: function (attribute, field) { return 'product[configurable_attributes_data][' + attribute.id + '][' + field + ']'; }, - - /** - * @param {Object} attribute - * @param {Object} option - * @param {String} field - * @see use in matrix.phtml - * @returns {String} - */ getOptionRowName: function (attribute, option, field) { return 'product[configurable_attributes_data][' + attribute.id + '][values][' + option.value + '][' + field + ']'; }, - - /** - * @param {Array} variations - * @param {Array} attributes - */ render: function (variations, attributes) { - var variationsPage; - - this.variations = variations; - - if (!_.isUndefined(attributes)) { - this.attributes(attributes); - } - this.paging.totalRecords = this.variations.length; - this.changeButtonWizard(); - variationsPage = this.prepareRenderPage(); - this.populateVariationMatrix(variationsPage); + this.populateVariationMatrix(variations); + this.attributes(attributes); this.initImageUpload(); - this.disableConfigurableAttributes(this.attributes); + this.disableConfigurableAttributes(attributes); this.showPrice(); - this.productMatrixSerialized(JSON.stringify(this.prepareVariations())); - }, - - /** - * Maps internal object structure to the server-accepted object structure. - * @returns {Object} - */ - prepareVariations: function () { - var mappedVariations = {}, - configurations = {}; - - _.each(this.variations, function (variation) { - var attributes; - - if (variation.productId) { - configurations[variation.productId] = { - 'status': variation.status === undefined ? 1 : parseInt(variation.status, 10) - }; - - if (this.associatedProducts.indexOf(variation.productId) === -1) { - this.associatedProducts.push(variation.productId); - } - - return; - } - - attributes = _.reduce(variation.options, function (memo, option) { - var attribute = {}; - - attribute[option['attribute_code']] = option.value; - - return _.extend(memo, attribute); - }, {}); - - this.generateImageGallery(variation); - mappedVariations[this.getVariationKey(variation.options)] = { - 'image': variation.image || '', - 'media_gallery': variation['media_gallery'] || {}, - 'name': variation.name || variation.sku, - 'configurable_attribute': JSON.stringify(attributes), - 'status': variation.status === undefined ? 1 : parseInt(variation.status, 10), - 'sku': variation.sku, - 'price': variation.price, - 'weight': variation.weight, - 'quantity_and_stock_status': { - 'qty': variation.quantity || null - } - }; - _.each(variation.imageTypes, function (imageFile, key) { - mappedVariations[this.getVariationKey(variation.options)][key] = imageFile; - }, this); - }, this); - this.configurations = configurations; - - return mappedVariations; - }, - - /** - * @returns {Array} - */ - prepareRenderPage: function () { - return this.variations.slice( - (this.paging.current - 1) * this.paging.pageSize, - this.paging.pageSize * this.paging.current - ); }, - - /** - * Changes label of variation generator button. - */ changeButtonWizard: function () { var $button = $('[data-action=open-steps-wizard] [data-role=button-label]'); - $button.text($button.attr('data-edit-label')); }, /** - * Get attributes options. + * Get attributes options * @see use in matrix.phtml - * @returns {Array} + * @function + * @event + * @returns {array} */ getAttributesOptions: function () { return this.showVariations() ? this.productMatrix()[0].options : []; }, - - /** - * @returns {Boolean} - */ showVariations: function () { return this.productMatrix().length > 0; }, - - /** - * @param {Array} variations - */ populateVariationMatrix: function (variations) { - var tempMatrix = []; - + this.productMatrix([]); _.each(variations, function (variation) { var attributes = _.reduce(variation.options, function (memo, option) { var attribute = {}; - attribute[option['attribute_code']] = option.value; return _.extend(memo, attribute); }, {}); - - tempMatrix.push(_.extend(variation, { + this.productMatrix.push(_.extend(variation, { productId: variation.productId || null, name: variation.name || variation.sku, weight: variation.weight, @@ -422,34 +203,16 @@ define([ variationKey: this.getVariationKey(variation.options), editable: variation.editable === undefined ? !variation.productId : variation.editable, productUrl: this.buildProductUrl(variation.productId), - status: variation.status === undefined ? 1 : parseInt(variation.status, 10), - - /** - * Validates variation price. - */ - validatePrice: function () { - $('[data-form="edit-product"]').validation('isValid'); - } + status: variation.status === undefined ? 1 : parseInt(variation.status, 10) })); }, this); - this.productMatrix(tempMatrix); }, - - /** - * @param {String} productId - * @returns {String} - */ buildProductUrl: function (productId) { return this.productUrl.replace('%id%', productId); }, - - /** - * @param {Number} rowIndex - */ removeProduct: function (rowIndex) { - var removedProduct = this.variations.splice(rowIndex, 1); - this.opened(false); + var removedProduct = this.productMatrix.splice(rowIndex, 1); delete this.productAttributesMap[this.getVariationKey(removedProduct[0].options)]; if (this.productMatrix().length === 0) { @@ -457,19 +220,8 @@ define([ $('[data-attribute-code="' + attribute.code + '"] select').removeProp('disabled'); }); } - - if (removedProduct[0].productId) { - rowIndex = this.associatedProducts.indexOf(removedProduct[0].productId); - this.associatedProducts.splice(rowIndex, 1); - } - this.render(this.variations); this.showPrice(); }, - - /** - * @param {String} rowIndex - * @see use in matrix.phtml - */ toggleProduct: function (rowIndex) { var product, row, productChanged = {}; @@ -487,12 +239,6 @@ define([ product.status = +!product.status; this.productMatrix.splice(rowIndex, 0, product); }, - - /** - * @param {String} rowIndex - * @see use in matrix.phtml - * @returns {This} - */ toggleList: function (rowIndex) { var state = false; @@ -503,11 +249,6 @@ define([ return this; }, - - /** - * @param {String} rowIndex - * @returns {This} - */ closeList: function (rowIndex) { if (this.opened() === rowIndex()) { this.opened(false); @@ -515,26 +256,12 @@ define([ return this; }, - - /** - * @param {Array} options - * @returns {String} - */ getVariationKey: function (options) { return _.pluck(options, 'value').sort().join('-'); }, - - /** - * @param {Array} options - * @returns {{Object}|null} - */ getProductIdByOptions: function (options) { return this.productAttributesMap[this.getVariationKey(options)] || null; }, - - /** - * Initialize product attributes map. - */ initProductAttributesMap: function () { if (this.productAttributesMap === null) { this.productAttributesMap = {}; @@ -547,78 +274,35 @@ define([ /** * Is show preview image * @see use in matrix.phtml - * @param {Object} variation - * @returns {Boolean} + * @function + * @event + * @param {object} variation + * @returns {*|boolean} */ isShowPreviewImage: function (variation) { - return variation.images.preview && - (!variation.editable || variation.images.file || variation.imageData && variation.imageData.url); - }, - - /** - * @param {Object} variation - * @returns {String} - */ - getVariationImage: function (variation) { - if (variation.imageData && variation.imageData.url) { - return variation.imageData.url; - } - - return variation.images.preview; + return variation.images.preview && (!variation.editable || variation.images.file); }, - - /** - * @param {Object} variation - * @see use in matrix.phtml - * @returns {String} - */ generateImageGallery: function (variation) { var gallery = [], imageFields = ['position', 'file', 'disabled', 'label']; - - _.extend(variation, { - 'media_gallery': { - 'images': {} - }, - 'imageTypes': {} - }); _.each(variation.images.images, function (image) { - variation['media_gallery'].images[image['file_id']] = {}; _.each(imageFields, function (field) { gallery.push( '' ); - - variation['media_gallery'].images[image['file_id']][field] = image[field] || ''; }, this); _.each(image.galleryTypes, function (imageType) { gallery.push( '' ); - - variation.imageTypes[imageType] = image.file; }, this); }, this); return gallery.join('\n'); }, - - /** - * @param {String} variationKey - * @return {Object} - */ - getVariationByKey: function (variationKey) { - return _.find(this.variations, function (variation) { - return variation.variationKey === variationKey; - }); - }, - - /** - * Initialize image uploader for variations. - */ initImageUpload: function () { require([ 'mage/template', @@ -627,13 +311,10 @@ define([ 'mage/translate', 'domReady!' ], function (mageTemplate) { - var matrix = $('[data-role=product-variations-matrix]'), - variations = this; + var matrix = $('[data-role=product-variations-matrix]'); matrix.find('[data-action=upload-image]').find('[name=image]').each(function () { - var imageColumn = $(this).closest('[data-column=image]'), - rowIndex = $(this).parents('tr').attr('data-row-number'), - variation = variations.getVariationByKey(rowIndex); + var imageColumn = $(this).closest('[data-column=image]'); if (imageColumn.find('[data-role=image]').length) { imageColumn.find('[data-toggle=dropdown]').dropdown().show(); @@ -642,13 +323,8 @@ define([ dataType: 'json', dropZone: $(this).closest('[data-role=row]'), acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, - - /** - * @param {Object} event - * @param {Object} data - */ done: function (event, data) { - var template, parentElement, uploaderControl, imageElement; + var tmpl, parentElement, uploaderControl, imageElement; if (!data.result) { return; @@ -659,15 +335,12 @@ define([ uploaderControl = parentElement.find('[data-action=upload-image]'); imageElement = parentElement.find('[data-role=image]'); - variation.image = data.result.file; - variation.imageData = data.result; - if (imageElement.length) { imageElement.attr('src', data.result.url); } else { - template = mageTemplate(matrix.find('[data-template-for=variation-image]').html()); + tmpl = mageTemplate(matrix.find('[data-template-for=variation-image]').html()); - $(template({ + $(tmpl({ data: data.result })).prependTo(uploaderControl); } @@ -679,39 +352,22 @@ define([ }); } }, - - /** - * @param {Object} event - */ start: function (event) { $(event.target).closest('[data-action=upload-image]').addClass('loading'); }, - - /** - * @param {Object} event - */ stop: function (event) { $(event.target).closest('[data-action=upload-image]').removeClass('loading'); } }); }); matrix.find('[data-action=no-image]').click(function (event) { - var parentElement = $(event.target).closest('[data-column=image]'), - rowIndex = $(this).parents('tr').attr('data-row-number'), - variation = variations.getVariationByKey(rowIndex); - - delete variation.image; - delete variation.imageData; + var parentElement = $(event.target).closest('[data-column=image]'); parentElement.find('[data-role=image]').remove(); parentElement.find('[name$="[image]"]').val(''); parentElement.find('[data-toggle=dropdown]').trigger('close.dropdown').hide(); }); - }.bind(this)); + }); }, - - /** - * @param {Array} attributes - */ disableConfigurableAttributes: function (attributes) { $('[data-attribute-code] select.disabled-configurable-elements') .removeClass('disabled-configurable-elements') @@ -722,13 +378,8 @@ define([ .prop('disabled', true); }); }, - - /** - * Toggle configurable product price input. - */ showPrice: function () { var priceContainer = $('[id="attribute-price-container"]'); - if (this.productMatrix().length !== 0) { priceContainer.hide(); priceContainer.find('input').prop('disabled', true); @@ -740,7 +391,7 @@ define([ /** * Get currency symbol - * @returns {String} + * @returns {*} */ getCurrencySymbol: function () { return this.currencySymbol; diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/variations/steps/summary-grid.html b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/variations/steps/summary-grid.html index d086ac5cdf9fc..5f0d83cfd14f9 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/variations/steps/summary-grid.html +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/template/variations/steps/summary-grid.html @@ -13,10 +13,6 @@
- -
diff --git a/app/code/Magento/Customer/Controller/Section/Load.php b/app/code/Magento/Customer/Controller/Section/Load.php index 19b361b2c830d..f299d3a438178 100644 --- a/app/code/Magento/Customer/Controller/Section/Load.php +++ b/app/code/Magento/Customer/Controller/Section/Load.php @@ -8,10 +8,13 @@ use Magento\Customer\CustomerData\SectionPoolInterface; use Magento\Framework\App\Action\Context; use Magento\Framework\Controller\Result\JsonFactory; -use Magento\Framework\Exception\LocalizedException; +use Magento\Customer\CustomerData\Section\Identifier; +use Magento\Framework\Escaper; /** * Customer section controller + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Load extends \Magento\Framework\App\Action\Action { @@ -30,16 +33,21 @@ class Load extends \Magento\Framework\App\Action\Action */ protected $sectionPool; + /** + * @var \Magento\Framework\Escaper + */ + private $escaper; + /** * @param Context $context * @param JsonFactory $resultJsonFactory - * @param \Magento\Customer\CustomerData\Section\Identifier $sectionIdentifier + * @param Identifier $sectionIdentifier * @param SectionPoolInterface $sectionPool */ public function __construct( Context $context, JsonFactory $resultJsonFactory, - \Magento\Customer\CustomerData\Section\Identifier $sectionIdentifier, + Identifier $sectionIdentifier, SectionPoolInterface $sectionPool ) { parent::__construct($context); @@ -48,6 +56,19 @@ public function __construct( $this->sectionPool = $sectionPool; } + /** + * Get new Escaper dependency for application code. + * @return \Magento\Framework\Escaper + * @deprecated + */ + private function getEscaper() + { + if ($this->escaper === null) { + $this->escaper = \Magento\Framework\App\ObjectManager::getInstance()->get(Escaper::class); + } + return $this->escaper; + } + /** * @return \Magento\Framework\Controller\Result\Json|\Magento\Framework\Controller\Result\Redirect */ @@ -70,7 +91,7 @@ public function execute() \Zend\Http\AbstractMessage::VERSION_11, 'Bad Request' ); - $response = ['message' => $e->getMessage()]; + $response = ['message' => $this->getEscaper()->escapeHtml($e->getMessage())]; } return $resultJson->setData($response); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php new file mode 100644 index 0000000000000..474925f6561a4 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php @@ -0,0 +1,152 @@ +objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->resultJsonFactory = $this->getMockBuilder(\Magento\Framework\Controller\Result\JsonFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->sectionIdentifier = $this->getMockBuilder(\Magento\Customer\CustomerData\Section\Identifier::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->sectionPool = $this->getMockBuilder(\Magento\Customer\CustomerData\SectionPoolInterface::class) + ->getMockForAbstractClass(); + + $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->controller = $this->objectManager->getObject( + 'Magento\Customer\Controller\Section\Load', + [ + 'resultJsonFactory' => $this->resultJsonFactory, + 'sectionIdentifier' => $this->sectionIdentifier, + 'sectionPool' => $this->sectionPool, + 'request' => $this->request + ] + ); + } + + public function testExecuteBadSectionData() + { + $resultJson = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resultJsonFactory->expects($this->once()) + ->method('create') + ->willReturn($resultJson); + + $this->request->expects($this->at(0)) + ->method('getParam') + ->with('sections') + ->willReturn('badSectionData'); + + $this->request->expects($this->at(1)) + ->method('getParam') + ->with('update_section_id') + ->willReturn(false); + + $message = '"badSectionData" section source is not supported'; + $expected = ['message' => $message]; + $this->sectionPool->expects($this->once()) + ->method('getSectionsData') + ->will($this->throwException(new \Exception)); + + $escaper = $this->getMockBuilder(\Magento\Framework\Escaper::class) + ->disableOriginalConstructor() + ->getMock(); + $escaper->expects($this->once()) + ->method('escapeHtml') + ->willReturn($message); + + $reflection = new \ReflectionClass(get_class($this->controller)); + $reflectionProperty = $reflection->getProperty('escaper'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->controller, $escaper); + + $resultJson->expects($this->once()) + ->method('setData') + ->with($expected) + ->willReturn(json_encode($expected)); + $this->assertSame(json_encode($expected), $this->controller->execute()); + } + + public function testExecuteSuccess() + { + $resultJson = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resultJsonFactory->expects($this->once()) + ->method('create') + ->willReturn($resultJson); + + $this->request->expects($this->at(0)) + ->method('getParam') + ->with('sections') + ->willReturn('cart'); + + $this->request->expects($this->at(1)) + ->method('getParam') + ->with('update_section_id') + ->willReturn(false); + + $expected = ['section' => 'cart']; + $this->sectionPool->expects($this->once()) + ->method('getSectionsData') + ->willReturn($expected); + + $resultJson->expects($this->once()) + ->method('setData') + ->with($expected) + ->willReturn(json_encode($expected)); + $this->assertSame(json_encode($expected), $this->controller->execute()); + } +} diff --git a/app/code/Magento/Customer/composer.json b/app/code/Magento/Customer/composer.json index 8c43ad3d16d5b..632fbcb2f19ad 100644 --- a/app/code/Magento/Customer/composer.json +++ b/app/code/Magento/Customer/composer.json @@ -29,7 +29,7 @@ "magento/module-customer-sample-data": "Sample Data version:100.0.*" }, "type": "magento2-module", - "version": "100.0.8", + "version": "100.0.9", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Dhl/composer.json b/app/code/Magento/Dhl/composer.json index 76bd8bbfe33f0..611362cdf8f11 100644 --- a/app/code/Magento/Dhl/composer.json +++ b/app/code/Magento/Dhl/composer.json @@ -19,7 +19,7 @@ "magento/module-checkout": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Dhl/view/frontend/web/js/model/shipping-rates-validation-rules.js b/app/code/Magento/Dhl/view/frontend/web/js/model/shipping-rates-validation-rules.js index c6e03eb9ebf84..e51a6cefa2cb0 100644 --- a/app/code/Magento/Dhl/view/frontend/web/js/model/shipping-rates-validation-rules.js +++ b/app/code/Magento/Dhl/view/frontend/web/js/model/shipping-rates-validation-rules.js @@ -15,6 +15,9 @@ define( }, 'country_id': { 'required': true + }, + 'city': { + 'required': true } }; } diff --git a/app/code/Magento/Fedex/Model/Carrier.php b/app/code/Magento/Fedex/Model/Carrier.php index c5b4ab7846ddc..6768c524d564b 100644 --- a/app/code/Magento/Fedex/Model/Carrier.php +++ b/app/code/Magento/Fedex/Model/Carrier.php @@ -9,10 +9,10 @@ namespace Magento\Fedex\Model; use Magento\Framework\Module\Dir; +use Magento\Framework\Xml\Security; use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Shipping\Model\Carrier\AbstractCarrierOnline; use Magento\Shipping\Model\Rate\Result; -use Magento\Framework\Xml\Security; /** * Fedex shipping implementation @@ -119,6 +119,25 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C */ protected $_productCollectionFactory; + /** + * @inheritdoc + */ + protected $_debugReplacePrivateDataKeys = [ + 'Key', 'Password', 'MeterNumber', + ]; + + /** + * Version of tracking service + * @var int + */ + private static $trackServiceVersion = 10; + + /** + * List of TrackReply errors + * @var array + */ + private static $trackingErrors = ['FAILURE', 'ERROR']; + /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory @@ -186,7 +205,7 @@ public function __construct( $wsdlBasePath = $configReader->getModuleDir(Dir::MODULE_ETC_DIR, 'Magento_Fedex') . '/wsdl/'; $this->_shipServiceWsdl = $wsdlBasePath . 'ShipService_v10.wsdl'; $this->_rateServiceWsdl = $wsdlBasePath . 'RateService_v10.wsdl'; - $this->_trackServiceWsdl = $wsdlBasePath . 'TrackService_v5.wsdl'; + $this->_trackServiceWsdl = $wsdlBasePath . 'TrackService_v' . self::$trackServiceVersion . '.wsdl'; } /** @@ -331,6 +350,10 @@ public function setRequest(RateRequest $request) } else { } + if ($request->getDestCity()) { + $r->setDestCity($request->getDestCity()); + } + $weight = $this->getTotalNumOfBoxes($request->getPackageWeight()); $r->setWeight($weight); if ($request->getFreeMethodWeight() != $request->getPackageWeight()) { @@ -360,6 +383,9 @@ public function setRequest(RateRequest $request) */ public function getResult() { + if (!$this->_result) { + $this->_result = $this->_trackFactory->create(); + } return $this->_result; } @@ -425,6 +451,10 @@ protected function _formRateRequest($purpose) ], ]; + if ($r->getDestCity()) { + $ratesRequest['RequestedShipment']['Recipient']['Address']['City'] = $r->getDestCity(); + } + if ($purpose == self::RATE_REQUEST_GENERAL) { $ratesRequest['RequestedShipment']['RequestedPackageLineItems'][0]['InsuredValue'] = [ 'Amount' => $r->getValue(), @@ -454,7 +484,7 @@ protected function _doRatesRequest($purpose) $ratesRequest = $this->_formRateRequest($purpose); $requestString = serialize($ratesRequest); $response = $this->_getCachedQuotes($requestString); - $debugData = ['request' => $ratesRequest]; + $debugData = ['request' => $this->filterDebugData($ratesRequest)]; if ($response === null) { try { $client = $this->_createRateSoapClient(); @@ -494,7 +524,10 @@ protected function _getQuotes() // make general request for all methods $response = $this->_doRatesRequest(self::RATE_REQUEST_GENERAL); $preparedGeneral = $this->_prepareRateResponse($response); - if (!$preparedGeneral->getError() || $this->_result->getError() && $preparedGeneral->getError()) { + if (!$preparedGeneral->getError() + || $this->_result->getError() && $preparedGeneral->getError() + || empty($this->_result->getAllRates()) + ) { $this->_result->append($preparedGeneral); } @@ -714,7 +747,7 @@ protected function _parseXmlResponse($response) $priceArr = []; if (strlen(trim($response)) > 0) { - $xml = $this->parseXml($response, 'Magento\Shipping\Model\Simplexml\Element'); + $xml = $this->parseXml($response, \Magento\Shipping\Model\Simplexml\Element::class); if (is_object($xml)) { if (is_object($xml->Error) && is_object($xml->Error->Message)) { $errorTitle = (string)$xml->Error->Message; @@ -752,6 +785,7 @@ protected function _parseXmlResponse($response) $error = $this->_rateErrorFactory->create(); $error->setCarrier('fedex'); $error->setCarrierTitle($this->getConfigData('title')); + $error->setErrorMessage($errorTitle); $error->setErrorMessage($this->getConfigData('specificerrmsg')); $result->append($error); } else { @@ -859,14 +893,14 @@ public function getCode($type, $code = '') 'from_us' => [ 'method' => ['INTERNATIONAL_FIRST', 'INTERNATIONAL_ECONOMY', 'INTERNATIONAL_PRIORITY'], ], - ] + ], ], [ 'containers' => ['FEDEX_10KG_BOX', 'FEDEX_25KG_BOX'], 'filters' => [ 'within_us' => [], 'from_us' => ['method' => ['INTERNATIONAL_PRIORITY']], - ] + ], ], [ 'containers' => ['YOUR_PACKAGING'], @@ -904,7 +938,7 @@ public function getCode($type, $code = '') 'INTERNATIONAL_PRIORITY_FREIGHT', ], ], - ] + ], ], ], 'delivery_confirmation_types' => [ @@ -1016,9 +1050,16 @@ protected function _getXMLTracking($tracking) 'AccountNumber' => $this->getConfigData('account'), 'MeterNumber' => $this->getConfigData('meter_number'), ], - 'Version' => ['ServiceId' => 'trck', 'Major' => '5', 'Intermediate' => '0', 'Minor' => '0'], - 'PackageIdentifier' => ['Type' => 'TRACKING_NUMBER_OR_DOORTAG', 'Value' => $tracking], - 'IncludeDetailedScans' => 1, + 'Version' => [ + 'ServiceId' => 'trck', + 'Major' => self::$trackServiceVersion, + 'Intermediate' => '0', + 'Minor' => '0', + ], + 'SelectionDetails' => [ + 'PackageIdentifier' => ['Type' => 'TRACKING_NUMBER_OR_DOORTAG', 'Value' => $tracking], + ], + 'ProcessingOptions' => 'INCLUDE_DETAILED_SCANS' ]; $requestString = serialize($trackRequest); $response = $this->_getCachedQuotes($requestString); @@ -1045,114 +1086,48 @@ protected function _getXMLTracking($tracking) /** * Parse tracking response * - * @param string[] $trackingValue + * @param string $trackingValue * @param \stdClass $response * @return void - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function _parseTrackingResponse($trackingValue, $response) { - if (is_object($response)) { - if ($response->HighestSeverity == 'FAILURE' || $response->HighestSeverity == 'ERROR') { - $errorTitle = (string)$response->Notifications->Message; - } elseif (isset($response->TrackDetails)) { - $trackInfo = $response->TrackDetails; - $resultArray['status'] = (string)$trackInfo->StatusDescription; - $resultArray['service'] = (string)$trackInfo->ServiceInfo; - $timestamp = isset( - $trackInfo->EstimatedDeliveryTimestamp - ) ? $trackInfo->EstimatedDeliveryTimestamp : $trackInfo->ActualDeliveryTimestamp; - $timestamp = strtotime((string)$timestamp); - if ($timestamp) { - $resultArray['deliverydate'] = date('Y-m-d', $timestamp); - $resultArray['deliverytime'] = date('H:i:s', $timestamp); - } - - $deliveryLocation = isset( - $trackInfo->EstimatedDeliveryAddress - ) ? $trackInfo->EstimatedDeliveryAddress : $trackInfo->ActualDeliveryAddress; - $deliveryLocationArray = []; - if (isset($deliveryLocation->City)) { - $deliveryLocationArray[] = (string)$deliveryLocation->City; - } - if (isset($deliveryLocation->StateOrProvinceCode)) { - $deliveryLocationArray[] = (string)$deliveryLocation->StateOrProvinceCode; - } - if (isset($deliveryLocation->CountryCode)) { - $deliveryLocationArray[] = (string)$deliveryLocation->CountryCode; - } - if ($deliveryLocationArray) { - $resultArray['deliverylocation'] = implode(', ', $deliveryLocationArray); - } - - $resultArray['signedby'] = (string)$trackInfo->DeliverySignatureName; - $resultArray['shippeddate'] = date('Y-m-d', (int)$trackInfo->ShipTimestamp); - if (isset($trackInfo->PackageWeight) && isset($trackInfo->Units)) { - $weight = (string)$trackInfo->PackageWeight; - $unit = (string)$trackInfo->Units; - $resultArray['weight'] = "{$weight} {$unit}"; - } - - $packageProgress = []; - if (isset($trackInfo->Events)) { - $events = $trackInfo->Events; - if (isset($events->Address)) { - $events = [$events]; - } - foreach ($events as $event) { - $tempArray = []; - $tempArray['activity'] = (string)$event->EventDescription; - $timestamp = strtotime((string)$event->Timestamp); - if ($timestamp) { - $tempArray['deliverydate'] = date('Y-m-d', $timestamp); - $tempArray['deliverytime'] = date('H:i:s', $timestamp); - } - if (isset($event->Address)) { - $addressArray = []; - $address = $event->Address; - if (isset($address->City)) { - $addressArray[] = (string)$address->City; - } - if (isset($address->StateOrProvinceCode)) { - $addressArray[] = (string)$address->StateOrProvinceCode; - } - if (isset($address->CountryCode)) { - $addressArray[] = (string)$address->CountryCode; - } - if ($addressArray) { - $tempArray['deliverylocation'] = implode(', ', $addressArray); - } - } - $packageProgress[] = $tempArray; - } - } - - $resultArray['progressdetail'] = $packageProgress; - } + if (!is_object($response) || empty($response->HighestSeverity)) { + $this->appendTrackingError($trackingValue, __('Invalid response from carrier')); + return; + } else if (in_array($response->HighestSeverity, self::$trackingErrors)) { + $this->appendTrackingError($trackingValue, (string) $response->Notifications->Message); + return; + } else if (empty($response->CompletedTrackDetails) || empty($response->CompletedTrackDetails->TrackDetails)) { + $this->appendTrackingError($trackingValue, __('No available tracking items')); + return; } - if (!$this->_result) { - $this->_result = $this->_trackFactory->create(); + $trackInfo = $response->CompletedTrackDetails->TrackDetails; + + // Fedex can return tracking details as single object instead array + if (is_object($trackInfo)) { + $trackInfo = [$trackInfo]; } - if (isset($resultArray)) { + $result = $this->getResult(); + $carrierTitle = $this->getConfigData('title'); + $counter = 0; + foreach ($trackInfo as $item) { $tracking = $this->_trackStatusFactory->create(); - $tracking->setCarrier('fedex'); - $tracking->setCarrierTitle($this->getConfigData('title')); + $tracking->setCarrier(self::CODE); + $tracking->setCarrierTitle($carrierTitle); $tracking->setTracking($trackingValue); - $tracking->addData($resultArray); - $this->_result->append($tracking); - } else { - $error = $this->_trackErrorFactory->create(); - $error->setCarrier('fedex'); - $error->setCarrierTitle($this->getConfigData('title')); - $error->setTracking($trackingValue); - $error->setErrorMessage( - $errorTitle ? $errorTitle : __('For some reason we can\'t retrieve tracking info right now.') + $tracking->addData($this->processTrackingDetails($item)); + $result->append($tracking); + $counter ++; + } + + // no available tracking details + if (!$counter) { + $this->appendTrackingError( + $trackingValue, __('For some reason we can\'t retrieve tracking info right now.') ); - $this->_result->append($error); } } @@ -1221,7 +1196,7 @@ protected function _getAuthDetails() 'TransactionDetail' => [ 'CustomerTransactionId' => '*** Express Domestic Shipping Request v9 using PHP ***', ], - 'Version' => ['ServiceId' => 'ship', 'Major' => '10', 'Intermediate' => '0', 'Minor' => '0'] + 'Version' => ['ServiceId' => 'ship', 'Major' => '10', 'Intermediate' => '0', 'Minor' => '0'], ]; } @@ -1250,7 +1225,6 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) $width = $packageParams->getWidth(); $length = $packageParams->getLength(); $weightUnits = $packageParams->getWeightUnits() == \Zend_Measure_Weight::POUND ? 'LB' : 'KG'; - $dimensionsUnits = $packageParams->getDimensionUnits() == \Zend_Measure_Length::INCH ? 'IN' : 'CM'; $unitPrice = 0; $itemsQty = 0; $itemsDesc = []; @@ -1393,12 +1367,12 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) // set dimensions if ($length || $width || $height) { - $requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions'] = []; - $dimenssions = &$requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions']; - $dimenssions['Length'] = $length; - $dimenssions['Width'] = $width; - $dimenssions['Height'] = $height; - $dimenssions['Units'] = $dimensionsUnits; + $requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions'] = [ + 'Length' => $length, + 'Width' => $width, + 'Height' => $height, + 'Units' => $packageParams->getDimensionUnits() == \Zend_Measure_Length::INCH ? 'IN' : 'CM', + ]; } return $this->_getAuthDetails() + $requestClient; @@ -1453,9 +1427,10 @@ protected function _doShipmentRequest(\Magento\Framework\DataObject $request) * @param array|object $trackingIds * @return string */ - private function getTrackingNumber($trackingIds) { + private function getTrackingNumber($trackingIds) + { return is_array($trackingIds) ? array_map( - function($val) { + function ($val) { return $val->TrackingNumber; }, $trackingIds @@ -1560,4 +1535,187 @@ public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $para { return $this->getCode('delivery_confirmation_types'); } + + /** + * Recursive replace sensitive fields in debug data by the mask + * @param string $data + * @return string + */ + protected function filterDebugData($data) + { + foreach (array_keys($data) as $key) { + if (is_array($data[$key])) { + $data[$key] = $this->filterDebugData($data[$key]); + } elseif (in_array($key, $this->_debugReplacePrivateDataKeys)) { + $data[$key] = self::DEBUG_KEYS_MASK; + } + } + return $data; + } + + /** + * Parse track details response from Fedex + * @param \stdClass $trackInfo + * @return array + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + private function processTrackingDetails(\stdClass $trackInfo) + { + $result = [ + 'shippeddate' => null, + 'deliverydate' => null, + 'deliverytime' => null, + 'deliverylocation' => null, + 'weight' => null, + 'progressdetail' => [], + ]; + + if (!empty($trackInfo->ShipTimestamp) && + ($datetime = \DateTime::createFromFormat(\DateTime::ISO8601, $trackInfo->ShipTimestamp)) !== false + ) { + $result['shippeddate'] = $datetime->format('Y-m-d'); + } + + $result['signedby'] = !empty($trackInfo->DeliverySignatureName) ? + (string) $trackInfo->DeliverySignatureName : + null; + + $result['status'] = (!empty($trackInfo->StatusDetail) && !empty($trackInfo->StatusDetail->Description)) ? + (string) $trackInfo->StatusDetail->Description : + null; + $result['service'] = (!empty($trackInfo->Service) && !empty($trackInfo->Service->Description)) ? + (string) $trackInfo->Service->Description : + null; + + $datetime = $this->getDeliveryDateTime($trackInfo); + if ($datetime) { + $result['deliverydate'] = $datetime->format('Y-m-d'); + $result['deliverytime'] = $datetime->format('H:i:s'); + } + + $address = null; + if (!empty($trackInfo->EstimatedDeliveryAddress)) { + $address = $trackInfo->EstimatedDeliveryAddress; + } elseif (!empty($trackInfo->ActualDeliveryAddress)) { + $address = $trackInfo->ActualDeliveryAddress; + } + + if (!empty($address)) { + $result['deliverylocation'] = $this->getDeliveryAddress($address); + } + + if (!empty($trackInfo->PackageWeight)) { + $result['weight'] = sprintf( + '%s %s', + (string) $trackInfo->PackageWeight->Value, + (string) $trackInfo->PackageWeight->Units + ); + } + + if (!empty($trackInfo->Events)) { + $events = $trackInfo->Events; + if (is_object($events)) { + $events = [$trackInfo->Events]; + } + $result['progressdetail'] = $this->processTrackDetailsEvents($events); + } + + return $result; + } + + /** + * Parse delivery datetime from tracking details + * @param \stdClass $trackInfo + * @return \Datetime|null + */ + private function getDeliveryDateTime(\stdClass $trackInfo) + { + $timestamp = null; + if (!empty($trackInfo->EstimatedDeliveryTimestamp)) { + $timestamp = $trackInfo->EstimatedDeliveryTimestamp; + } elseif (!empty($trackInfo->ActualDeliveryTimestamp)) { + $timestamp = $trackInfo->ActualDeliveryTimestamp; + } + + return $timestamp ? \DateTime::createFromFormat(\DateTime::ISO8601, $timestamp) : null; + } + + /** + * Get delivery address details in string representation + * Return City, State, Country Code + * + * @param \stdClass $address + * @return \Magento\Framework\Phrase|string + */ + private function getDeliveryAddress(\stdClass $address) + { + $details = []; + + if (!empty($address->City)) { + $details[] = (string) $address->City; + } + + if (!empty($address->StateOrProvinceCode)) { + $details[] = (string) $address->StateOrProvinceCode; + } + + if (!empty($address->CountryCode)) { + $details[] = (string) $address->CountryCode; + } + + return implode(', ', $details); + } + + /** + * Parse tracking details events from response + * Return list of items in such format: + * ['activity', 'deliverydate', 'deliverytime', 'deliverylocation'] + * + * @param array $events + * @return array + */ + private function processTrackDetailsEvents(array $events) + { + $result = []; + /** @var \stdClass $event */ + foreach ($events as $event) { + $item = [ + 'activity' => (string) $event->EventDescription, + 'deliverydate' => null, + 'deliverytime' => null, + 'deliverylocation' => null + ]; + + if (!empty($event->Timestamp)) { + $datetime = \DateTime::createFromFormat(\DateTime::ISO8601, $event->Timestamp); + $item['deliverydate'] = $datetime->format('Y-m-d'); + $item['deliverytime'] = $datetime->format('H:i:s'); + } + + if (!empty($event->Address)) { + $item['deliverylocation'] = $this->getDeliveryAddress($event->Address); + } + + $result[] = $item; + } + + return $result; + } + + /** + * Append error message to rate result instance + * @param string $trackingValue + * @param string $errorMessage + */ + private function appendTrackingError($trackingValue, $errorMessage) + { + $error = $this->_trackErrorFactory->create(); + $error->setCarrier('fedex'); + $error->setCarrierTitle($this->getConfigData('title')); + $error->setTracking($trackingValue); + $error->setErrorMessage($errorMessage); + $result = $this->getResult(); + $result->append($error); + } } diff --git a/app/code/Magento/Fedex/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Fedex/Test/Unit/Model/CarrierTest.php index 41a48912ac4e4..385ddf8414832 100644 --- a/app/code/Magento/Fedex/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Fedex/Test/Unit/Model/CarrierTest.php @@ -5,131 +5,200 @@ */ namespace Magento\Fedex\Test\Unit\Model; -use Magento\Quote\Model\Quote\Address\RateRequest; -use Magento\Framework\DataObject; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\CatalogInventory\Model\StockRegistry; +use Magento\Directory\Helper\Data; +use Magento\Directory\Model\Country; +use Magento\Directory\Model\CountryFactory; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Directory\Model\RegionFactory; +use Magento\Fedex\Model\Carrier; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Module\Dir\Reader; +use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Xml\Security; +use Magento\Quote\Model\Quote\Address\RateRequest; +use Magento\Quote\Model\Quote\Address\RateResult\Error as RateResultError; +use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory as RateErrorFactory; +use Magento\Quote\Model\Quote\Address\RateResult\Method; +use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory; +use Magento\Shipping\Model\Rate\Result as RateResult; +use Magento\Shipping\Model\Rate\ResultFactory as RateResultFactory; +use Magento\Shipping\Model\Simplexml\ElementFactory; +use Magento\Shipping\Model\Tracking\Result; +use Magento\Shipping\Model\Tracking\Result\Error; +use Magento\Shipping\Model\Tracking\Result\ErrorFactory; +use Magento\Shipping\Model\Tracking\Result\Status; +use Magento\Shipping\Model\Tracking\Result\StatusFactory; +use Magento\Shipping\Model\Tracking\ResultFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Psr\Log\LoggerInterface; /** - * Class CarrierTest - * @package Magento\Fedex\Model - * TODO refactor me + * CarrierTest contains units test for Fedex carrier methods + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CarrierTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManager */ - protected $_helper; + private $helper; /** - * @var \Magento\Fedex\Model\Carrier + * @var Carrier|MockObject */ - protected $_model; + private $model; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $scope; + private $scope; /** - * Model under test - * - * @var \Magento\Quote\Model\Quote\Address\RateResult\Error|\PHPUnit_Framework_MockObject_MockObject + * @var Error|MockObject */ - protected $error; + private $error; /** - * @var \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ErrorFactory|MockObject */ - protected $errorFactory; + private $errorFactory; /** - * @return void + * @var ErrorFactory|MockObject */ - public function setUp() - { - $this->scope = $this->getMockBuilder( - '\Magento\Framework\App\Config\ScopeConfigInterface' - )->disableOriginalConstructor()->getMock(); - - $this->scope->expects( - $this->any() - )->method( - 'getValue' - )->will( - $this->returnCallback([$this, 'scopeConfiggetValue']) - ); - - $country = $this->getMock( - 'Magento\Directory\Model\Country', - ['load', 'getData', '__wakeup'], - [], - '', - false - ); - $country->expects($this->any())->method('load')->will($this->returnSelf()); - $countryFactory = $this->getMock('Magento\Directory\Model\CountryFactory', ['create'], [], '', false); - $countryFactory->expects($this->any())->method('create')->will($this->returnValue($country)); - - $rate = $this->getMock('Magento\Shipping\Model\Rate\Result', ['getError'], [], '', false); - $rateFactory = $this->getMock('Magento\Shipping\Model\Rate\ResultFactory', ['create'], [], '', false); - $rateFactory->expects($this->any())->method('create')->will($this->returnValue($rate)); - - $this->error = $this->getMockBuilder('\Magento\Quote\Model\Quote\Address\RateResult\Error') - ->setMethods(['setCarrier', 'setCarrierTitle', 'setErrorMessage']) + private $trackErrorFactory; + + /** + * @var StatusFactory|MockObject + */ + private $statusFactory; + + /** + * @var Result + */ + private $result; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->helper = new ObjectManager($this); + $this->scope = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->scope->expects(static::any()) + ->method('getValue') + ->willReturnCallback([$this, 'scopeConfigGetValue']); + + $countryFactory = $this->getCountryFactory(); + $rateFactory = $this->getRateFactory(); + $storeManager = $this->getStoreManager(); + $resultFactory = $this->getResultFactory(); + $this->initRateErrorFactory(); + + $rateMethodFactory = $this->getRateMethodFactory(); + + $this->trackErrorFactory = $this->getMockBuilder(ErrorFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) ->getMock(); - $this->errorFactory = $this->getMockBuilder('Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory') + + $this->statusFactory = $this->getMockBuilder(StatusFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->errorFactory->expects($this->any())->method('create')->willReturn($this->error); - - $store = $this->getMock('Magento\Store\Model\Store', ['getBaseCurrencyCode', '__wakeup'], [], '', false); - $storeManager = $this->getMockForAbstractClass('Magento\Store\Model\StoreManagerInterface'); - $storeManager->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $priceCurrency = $this->getMockBuilder('Magento\Framework\Pricing\PriceCurrencyInterface')->getMock(); - - $rateMethod = $this->getMock( - 'Magento\Quote\Model\Quote\Address\RateResult\Method', - null, - ['priceCurrency' => $priceCurrency] - ); - $rateMethodFactory = $this->getMock( - 'Magento\Quote\Model\Quote\Address\RateResult\MethodFactory', - ['create'], - [], - '', - false - ); - $rateMethodFactory->expects($this->any())->method('create')->will($this->returnValue($rateMethod)); - $this->_model = $this->getMock( - 'Magento\Fedex\Model\Carrier', - ['_getCachedQuotes', '_debug'], - [ - 'scopeConfig' => $this->scope, - 'rateErrorFactory' => $this->errorFactory, - 'logger' => $this->getMock('Psr\Log\LoggerInterface'), - 'xmlSecurity' => new Security(), - 'xmlElFactory' => $this->getMock('Magento\Shipping\Model\Simplexml\ElementFactory', [], [], '', false), - 'rateFactory' => $rateFactory, - 'rateMethodFactory' => $rateMethodFactory, - 'trackFactory' => $this->getMock('Magento\Shipping\Model\Tracking\ResultFactory', [], [], '', false), - 'trackErrorFactory' => - $this->getMock('Magento\Shipping\Model\Tracking\Result\ErrorFactory', [], [], '', false), - 'trackStatusFactory' => - $this->getMock('Magento\Shipping\Model\Tracking\Result\StatusFactory', [], [], '', false), - 'regionFactory' => $this->getMock('Magento\Directory\Model\RegionFactory', [], [], '', false), - 'countryFactory' => $countryFactory, - 'currencyFactory' => $this->getMock('Magento\Directory\Model\CurrencyFactory', [], [], '', false), - 'directoryData' => $this->getMock('Magento\Directory\Helper\Data', [], [], '', false), - 'stockRegistry' => $this->getMock('Magento\CatalogInventory\Model\StockRegistry', [], [], '', false), - 'storeManager' => $storeManager, - 'configReader' => $this->getMock('Magento\Framework\Module\Dir\Reader', [], [], '', false), - 'productCollectionFactory' => - $this->getMock('Magento\Catalog\Model\ResourceModel\Product\CollectionFactory', [], [], '', false), - 'data' => [] - ] - ); + + $elementFactory = $this->getMockBuilder(ElementFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $collectionFactory = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $regionFactory = $this->getMockBuilder(RegionFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $currencyFactory = $this->getMockBuilder(CurrencyFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $data = $this->getMockBuilder(Data::class) + ->disableOriginalConstructor() + ->getMock(); + + $stockRegistry = $this->getMockBuilder(StockRegistry::class) + ->disableOriginalConstructor() + ->getMock(); + + $reader = $this->getMockBuilder(Reader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = $this->getMockBuilder(Carrier::class) + ->setMethods(['_getCachedQuotes', '_debug']) + ->setConstructorArgs( + [ + 'scopeConfig' => $this->scope, + 'rateErrorFactory' => $this->errorFactory, + 'logger' => $this->getMock(LoggerInterface::class), + 'xmlSecurity' => new Security(), + 'xmlElFactory' => $elementFactory, + 'rateFactory' => $rateFactory, + 'rateMethodFactory' => $rateMethodFactory, + 'trackFactory' => $resultFactory, + 'trackErrorFactory' => $this->trackErrorFactory, + 'trackStatusFactory' => $this->statusFactory, + 'regionFactory' => $regionFactory, + 'countryFactory' => $countryFactory, + 'currencyFactory' => $currencyFactory, + 'directoryData' => $data, + 'stockRegistry' => $stockRegistry, + 'storeManager' => $storeManager, + 'configReader' => $reader, + 'productCollectionFactory' => $collectionFactory, + ] + ) + ->getMock(); + } + + /** + * @covers \Magento\Fedex\Model\Carrier::setRequest + */ + public function testSetRequestWithoutCity() + { + $request = $this->getMockBuilder(RateRequest::class) + ->disableOriginalConstructor() + ->setMethods(['getDestCity']) + ->getMock(); + $request->expects($this->once()) + ->method('getDestCity') + ->willReturn(null); + $this->model->setRequest($request); + } + + /** + * @covers \Magento\Fedex\Model\Carrier::setRequest + */ + public function testSetRequestWithCity() + { + $request = $this->getMockBuilder(RateRequest::class) + ->disableOriginalConstructor() + ->setMethods(['getDestCity']) + ->getMock(); + $request->expects(static::exactly(2)) + ->method('getDestCity') + ->willReturn('Small Town'); + $this->model->setRequest($request); } /** @@ -137,7 +206,7 @@ public function setUp() * @param $path * @return null|string */ - public function scopeConfiggetValue($path) + public function scopeConfigGetValue($path) { switch ($path) { case 'carriers/fedex/showmethod': @@ -147,44 +216,57 @@ public function scopeConfiggetValue($path) return 'ServiceType'; break; } + return null; } /** + * @param float $amount + * @param string $rateType + * @param float $expected * @dataProvider collectRatesDataProvider */ public function testCollectRatesRateAmountOriginBased($amount, $rateType, $expected) { - $this->scope->expects($this->any())->method('isSetFlag')->will($this->returnValue(true)); + $this->scope->expects(static::any()) + ->method('isSetFlag') + ->willReturn(true); // @codingStandardsIgnoreStart - $netAmount = new \Magento\Framework\DataObject([]); + $netAmount = new \stdClass(); $netAmount->Amount = $amount; - $totalNetCharge = new \Magento\Framework\DataObject([]); + $totalNetCharge = new \stdClass(); $totalNetCharge->TotalNetCharge = $netAmount; $totalNetCharge->RateType = $rateType; - $ratedShipmentDetail = new \Magento\Framework\DataObject([]); + $ratedShipmentDetail = new \stdClass(); $ratedShipmentDetail->ShipmentRateDetail = $totalNetCharge; - $rate = new \Magento\Framework\DataObject([]); + $rate = new \stdClass(); $rate->ServiceType = 'ServiceType'; $rate->RatedShipmentDetails = [$ratedShipmentDetail]; - $response = new \Magento\Framework\DataObject([]); + $response = new \stdClass(); $response->HighestSeverity = 'SUCCESS'; $response->RateReplyDetails = $rate; + // @codingStandardsIgnoreEnd - $this->_model->expects($this->any())->method('_getCachedQuotes')->will( - $this->returnValue(serialize($response)) - ); - $request = $this->getMock('Magento\Quote\Model\Quote\Address\RateRequest', [], [], '', false); - foreach ($this->_model->collectRates($request)->getAllRates() as $allRates) { + $this->model->expects(static::any()) + ->method('_getCachedQuotes') + ->willReturn(serialize($response)); + $request = $this->getMockBuilder(RateRequest::class) + ->disableOriginalConstructor() + ->getMock(); + + foreach ($this->model->collectRates($request)->getAllRates() as $allRates) { $this->assertEquals($expected, $allRates->getData('cost')); } - // @codingStandardsIgnoreEnd } + /** + * Get list of rates variations + * @return array + */ public function collectRatesDataProvider() { return [ @@ -201,15 +283,355 @@ public function collectRatesDataProvider() public function testCollectRatesErrorMessage() { - $this->scope->expects($this->once())->method('isSetFlag')->willReturn(false); + $this->scope->expects(static::once()) + ->method('isSetFlag') + ->willReturn(false); - $this->error->expects($this->once())->method('setCarrier')->with('fedex'); - $this->error->expects($this->once())->method('setCarrierTitle'); - $this->error->expects($this->once())->method('setErrorMessage'); + $this->error->expects(static::once()) + ->method('setCarrier') + ->with('fedex'); + $this->error->expects(static::once()) + ->method('setCarrierTitle'); + $this->error->expects(static::once()) + ->method('setErrorMessage'); $request = new RateRequest(); $request->setPackageWeight(1); - $this->assertSame($this->error, $this->_model->collectRates($request)); + static::assertSame($this->error, $this->model->collectRates($request)); + } + + /** + * @param string $data + * @param array $maskFields + * @param string $expected + * @dataProvider logDataProvider + */ + public function testFilterDebugData($data, array $maskFields, $expected) + { + $refClass = new \ReflectionClass(Carrier::class); + $property = $refClass->getProperty('_debugReplacePrivateDataKeys'); + $property->setAccessible(true); + $property->setValue($this->model, $maskFields); + + $refMethod = $refClass->getMethod('filterDebugData'); + $refMethod->setAccessible(true); + $result = $refMethod->invoke($this->model, $data); + static::assertEquals($expected, $result); + } + + /** + * Get list of variations + */ + public function logDataProvider() + { + return [ + [ + [ + 'WebAuthenticationDetail' => [ + 'UserCredential' => [ + 'Key' => 'testKey', + 'Password' => 'testPassword', + ], + ], + 'ClientDetail' => [ + 'AccountNumber' => 4121213, + 'MeterNumber' => 'testMeterNumber', + ], + ], + ['Key', 'Password', 'MeterNumber'], + [ + 'WebAuthenticationDetail' => [ + 'UserCredential' => [ + 'Key' => '****', + 'Password' => '****', + ], + ], + 'ClientDetail' => [ + 'AccountNumber' => 4121213, + 'MeterNumber' => '****', + ], + ], + ], + ]; + } + + /** + * @covers \Magento\Fedex\Model\Carrier::getTracking + */ + public function testGetTrackingErrorResponse() + { + $tracking = '123456789012'; + $errorMessage = 'Tracking information is unavailable.'; + + // @codingStandardsIgnoreStart + $response = new \stdClass(); + $response->HighestSeverity = 'ERROR'; + $response->Notifications = new \stdClass(); + $response->Notifications->Message = $errorMessage; + // @codingStandardsIgnoreEnd + + $this->model->expects(static::once()) + ->method('_getCachedQuotes') + ->willReturn(serialize($response)); + + $error = $this->helper->getObject(Error::class); + $this->trackErrorFactory->expects(static::once()) + ->method('create') + ->willReturn($error); + + $this->model->getTracking($tracking); + $tracks = $this->model->getResult()->getAllTrackings(); + + static::assertEquals(1, count($tracks)); + + /** @var Error $current */ + $current = $tracks[0]; + static::assertInstanceOf(Error::class, $current); + static::assertEquals(__($errorMessage), $current->getErrorMessage()); + } + + /** + * @covers \Magento\Fedex\Model\Carrier::getTracking + */ + public function testGetTracking() + { + $tracking = '123456789012'; + + // @codingStandardsIgnoreStart + $response = new \stdClass(); + $response->HighestSeverity = 'SUCCESS'; + $response->CompletedTrackDetails = new \stdClass(); + + $trackDetails = new \stdClass(); + $trackDetails->ShipTimestamp = '2016-08-05T14:06:35+00:00'; + $trackDetails->DeliverySignatureName = 'signature'; + + $trackDetails->StatusDetail = new \stdClass(); + $trackDetails->StatusDetail->Description = 'SUCCESS'; + + $trackDetails->Service = new \stdClass(); + $trackDetails->Service->Description = 'ground'; + $trackDetails->EstimatedDeliveryTimestamp = '2016-08-10T10:20:26+00:00'; + + $trackDetails->EstimatedDeliveryAddress = new \stdClass(); + $trackDetails->EstimatedDeliveryAddress->City = 'Culver City'; + $trackDetails->EstimatedDeliveryAddress->StateOrProvinceCode = 'CA'; + $trackDetails->EstimatedDeliveryAddress->CountryCode = 'US'; + + $trackDetails->PackageWeight = new \stdClass(); + $trackDetails->PackageWeight->Value = 23; + $trackDetails->PackageWeight->Units = 'LB'; + + $response->CompletedTrackDetails->TrackDetails = [$trackDetails]; + // @codingStandardsIgnoreEnd + + $this->model->expects(static::once()) + ->method('_getCachedQuotes') + ->willReturn(serialize($response)); + + $status = $this->helper->getObject(Status::class); + $this->statusFactory->expects(static::once()) + ->method('create') + ->willReturn($status); + + $this->model->getTracking($tracking); + $tracks = $this->model->getResult()->getAllTrackings(); + static::assertEquals(1, count($tracks)); + + $current = $tracks[0]; + $fields = [ + 'signedby', + 'status', + 'service', + 'shippeddate', + 'deliverydate', + 'deliverytime', + 'deliverylocation', + 'weight', + ]; + array_walk($fields, function ($field) use ($current) { + static::assertNotEmpty($current[$field]); + }); + + static::assertEquals('2016-08-10', $current['deliverydate']); + static::assertEquals('10:20:26', $current['deliverytime']); + static::assertEquals('2016-08-05', $current['shippeddate']); + } + + /** + * @covers \Magento\Fedex\Model\Carrier::getTracking + */ + public function testGetTrackingWithEvents() + { + $tracking = '123456789012'; + + // @codingStandardsIgnoreStart + $response = new \stdClass(); + $response->HighestSeverity = 'SUCCESS'; + $response->CompletedTrackDetails = new \stdClass(); + + $event = new \stdClass(); + $event->EventDescription = 'Test'; + $event->Timestamp = '2016-08-05T19:14:53+00:00'; + $event->Address = new \stdClass(); + + $event->Address->City = 'Culver City'; + $event->Address->StateOrProvinceCode = 'CA'; + $event->Address->CountryCode = 'US'; + + $trackDetails = new \stdClass(); + $trackDetails->Events = $event; + + $response->CompletedTrackDetails->TrackDetails = $trackDetails; + // @codingStandardsIgnoreEnd + + $this->model->expects(static::once()) + ->method('_getCachedQuotes') + ->willReturn(serialize($response)); + + $status = $this->helper->getObject(Status::class); + $this->statusFactory->expects(static::once()) + ->method('create') + ->willReturn($status); + + $this->model->getTracking($tracking); + $tracks = $this->model->getResult()->getAllTrackings(); + static::assertEquals(1, count($tracks)); + + $current = $tracks[0]; + static::assertNotEmpty($current['progressdetail']); + static::assertEquals(1, count($current['progressdetail'])); + + $event = $current['progressdetail'][0]; + $fields = ['activity', 'deliverydate', 'deliverytime', 'deliverylocation']; + array_walk($fields, function ($field) use ($event) { + static::assertNotEmpty($event[$field]); + }); + static::assertEquals('2016-08-05', $event['deliverydate']); + static::assertEquals('19:14:53', $event['deliverytime']); + } + + /** + * Init RateErrorFactory and RateResultErrors mocks + * @return void + */ + private function initRateErrorFactory() + { + $this->error = $this->getMockBuilder(RateResultError::class) + ->disableOriginalConstructor() + ->setMethods(['setCarrier', 'setCarrierTitle', 'setErrorMessage']) + ->getMock(); + $this->errorFactory = $this->getMockBuilder(RateErrorFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->errorFactory->expects(static::any()) + ->method('create') + ->willReturn($this->error); + } + + /** + * Creates mock rate result factory + * @return RateResultFactory|MockObject + */ + private function getRateFactory() + { + $rate = $this->getMockBuilder(RateResult::class) + ->disableOriginalConstructor() + ->setMethods(['getError']) + ->getMock(); + $rateFactory = $this->getMockBuilder(RateResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $rateFactory->expects(static::any()) + ->method('create') + ->willReturn($rate); + + return $rateFactory; + } + + /** + * Creates mock object for CountryFactory class + * @return CountryFactory|MockObject + */ + private function getCountryFactory() + { + $country = $this->getMockBuilder(Country::class) + ->disableOriginalConstructor() + ->setMethods(['load', 'getData']) + ->getMock(); + $country->expects(static::any()) + ->method('load') + ->willReturnSelf(); + + $countryFactory = $this->getMockBuilder(CountryFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $countryFactory->expects(static::any()) + ->method('create') + ->willReturn($country); + + return $countryFactory; + } + + /** + * Creates mock object for ResultFactory class + * @return ResultFactory|MockObject + */ + private function getResultFactory() + { + $resultFactory = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->result = $this->helper->getObject(Result::class); + $resultFactory->expects(static::any()) + ->method('create') + ->willReturn($this->result); + + return $resultFactory; + } + + /** + * Creates mock object for store manager + * @return StoreManagerInterface|MockObject + */ + private function getStoreManager() + { + $store = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->setMethods(['getBaseCurrencyCode']) + ->getMock(); + $storeManager = $this->getMock(StoreManagerInterface::class); + $storeManager->expects(static::any()) + ->method('getStore') + ->willReturn($store); + + return $storeManager; + } + + /** + * Creates mock object for rate method factory + * @return MethodFactory|MockObject + */ + private function getRateMethodFactory() + { + $priceCurrency = $this->getMock(PriceCurrencyInterface::class); + $rateMethod = $this->getMockBuilder(Method::class) + ->setConstructorArgs(['priceCurrency' => $priceCurrency]) + ->setMethods(null) + ->getMock(); + $rateMethodFactory = $this->getMockBuilder(MethodFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $rateMethodFactory->expects(static::any()) + ->method('create') + ->willReturn($rateMethod); + + return $rateMethodFactory; } } diff --git a/app/code/Magento/Fedex/composer.json b/app/code/Magento/Fedex/composer.json index 7f66babdcc999..88e066ff32158 100644 --- a/app/code/Magento/Fedex/composer.json +++ b/app/code/Magento/Fedex/composer.json @@ -15,7 +15,7 @@ "lib-libxml": "*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Fedex/etc/wsdl/TrackService_v5.wsdl b/app/code/Magento/Fedex/etc/wsdl/TrackService_v10.wsdl similarity index 63% rename from app/code/Magento/Fedex/etc/wsdl/TrackService_v5.wsdl rename to app/code/Magento/Fedex/etc/wsdl/TrackService_v10.wsdl index f3ceaf5056a20..77f53e02a539a 100644 --- a/app/code/Magento/Fedex/etc/wsdl/TrackService_v5.wsdl +++ b/app/code/Magento/Fedex/etc/wsdl/TrackService_v10.wsdl @@ -1,12 +1,12 @@ - + - + + + - - @@ -14,7 +14,7 @@ Descriptive data for a physical location. May be used as an actual physical address (place to which one could go), or as a container of "address parts" which should be handled as a unit (such as a city-state-ZIP combination within the US). - + Combination of number, street name, etc. At least one line is required for a valid physical address; empty lines should not be included. @@ -44,6 +44,11 @@ The two-letter code used to identify a country. + + + The fully spelt out name of a country. + + Indicates whether this address residential (as opposed to commercial). @@ -51,6 +56,48 @@ + + + Specifies the different appointment times on a specific date. + + + + + + Different appointment time windows on the date specified. + + + + + + + Specifies the details about the appointment time window. + + + + + The description that FedEx Ground uses for the appointment window being specified. + + + + + Specifies the window of time for an appointment. + + + + + + + + The description that FedEx uses for a given appointment window. + + + + + + + + Identifies where a tracking event occurs. @@ -73,11 +120,18 @@ + + + + + + + Identification of a FedEx operating company (transportation). @@ -108,7 +162,7 @@ - Only used in transactions which require identification of the Fed Ex Office integrator. + Only used in transactions which require identification of the FedEx Office integrator. @@ -118,6 +172,75 @@ + + + + + Value used to identify a commodity description; must be unique within the containing shipment. + + + + + + + + + + + + + Contains only additional quantitative information other than weight and quantity to calculate duties and taxes. + + + + + + + Defines additional characteristic of commodity used to calculate duties and taxes + + + + + + + + + All data required for this commodity in NAFTA Certificate of Origin. + + + + + + + + + + + True if duplicate packages (more than one package with the same tracking number) have been found, and only limited data will be provided for each one. + + + + + True if additional packages remain to be retrieved. + + + + + Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true). + + + + + Identifies the total number of available track details across all pages. + + + + + Contains detailed tracking information for the requested packages(s). + + + + The descriptive data for a point-of-contact person. @@ -148,6 +271,11 @@ Identifies the phone extension associated with this contact. + + + Identifies a toll free number, if any, associated with this contact. + + Identifies the pager number associated with this contact. @@ -171,6 +299,84 @@ + + + + + + + + + + + + + Unique identifier for the customer exception request. + + + + + + + + + + + + + Specifies additional description about customs options. This is a required field when the customs options type is "OTHER". + + + + + + + + + + + + + + + + + + + + + + + + + + + Details about the eligibility for a delivery option. + + + + + Type of delivery option. + + + + + Eligibility of the customer for the specific delivery option. + + + + + + + Specifies the different option types for delivery. + + + + + + + + The dimensions of this package and the unit type used for the measurements. @@ -280,6 +486,43 @@ + + + + + + Customer-declared value, with data type and legal values depending on excise condition, used in defining the taxable value of the item. + + + + + + + Specifies different values of eligibility status + + + + + + + + + + Identifies a kind of FedEx facility. + + + + + + + + + + + + + + CM = centimeters, IN = inches @@ -289,6 +532,15 @@ + + + Time Range specified in local time. + + + + + + Identifies the representation of human-readable text. @@ -306,6 +558,73 @@ + + + + + + + + + + + + + + + + + Defined by NAFTA regulations. + + + + + Defined by NAFTA regulations. + + + + + Identification of which producer is associated with this commodity (if multiple producers are used in a single shipment). + + + + + + Date range over which RVC net cost was calculated. + + + + + + + + + + + + + See instructions for NAFTA Certificate of Origin for code definitions. + + + + + + + + + + + + + See instructions for NAFTA Certificate of Origin for code definitions. + + + + + + + + The descriptive data regarding the result of the submitted transaction. @@ -382,7 +701,18 @@ Identification for a FedEx operating company (transportation and non-transportation). + + + + + + + + + + + @@ -394,11 +724,42 @@ + + + + + + + + + When the MoreData field = true in a TrackReply the PagingToken must be sent in the subsequent TrackRequest to retrieve the next page of data. + + + + + Specifies the number of results to display per page when the there is more than one page in the subsequent TrackReply. + + + + + + + + + + + + + + + + + Tracking number and additional shipment data used to identify a unique shipment for proof of delivery. @@ -431,13 +792,115 @@ - - - - - - - + + + + + This contains the severity type of the most severe Notification in the Notifications array. + + + + + Information about the request/reply such was the transaction successful or not, and any additional information relevant to the request and/or reply. There may be multiple Notifications in a reply. + + + + + Contains the CustomerTransactionDetail that is echoed back to the caller for matching requests and replies and a Localization element for defining the language/translation used in the reply data. + + + + + Contains the version of the reply being used. + + + + + True if duplicate packages (more than one package with the same tracking number) have been found, the packages array contains information about each duplicate. Use this information to determine which of the tracking numbers is the one you need and resend your request using the tracking number and TrackingNumberUniqueIdentifier for that package. + + + + + True if additional packages remain to be retrieved. + + + + + Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true). + + + + + Information about the notifications that are available for this tracking number. If there are duplicates the ship date and destination address information is returned for determining which TrackingNumberUniqueIdentifier to use on a subsequent request. + + + + + + + + + Descriptive data to be used in authentication of the sender's identity (and right to use FedEx web services). + + + + + Descriptive data identifying the client submitting the transaction. + + + + + Contains a free form field that is echoed back in the reply to match requests with replies and data that governs the data payload language/translations + + + + + + The tracking number to which the notifications will be triggered from. + + + + + Indicates whether to return tracking information for all associated packages. + + + + + When the MoreDataAvailable field is true in a TrackNotificationReply the PagingToken must be sent in the subsequent TrackNotificationRequest to retrieve the next page of data. + + + + + Use this field when your original request informs you that there are duplicates of this tracking number. If you get duplicates you will also receive some information about each of the duplicate tracking numbers to enable you to chose one and resend that number along with the TrackingNumberUniqueId to get notifications for that tracking number. + + + + + To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates. + + + + + To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates. + + + + + Included in the email notification identifying the requester of this notification. + + + + + Included in the email notification identifying the requester of this notification. + + + + + Who to send the email notifications to and for which events. The notificationRecipientType and NotifyOnShipment fields are not used in this request. + + + + The service type of the package/shipment. @@ -449,11 +912,34 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -465,10 +951,19 @@ + + + + + + + + + FedEx Signature Proof Of Delivery Fax reply. @@ -554,6 +1049,7 @@ + @@ -635,6 +1131,32 @@ + + + + + Specifies the status of the track special instructions requested. + + + + + Time when the status was changed. + + + + + + + + + + + + + + + + Each instance of this data type represents a barcode whose content must be represented as ASCII text (i.e. not binary data). @@ -662,22 +1184,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + The delivery location at the delivered to address. + + + + + + + + + + + + + + + + Detailed tracking information about a particular package. @@ -699,16 +1263,12 @@ When duplicate tracking numbers exist this data is returned with summary information for each of the duplicates. The summary information is used to determine which of the duplicates the intended tracking number is. This identifier is used on a subsequent track request to retrieve the tracking data for the desired tracking number. - + - A code that identifies this type of status. This is the most recent status. - - - - - A human-readable description of this status. + Specifies details about the status of the shipment being tracked. + Used to report the status of a piece of a multiple piece shipment which is no longer traveling with the rest of the packages in the shipment or has not been accounted for. @@ -719,6 +1279,8 @@ Used to convey information such as. 1. FedEx has received information about a package but has not yet taken possession of it. 2. FedEx has handed the package off to a third party for final delivery. 3. The package delivery has been cancelled + + Identifies a FedEx operating company (transportation). @@ -729,24 +1291,34 @@ Identifies operating transportation company that is the specific to the carrier code. + + + Specifies a detailed description about the carrier or the operating company. + + + + + If the package was interlined to a cartage agent, this is the name of the cartage agent. (Returned for CSR SL only.) + + Specifies the FXO production centre contact and address. - + Other related identifiers for this package such as reference numbers. - + - Retained for legacy compatibility only. User/screen friendly description of the Service type (e.g. Priority Overnight). + (Returned for CSR SL only.) - + - Strict representation of the Service type (e.g. PRIORITY_OVERNIGHT). + Specifies details about service such as service description and type. @@ -789,8 +1361,40 @@ The number of packages in this shipment. - - + + + Specifies the details about the SPOC details. + + + + + + + + + + + + + Specifies the reason for return. + + + + + + List of special handlings that applied to this package. (Returned for CSR SL only.) + + + + + (Returned for CSR SL only.) + + + + + Indicates last-known possession of package (Returned for CSR SL only.) + + The address information for the shipper. @@ -801,6 +1405,11 @@ The address of the FedEx pickup location/facility. + + + (Returned for CSR SL only.) + + Estimated package pickup time for shipments that haven't been picked up. @@ -821,16 +1430,54 @@ Total distance package still has to travel. Returned for Custom Critical shipments. + + + Provides additional details about package delivery. + + + + + (Returned for CSR SL only.) + + + + + This is the latest updated destination address. + + The address this package is to be (or has been) delivered. + + + + The address this package is requested to placed on hold. + + + + + (Returned for CSR SL only.) + + The address of the FedEx delivery location/facility. + + + + + Date and time the package should be (or should have been) delivered. (Returned for CSR SL only.) + + + + + Date and time the package would be delivered if the package has appointment delivery as a special service. + + Projected package delivery time based on ship time stamp, service and destination. Not populated if delivery has already occurred. @@ -861,16 +1508,28 @@ User/screen friendly representation of the DeliveryLocationType (delivery location at the delivered to address). Can be returned in localized text. + + + Specifies the number of delivery attempts made to deliver the shipment. + + This is either the name of the person that signed for the package or "Signature not requested" or "Signature on file". - + - True if signed for by signature image is available. + Specifies the details about the count of the packages delivered at the delivery location and the count of the packages at the origin. + + + Specifies the total number of unique addresses on the CRNs in a consolidation. + + + + The types of email notifications that are available for the package. @@ -881,9 +1540,9 @@ Returned for cargo shipments only when they are currently split across vehicles. - + - Indicates redirection eligibility as determined by tracking service, subject to refinement/override by redirect-to-hold service. + Specifies the details about the eligibility for different delivery options. @@ -893,6 +1552,11 @@ + + + + + FedEx scanning information about a package. @@ -928,6 +1592,11 @@ Address information of the station that is responsible for the scan. + + + FedEx location ID where the scan took place. (Returned for CSR SL only.) + + Indicates where the arrival actually occurred. @@ -945,6 +1614,7 @@ + @@ -953,9 +1623,11 @@ + + @@ -1010,123 +1682,11 @@ - - - FedEx Track Notification reply. - - - - - This contains the severity type of the most severe Notification in the Notifications array. - - - - - Information about the request/reply such was the transaction successful or not, and any additional information relevant to the request and/or reply. There may be multiple Notifications in a reply. - - - - - Contains the CustomerTransactionDetail that is echoed back to the caller for matching requests and replies and a Localization element for defining the language/translation used in the reply data. - - - - - Contains the version of the reply being used. - - - - - True if duplicate packages (more than one package with the same tracking number) have been found, the packages array contains information about each duplicate. Use this information to determine which of the tracking numbers is the one you need and resend your request using the tracking number and TrackingNumberUniqueIdentifier for that package. - - - - - True if additional packages remain to be retrieved. - - - - - Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true). - - - - - Information about the notifications that are available for this tracking number. If there are duplicates the ship date and destination address information is returned for determining which TrackingNumberUniqueIdentifier to use on a subsequent request. - - - - - - - FedEx Track Notification request. - + - - - Descriptive data to be used in authentication of the sender's identity (and right to use FedEx web services). - - - - - Descriptive data identifying the client submitting the transaction. - - - - - Contains a free form field that is echoed back in the reply to match requests with replies and data that governs the data payload language/translations - - - - - Identifies the version/level of a service operation expected by a caller (in each request) and performed by the callee (in each reply). - - - - - The tracking number to which the notifications will be triggered from. - - - - - Indicates whether to return tracking information for all associated packages. - - - - - When the MoreDataAvailable field is true in a TrackNotificationReply the PagingToken must be sent in the subsequent TrackNotificationRequest to retrieve the next page of data. - - - - - Use this field when your original request informs you that there are duplicates of this tracking number. If you get duplicates you will also receive some information about each of the duplicate tracking numbers to enable you to chose one and resend that number along with the TrackingNumberUniqueId to get notifications for that tracking number. - - - - - To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates. - - - - - To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates. - - - - - Included in the email notification identifying the requester of this notification. - - - - - Included in the email notification identifying the requester of this notification. - - - - - Who to send the email notifications to and for which events. The notificationRecipientType and NotifyOnShipment fields are not used in this request. - - + + + @@ -1134,18 +1694,41 @@ The type and value of the package identifier that is to be used to retrieve the tracking information for a package. - + - The value to be used to retrieve tracking information for a package. + The type of the Value to be used to retrieve tracking information for a package (e.g. SHIPPER_REFERENCE, PURCHASE_ORDER, TRACKING_NUMBER_OR_DOORTAG, etc..) . - + - The type of the Value to be used to retrieve tracking information for a package (e.g. SHIPPER_REFERENCE, PURCHASE_ORDER, TRACKING_NUMBER_OR_DOORTAG, etc..) . + The value to be used to retrieve tracking information for a package. + + + + + + + + + + + + + + + + + + + + + + + Used to report the status of a piece of a multiple piece shipment which is no longer traveling with the rest of the packages in the shipment or has not been accounted for. @@ -1188,24 +1771,9 @@ Contains the version of the reply being used. - - - True if duplicate packages (more than one package with the same tracking number) have been found, and only limited data will be provided for each one. - - - - - True if additional packages remain to be retrieved. - - - - - Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true). - - - + - Contains detailed tracking information for the requested packages(s). + Contains detailed tracking entity information. @@ -1235,6 +1803,46 @@ The version of the request being used. + + + Specifies the details needed to select the shipment being requested to be tracked. + + + + + The customer can specify a desired time out value for this particular transaction. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The FedEx operating company (transportation) used for this package's delivery. @@ -1245,7 +1853,7 @@ Identifies operating transportation company that is the specific to the carrier code. - + The type and value of the package identifier that is to be used to retrieve the tracking information for a package or group of packages. @@ -1270,29 +1878,181 @@ For tracking by references information either the account number or destination postal code and country must be provided. + + + Specifies the SPOD account number for the shipment being tracked. + + For tracking by references information either the account number or destination postal code and country must be provided. - + - If false the reply will contain summary/profile data including current status. If true the reply contains profile + detailed scan activity for each package. + Specifies the details about how to retrieve the subsequent pages when there is more than one page in the TrackReply. - + - When the MoreData field = true in a TrackReply the PagingToken must be sent in the subsequent TrackRequest to retrieve the next page of data. + The customer can specify a desired time out value for this particular tracking number. - + + + + + + + Specifies a shorter description for the service that is calculated per the service code. + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Specifies the status and status update time of the track special instructions. + + + + + Specifies the estimated delivery time that was originally estimated when the shipment was shipped. + + + + + Specifies the time the customer requested a change to the shipment. + + + + + The requested appointment time for delivery. + + + + Used when a cargo shipment is split across vehicles. This is used to give the status of each part of the shipment. @@ -1320,6 +2080,26 @@ + + + + + + + + + + + Specifies the details about the status of the track information for the shipments being tracked. + + + + + + + + + Descriptive data that governs data payload language/translations. The TransactionDetail from the request is echoed back to the caller in the corresponding reply. @@ -1368,6 +2148,11 @@ Used in authentication of the sender's identity. + + + This was renamed from cspCredential. + + Credential used to authenticate a specific software application. This value is provided by FedEx after registration. @@ -1402,7 +2187,7 @@ Identifies a system or sub-system which performs an operation. - + Identifies the service business level. @@ -1421,6 +2206,9 @@ + + + @@ -1433,11 +2221,8 @@ - - - - - + + @@ -1446,10 +2231,6 @@ - - - - @@ -1462,11 +2243,15 @@ + + + + - - + + @@ -1474,8 +2259,8 @@ - - + + @@ -1483,8 +2268,8 @@ - - + + @@ -1492,8 +2277,8 @@ - - + + @@ -1504,7 +2289,7 @@ - + - + \ No newline at end of file diff --git a/app/code/Magento/Fedex/view/frontend/web/js/model/shipping-rates-validation-rules.js b/app/code/Magento/Fedex/view/frontend/web/js/model/shipping-rates-validation-rules.js index c6e03eb9ebf84..e51a6cefa2cb0 100644 --- a/app/code/Magento/Fedex/view/frontend/web/js/model/shipping-rates-validation-rules.js +++ b/app/code/Magento/Fedex/view/frontend/web/js/model/shipping-rates-validation-rules.js @@ -15,6 +15,9 @@ define( }, 'country_id': { 'required': true + }, + 'city': { + 'required': true } }; } diff --git a/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php b/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php index 84b6e554d4cfb..8616c186555e3 100644 --- a/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php +++ b/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php @@ -49,20 +49,13 @@ public function getMailingAddress() } /** - * Enter description here... - * + * @deprecated * @return $this */ protected function _convertAdditionalData() { - $details = @unserialize($this->getInfo()->getAdditionalData()); - if (is_array($details)) { - $this->_payableTo = isset($details['payable_to']) ? (string)$details['payable_to'] : ''; - $this->_mailingAddress = isset($details['mailing_address']) ? (string)$details['mailing_address'] : ''; - } else { - $this->_payableTo = ''; - $this->_mailingAddress = ''; - } + $this->_payableTo = $this->getInfo()->getAdditionalInformation('payable_to'); + $this->_mailingAddress = $this->getInfo()->getAdditionalInformation('mailing_address'); return $this; } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php index cafbad48edc68..27209fc8d3538 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php @@ -5,81 +5,118 @@ */ namespace Magento\OfflinePayments\Test\Unit\Block\Info; +use Magento\Framework\View\Element\Template\Context; +use Magento\OfflinePayments\Block\Info\Checkmo; +use Magento\Payment\Model\Info; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * CheckmoTest contains list of test for block methods testing + */ class CheckmoTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\OfflinePayments\Block\Info\Checkmo + * @var Info|MockObject + */ + private $info; + + /** + * @var Checkmo */ - protected $_model; + private $block; + /** + * @inheritdoc + */ protected function setUp() { - $context = $this->getMock('Magento\Framework\View\Element\Template\Context', [], [], '', false); - $this->_model = new \Magento\OfflinePayments\Block\Info\Checkmo($context); + $context = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->info = $this->getMockBuilder(Info::class) + ->disableOriginalConstructor() + ->setMethods(['getAdditionalInformation']) + ->getMock(); + + $this->block = new Checkmo($context); } /** + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getPayableTo + * @param array $details + * @param string|null $expected * @dataProvider getPayableToDataProvider */ public function testGetPayableTo($details, $expected) { - $info = $this->getMock('Magento\Payment\Model\Info', ['getAdditionalData'], [], '', false); - $info->expects($this->once()) - ->method('getAdditionalData') - ->willReturn(serialize($details)); - $this->_model->setData('info', $info); + $this->info->expects(static::at(0)) + ->method('getAdditionalInformation') + ->with('payable_to') + ->willReturn($details); + $this->block->setData('info', $this->info); - $this->assertEquals($expected, $this->_model->getPayableTo()); + static::assertEquals($expected, $this->block->getPayableTo()); } /** + * Get list of variations for payable configuration option testing * @return array */ public function getPayableToDataProvider() { return [ - [['payable_to' => 'payable'], 'payable'], - ['', ''] + ['payable_to' => 'payable', 'payable'], + ['', null] ]; } /** + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress + * @param array $details + * @param string|null $expected * @dataProvider getMailingAddressDataProvider */ public function testGetMailingAddress($details, $expected) { - $info = $this->getMock('Magento\Payment\Model\Info', ['getAdditionalData'], [], '', false); - $info->expects($this->once()) - ->method('getAdditionalData') - ->willReturn(serialize($details)); - $this->_model->setData('info', $info); + $this->info->expects(static::at(1)) + ->method('getAdditionalInformation') + ->with('mailing_address') + ->willReturn($details); + $this->block->setData('info', $this->info); - $this->assertEquals($expected, $this->_model->getMailingAddress()); + static::assertEquals($expected, $this->block->getMailingAddress()); } /** + * Get list of variations for mailing address testing * @return array */ public function getMailingAddressDataProvider() { return [ - [['mailing_address' => 'blah@blah.com'], 'blah@blah.com'], - ['', ''] + ['mailing_address' => 'blah@blah.com', 'blah@blah.com'], + ['mailing_address' => '', null] ]; } + /** + * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress + */ public function testConvertAdditionalDataIsNeverCalled() { - $info = $this->getMock('Magento\Payment\Model\Info', ['getAdditionalData'], [], '', false); - $info->expects($this->once()) - ->method('getAdditionalData') - ->willReturn(serialize(['mailing_address' => 'blah@blah.com'])); - $this->_model->setData('info', $info); + $mailingAddress = 'blah@blah.com'; + $this->info->expects(static::at(1)) + ->method('getAdditionalInformation') + ->with('mailing_address') + ->willReturn($mailingAddress); + $this->block->setData('info', $this->info); // First we set the property $this->_mailingAddress - $this->_model->getMailingAddress(); + $this->block->getMailingAddress(); // And now we get already setted property $this->_mailingAddress - $this->assertEquals('blah@blah.com', $this->_model->getMailingAddress()); + static::assertEquals($mailingAddress, $this->block->getMailingAddress()); } } diff --git a/app/code/Magento/OfflinePayments/composer.json b/app/code/Magento/OfflinePayments/composer.json index 7bf3ac42f0131..7f344520fc626 100644 --- a/app/code/Magento/OfflinePayments/composer.json +++ b/app/code/Magento/OfflinePayments/composer.json @@ -8,7 +8,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml index d5bff77e002d1..8c5cfd50cc110 100644 --- a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml +++ b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml @@ -10,7 +10,7 @@ */ ?> escapeHtml($block->getMethod()->getTitle()) ?> -getInfo()->getAdditionalData()): ?> +getInfo()->getAdditionalInformation()): ?> getPayableTo()): ?>
escapeHtml(__('Make Check payable to: %1', $block->getPayableTo())) ?> diff --git a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml index 6195cdbd77654..5587ac239d37e 100644 --- a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml +++ b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml @@ -11,7 +11,7 @@ ?> escapeHtml($block->getMethod()->getTitle()) ?> {{pdf_row_separator}} -getInfo()->getAdditionalData()): ?> +getInfo()->getAdditionalInformation()): ?> {{pdf_row_separator}} getPayableTo()): ?> escapeHtml(__('Make Check payable to: %1', $block->getPayableTo())) ?> diff --git a/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml b/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml index f0dbff1add32b..3c0b6bb23086a 100644 --- a/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml +++ b/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml @@ -11,7 +11,7 @@ ?>
escapeHtml($block->getMethod()->getTitle()) ?>
- getInfo()->getAdditionalData()): ?> + getInfo()->getAdditionalInformation()): ?> getPayableTo()): ?>
escapeHtml(__('Make Check payable to')) ?> diff --git a/app/code/Magento/Payment/composer.json b/app/code/Magento/Payment/composer.json index 5fa892b0063e3..7fa5611ae4377 100644 --- a/app/code/Magento/Payment/composer.json +++ b/app/code/Magento/Payment/composer.json @@ -12,7 +12,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php b/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php index f0edccbaf812a..637c4aa007220 100644 --- a/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php +++ b/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php @@ -7,6 +7,8 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\File\Uploader; +use \Magento\Framework\Validator\AllowedProtocols; +use \Magento\Framework\Exception\LocalizedException; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -43,6 +45,13 @@ class RetrieveImage extends \Magento\Backend\App\Action */ protected $fileUtility; + /** + * URI validator + * + * @var AllowedProtocols + */ + private $validator; + /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory @@ -78,6 +87,7 @@ public function execute() $baseTmpMediaPath = $this->mediaConfig->getBaseTmpMediaPath(); try { $remoteFileUrl = $this->getRequest()->getParam('remote_image'); + $this->validateRemoteFile($remoteFileUrl); $originalFileName = basename($remoteFileUrl); $localFileName = Uploader::getCorrectFileName($originalFileName); $localTmpFileName = Uploader::getDispretionPath($localFileName) . DIRECTORY_SEPARATOR . $localFileName; @@ -98,6 +108,41 @@ public function execute() return $response; } + /** + * Get URI validator + * + * @return AllowedProtocols + */ + private function getValidator() + { + if ($this->validator === null) { + $this->validator = $this->_objectManager->get(AllowedProtocols::class); + } + + return $this->validator; + } + + /** + * Validate remote file + * + * @param string $remoteFileUrl + * @throws LocalizedException + * + * @return $this + */ + private function validateRemoteFile($remoteFileUrl) + { + /** @var AllowedProtocols $validator */ + $validator = $this->getValidator(); + if (!$validator->isValid($remoteFileUrl)) { + throw new LocalizedException( + __("Protocol isn't allowed") + ); + } + + return $this; + } + /** * @param string $fileName * @return mixed diff --git a/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php b/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php index 46e128488707f..f2f540a254198 100644 --- a/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php +++ b/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php @@ -71,6 +71,7 @@ class RetrieveImageTest extends \PHPUnit_Framework_TestCase */ public function setUp() { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->contextMock = $this->getMock('\Magento\Backend\App\Action\Context', [], [], '', false); $this->rawFactoryMock = $this->getMock('\Magento\Framework\Controller\Result\RawFactory', ['create'], [], '', false); @@ -95,9 +96,16 @@ public function setUp() $this->storageFileMock = $this->getMock('\Magento\MediaStorage\Model\ResourceModel\File\Storage\File', [], [], '', false); $this->request = $this->getMock('\Magento\Framework\App\RequestInterface'); - $this->contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->request)); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $managerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMockForAbstractClass(); + $managerMock->expects($this->once()) + ->method('get') + ->willReturn(new \Magento\Framework\Validator\AllowedProtocols()); + $this->contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->request)); + $this->contextMock->expects($this->any())->method('getObjectManager')->willReturn($managerMock); $this->image = $objectManager->getObject( '\Magento\ProductVideo\Controller\Adminhtml\Product\Gallery\RetrieveImage', diff --git a/app/code/Magento/ProductVideo/composer.json b/app/code/Magento/ProductVideo/composer.json index ffb2c663487e9..0576271160fb7 100644 --- a/app/code/Magento/ProductVideo/composer.json +++ b/app/code/Magento/ProductVideo/composer.json @@ -13,7 +13,7 @@ "magento/magento-composer-installer": "*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "proprietary" ], diff --git a/app/code/Magento/ProductVideo/i18n/en_US.csv b/app/code/Magento/ProductVideo/i18n/en_US.csv index 7047317396999..5cbf48373b768 100644 --- a/app/code/Magento/ProductVideo/i18n/en_US.csv +++ b/app/code/Magento/ProductVideo/i18n/en_US.csv @@ -7,3 +7,4 @@ "Preview Image","Preview Image" "Get Video Information","Get Video Information" "Youtube or Vimeo supported","Youtube or Vimeo supported" +"Protocol isn't allowed", "Protocol isn't allowed" diff --git a/app/code/Magento/Quote/Api/GuestShipmentEstimationInterface.php b/app/code/Magento/Quote/Api/GuestShipmentEstimationInterface.php new file mode 100644 index 0000000000000..b0690c6be445b --- /dev/null +++ b/app/code/Magento/Quote/Api/GuestShipmentEstimationInterface.php @@ -0,0 +1,15 @@ +quoteIdMaskFactory->create()->load($cartId, 'masked_id'); return $this->shippingMethodManagement->estimateByAddress($quoteIdMask->getQuoteId(), $address); } + + /** + * @inheritdoc + */ + public function estimateByExtendedAddress($cartId, AddressInterface $address) + { + /** @var $quoteIdMask QuoteIdMask */ + $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id'); + + return $this->getShipmentEstimationManagement() + ->estimateByExtendedAddress((int) $quoteIdMask->getQuoteId(), $address); + } + + /** + * Get shipment estimation management service + * @return ShipmentEstimationInterface + * @deprecated + */ + private function getShipmentEstimationManagement() + { + if ($this->shipmentEstimationManagement === null) { + $this->shipmentEstimationManagement = ObjectManager::getInstance() + ->get(ShipmentEstimationInterface::class); + } + return $this->shipmentEstimationManagement; + } } diff --git a/app/code/Magento/Quote/Model/ShippingMethodManagement.php b/app/code/Magento/Quote/Model/ShippingMethodManagement.php index 3f6ef3ca248df..2eb473a08e140 100644 --- a/app/code/Magento/Quote/Model/ShippingMethodManagement.php +++ b/app/code/Magento/Quote/Model/ShippingMethodManagement.php @@ -5,10 +5,14 @@ */ namespace Magento\Quote\Model; +use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\StateException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\StateException; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\EstimateAddressInterface; +use Magento\Quote\Api\ShipmentEstimationInterface; +use Magento\Quote\Model\Quote; /** * Shipping method read service. @@ -16,7 +20,8 @@ */ class ShippingMethodManagement implements \Magento\Quote\Api\ShippingMethodManagementInterface, - \Magento\Quote\Model\ShippingMethodManagementInterface + \Magento\Quote\Model\ShippingMethodManagementInterface, + ShipmentEstimationInterface { /** * Quote repository. @@ -185,6 +190,21 @@ public function estimateByAddress($cartId, \Magento\Quote\Api\Data\EstimateAddre ); } + /** + * @inheritdoc + */ + public function estimateByExtendedAddress($cartId, AddressInterface $address) + { + /** @var \Magento\Quote\Model\Quote $quote */ + $quote = $this->quoteRepository->getActive($cartId); + + // no methods applicable for empty carts or carts with virtual products + if ($quote->isVirtual() || 0 == $quote->getItemsCount()) { + return []; + } + return $this->getShippingMethods($quote, $address->getData()); + } + /** * {@inheritDoc} */ @@ -219,14 +239,29 @@ public function estimateByAddressId($cartId, $addressId) * @return \Magento\Quote\Api\Data\ShippingMethodInterface[] An array of shipping methods. */ protected function getEstimatedRates(\Magento\Quote\Model\Quote $quote, $country, $postcode, $regionId, $region) + { + $data = [ + EstimateAddressInterface::KEY_COUNTRY_ID => $country, + EstimateAddressInterface::KEY_POSTCODE => $postcode, + EstimateAddressInterface::KEY_REGION_ID => $regionId, + EstimateAddressInterface::KEY_REGION => $region + ]; + return $this->getShippingMethods($quote, $data); + } + + /** + * Get list of available shipping methods + * @param \Magento\Quote\Model\Quote $quote + * @param array $addressData + * @return \Magento\Quote\Api\Data\ShippingMethodInterface[] + */ + private function getShippingMethods(Quote $quote, array $addressData) { $output = []; $shippingAddress = $quote->getShippingAddress(); - $shippingAddress->setCountryId($country); - $shippingAddress->setPostcode($postcode); - $shippingAddress->setRegionId($regionId); - $shippingAddress->setRegion($region); + $shippingAddress->addData($addressData); $shippingAddress->setCollectShippingRates(true); + $this->totalsCollector->collectAddressTotals($quote, $shippingAddress); $shippingRates = $shippingAddress->getGroupedAllShippingRates(); foreach ($shippingRates as $carrierRates) { diff --git a/app/code/Magento/Quote/Test/Unit/Model/GuestCart/GuestShippingMethodManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/GuestCart/GuestShippingMethodManagementTest.php index 2b428728ca7cf..9521040920874 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/GuestCart/GuestShippingMethodManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/GuestCart/GuestShippingMethodManagementTest.php @@ -7,10 +7,17 @@ namespace Magento\Quote\Test\Unit\Model\GuestCart; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\ShipmentEstimationInterface; +use Magento\Quote\Api\Data\ShippingMethodInterface; +use Magento\Quote\Model\GuestCart\GuestShippingMethodManagement; + class GuestShippingMethodManagementTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\Quote\Api\GuestShippingMethodManagementInterface + * @var GuestShippingMethodManagement */ private $model; @@ -25,19 +32,24 @@ class GuestShippingMethodManagementTest extends \PHPUnit_Framework_TestCase private $quoteIdMaskFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ShipmentEstimationInterface|MockObject */ - private $quoteIdMaskMock; + private $shipmentEstimationManagement; /** - * @var string + * @var QuoteIdMask|MockObject */ - private $maskedCartId; + private $quoteIdMask; /** * @var string */ - private $cartId; + private $maskedCartId = 'f216207248d65c789b17be8545e0aa73'; + + /** + * @var int + */ + private $cartId = 867; protected function setUp() { @@ -46,22 +58,26 @@ protected function setUp() $this->shippingMethodManagementMock = $this->getMock('Magento\Quote\Model\ShippingMethodManagement', [], [], '', false); - $this->maskedCartId = 'f216207248d65c789b17be8545e0aa73'; - $this->cartId = 867; - $guestCartTestHelper = new GuestCartTestHelper($this); - list($this->quoteIdMaskFactoryMock, $this->quoteIdMaskMock) = $guestCartTestHelper->mockQuoteIdMask( + list($this->quoteIdMaskFactoryMock, $this->quoteIdMask) = $guestCartTestHelper->mockQuoteIdMask( $this->maskedCartId, $this->cartId ); + $this->shipmentEstimationManagement = $this->getMockForAbstractClass(ShipmentEstimationInterface::class); + $this->model = $objectManager->getObject( - 'Magento\Quote\Model\GuestCart\GuestShippingMethodManagement', + GuestShippingMethodManagement::class, [ 'shippingMethodManagement' => $this->shippingMethodManagementMock, 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock, ] ); + + $refObject = new \ReflectionClass(GuestShippingMethodManagement::class); + $refProperty = $refObject->getProperty('shipmentEstimationManagement'); + $refProperty->setAccessible(true); + $refProperty->setValue($this->model, $this->shipmentEstimationManagement); } public function testSet() @@ -99,4 +115,23 @@ public function testGet() $this->assertEquals($retValue, $this->model->get($this->maskedCartId)); } + + /** + * @covers \Magento\Quote\Model\GuestCart\GuestShippingMethodManagement::getShipmentEstimationManagement + */ + public function testEstimateByExtendedAddress() + { + $address = $this->getMockForAbstractClass(AddressInterface::class); + + $methodObject = $this->getMockForAbstractClass(ShippingMethodInterface::class); + $expectedRates = [$methodObject]; + + $this->shipmentEstimationManagement->expects(static::once()) + ->method('estimateByExtendedAddress') + ->with($this->cartId, $address) + ->willReturn($expectedRates); + + $carriersRates = $this->model->estimateByExtendedAddress($this->maskedCartId, $address); + static::assertEquals($expectedRates, $carriersRates); + } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php index ed1b0188a4de8..6f570ee040350 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php @@ -7,11 +7,20 @@ namespace Magento\Quote\Test\Unit\Model; -use \Magento\Quote\Model\ShippingMethodManagement; - -use Magento\Quote\Api\Data\ShippingMethodInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Api\Data\ShippingMethodInterface; +use Magento\Quote\Model\Cart\ShippingMethodConverter; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\Quote\Address\Rate; +use Magento\Quote\Model\Quote\TotalsCollector; +use Magento\Quote\Model\QuoteRepository; +use Magento\Quote\Model\ShippingMethodManagement; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ShippingMethodManagementTest extends \PHPUnit_Framework_TestCase { /** @@ -22,42 +31,47 @@ class ShippingMethodManagementTest extends \PHPUnit_Framework_TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $quoteRepositoryMock; + protected $shippingMethodMock; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $quoteMock; + protected $methodDataFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ShippingMethodConverter|MockObject */ - protected $shippingAddressMock; + protected $converter; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ - protected $shippingMethodMock; + protected $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var QuoteRepository|MockObject */ - protected $methodDataFactoryMock; + private $quoteRepository; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Quote|MockObject */ - protected $converterMock; + private $quote; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var Address|MockObject */ - protected $objectManager; + private $shippingAddress; + + /** + * @var TotalsCollector|MockObject + */ + private $totalsCollector; protected function setUp() { $this->objectManager = new ObjectManager($this); - $this->quoteRepositoryMock = $this->getMock('\Magento\Quote\Api\CartRepositoryInterface'); + $this->quoteRepository = $this->getMock('\Magento\Quote\Api\CartRepositoryInterface'); $this->methodDataFactoryMock = $this->getMock( '\Magento\Quote\Api\Data\ShippingMethodInterfaceFactory', [ @@ -69,9 +83,9 @@ protected function setUp() ); $this->storeMock = $this->getMock('\Magento\Store\Model\Store', [], [], '', false); - $this->quoteMock = $this->getMock( - '\Magento\Quote\Model\Quote', - [ + $this->quote = $this->getMockBuilder(Quote::class) + ->disableOriginalConstructor() + ->setMethods([ 'getShippingAddress', 'isVirtual', 'getItemsCount', @@ -80,14 +94,12 @@ protected function setUp() 'collectTotals', 'save', '__wakeup', - ], - [], - '', - false - ); - $this->shippingAddressMock = $this->getMock( - '\Magento\Quote\Model\Quote\Address', - [ + ]) + ->getMock(); + + $this->shippingAddress = $this->getMockBuilder(Address::class) + ->disableOriginalConstructor() + ->setMethods([ 'getCountryId', 'getShippingMethod', 'getShippingDescription', @@ -97,27 +109,30 @@ protected function setUp() 'collectShippingRates', 'requestShippingRates', 'setShippingMethod', - '__wakeup', 'getShippingRateByCode', - ], - [], - '', - false - ); - $this->converterMock = $this->getMock( - '\Magento\Quote\Model\Cart\ShippingMethodConverter', - [], - [], - '', - false - ); + 'addData', + 'setCollectShippingRates', + '__wakeup', + ]) + ->getMock(); + + $this->converter = $this->getMockBuilder(ShippingMethodConverter::class) + ->disableOriginalConstructor() + ->setMethods(['modelToDataObject']) + ->getMock(); + + $this->totalsCollector = $this->getMockBuilder(TotalsCollector::class) + ->disableOriginalConstructor() + ->setMethods(['collectAddressTotals']) + ->getMock(); $this->model = $this->objectManager->getObject( - 'Magento\Quote\Model\ShippingMethodManagement', + ShippingMethodManagement::class, [ - 'quoteRepository' => $this->quoteRepositoryMock, + 'quoteRepository' => $this->quoteRepository, 'methodDataFactory' => $this->methodDataFactoryMock, - 'converter' => $this->converterMock, + 'converter' => $this->converter, + 'totalsCollector' => $this->totalsCollector ] ); } @@ -129,11 +144,11 @@ protected function setUp() public function testGetMethodWhenShippingAddressIsNotSet() { $cartId = 666; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->shippingAddressMock->expects($this->once())->method('getCountryId')->will($this->returnValue(null)); + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->shippingAddress->expects($this->once())->method('getCountryId')->will($this->returnValue(null)); $this->assertNull($this->model->get($cartId)); } @@ -143,27 +158,27 @@ public function testGetMethod() $cartId = 666; $countryId = 1; $currencyCode = 'US_dollar'; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->quoteMock->expects($this->once()) + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->quote->expects($this->once()) ->method('getQuoteCurrencyCode')->willReturn($currencyCode); - $this->shippingAddressMock->expects($this->any()) + $this->shippingAddress->expects($this->any()) ->method('getCountryId')->will($this->returnValue($countryId)); - $this->shippingAddressMock->expects($this->any()) + $this->shippingAddress->expects($this->any()) ->method('getShippingMethod')->will($this->returnValue('one_two')); - $this->shippingAddressMock->expects($this->once())->method('collectShippingRates')->willReturnSelf(); + $this->shippingAddress->expects($this->once())->method('collectShippingRates')->willReturnSelf(); $shippingRateMock = $this->getMock('\Magento\Quote\Model\Quote\Address\Rate', [], [], '', false); - $this->shippingAddressMock->expects($this->once()) + $this->shippingAddress->expects($this->once()) ->method('getShippingRateByCode') ->with('one_two') ->willReturn($shippingRateMock); $this->shippingMethodMock = $this->getMock('\Magento\Quote\Api\Data\ShippingMethodInterface'); - $this->converterMock->expects($this->once()) + $this->converter->expects($this->once()) ->method('modelToDataObject') ->with($shippingRateMock, $currencyCode) ->willReturn($this->shippingMethodMock); @@ -175,13 +190,13 @@ public function testGetMethodIfMethodIsNotSet() $cartId = 666; $countryId = 1; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->shippingAddressMock->expects($this->any()) + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->shippingAddress->expects($this->any()) ->method('getCountryId')->will($this->returnValue($countryId)); - $this->shippingAddressMock->expects($this->any()) + $this->shippingAddress->expects($this->any()) ->method('getShippingMethod')->will($this->returnValue(null)); $this->assertNull($this->model->get($cartId)); @@ -190,9 +205,9 @@ public function testGetMethodIfMethodIsNotSet() public function testGetListForVirtualCart() { $cartId = 834; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once()) + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once()) ->method('isVirtual')->will($this->returnValue(true)); $this->assertEquals([], $this->model->getList($cartId)); @@ -201,11 +216,11 @@ public function testGetListForVirtualCart() public function testGetListForEmptyCart() { $cartId = 834; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once()) + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once()) ->method('isVirtual')->will($this->returnValue(false)); - $this->quoteMock->expects($this->once()) + $this->quote->expects($this->once()) ->method('getItemsCount')->will($this->returnValue(0)); $this->assertEquals([], $this->model->getList($cartId)); @@ -218,15 +233,15 @@ public function testGetListForEmptyCart() public function testGetListWhenShippingAddressIsNotSet() { $cartId = 834; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once()) + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once()) ->method('isVirtual')->will($this->returnValue(false)); - $this->quoteMock->expects($this->once()) + $this->quote->expects($this->once()) ->method('getItemsCount')->will($this->returnValue(3)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->shippingAddressMock->expects($this->once())->method('getCountryId')->will($this->returnValue(null)); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->shippingAddress->expects($this->once())->method('getCountryId')->will($this->returnValue(null)); $this->model->getList($cartId); } @@ -234,27 +249,27 @@ public function testGetListWhenShippingAddressIsNotSet() public function testGetList() { $cartId = 834; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once()) + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once()) ->method('isVirtual')->will($this->returnValue(false)); - $this->quoteMock->expects($this->once()) + $this->quote->expects($this->once()) ->method('getItemsCount')->will($this->returnValue(3)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->shippingAddressMock->expects($this->once())->method('getCountryId')->will($this->returnValue(345)); - $this->shippingAddressMock->expects($this->once())->method('collectShippingRates'); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->shippingAddress->expects($this->once())->method('getCountryId')->will($this->returnValue(345)); + $this->shippingAddress->expects($this->once())->method('collectShippingRates'); $shippingRateMock = $this->getMock('\Magento\Quote\Model\Quote\Address\Rate', [], [], '', false); - $this->shippingAddressMock->expects($this->once()) + $this->shippingAddress->expects($this->once()) ->method('getGroupedAllShippingRates') ->will($this->returnValue([[$shippingRateMock]])); $currencyCode = 'EUR'; - $this->quoteMock->expects($this->once()) + $this->quote->expects($this->once()) ->method('getQuoteCurrencyCode') ->will($this->returnValue($currencyCode)); - $this->converterMock->expects($this->once()) + $this->converter->expects($this->once()) ->method('modelToDataObject') ->with($shippingRateMock, $currencyCode) ->will($this->returnValue('RateValue')); @@ -270,10 +285,10 @@ public function testSetMethodWithInputException() $cartId = 12; $carrierCode = 34; $methodCode = 56; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(0)); - $this->quoteMock->expects($this->never())->method('isVirtual'); + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once())->method('getItemsCount')->will($this->returnValue(0)); + $this->quote->expects($this->never())->method('isVirtual'); $this->model->set($cartId, $carrierCode, $methodCode); } @@ -288,10 +303,10 @@ public function testSetMethodWithVirtualProduct() $carrierCode = 34; $methodCode = 56; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); - $this->quoteMock->expects($this->once())->method('isVirtual')->will($this->returnValue(true)); + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); + $this->quote->expects($this->once())->method('isVirtual')->will($this->returnValue(true)); $this->model->set($cartId, $carrierCode, $methodCode); } @@ -305,13 +320,13 @@ public function testSetMethodWithoutShippingAddress() $cartId = 12; $carrierCode = 34; $methodCode = 56; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); - $this->quoteMock->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->shippingAddressMock->expects($this->once())->method('getCountryId')->will($this->returnValue(null)); + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); + $this->quote->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->shippingAddress->expects($this->once())->method('getCountryId')->will($this->returnValue(null)); $this->model->set($cartId, $carrierCode, $methodCode); } @@ -326,19 +341,19 @@ public function testSetMethodWithNotFoundMethod() $carrierCode = 34; $methodCode = 56; $countryId = 1; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); - $this->quoteMock->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->shippingAddressMock->expects($this->once()) + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); + $this->quote->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->shippingAddress->expects($this->once()) ->method('getCountryId')->will($this->returnValue($countryId)); - $this->shippingAddressMock->expects($this->once()) + $this->shippingAddress->expects($this->once()) ->method('setShippingMethod')->with($carrierCode . '_' . $methodCode); - $this->shippingAddressMock->expects($this->once()) + $this->shippingAddress->expects($this->once()) ->method('getShippingRateByCode')->will($this->returnValue(false)); - $this->shippingAddressMock->expects($this->never())->method('save'); + $this->shippingAddress->expects($this->never())->method('save'); $this->model->set($cartId, $carrierCode, $methodCode); } @@ -354,23 +369,23 @@ public function testSetMethodWithCouldNotSaveException() $methodCode = 56; $countryId = 1; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); - $this->quoteMock->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->shippingAddressMock->expects($this->once()) + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); + $this->quote->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->shippingAddress->expects($this->once()) ->method('getCountryId')->will($this->returnValue($countryId)); - $this->shippingAddressMock->expects($this->once()) + $this->shippingAddress->expects($this->once()) ->method('setShippingMethod')->with($carrierCode . '_' . $methodCode); - $this->shippingAddressMock->expects($this->once()) + $this->shippingAddress->expects($this->once()) ->method('getShippingRateByCode')->will($this->returnValue(true)); $exception = new \Exception('Custom Error'); - $this->quoteMock->expects($this->once())->method('collectTotals')->will($this->returnSelf()); - $this->quoteRepositoryMock->expects($this->once()) + $this->quote->expects($this->once())->method('collectTotals')->will($this->returnSelf()); + $this->quoteRepository->expects($this->once()) ->method('save') - ->with($this->quoteMock) + ->with($this->quote) ->willThrowException($exception); $this->model->set($cartId, $carrierCode, $methodCode); @@ -385,13 +400,13 @@ public function testSetMethodWithoutAddress() $cartId = 12; $carrierCode = 34; $methodCode = 56; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); - $this->quoteMock->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->shippingAddressMock->expects($this->once())->method('getCountryId'); + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); + $this->quote->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->shippingAddress->expects($this->once())->method('getCountryId'); $this->model->set($cartId, $carrierCode, $methodCode); } @@ -402,21 +417,99 @@ public function testSetMethod() $carrierCode = 34; $methodCode = 56; $countryId = 1; - $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive')->with($cartId)->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); - $this->quoteMock->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); - $this->quoteMock->expects($this->once()) - ->method('getShippingAddress')->will($this->returnValue($this->shippingAddressMock)); - $this->shippingAddressMock->expects($this->once()) + $this->quoteRepository->expects($this->once()) + ->method('getActive')->with($cartId)->will($this->returnValue($this->quote)); + $this->quote->expects($this->once())->method('getItemsCount')->will($this->returnValue(1)); + $this->quote->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); + $this->quote->expects($this->once()) + ->method('getShippingAddress')->will($this->returnValue($this->shippingAddress)); + $this->shippingAddress->expects($this->once()) ->method('getCountryId')->will($this->returnValue($countryId)); - $this->shippingAddressMock->expects($this->once()) + $this->shippingAddress->expects($this->once()) ->method('setShippingMethod')->with($carrierCode . '_' . $methodCode); - $this->shippingAddressMock->expects($this->once()) + $this->shippingAddress->expects($this->once()) ->method('getShippingRateByCode')->will($this->returnValue(true)); - $this->quoteMock->expects($this->once())->method('collectTotals')->will($this->returnSelf()); - $this->quoteRepositoryMock->expects($this->once())->method('save')->with($this->quoteMock); + $this->quote->expects($this->once())->method('collectTotals')->will($this->returnSelf()); + $this->quoteRepository->expects($this->once())->method('save')->with($this->quote); $this->assertTrue($this->model->set($cartId, $carrierCode, $methodCode)); } + + /** + * @covers \Magento\Quote\Model\ShippingMethodManagement::estimateByExtendedAddress + */ + public function testEstimateByExtendedAddress() + { + $cartId = 1; + + $addressData = [ + 'region' => 'California', + 'region_id' => 23, + 'country_id' => 1, + 'postcode' => 90200 + ]; + $currencyCode = 'UAH'; + + $address = $this->getMockBuilder(Address::class) + ->disableOriginalConstructor() + ->setMethods(['getData']) + ->getMock(); + + $this->quoteRepository->expects(static::once()) + ->method('getActive') + ->with($cartId) + ->willReturn($this->quote); + + $this->quote->expects(static::once()) + ->method('isVirtual') + ->willReturn(false); + $this->quote->expects(static::once()) + ->method('getItemsCount') + ->willReturn(1); + + $address->expects(static::once()) + ->method('getData') + ->willReturn($addressData); + + $this->quote->expects(static::once()) + ->method('getShippingAddress') + ->willReturn($this->shippingAddress); + + $this->shippingAddress->expects(static::once()) + ->method('addData') + ->with($addressData) + ->willReturnSelf(); + $this->shippingAddress->expects(static::once()) + ->method('setCollectShippingRates') + ->with(true) + ->willReturnSelf(); + + $this->totalsCollector->expects(static::once()) + ->method('collectAddressTotals') + ->with($this->quote, $this->shippingAddress) + ->willReturnSelf(); + + $rate = $this->getMockBuilder(Rate::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $methodObject = $this->getMockForAbstractClass(ShippingMethodInterface::class); + $expectedRates = [$methodObject]; + + $this->shippingAddress->expects(static::once()) + ->method('getGroupedAllShippingRates') + ->willReturn([[$rate]]); + + $this->quote->expects(static::once()) + ->method('getQuoteCurrencyCode') + ->willReturn($currencyCode); + + $this->converter->expects(static::once()) + ->method('modelToDataObject') + ->with($rate, $currencyCode) + ->willReturn($methodObject); + + $carriersRates = $this->model->estimateByExtendedAddress($cartId, $address); + static::assertEquals($expectedRates, $carriersRates); + } } diff --git a/app/code/Magento/Quote/composer.json b/app/code/Magento/Quote/composer.json index a7c35778ba714..83baf4bef4ded 100644 --- a/app/code/Magento/Quote/composer.json +++ b/app/code/Magento/Quote/composer.json @@ -20,7 +20,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index f0f03d0b51f63..2e53f17c127df 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -7,6 +7,7 @@ --> + @@ -35,6 +36,7 @@ + diff --git a/app/code/Magento/Quote/etc/webapi.xml b/app/code/Magento/Quote/etc/webapi.xml index eb2d909cacffc..d76f7f614208e 100644 --- a/app/code/Magento/Quote/etc/webapi.xml +++ b/app/code/Magento/Quote/etc/webapi.xml @@ -104,6 +104,18 @@ + + + + + + + + + + + + @@ -116,7 +128,7 @@ - + @@ -142,7 +154,7 @@ - + diff --git a/app/code/Magento/Sales/composer.json b/app/code/Magento/Sales/composer.json index 8d6ec90ae9468..fc07d17fd323f 100644 --- a/app/code/Magento/Sales/composer.json +++ b/app/code/Magento/Sales/composer.json @@ -32,7 +32,7 @@ "magento/module-sales-sample-data": "Sample Data version:100.0.*" }, "type": "magento2-module", - "version": "100.0.9", + "version": "100.0.10", "license": [ "OSL-3.0", "AFL-3.0" 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 b3723d56ac28d..aee9182bde893 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml @@ -36,7 +36,10 @@ helper('Magento\Sales\Helper\Reorder')->canReorder($_order->getEntityId())) : ?> - + diff --git a/app/code/Magento/Sales/view/frontend/templates/order/info/buttons.phtml b/app/code/Magento/Sales/view/frontend/templates/order/info/buttons.phtml index 2e1145662c3f8..9643bd2267622 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/info/buttons.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/info/buttons.phtml @@ -10,7 +10,10 @@
getOrder() ?> helper('Magento\Sales\Helper\Reorder')->canReorder($_order->getEntityId())) : ?> - + diff --git a/app/code/Magento/Sales/view/frontend/templates/order/recent.phtml b/app/code/Magento/Sales/view/frontend/templates/order/recent.phtml index b351fb6c86d54..caf8a4abd2cd8 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/recent.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/recent.phtml @@ -46,7 +46,10 @@ helper('Magento\Sales\Helper\Reorder')->canReorder($_order->getEntityId())) : ?> - + diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php index 7487bb8a50df1..be621dbb139fb 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrier.php @@ -16,6 +16,8 @@ */ abstract class AbstractCarrier extends \Magento\Framework\DataObject implements AbstractCarrierInterface { + const DEBUG_KEYS_MASK = '****'; + /** * Carrier's code * diff --git a/app/code/Magento/Shipping/composer.json b/app/code/Magento/Shipping/composer.json index f5783f3a2b9af..8e0ea5cefbad8 100644 --- a/app/code/Magento/Shipping/composer.json +++ b/app/code/Magento/Shipping/composer.json @@ -24,7 +24,7 @@ "magento/module-ups": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml new file mode 100644 index 0000000000000..d655d96e1dd36 --- /dev/null +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml @@ -0,0 +1,95 @@ +getData('track'); +$email = $block->getData('storeSupportEmail'); +$fields = [ + 'Status' => 'getStatus', + 'Signed by' => 'getSignedby', + 'Delivered to' => 'getDeliveryLocation', + 'Shipped or billed on' => 'getShippedDate', + 'Service Type' => 'getService', + 'Weight' => 'getWeight', +]; +?> +
+ + + + + + + + getCarrierTitle()): ?> + + + + + + getErrorMessage()): ?> + + + + + getTrackSummary()): ?> + + + + + getUrl()): ?> + + + + + + $property): ?> + $property())): ?> + + + + + + + + getDeliverydate()): ?> + + + + + + + + + + + + + + +
escapeHtml(__('Order tracking')); ?>
escapeHtml(__('Tracking Number:')); ?>escapeHtml($track->getTracking()); ?>
escapeHtml(__('Carrier:')); ?>escapeHtml($track->getCarrierTitle()); ?>
escapeHtml(__('Error:')); ?> + escapeHtml(__('Tracking information is currently not available. Please ')); ?> + getContactUsEnabled()) : ?> + + escapeHtml(__('contact us')); ?> + + escapeHtml(__(' for more information or ')); ?> + + escapeHtml(__('email us at ')); ?> + +
escapeHtml(__('Info:')); ?>escapeHtml($track->getTrackSummary()); ?>
escapeHtml(__('Track:')); ?> + + escapeUrl($track->getUrl()); ?> + +
escapeHtml(__($title . ':')); ?>escapeHtml($track->$property()); ?>
escapeHtml(__('Delivered on:')); ?> + formatDeliveryDateTime($track->getDeliverydate(), $track->getDeliverytime()); ?> +
+ escapeHtml($track['title']) : $block->escapeHtml(__('N/A'))); ?>: + escapeHtml($track['number']) : ''); ?>
diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/popup.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/popup.phtml index e9bb00df8b852..412cb4b411a98 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/tracking/popup.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/popup.phtml @@ -4,156 +4,60 @@ * See COPYING.txt for license details. */ +use Magento\Framework\View\Element\Template; + // @codingStandardsIgnoreFile +/** @var $block \Magento\Shipping\Block\Tracking\Popup */ + +$results = $block->getTrackingInfo(); ?> - -getTrackingInfo(); ?>
- 0): ?> - $_result): ?> - -
- - 0): ?> - - -
- - - - - - - - - getCarrierTitle()): ?> - - - - - - getErrorMessage()): ?> - - - - - getTrackSummary()): ?> - - - - - getUrl()): ?> - - - - - - getStatus()): ?> - - - - - - - getDeliverydate()): ?> - - - - - - - getSignedby()): ?> - - - - - - - getDeliveryLocation()): ?> - - - - - - - getShippedDate()): ?> - - - - - - - getService()): ?> - - - - - - - getWeight()): ?> - - - - - - - - - - - - - - -
escapeHtml($track->getTracking()); ?>
escapeHtml($track->getCarrierTitle()); ?>
getContactUsEnabled()) : ?>getStoreSupportEmail() ?>
getTrackSummary(); ?>
escapeHtml($track->getUrl()); ?>
getStatus(); ?>
formatDeliveryDateTime($track->getDeliverydate(), $track->getDeliverytime()); ?>
getSignedby(); ?>
getDeliveryLocation(); ?>
getShippedDate(); ?>
getService(); ?>
getWeight(); ?>
escapeHtml($track['title']) : __('N/A')); ?>:escapeHtml($track['number']) : ''); ?>
-
- getProgressdetail())>0): ?> + + $result): ?> + +
escapeHtml(__('Shipment #')) . $shipId; ?>
+ + + $track): ?>
- - - - - - - - - - - - getProgressdetail() as $_detail): ?> - formatDeliveryDate($_detail['deliverydate']) : '') ?> - formatDeliveryTime($_detail['deliverytime'], $_detail['deliverydate']) : '') ?> - - - - - - - - -
+ addChild('shipping.tracking.details.' . $counter, Template::class, [ + 'track' => $track, + 'template' => 'Magento_Shipping::tracking/details.phtml', + 'storeSupportEmail' => $block->getStoreSupportEmail() + ] + ); + ?> + getChildHtml('shipping.tracking.details.' . $counter); ?>
- - - - - - - -
- - - + getProgressdetail())): ?> + addChild('shipping.tracking.progress.'. $counter, Template::class, [ + 'track' => $track, + 'template' => 'Magento_Shipping::tracking/progress.phtml' + ]); + ?> + getChildHtml('shipping.tracking.progress.' . $counter); ?> + + + +
+
escapeHtml(__('There is no tracking available for this shipment.')); ?>
+
+ + -
+
+
escapeHtml(__('There is no tracking available.')); ?>
+
diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml new file mode 100644 index 0000000000000..d5ce13a277f8b --- /dev/null +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml @@ -0,0 +1,43 @@ +getData('track'); +?> +
+ + + + + + + + + + + + getProgressdetail() as $detail): ?> + formatDeliveryDate($detail['deliverydate']) : ''); ?> + formatDeliveryTime($detail['deliverytime'], $detail['deliverydate']) : ''); ?> + + + + + + + + +
escapeHtml(__('Track history')); ?>
escapeHtml(__('Location')); ?>escapeHtml(__('Date')); ?>escapeHtml(__('Local Time')); ?>escapeHtml(__('Description')); ?>
+ escapeHtml($detail['deliverylocation']) : ''); ?> + + + + + escapeHtml($detail['activity']) : ''); ?> +
+
\ No newline at end of file diff --git a/app/code/Magento/Swatches/composer.json b/app/code/Magento/Swatches/composer.json index 47ea1163f5f91..8b14185feb056 100644 --- a/app/code/Magento/Swatches/composer.json +++ b/app/code/Magento/Swatches/composer.json @@ -19,7 +19,7 @@ "magento/module-swatches-sample-data": "Sample Data version:100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "proprietary" ], diff --git a/app/code/Magento/Swatches/view/frontend/layout/catalogsearch_advanced_result.xml b/app/code/Magento/Swatches/view/frontend/layout/catalogsearch_advanced_result.xml new file mode 100644 index 0000000000000..3b17bac8e1540 --- /dev/null +++ b/app/code/Magento/Swatches/view/frontend/layout/catalogsearch_advanced_result.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/app/code/Magento/Swatches/view/frontend/layout/catalogsearch_result_index.xml b/app/code/Magento/Swatches/view/frontend/layout/catalogsearch_result_index.xml new file mode 100644 index 0000000000000..3b17bac8e1540 --- /dev/null +++ b/app/code/Magento/Swatches/view/frontend/layout/catalogsearch_result_index.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/app/code/Magento/Swatches/view/frontend/layout/wishlist_index_configure_type_configurable.xml b/app/code/Magento/Swatches/view/frontend/layout/wishlist_index_configure_type_configurable.xml new file mode 100644 index 0000000000000..9b77cab3a1dcb --- /dev/null +++ b/app/code/Magento/Swatches/view/frontend/layout/wishlist_index_configure_type_configurable.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/app/code/Magento/Ups/composer.json b/app/code/Magento/Ups/composer.json index 34ed77729d4d9..2a179f3cf69c7 100644 --- a/app/code/Magento/Ups/composer.json +++ b/app/code/Magento/Ups/composer.json @@ -13,7 +13,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/User/Block/User/Edit.php b/app/code/Magento/User/Block/User/Edit.php index 896caee0c7154..e17aa61e66de5 100644 --- a/app/code/Magento/User/Block/User/Edit.php +++ b/app/code/Magento/User/Block/User/Edit.php @@ -48,11 +48,25 @@ protected function _construct() parent::_construct(); $this->buttonList->update('save', 'label', __('Save User')); - $this->buttonList->update('delete', 'label', __('Delete User')); + $this->buttonList->remove('delete'); $objId = $this->getRequest()->getParam($this->_objectId); if (!empty($objId)) { + $this->addButton( + 'delete', + [ + 'label' => __('Delete User'), + 'class' => 'delete', + 'onclick' => sprintf( + 'deleteConfirm("%s", "%s", %s)', + __('Are you sure you want to do this?'), + $this->getUrl('adminhtml/*/delete'), + json_encode(['data' => ['user_id' => $objId]]) + ), + ] + ); + $deleteConfirmMsg = __("Are you sure you want to revoke the user\'s tokens?"); $this->addButton( 'invalidate', diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php index 3afaafef92fd5..e79b5e9605742 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/ResetPasswordPost.php @@ -40,11 +40,22 @@ public function execute() $user->setRpToken(null); $user->setRpTokenCreatedAt(null); try { - $user->save(); - $this->messageManager->addSuccess(__('You updated your password.')); - $this->getResponse()->setRedirect( - $this->_objectManager->get('Magento\Backend\Helper\Data')->getHomePageUrl() - ); + $errors = $user->validate(); + if ($errors !== true && !empty($errors)) { + foreach ($errors as $error) { + $this->messageManager->addError($error); + $this->_redirect( + 'adminhtml/auth/resetpassword', + ['_nosecret' => true, '_query' => ['id' => $userId, 'token' => $passwordResetToken]] + ); + } + } else { + $user->save(); + $this->messageManager->addSuccess(__('You updated your password.')); + $this->getResponse()->setRedirect( + $this->_objectManager->get(\Magento\Backend\Helper\Data::class)->getHomePageUrl() + ); + } } catch (\Magento\Framework\Validator\Exception $exception) { $this->messageManager->addMessages($exception->getMessages()); $this->_redirect( diff --git a/app/code/Magento/User/Controller/Adminhtml/User/Delete.php b/app/code/Magento/User/Controller/Adminhtml/User/Delete.php index 77f096cca1991..570cf4132c646 100644 --- a/app/code/Magento/User/Controller/Adminhtml/User/Delete.php +++ b/app/code/Magento/User/Controller/Adminhtml/User/Delete.php @@ -13,9 +13,9 @@ class Delete extends \Magento\User\Controller\Adminhtml\User */ public function execute() { - $currentUser = $this->_objectManager->get('Magento\Backend\Model\Auth\Session')->getUser(); + $currentUser = $this->_objectManager->get(\Magento\Backend\Model\Auth\Session::class)->getUser(); - if ($userId = $this->getRequest()->getParam('user_id')) { + if ($userId = (int)$this->getRequest()->getPost('user_id')) { if ($currentUser->getId() == $userId) { $this->messageManager->addError(__('You cannot delete your own account.')); $this->_redirect('adminhtml/*/edit', ['user_id' => $userId]); diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php index 91d17c9fec402..0cb6ff026ba39 100644 --- a/app/code/Magento/User/Model/User.php +++ b/app/code/Magento/User/Model/User.php @@ -293,9 +293,8 @@ protected function validatePasswordChange() } // Check whether password was used before - $passwordHash = $this->_encryptor->getHash($password, false); foreach ($this->getResource()->getOldPasswords($this) as $oldPasswordHash) { - if ($passwordHash === $oldPasswordHash) { + if ($this->_encryptor->isValidHash($password, $oldPasswordHash)) { return [$errorMessage]; } } diff --git a/app/code/Magento/User/Observer/Backend/TrackAdminNewPasswordObserver.php b/app/code/Magento/User/Observer/Backend/TrackAdminNewPasswordObserver.php index 27fe5db149f4d..7e9664ed699ce 100644 --- a/app/code/Magento/User/Observer/Backend/TrackAdminNewPasswordObserver.php +++ b/app/code/Magento/User/Observer/Backend/TrackAdminNewPasswordObserver.php @@ -35,13 +35,6 @@ class TrackAdminNewPasswordObserver implements ObserverInterface */ protected $authSession; - /** - * Encryption model - * - * @var \Magento\Framework\Encryption\EncryptorInterface - */ - protected $encryptor; - /** * Message manager interface * @@ -53,20 +46,17 @@ class TrackAdminNewPasswordObserver implements ObserverInterface * @param \Magento\User\Model\Backend\Config\ObserverConfig $observerConfig * @param \Magento\User\Model\ResourceModel\User $userResource * @param \Magento\Backend\Model\Auth\Session $authSession - * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor * @param \Magento\Framework\Message\ManagerInterface $messageManager */ public function __construct( \Magento\User\Model\Backend\Config\ObserverConfig $observerConfig, \Magento\User\Model\ResourceModel\User $userResource, \Magento\Backend\Model\Auth\Session $authSession, - \Magento\Framework\Encryption\EncryptorInterface $encryptor, \Magento\Framework\Message\ManagerInterface $messageManager ) { $this->observerConfig = $observerConfig; $this->userResource = $userResource; $this->authSession = $authSession; - $this->encryptor = $encryptor; $this->messageManager = $messageManager; } @@ -81,10 +71,9 @@ public function execute(EventObserver $observer) /* @var $user \Magento\User\Model\User */ $user = $observer->getEvent()->getObject(); if ($user->getId()) { - $password = $user->getCurrentPassword(); + $passwordHash = $user->getPassword(); $passwordLifetime = $this->observerConfig->getAdminPasswordLifetime(); - if ($passwordLifetime && $password && !$user->getForceNewPassword()) { - $passwordHash = $this->encryptor->getHash($password, false); + if ($passwordLifetime && $passwordHash && !$user->getForceNewPassword()) { $this->userResource->trackPassword($user, $passwordHash, $passwordLifetime); $this->messageManager->getMessages()->deleteMessageByIdentifier('magento_user_password_expired'); $this->authSession->unsPciAdminUserIsPasswordExpired(); diff --git a/app/code/Magento/User/Test/Unit/Model/UserTest.php b/app/code/Magento/User/Test/Unit/Model/UserTest.php index 4cdbbe956903d..d992efc0a8e46 100644 --- a/app/code/Magento/User/Test/Unit/Model/UserTest.php +++ b/app/code/Magento/User/Test/Unit/Model/UserTest.php @@ -658,15 +658,9 @@ public function testCheckPasswordChangeEqualToPrevious() $this->model->setPassword($newPassword) ->setId(1) ->setOrigData('password', $oldPassword); - $this->encryptorMock->expects($this->once()) + $this->encryptorMock->expects($this->atLeastOnce()) ->method('isValidHash') - ->with($newPassword, $oldPassword) - ->willReturn(false); - - $this->encryptorMock->expects($this->once()) - ->method('getHash') - ->with($newPassword, false) - ->willReturn($newPasswordHash); + ->will($this->onConsecutiveCalls(false, true)); $this->resourceMock->expects($this->once())->method('getOldPasswords')->willReturn(['hash1', $newPasswordHash]); @@ -690,20 +684,13 @@ public function testCheckPasswordChangeValid() $validatorMock->expects($this->once())->method('isValid')->willReturn(true); $newPassword = "NEWmYn3wpassw0rd"; - $newPasswordHash = "new password hash"; $oldPassword = "OLDmYn3wpassw0rd"; $this->model->setPassword($newPassword) ->setId(1) ->setOrigData('password', $oldPassword); - $this->encryptorMock->expects($this->once()) + $this->encryptorMock->expects($this->atLeastOnce()) ->method('isValidHash') - ->with($newPassword, $oldPassword) - ->willReturn(false); - - $this->encryptorMock->expects($this->once()) - ->method('getHash') - ->with($newPassword, false) - ->willReturn($newPasswordHash); + ->will($this->onConsecutiveCalls(false, false, false)); $this->resourceMock->expects($this->once())->method('getOldPasswords')->willReturn(['hash1', 'hash2']); diff --git a/app/code/Magento/User/Test/Unit/Observer/Backend/TrackAdminNewPasswordObserverTest.php b/app/code/Magento/User/Test/Unit/Observer/Backend/TrackAdminNewPasswordObserverTest.php index cd2a5055f6c1e..9895535e5e48e 100644 --- a/app/code/Magento/User/Test/Unit/Observer/Backend/TrackAdminNewPasswordObserverTest.php +++ b/app/code/Magento/User/Test/Unit/Observer/Backend/TrackAdminNewPasswordObserverTest.php @@ -24,9 +24,6 @@ class TrackAdminNewPasswordObserverTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Backend\Model\Auth\Session|\PHPUnit_Framework_MockObject_MockObject */ protected $authSessionMock; - /** @var \Magento\Framework\Encryption\EncryptorInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $encryptorMock; - /** @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $managerInterfaceMock; @@ -57,11 +54,6 @@ public function setUp() ] )->getMock(); - $this->encryptorMock = $this->getMockBuilder('\Magento\Framework\Encryption\EncryptorInterface') - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - $this->managerInterfaceMock = $this->getMockBuilder('Magento\Framework\Message\ManagerInterface') ->disableOriginalConstructor() ->setMethods([]) @@ -82,7 +74,6 @@ public function setUp() 'observerConfig' => $this->observerConfig, 'userResource' => $this->userMock, 'authSession' => $this->authSessionMock, - 'encryptor' => $this->encryptorMock, 'messageManager' => $this->managerInterfaceMock, ] ); @@ -91,7 +82,6 @@ public function setUp() public function testTrackAdminPassword() { $newPW = "mYn3wpassw0rd"; - $oldPW = "notsecure"; $uid = 123; /** @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject $eventObserverMock */ $eventObserverMock = $this->getMockBuilder('Magento\Framework\Event\Observer') @@ -108,19 +98,18 @@ public function testTrackAdminPassword() /** @var \Magento\User\Model\User|\PHPUnit_Framework_MockObject_MockObject $userMock */ $userMock = $this->getMockBuilder('Magento\User\Model\User') ->disableOriginalConstructor() - ->setMethods(['getId', 'getCurrentPassword', 'getForceNewPassword']) + ->setMethods(['getId', 'getPassword', 'getForceNewPassword']) ->getMock(); $eventObserverMock->expects($this->once())->method('getEvent')->willReturn($eventMock); $eventMock->expects($this->once())->method('getObject')->willReturn($userMock); $userMock->expects($this->once())->method('getId')->willReturn($uid); - $userMock->expects($this->once())->method('getCurrentPassword')->willReturn($newPW); + $userMock->expects($this->once())->method('getPassword')->willReturn($newPW); $this->configInterfaceMock ->expects($this->atLeastOnce()) ->method('getValue') ->willReturn(1); $userMock->expects($this->once())->method('getForceNewPassword')->willReturn(false); - $this->encryptorMock->expects($this->once())->method('getHash')->willReturn(md5($oldPW)); /** @var \Magento\Framework\Message\Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ $collectionMock = $this->getMockBuilder('Magento\Framework\Message\Collection') diff --git a/app/code/Magento/User/composer.json b/app/code/Magento/User/composer.json index 4810ed7ed122e..5cceb20b6c770 100644 --- a/app/code/Magento/User/composer.json +++ b/app/code/Magento/User/composer.json @@ -11,7 +11,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/design/adminhtml/Magento/backend/Magento_GiftRegistry/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_GiftRegistry/web/css/source/_module.less new file mode 100644 index 0000000000000..fcd0382ec6f88 --- /dev/null +++ b/app/design/adminhtml/Magento/backend/Magento_GiftRegistry/web/css/source/_module.less @@ -0,0 +1,41 @@ +// /** +// * Copyright © 2016 Magento. All rights reserved. +// * See COPYING.txt for license details. +// */ + +// +// Gift Registry styles +// _____________________________________________ + +.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { + + .gift-registry-info { + float: left; + #mix-grid .width(6,12); + } + + .gift-registry-address { + float: right; + #mix-grid .width(6,12); + } +} + +.form-sharing-info { + .admin__legend { + &:extend(.abs-clearfix all); + border-bottom: 1px solid @color-gray80; + margin-bottom: 1.7rem; + padding: 1.4rem 0 .5rem; + span { + &:extend(h2); + float: left; + margin: 0; + } + } + + .sharing-info-textarea { + display: block; + height: 50px; + margin-bottom: @indent__m; + } +} diff --git a/app/design/adminhtml/Magento/backend/composer.json b/app/design/adminhtml/Magento/backend/composer.json index 7af579d7e7588..cf433a3f5d8a7 100644 --- a/app/design/adminhtml/Magento/backend/composer.json +++ b/app/design/adminhtml/Magento/backend/composer.json @@ -6,7 +6,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-theme", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/composer.json b/composer.json index 832e806584189..87e1d392ab53a 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "magento/magento2ce", "description": "Magento 2 (Community Edition)", "type": "project", - "version": "2.0.9", + "version": "2.0.10", "license": [ "OSL-3.0", "AFL-3.0" @@ -40,7 +40,7 @@ "magento/zendframework1": "1.12.16", "colinmollenhour/credis": "1.6", "colinmollenhour/php-redis-session-abstract": "1.1", - "composer/composer": "1.0.0-alpha10", + "composer/composer": "<=1.0.0-alpha10", "monolog/monolog": "1.16.0", "oyejorge/less.php": "1.7.0.3", "pelago/emogrifier": "0.1.1", @@ -83,14 +83,14 @@ "magento/module-advanced-pricing-import-export": "100.0.6", "magento/module-authorization": "100.0.5", "magento/module-authorizenet": "100.0.6", - "magento/module-backend": "100.0.7", + "magento/module-backend": "100.0.8", "magento/module-backup": "100.0.6", - "magento/module-braintree": "100.0.6", + "magento/module-braintree": "100.0.7", "magento/module-bundle": "100.0.5", "magento/module-bundle-import-export": "100.0.5", "magento/module-cache-invalidate": "100.0.5", "magento/module-captcha": "100.0.7", - "magento/module-catalog": "100.0.9", + "magento/module-catalog": "100.0.10", "magento/module-catalog-import-export": "100.0.7", "magento/module-catalog-inventory": "100.0.6", "magento/module-catalog-rule": "100.0.6", @@ -98,7 +98,7 @@ "magento/module-catalog-search": "100.0.5", "magento/module-catalog-url-rewrite": "100.0.6", "magento/module-catalog-widget": "100.0.5", - "magento/module-checkout": "100.0.8", + "magento/module-checkout": "100.0.9", "magento/module-checkout-agreements": "100.0.5", "magento/module-cms": "100.0.5", "magento/module-cms-url-rewrite": "100.0.5", @@ -109,18 +109,18 @@ "magento/module-cookie": "100.0.5", "magento/module-cron": "100.0.5", "magento/module-currency-symbol": "100.0.5", - "magento/module-customer": "100.0.8", + "magento/module-customer": "100.0.9", "magento/module-customer-import-export": "100.0.5", "magento/module-deploy": "100.0.6", "magento/module-developer": "100.0.5", - "magento/module-dhl": "100.0.5", + "magento/module-dhl": "100.0.6", "magento/module-directory": "100.0.5", "magento/module-downloadable": "100.0.5", "magento/module-downloadable-import-export": "100.0.5", "magento/module-eav": "100.0.7", "magento/module-email": "100.0.5", "magento/module-encryption-key": "100.0.5", - "magento/module-fedex": "100.0.5", + "magento/module-fedex": "100.0.6", "magento/module-gift-message": "100.0.5", "magento/module-google-adwords": "100.0.5", "magento/module-google-analytics": "100.0.5", @@ -136,39 +136,39 @@ "magento/module-multishipping": "100.0.6", "magento/module-new-relic-reporting": "100.0.5", "magento/module-newsletter": "100.0.7", - "magento/module-offline-payments": "100.0.6", + "magento/module-offline-payments": "100.0.7", "magento/module-offline-shipping": "100.0.5", "magento/module-page-cache": "100.0.6", - "magento/module-payment": "100.0.6", + "magento/module-payment": "100.0.7", "magento/module-paypal": "100.0.6", "magento/module-persistent": "100.0.5", "magento/module-product-alert": "100.0.6", - "magento/module-product-video": "100.0.6", - "magento/module-quote": "100.0.6", + "magento/module-product-video": "100.0.7", + "magento/module-quote": "100.0.7", "magento/module-reports": "100.0.6", "magento/module-require-js": "100.0.5", "magento/module-review": "100.0.5", "magento/module-rss": "100.0.5", "magento/module-rule": "100.0.5", - "magento/module-sales": "100.0.9", + "magento/module-sales": "100.0.10", "magento/module-sales-rule": "100.0.6", "magento/module-sales-sequence": "100.0.5", "magento/module-sample-data": "100.0.5", "magento/module-search": "100.0.5", "magento/module-send-friend": "100.0.5", - "magento/module-shipping": "100.0.5", + "magento/module-shipping": "100.0.6", "magento/module-sitemap": "100.0.5", "magento/module-store": "100.0.6", "magento/module-swagger": "100.0.5", - "magento/module-swatches": "100.0.6", + "magento/module-swatches": "100.0.7", "magento/module-tax": "100.0.5", "magento/module-tax-import-export": "100.0.5", "magento/module-theme": "100.0.6", "magento/module-translation": "100.0.5", "magento/module-ui": "100.0.5", - "magento/module-ups": "100.0.5", + "magento/module-ups": "100.0.6", "magento/module-url-rewrite": "100.0.5", - "magento/module-user": "100.0.5", + "magento/module-user": "100.0.6", "magento/module-usps": "100.0.5", "magento/module-variable": "100.0.5", "magento/module-version": "100.0.5", @@ -177,7 +177,7 @@ "magento/module-weee": "100.0.5", "magento/module-widget": "100.0.6", "magento/module-wishlist": "100.0.6", - "magento/theme-adminhtml-backend": "100.0.5", + "magento/theme-adminhtml-backend": "100.0.6", "magento/theme-frontend-blank": "100.0.5", "magento/theme-frontend-luma": "100.0.5", "magento/language-de_de": "100.0.5", @@ -187,7 +187,7 @@ "magento/language-nl_nl": "100.0.5", "magento/language-pt_br": "100.0.5", "magento/language-zh_hans_cn": "100.0.5", - "magento/framework": "100.0.11", + "magento/framework": "100.0.12", "trentrichardson/jquery-timepicker-addon": "1.4.3", "colinmollenhour/cache-backend-redis": "1.8", "components/jquery": "1.11.0", diff --git a/composer.lock b/composer.lock index ebe395b8bd461..a519b488d57f9 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": "22776bfe7806fa08ecbfe25d5a726039", - "content-hash": "4b0f232bbe37d12c183970282c392c1c", + "hash": "4b7aa13b41faef16947857eba30acdbe", + "content-hash": "f2b8fd464a75b0b0f9ba6f9736a4c4b6", "packages": [ { "name": "braintree/braintree_php", @@ -709,22 +709,30 @@ }, { "name": "psr/log", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + "reference": "5277094ed527a1c4477177d102fe4c53551953e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "url": "https://api.github.com/repos/php-fig/log/zipball/5277094ed527a1c4477177d102fe4c53551953e0", + "reference": "5277094ed527a1c4477177d102fe4c53551953e0", "shasum": "" }, + "require": { + "php": ">=5.3.0" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { - "psr-0": { - "Psr\\Log\\": "" + "psr-4": { + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -738,25 +746,26 @@ } ], "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], - "time": "2012-12-21 11:40:51" + "time": "2016-09-19 16:02:08" }, { "name": "seld/jsonlint", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "66834d3e3566bb5798db7294619388786ae99394" + "reference": "e827b5254d3e58c736ea2c5616710983d80b0b70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/66834d3e3566bb5798db7294619388786ae99394", - "reference": "66834d3e3566bb5798db7294619388786ae99394", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e827b5254d3e58c736ea2c5616710983d80b0b70", + "reference": "e827b5254d3e58c736ea2c5616710983d80b0b70", "shasum": "" }, "require": { @@ -789,7 +798,7 @@ "parser", "validator" ], - "time": "2015-11-21 02:21:41" + "time": "2016-09-14 15:17:56" }, { "name": "symfony/console", @@ -851,7 +860,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.8.9", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -911,16 +920,16 @@ }, { "name": "symfony/finder", - "version": "v2.8.9", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "60804d88691e4a73bbbb3035eb1d9f075c5c2c10" + "reference": "bec5533e6ed650547d6ec8de4b541dc9929066f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/60804d88691e4a73bbbb3035eb1d9f075c5c2c10", - "reference": "60804d88691e4a73bbbb3035eb1d9f075c5c2c10", + "url": "https://api.github.com/repos/symfony/finder/zipball/bec5533e6ed650547d6ec8de4b541dc9929066f7", + "reference": "bec5533e6ed650547d6ec8de4b541dc9929066f7", "shasum": "" }, "require": { @@ -956,20 +965,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-07-26 08:02:44" + "time": "2016-08-26 11:57:43" }, { "name": "symfony/process", - "version": "v2.8.9", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d20332e43e8774ff8870b394f3dd6020cc7f8e0c" + "reference": "05a03ed27073638658cab9405d99a67dd1014987" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d20332e43e8774ff8870b394f3dd6020cc7f8e0c", - "reference": "d20332e43e8774ff8870b394f3dd6020cc7f8e0c", + "url": "https://api.github.com/repos/symfony/process/zipball/05a03ed27073638658cab9405d99a67dd1014987", + "reference": "05a03ed27073638658cab9405d99a67dd1014987", "shasum": "" }, "require": { @@ -1005,7 +1014,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2016-07-28 11:13:19" + "time": "2016-09-06 10:55:00" }, { "name": "tedivm/jshrink", @@ -2654,35 +2663,35 @@ }, { "name": "fabpot/php-cs-fixer", - "version": "v1.11.6", + "version": "v1.12.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "41dc93abd2937a85a3889e28765231d574d2bac8" + "reference": "d33ee60f3d3e6152888b7f3a385f49e5c43bf1bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/41dc93abd2937a85a3889e28765231d574d2bac8", - "reference": "41dc93abd2937a85a3889e28765231d574d2bac8", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/d33ee60f3d3e6152888b7f3a385f49e5c43bf1bf", + "reference": "d33ee60f3d3e6152888b7f3a385f49e5c43bf1bf", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3.6", - "sebastian/diff": "~1.1", - "symfony/console": "~2.3|~3.0", - "symfony/event-dispatcher": "~2.1|~3.0", - "symfony/filesystem": "~2.1|~3.0", - "symfony/finder": "~2.1|~3.0", - "symfony/process": "~2.3|~3.0", - "symfony/stopwatch": "~2.5|~3.0" + "php": "^5.3.6 || >=7.0 <7.2", + "sebastian/diff": "^1.1", + "symfony/console": "^2.3 || ^3.0", + "symfony/event-dispatcher": "^2.1 || ^3.0", + "symfony/filesystem": "^2.1 || ^3.0", + "symfony/finder": "^2.1 || ^3.0", + "symfony/process": "^2.3 || ^3.0", + "symfony/stopwatch": "^2.5 || ^3.0" }, "conflict": { "hhvm": "<3.9" }, "require-dev": { "phpunit/phpunit": "^4.5|^5", - "satooshi/php-coveralls": "^0.7.1" + "satooshi/php-coveralls": "^1.0" }, "bin": [ "php-cs-fixer" @@ -2709,7 +2718,7 @@ ], "description": "A tool to automatically fix PHP code style", "abandoned": "friendsofphp/php-cs-fixer", - "time": "2016-07-22 06:46:28" + "time": "2016-09-07 06:48:24" }, { "name": "league/climate", @@ -3421,23 +3430,23 @@ }, { "name": "sebastian/environment", - "version": "1.3.7", + "version": "1.3.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716" + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716", - "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^4.8 || ^5.0" }, "type": "library", "extra": { @@ -3467,7 +3476,7 @@ "environment", "hhvm" ], - "time": "2016-05-17 03:18:57" + "time": "2016-08-18 05:49:44" }, { "name": "sebastian/exporter", @@ -3754,16 +3763,16 @@ }, { "name": "symfony/config", - "version": "v2.8.9", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "4275ef5b59f18959df0eee3991e9ca0cc208ffd4" + "reference": "005bf10c156335ede2e89fb9a9ee10a0b742bc84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/4275ef5b59f18959df0eee3991e9ca0cc208ffd4", - "reference": "4275ef5b59f18959df0eee3991e9ca0cc208ffd4", + "url": "https://api.github.com/repos/symfony/config/zipball/005bf10c156335ede2e89fb9a9ee10a0b742bc84", + "reference": "005bf10c156335ede2e89fb9a9ee10a0b742bc84", "shasum": "" }, "require": { @@ -3803,20 +3812,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2016-07-26 08:02:44" + "time": "2016-08-16 14:56:08" }, { "name": "symfony/dependency-injection", - "version": "v2.8.9", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "f2b5a00d176f6a201dc430375c0ef37706ea3d12" + "reference": "0a732a9cafc30e54077967da4d019e1d618a8cb9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f2b5a00d176f6a201dc430375c0ef37706ea3d12", - "reference": "f2b5a00d176f6a201dc430375c0ef37706ea3d12", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/0a732a9cafc30e54077967da4d019e1d618a8cb9", + "reference": "0a732a9cafc30e54077967da4d019e1d618a8cb9", "shasum": "" }, "require": { @@ -3866,20 +3875,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2016-07-30 07:20:35" + "time": "2016-09-06 23:19:39" }, { "name": "symfony/filesystem", - "version": "v2.8.9", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "ab4c3f085c8f5a56536845bf985c4cef30bf75fd" + "reference": "44b499521defddf2eae17a18c811bbdae4f98bdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/ab4c3f085c8f5a56536845bf985c4cef30bf75fd", - "reference": "ab4c3f085c8f5a56536845bf985c4cef30bf75fd", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/44b499521defddf2eae17a18c811bbdae4f98bdf", + "reference": "44b499521defddf2eae17a18c811bbdae4f98bdf", "shasum": "" }, "require": { @@ -3915,11 +3924,11 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2016-07-20 05:41:28" + "time": "2016-09-06 10:55:00" }, { "name": "symfony/stopwatch", - "version": "v3.1.3", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -3968,16 +3977,16 @@ }, { "name": "symfony/yaml", - "version": "v2.8.9", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "0ceab136f43ed9d3e97b3eea32a7855dc50c121d" + "reference": "e7540734bad981fe59f8ef14b6fc194ae9df8d9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/0ceab136f43ed9d3e97b3eea32a7855dc50c121d", - "reference": "0ceab136f43ed9d3e97b3eea32a7855dc50c121d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e7540734bad981fe59f8ef14b6fc194ae9df8d9c", + "reference": "e7540734bad981fe59f8ef14b6fc194ae9df8d9c", "shasum": "" }, "require": { @@ -4013,7 +4022,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-07-17 09:06:15" + "time": "2016-09-02 01:57:56" } ], "aliases": [], diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/CheckboxwithlabelElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/CheckboxwithlabelElement.php new file mode 100644 index 0000000000000..3b69229313e54 --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/CheckboxwithlabelElement.php @@ -0,0 +1,28 @@ +eventManager->dispatchEvent(['set_value'], [__METHOD__, $this->getAbsoluteSelector()]); + if (($this->isSelected() && $value == 'No') || (!$this->isSelected() && $value == 'Yes')) { + $this->find('./following-sibling::label', Locator::SELECTOR_XPATH)->click(); + } + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Version.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Version.php new file mode 100644 index 0000000000000..69a83ba3472c4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Version.php @@ -0,0 +1,28 @@ +_rootElement->find($this->backendVersion, Locator::SELECTOR_CLASS_NAME)->getText(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml index d0607947c23d0..11fb3e5a15b85 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml @@ -15,5 +15,6 @@ + diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Handler/ConfigurableProduct/Curl.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Handler/ConfigurableProduct/Curl.php index 713e30f5dadc1..0f7ffa68d7b26 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Handler/ConfigurableProduct/Curl.php +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Handler/ConfigurableProduct/Curl.php @@ -63,10 +63,7 @@ protected function prepareData(FixtureInterface $product, $prefix = null) $data['new-variations-attribute-set-id'] = $attributeSetId; $data['associated_product_ids'] = $this->prepareAssociatedProductIds($configurableAttributesData); - $this->replaceMappingData($data); - $data['configurable-matrix-serialized'] = json_encode($data['variations-matrix']); - $data['associated_product_ids_serialized'] = json_encode($data['associated_product_ids']); - return $data; + return $this->replaceMappingData($data); } /** diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Authentication.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Authentication.php new file mode 100644 index 0000000000000..4973c8e72174e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Authentication.php @@ -0,0 +1,55 @@ +_rootElement->find($this->save, Locator::SELECTOR_CSS)->click(); + } + + /** + * Ensure the form is loaded and fill the root form + * + * @param FixtureInterface $fixture + * @param SimpleElement|null $element + * @return $this + */ + public function fill(FixtureInterface $fixture, SimpleElement $element = null) + { + $this->waitForElementVisible($this->firstField); + return parent::fill($fixture, $element); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Authentication.xml b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Authentication.xml new file mode 100644 index 0000000000000..42e6ec97c35dd --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Authentication.xml @@ -0,0 +1,17 @@ + + + + + + [name='username'] + + + [name='password'] + + + diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/CreateBackup.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/CreateBackup.php new file mode 100644 index 0000000000000..72d3853bd1674 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/CreateBackup.php @@ -0,0 +1,35 @@ +_rootElement->find($this->startUpdate, Locator::SELECTOR_CSS)->click(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/CreateBackup.xml b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/CreateBackup.xml new file mode 100644 index 0000000000000..d36dc55ed12ef --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/CreateBackup.xml @@ -0,0 +1,23 @@ + + + + + + #optionsCode + checkboxwithlabel + + + #optionsMedia + checkboxwithlabel + + + #optionsDb + checkboxwithlabel + + + diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Home.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Home.php new file mode 100644 index 0000000000000..9651a15c86572 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Home.php @@ -0,0 +1,31 @@ +_rootElement->find($this->systemUpgrade, Locator::SELECTOR_CSS)->click(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Readiness.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Readiness.php new file mode 100644 index 0000000000000..648627dc8f475 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/Readiness.php @@ -0,0 +1,160 @@ +_rootElement->find($this->readinessCheck, Locator::SELECTOR_CSS)->click(); + $this->waitForElementVisible($this->completedMessage, Locator::SELECTOR_CSS); + } + + /** + * Click on 'Next' button. + * + * @return void + */ + public function clickNext() + { + $this->_rootElement->find($this->next, Locator::SELECTOR_CSS)->click(); + } + + /** + * Get Updater application check result. + * + * @return string + */ + public function getUpdaterApplicationCheck() + { + return $this->_rootElement->find($this->updaterApplicationCheck, Locator::SELECTOR_CSS)->getText(); + } + + /** + * Get cron script check result. + * + * @return string + */ + public function getCronScriptCheck() + { + return $this->_rootElement->find($this->cronScriptCheck, Locator::SELECTOR_CSS)->getText(); + } + + /** + * Get dependency check result. + * + * @return string + */ + public function getDependencyCheck() + { + return $this->_rootElement->find($this->dependencyCheck, Locator::SELECTOR_CSS)->getText(); + } + + /** + * Get PHP Version check result. + * + * @return string + */ + public function getPhpVersionCheck() + { + return $this->_rootElement->find($this->phpVersionCheck, Locator::SELECTOR_CSS)->getText(); + } + + /** + * Get setting check result. + * + * @return string + */ + public function getSettingsCheck() + { + return $this->_rootElement->find($this->phpSettingsCheck, Locator::SELECTOR_CSS)->getText(); + } + + /** + * Get PHP Extensions check result. + * + * @return string + */ + public function getPhpExtensionsCheck() + { + return $this->_rootElement->find($this->phpExtensionCheck, Locator::SELECTOR_CSS)->getText(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SelectVersion.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SelectVersion.php new file mode 100644 index 0000000000000..d1979716e1f2c --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SelectVersion.php @@ -0,0 +1,66 @@ +_rootElement->find($this->next, Locator::SELECTOR_CSS)->click(); + } + + /** + * Ensure the form is loaded and fill the root form + * + * @param FixtureInterface $fixture + * @param SimpleElement|null $element + * @return $this + */ + public function fill(FixtureInterface $fixture, SimpleElement $element = null) + { + $this->waitForElementVisible($this->firstField); + return parent::fill($fixture, $element); + } + + /** + * Choose 'yes' for upgrade option called 'Other components' + * + * @return void + */ + public function chooseUpgradeOtherComponents() + { + $this->_rootElement->find("[for=yesUpdateComponents]", Locator::SELECTOR_CSS)->click(); + $this->waitForElementVisible("[ng-show='componentsProcessed']"); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SelectVersion.xml b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SelectVersion.xml new file mode 100644 index 0000000000000..eb594d124604f --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SelectVersion.xml @@ -0,0 +1,15 @@ + + + + + + #selectVersion + select + + + diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SuccessMessage.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SuccessMessage.php new file mode 100644 index 0000000000000..a175ff4b32965 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SuccessMessage.php @@ -0,0 +1,30 @@ +waitForElementVisible($this->successMessage, Locator::SELECTOR_CLASS_NAME); + return $this->_rootElement->find($this->successMessage, Locator::SELECTOR_CLASS_NAME)->getText(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SystemConfig.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SystemConfig.php new file mode 100644 index 0000000000000..e3e68a8cfcdcb --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SystemConfig.php @@ -0,0 +1,31 @@ +_rootElement->find($this->systemConfig, Locator::SELECTOR_CSS)->click(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SystemUpgrade.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SystemUpgrade.php new file mode 100644 index 0000000000000..5af372786ba94 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Block/SystemUpgrade.php @@ -0,0 +1,48 @@ +_rootElement->find($this->systemUpgradeMessage, Locator::SELECTOR_NAME)->getText(); + } + + /** + * Click on 'Upgrade' button. + * + * @return void + */ + public function clickSystemUpgrade() + { + $this->_rootElement->find($this->upgrade, Locator::SELECTOR_CSS)->click(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertApplicationVersion.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertApplicationVersion.php new file mode 100644 index 0000000000000..7f11a0de69aed --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertApplicationVersion.php @@ -0,0 +1,43 @@ +getApplicationVersion()->getVersion(), + 'Application version is incorrect.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return "Application new version is correct."; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertSuccessMessage.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertSuccessMessage.php new file mode 100644 index 0000000000000..6b1ac3996751f --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertSuccessMessage.php @@ -0,0 +1,48 @@ +getSuccessMessage()->getUpdaterStatus(), + 'Success message is incorrect.' + ); + \PHPUnit_Framework_Assert::assertContains( + $package, + $setupWizard->getSuccessMessage()->getUpdaterStatus(), + 'Updated package is incorrect.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return "System Upgrade success message is correct."; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertSuccessfulReadinessCheck.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertSuccessfulReadinessCheck.php new file mode 100644 index 0000000000000..9bf88a7072bb6 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertSuccessfulReadinessCheck.php @@ -0,0 +1,96 @@ +getReadiness()->getUpdaterApplicationCheck(), + 'Updater application check is incorrect.' + ); + \PHPUnit_Framework_Assert::assertContains( + self::CRON_SCRIPT_MESSAGE, + $setupWizard->getReadiness()->getCronScriptCheck(), + 'Cron scripts are incorrect.' + ); + \PHPUnit_Framework_Assert::assertContains( + self::DEPENDENCY_CHECK_MESSAGE, + $setupWizard->getReadiness()->getDependencyCheck(), + 'Dependency check is incorrect.' + ); + \PHPUnit_Framework_Assert::assertContains( + self::PHP_VERSION_MESSAGE, + $setupWizard->getReadiness()->getPhpVersionCheck(), + 'PHP version is incorrect.' + ); + \PHPUnit_Framework_Assert::assertContains( + self::PHP_SETTING_REGEXP, + $setupWizard->getReadiness()->getSettingsCheck(), + 'PHP settings check failed.' + ); + \PHPUnit_Framework_Assert::assertRegExp( + self::PHP_EXTENSIONS_REGEXP, + $setupWizard->getReadiness()->getPhpExtensionsCheck(), + 'PHP extensions missed.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return "System Upgrade readiness check passed."; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertVersionAndEditionCheck.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertVersionAndEditionCheck.php new file mode 100644 index 0000000000000..5961b068fc530 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Constraint/AssertVersionAndEditionCheck.php @@ -0,0 +1,44 @@ +getSystemUpgrade()->getUpgradeMessage(), + 'Updater application check is incorrect.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return "System Upgrade edition and version check passed."; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Fixture/Upgrade.xml b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Fixture/Upgrade.xml new file mode 100644 index 0000000000000..eaae274e77d28 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Fixture/Upgrade.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/Page/Adminhtml/SetupWizard.xml b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Page/Adminhtml/SetupWizard.xml new file mode 100644 index 0000000000000..73b07ca848c1d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/Page/Adminhtml/SetupWizard.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/TestCase/UpgradeSystemTest.php b/dev/tests/functional/tests/app/Magento/Upgrade/Test/TestCase/UpgradeSystemTest.php new file mode 100644 index 0000000000000..f5933fa91e51d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/TestCase/UpgradeSystemTest.php @@ -0,0 +1,134 @@ +adminDashboard = $adminDashboard; + $this->setupWizard = $setupWizard; + } + + /** + * @param FixtureFactory $fixtureFactory + * @param AssertSuccessfulReadinessCheck $assertReadiness + * @param AssertVersionAndEditionCheck $assertVersionAndEdition + * @param AssertSuccessMessage $assertSuccessMessage + * @param AssertApplicationVersion $assertApplicationVersion + * @param array $upgrade + * @return void + */ + public function test( + FixtureFactory $fixtureFactory, + AssertSuccessfulReadinessCheck $assertReadiness, + AssertVersionAndEditionCheck $assertVersionAndEdition, + AssertSuccessMessage $assertSuccessMessage, + AssertApplicationVersion $assertApplicationVersion, + $upgrade = [] + ) { + // Create fixture + $upgradeFixture = $fixtureFactory->create('Magento\Upgrade\Test\Fixture\Upgrade', ['data' => $upgrade]); + $createBackupConfig = array_intersect_key( + $upgrade, + ['optionsCode' => '', 'optionsMedia' => '', 'optionsDb' => ''] + ); + $createBackupFixture = $fixtureFactory->create( + 'Magento\Upgrade\Test\Fixture\Upgrade', + ['data' => $createBackupConfig] + ); + $version = $upgrade['upgradeVersion']; + + $suffix = "( (CE|EE))$"; + $normalVersion = '(0|[1-9]\d*)'; + $preReleaseVersion = "((0(?!\\d+(\\.|\\+|{$suffix}))|[1-9A-Za-z])[0-9A-Za-z-]*)"; + $buildVersion = '([0-9A-Za-z][0-9A-Za-z-]*)'; + $versionPattern = "/^{$normalVersion}(\\.{$normalVersion}){2}" + . "(-{$preReleaseVersion}(\\.{$preReleaseVersion})*)?" + . "(\\+{$buildVersion}(\\.{$buildVersion})*)?{$suffix}/"; + + if (preg_match($versionPattern, $version)) { + preg_match("/(.*){$suffix}/", $version, $matches); + $version = $matches[1]; + } else { + $this->fail( + "Provided version format does not comply with semantic versioning specification. Got '{$version}'" + ); + } + + // Authenticate in admin area + $this->adminDashboard->open(); + + // Open Web Setup Wizard + $this->setupWizard->open(); + + // Authenticate on repo.magento.com + if ($upgrade['needAuthentication'] === 'Yes') { + $this->setupWizard->getSystemConfig()->clickSystemConfig(); + $this->setupWizard->getAuthentication()->fill($upgradeFixture); + $this->setupWizard->getAuthentication()->clickSaveConfig(); + $this->setupWizard->open(); + } + + // Select upgrade to version + $this->setupWizard->getSystemUpgradeHome()->clickSystemUpgrade(); + $this->setupWizard->getSelectVersion()->fill($upgradeFixture); + if ($upgrade['otherComponents'] === 'Yes') { + $this->setupWizard->getSelectVersion()->chooseUpgradeOtherComponents(); + } + $this->setupWizard->getSelectVersion()->clickNext(); + + // Readiness Check + $this->setupWizard->getReadiness()->clickReadinessCheck(); + $assertReadiness->processAssert($this->setupWizard); + $this->setupWizard->getReadiness()->clickNext(); + + // Create Backup page + $this->setupWizard->getCreateBackup()->fill($createBackupFixture); + $this->setupWizard->getCreateBackup()->clickNext(); + + // Check info and press 'Upgrade' button + $assertVersionAndEdition->processAssert($this->setupWizard, $upgrade['package'], $version); + $this->setupWizard->getSystemUpgrade()->clickSystemUpgrade(); + + $assertSuccessMessage->processAssert($this->setupWizard, $upgrade['package']); + + // Check application version + $this->adminDashboard->open(); + $assertApplicationVersion->processAssert($this->adminDashboard, $version); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Upgrade/Test/TestCase/UpgradeSystemTest.xml b/dev/tests/functional/tests/app/Magento/Upgrade/Test/TestCase/UpgradeSystemTest.xml new file mode 100644 index 0000000000000..3b88f360ec156 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Upgrade/Test/TestCase/UpgradeSystemTest.xml @@ -0,0 +1,23 @@ + + + + + + System Upgrade Page + {version} + {package} + No + {publicKey} + {privateKey} + No + No + No + {otherComponents} + + + diff --git a/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/upgrade.xml b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/upgrade.xml new file mode 100644 index 0000000000000..4d883813f938f --- /dev/null +++ b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/upgrade.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php index 05a02646ab8d5..e2c453062bdff 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php @@ -204,4 +204,51 @@ private function _getQuoteItemIdByProductId($quote, $productId) } return null; } + + /** + * Test for \Magento\Checkout\Controller\Cart::execute() with simple product + * + * @param string $area + * @param string $expectedPrice + * @magentoDataFixture Magento/Catalog/_files/products.php + * @magentoAppIsolation enabled + * @dataProvider addAddProductDataProvider + */ + public function testAddToCartSimpleProduct($area, $expectedPrice) + { + $formKey = $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class); + $postData = [ + 'qty' => '1', + 'product' => '1', + 'custom_price' => 1, + 'form_key' => $formKey->getFormKey(), + 'isAjax' => 1 + ]; + \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea($area); + $this->getRequest()->setPostValue($postData); + + $quote = $this->_objectManager->create(\Magento\Checkout\Model\Cart::class); + /** @var \Magento\Checkout\Controller\Cart\Add $controller */ + $controller = $this->_objectManager->create(\Magento\Checkout\Controller\Cart\Add::class, [$quote]); + $controller->execute(); + + $this->assertContains(json_encode([]), $this->getResponse()->getBody()); + $items = $quote->getItems()->getItems(); + $this->assertTrue(is_array($items), 'Quote doesn\'t have any items'); + $this->assertCount(1, $items, 'Expected quote items not equal to 1'); + $item = reset($items); + $this->assertEquals(1, $item->getProductId(), 'Quote has more than one product'); + $this->assertEquals($expectedPrice, $item->getPrice(), 'Expected product price failed'); + } + + /** + * Data provider for testAddToCartSimpleProduct + */ + public function addAddProductDataProvider() + { + return [ + 'frontend' => ['frontend', 'expected_price' => 10], + 'adminhtml' => ['adminhtml', 'expected_price' => 1] + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php index 15b7e001622e1..793d6ec8ab33e 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php @@ -16,11 +16,10 @@ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendControl public function testSaveActionAssociatedProductIds() { $associatedProductIds = [3, 14, 15, 92]; - $associatedProductIdsSerialized = json_encode($associatedProductIds); $this->getRequest()->setPostValue( [ 'attributes' => [$this->_getConfigurableAttribute()->getId()], - 'associated_product_ids_serialized' => $associatedProductIdsSerialized, + 'associated_product_ids' => $associatedProductIds, ] ); diff --git a/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php b/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php index 472a9024fb831..96e998664cba8 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php @@ -11,7 +11,7 @@ */ namespace Magento\Framework\DB\Adapter\Pdo; -use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Select; class MysqlTest extends \PHPUnit_Framework_TestCase { @@ -132,4 +132,93 @@ protected function _getConnection() } return $this->_connection; } + + /** + * Test multi-queries + * @dataProvider providerSqlInjections + * @param $sql + * @param bool $pdoMultiQueryValue + */ + public function testMultiQueryConnect($sql, $pdoMultiQueryValue = false) + { + $connection = $this->_getCustomConnection($pdoMultiQueryValue); + $isError = false; + + if (!$connection instanceof \Magento\Framework\DB\Adapter\Pdo\Mysql) { + $this->markTestSkipped('This test is for \Magento\Framework\DB\Adapter\Pdo\Mysql'); + } + + $connection->closeConnection(); + try { + $connection->query($sql); + } catch (\Exception $exception) { + if ($exception instanceof \Zend_Db_Statement_Exception) { + $isError = true; + } + if ($exception instanceof \Magento\Framework\Exception\LocalizedException + && $exception->getMessage() == "Cannot execute multiple queries" + ) { + $isError = true; + } + } + + $this->assertTrue($isError); + $connection->closeConnection(); + } + + /** + * @return array + */ + public function providerSqlInjections() + { + return [ + //MAGETWO-56542 + ["select MD5(\";(/*\n//*/\");DELETE FROM some_other_table #)", false], + [urlencode("select MD5(\";(/*\n//*/\");DELETE FROM some_other_table #)"), false], + + ["select 1; DELETE FROM some_other_table ;", true], + [urlencode("select 1; DELETE FROM some_other_table ;"), true], + + ["select MD5(\";(/*\n//*/\");DELETE FROM some_other_table #)", true], + [urlencode("select MD5(\";(/*\n//*/\");DELETE FROM some_other_table #)"), true], + + ]; + } + + /** + * @param bool $pdoMultiQueryValue + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ + protected function _getCustomConnection($pdoMultiQueryValue) + { + $connectionFactory = new \Magento\Framework\App\ResourceConnection\ConnectionFactory( + \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ); + + $dbInstance = \Magento\TestFramework\Helper\Bootstrap::getInstance() + ->getBootstrap() + ->getApplication() + ->getDbInstance(); + $dbConfig = [ + 'host' => $dbInstance->getHost(), + 'username' => $dbInstance->getUser(), + 'password' => $dbInstance->getPassword(), + 'dbname' => $dbInstance->getSchema(), + 'active' => true, + 'driver_options' => [\PDO::MYSQL_ATTR_MULTI_STATEMENTS => $pdoMultiQueryValue] + ]; + $connection = $connectionFactory->create($dbConfig); + return $connection; + } + + public function testSimpleSelect() + { + $select = new Select($this->_getConnection()); + $select->from("test") + ->where("1=?", 1) + ->group("field") + ->order("field " . Select::SQL_DESC); + + $this->assertEquals("SELECT `test`.* FROM `test` WHERE (1=1) GROUP BY `field` ORDER BY `field` DESC", $select->assemble()); + } } diff --git a/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/DeleteTest.php b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/DeleteTest.php new file mode 100644 index 0000000000000..bf43fd9f29c85 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/DeleteTest.php @@ -0,0 +1,31 @@ +create(\Magento\User\Model\User::class); + /** @var \Magento\Framework\Message\ManagerInterface $messageManager */ + $messageManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Message\ManagerInterface::class); + $user->load(1); + $this->getRequest()->setPostValue('user_id', $user->getId() . '_suffix_ignored_in_mysql_casting_to_int'); + + $this->dispatch('backend/admin/user/delete'); + $message = $messageManager->getMessages()->getLastAddedMessage()->getText(); + $this->assertEquals('You cannot delete your own account.', $message); + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt index 52801dd703758..d2a50ecce78a7 100644 --- a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt @@ -161,6 +161,7 @@ app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/attr app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/bulk.js app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/select_attributes.js app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/summary.js +app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js app/code/Magento/ConfigurableProduct/view/frontend/requirejs-config.js app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js app/code/Magento/Cookie/view/adminhtml/requirejs-config.js @@ -730,6 +731,7 @@ vendor/magento/module-configurable-product/view/adminhtml/web/js/variations/step vendor/magento/module-configurable-product/view/adminhtml/web/js/variations/steps/bulk.js vendor/magento/module-configurable-product/view/adminhtml/web/js/variations/steps/select_attributes.js vendor/magento/module-configurable-product/view/adminhtml/web/js/variations/steps/summary.js +vendor/magento/module-configurable-product/view/adminhtml/web/js/variations/variations.js vendor/magento/module-configurable-product/view/frontend/requirejs-config.js vendor/magento/module-configurable-product/view/frontend/web/js/configurable.js vendor/magento/module-cookie/-view/adminhtml/requirejs-config.js diff --git a/lib/internal/Magento/Framework/AppInterface.php b/lib/internal/Magento/Framework/AppInterface.php index d704150b0efbd..e7961b193c46c 100644 --- a/lib/internal/Magento/Framework/AppInterface.php +++ b/lib/internal/Magento/Framework/AppInterface.php @@ -17,7 +17,7 @@ interface AppInterface /** * Magento version */ - const VERSION = '2.0.9'; + const VERSION = '2.0.10'; /** * Launch application diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 4f830a113b92e..40884c83bbaca 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -310,6 +310,9 @@ public function convertDateTime($datetime) /** * Creates a PDO object and connects to the database. * + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * * @return void * @throws \Zend_Db_Adapter_Exception */ @@ -334,6 +337,12 @@ protected function _connect() list($this->_config['host'], $this->_config['port']) = explode(':', $this->_config['host']); } + if (defined("\\PDO::MYSQL_ATTR_MULTI_STATEMENTS")) { + if (!isset($this->_config['driver_options'][\PDO::MYSQL_ATTR_MULTI_STATEMENTS])) { + $this->_config['driver_options'][\PDO::MYSQL_ATTR_MULTI_STATEMENTS] = false; + } + } + $this->logger->startTimer(); parent::_connect(); $this->logger->logStats(LoggerInterface::TYPE_CONNECT, ''); @@ -499,9 +508,12 @@ protected function _query($sql, $bind = []) */ public function query($sql, $bind = []) { - if (strpos(rtrim($sql, " \t\n\r\0;"), ';') !== false && count($this->_splitMultiQuery($sql)) > 1) { - throw new \Magento\Framework\Exception\LocalizedException(new Phrase('Cannot execute multiple queries')); + if (strpos(rtrim($sql, " \t\n\r\0;"), ';') !== false) { + if ($this->isMultiQuery($sql)) { + throw new \Magento\Framework\Exception\LocalizedException(new Phrase('Cannot execute multiple queries')); + } } + return $this->_query($sql, $bind); } @@ -3760,4 +3772,13 @@ public function getTables($likeCondition = null) } return $tables; } + + /** + * @param $sql + * @return bool + */ + private function isMultiQuery($sql) + { + return count($this->_splitMultiQuery(preg_replace("/(\\n)|\n|(\\t)|\t|(\\r)|\r|(\\f)|\f/", "", $sql))) > 1; + } } diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/SelectTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/SelectTest.php index 6ef86448e820d..47af797c22b53 100644 --- a/lib/internal/Magento/Framework/DB/Test/Unit/SelectTest.php +++ b/lib/internal/Magento/Framework/DB/Test/Unit/SelectTest.php @@ -7,8 +7,6 @@ use \Magento\Framework\DB\Select; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - class SelectTest extends \PHPUnit_Framework_TestCase { public function testWhere() @@ -39,7 +37,7 @@ public function testWhere() * * @param int $callCount * @param string|null $returnValue - * @return \Magento\Framework\DB\Adapter\Pdo\Mysql|PHPUnit_Framework_MockObject_MockObject + * @return \Magento\Framework\DB\Adapter\Pdo\Mysql|\PHPUnit_Framework_MockObject_MockObject */ protected function _getConnectionMockWithMockedQuote($callCount, $returnValue = null) { @@ -56,4 +54,58 @@ protected function _getConnectionMockWithMockedQuote($callCount, $returnValue = } return $connection; } + + /** + * + * Test for group by + * + */ + public function testGroupBy() + { + $select = new Select($this->_getConnectionMockWithMockedQuote(0)); + $select->from('test')->group("field"); + $this->assertEquals("SELECT `test`.* FROM `test` GROUP BY `field`", $select->assemble()); + + $select = new Select($this->_getConnectionMockWithMockedQuote(0)); + $select->from('test')->group("(case when ((SELECT 1 ) = '1') then 2 else 3 end)"); + $this->assertEquals( + "SELECT `test`.* FROM `test` GROUP BY (case when ((SELECT 1 ) = '1') then 2 else 3 end)", + $select->assemble() + ); + + $select = new Select($this->_getConnectionMockWithMockedQuote(0)); + $select->from('test')->group(new \Zend_Db_Expr("(case when ((SELECT 1 ) = '1') then 2 else 3 end)")); + $this->assertEquals( + "SELECT `test`.* FROM `test` GROUP BY (case when ((SELECT 1 ) = '1') then 2 else 3 end)", + $select->assemble() + ); + } + + /** + * Test order + * + * @dataProvider providerOrder + * @param string $expected + * @param string $orderValue + */ + public function testOrder($expected, $orderValue) + { + $select = new Select($this->_getConnectionMockWithMockedQuote(0)); + $select->from('test')->order($orderValue); + $this->assertEquals($expected, $select->assemble()); + } + + public function providerOrder() + { + return [ + ["SELECT `test`.* FROM `test` ORDER BY `field` ASC", " field " . Select::SQL_ASC], + ["SELECT `test`.* FROM `test` ORDER BY `field` DESC", " field " . Select::SQL_DESC], + + ["SELECT `test`.* FROM `test` ORDER BY field ASC", new \Zend_Db_Expr("field " . Select::SQL_ASC)], + ["SELECT `test`.* FROM `test` ORDER BY field DESC", new \Zend_Db_Expr("field " . Select::SQL_DESC)], + + ["SELECT `test`.* FROM `test` ORDER BY (case when ((SELECT 1 ) = '1') then 2 else 3 end) DESC", + "(case when ((SELECT 1 ) = '1') then 2 else 3 end)" . Select::SQL_DESC], + ]; + } } diff --git a/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php b/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php index 10f1289b90c18..ad4746a36513c 100644 --- a/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php +++ b/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php @@ -31,7 +31,7 @@ class MaliciousCode implements \Zend_Filter_Interface //js attributes '/(ondblclick|onclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|onload|onunload|onerror)=[^<]*(?=\/*\>)/Uis', //tags - '/<\/?(script|meta|link|frame|iframe).*>/Uis', + '/<\/?(script|meta|link|frame|iframe|object).*>/Uis', //base64 usage '/src=[^<]*base64[^<]*(?=\/*\>)/Uis', ]; diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php index 512d8e89750bc..93de72e3c57c5 100644 --- a/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php +++ b/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php @@ -89,6 +89,7 @@ public function filterDataProvider() 'Tag is removed SomeLink', 'Tag is removed SomeFrame', 'Tag is removed ', + 'Tag is removed SomeObject', ], [ 'Tag is removed SomeScript', @@ -96,6 +97,7 @@ public function filterDataProvider() 'Tag is removed SomeLink', 'Tag is removed SomeFrame', 'Tag is removed SomeIFrame', + 'Tag is removed SomeObject', ], ], 'Base64' => [ diff --git a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php index fb00e9f59c830..c4e9d0054d3a4 100644 --- a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php @@ -41,7 +41,10 @@ class Curl implements \Zend_Http_Client_Adapter_Interface 'ssl_cert' => CURLOPT_SSLCERT, 'userpwd' => CURLOPT_USERPWD, 'useragent' => CURLOPT_USERAGENT, - 'referer' => CURLOPT_REFERER + 'referer' => CURLOPT_REFERER, + 'protocols' => CURLOPT_PROTOCOLS, + 'verifypeer' => CURLOPT_SSL_VERIFYPEER, + 'verifyhost' => CURLOPT_SSL_VERIFYHOST, ]; /** @@ -51,12 +54,18 @@ class Curl implements \Zend_Http_Client_Adapter_Interface */ protected $_options = []; + public function __construct() + { + // as we support PHP 5.5.x in Magento 2.0.x we can't do this in declaration + $this->_config['protocols'] = (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | CURLPROTO_FTPS); + $this->_config['verifypeer'] = true; + $this->_config['verifyhost'] = 2; + } + /** * Apply current configuration array to transport resource * * @return \Magento\Framework\HTTP\Adapter\Curl - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function _applyConfig() { @@ -65,22 +74,28 @@ protected function _applyConfig() curl_setopt($this->_getResource(), $option, $value); } - if (empty($this->_config)) { - return $this; + // apply config options + foreach ($this->getDefaultConfig() as $option => $value) { + curl_setopt($this->_getResource(), $option, $value); } - $verifyPeer = isset($this->_config['verifypeer']) ? $this->_config['verifypeer'] : true; - curl_setopt($this->_getResource(), CURLOPT_SSL_VERIFYPEER, $verifyPeer); - - $verifyHost = isset($this->_config['verifyhost']) ? $this->_config['verifyhost'] : 2; - curl_setopt($this->_getResource(), CURLOPT_SSL_VERIFYHOST, $verifyHost); + return $this; + } - foreach ($this->_config as $param => $curlOption) { + /** + * Get default options + * + * @return array + */ + private function getDefaultConfig() + { + $config = []; + foreach (array_keys($this->_config) as $param) { if (array_key_exists($param, $this->_allowedParams)) { - curl_setopt($this->_getResource(), $this->_allowedParams[$param], $this->_config[$param]); + $config[$this->_allowedParams[$param]] = $this->_config[$param]; } } - return $this; + return $config; } /** @@ -116,7 +131,9 @@ public function addOption($option, $value) */ public function setConfig($config = []) { - $this->_config = $config; + foreach ($config as $key => $value) { + $this->_config[$key] = $value; + } return $this; } @@ -268,6 +285,13 @@ public function multiRequest($urls, $options = []) $multihandle = curl_multi_init(); + // add default parameters + foreach ($this->getDefaultConfig() as $defaultOption => $defaultValue) { + if (!isset($options[$defaultOption])) { + $options[$defaultOption] = $defaultValue; + } + } + foreach ($urls as $key => $url) { $handles[$key] = curl_init(); curl_setopt($handles[$key], CURLOPT_URL, $url); diff --git a/lib/internal/Magento/Framework/HTTP/Test/Unit/Adapter/CurlTest.php b/lib/internal/Magento/Framework/HTTP/Test/Unit/Adapter/CurlTest.php index 255be0a5596a6..dae8ba1931014 100644 --- a/lib/internal/Magento/Framework/HTTP/Test/Unit/Adapter/CurlTest.php +++ b/lib/internal/Magento/Framework/HTTP/Test/Unit/Adapter/CurlTest.php @@ -10,12 +10,17 @@ class CurlTest extends \PHPUnit_Framework_TestCase { - /** @var Curl */ + /** + * @var Curl + */ protected $model; - /** @var \Closure */ + /** + * @var \Closure + */ public static $curlExectClosure; + protected function setUp() { require_once __DIR__ . '/_files/curl_exec_mock.php'; @@ -42,4 +47,3 @@ public function readDataProvider() ]; } } - diff --git a/lib/internal/Magento/Framework/Session/SessionManager.php b/lib/internal/Magento/Framework/Session/SessionManager.php index bbbb385fe5b24..1c6eef003a5e0 100644 --- a/lib/internal/Magento/Framework/Session/SessionManager.php +++ b/lib/internal/Magento/Framework/Session/SessionManager.php @@ -298,6 +298,7 @@ public function destroy(array $options = null) return; } + session_regenerate_id(true); session_destroy(); if ($options['send_expire_cookie']) { $this->expireSessionCookie(); diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ObjectManager.php b/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ObjectManager.php index a0844a4a07ba4..f059ca5fbfac1 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ObjectManager.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ObjectManager.php @@ -313,4 +313,20 @@ private function _getMockObject($argClassName, array $arguments) return $object; } } + + /** + * Set mocked property + * + * @param object $object + * @param string $propertyName + * @param object $propertyValue + * @return void + */ + public function setBackwardCompatibleProperty($object, $propertyName, $propertyValue) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($propertyName); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($object, $propertyValue); + } } diff --git a/lib/internal/Magento/Framework/Validator/AllowedProtocols.php b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php new file mode 100644 index 0000000000000..3c7bbb3d99723 --- /dev/null +++ b/lib/internal/Magento/Framework/Validator/AllowedProtocols.php @@ -0,0 +1,59 @@ +listOfProtocols = $listOfProtocols; + } + } + + /** + * Validate URI + * + * @param string $value + * @return bool + */ + public function isValid($value) + { + $uri = new Uri($value); + $isValid = in_array( + strtolower($uri->getScheme()), + $this->listOfProtocols + ); + if (!$isValid) { + $this->_addMessages(["Protocol isn't allowed"]); + } + return $isValid; + } +} diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index fdbe5796d9fc7..ee90c24e5dc9d 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -2,7 +2,7 @@ "name": "magento/framework", "description": "N/A", "type": "magento2-library", - "version": "100.0.11", + "version": "100.0.12", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/lib/web/mage/adminhtml/globals.js b/lib/web/mage/adminhtml/globals.js index ad9e500132c3e..e94b1cce4894b 100644 --- a/lib/web/mage/adminhtml/globals.js +++ b/lib/web/mage/adminhtml/globals.js @@ -3,8 +3,9 @@ * See COPYING.txt for license details. */ define([ - 'Magento_Ui/js/modal/confirm' -], function (confirm) { + 'Magento_Ui/js/modal/confirm', + 'mage/dataPost' +], function (confirm, dataPost) { 'use strict'; /** @@ -19,14 +20,20 @@ define([ * Helper for onclick action. * @param {String} message * @param {String} url + * @param {Object} postData * @returns {boolean} */ - window.deleteConfirm = function (message, url) { + window.deleteConfirm = function (message, url, postData) { confirm({ content: message, actions: { confirm: function () { - setLocation(url); + if (postData !== undefined) { + postData.action = url; + dataPost().postData(postData); + } else { + setLocation(url); + } } } }); diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/advanced/skins/default/content.css b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/advanced/skins/default/content.css index cf75ec4c46a6a..29c7528e2f7cb 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/advanced/skins/default/content.css +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/themes/advanced/skins/default/content.css @@ -3,8 +3,9 @@ * See COPYING.txt for license details. */ +html {height: 100%;} body, td, pre { color:#2f2f2f; font-family:Arial, Helvetica, sans-serif; font-size:12px; margin:8px; } -body { background:#fff; } +body {background:#fff; min-height: calc(100% - 20px);} body.mceForceColors { background:#fff; color:#2f2f2f; } h1 {font-size: 2em} h2 {font-size: 1.5em} diff --git a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php index 777f87515d665..548c3f64f1775 100644 --- a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php +++ b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php @@ -3,7 +3,6 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Setup\Mvc\Bootstrap; use Magento\Framework\App\Bootstrap as AppBootstrap; @@ -102,7 +101,7 @@ public function onBootstrap(MvcEvent $e) * Check if user login * * @param \Zend\Mvc\MvcEvent $event - * @return bool + * @return false|\Zend\Http\Response * @throws \Magento\Framework\Exception\LocalizedException */ public function authPreDispatch($event) @@ -135,17 +134,27 @@ public function authPreDispatch($event) 'appState' => $adminAppState ] ); - if (!$objectManager->get(\Magento\Backend\Model\Auth::class)->isLoggedIn()) { + + /** @var \Magento\Backend\Model\Auth $auth */ + $authentication = $objectManager->get(\Magento\Backend\Model\Auth::class); + + if ( + !$authentication->isLoggedIn() || + !$adminSession->isAllowed('Magento_Backend::setup_wizard') + ) { $adminSession->destroy(); + /** @var \Zend\Http\Response $response */ $response = $event->getResponse(); $baseUrl = Http::getDistroBaseUrlPath($_SERVER); $response->getHeaders()->addHeaderLine('Location', $baseUrl . 'index.php/session/unlogin'); $response->setStatusCode(302); $event->stopPropagation(); + return $response; } } } + return false; } diff --git a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php index 6f397a9c880a9..7b56614027f71 100644 --- a/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Mvc/Bootstrap/InitParamListenerTest.php @@ -3,7 +3,6 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Setup\Test\Unit\Mvc\Bootstrap; use \Magento\Setup\Mvc\Bootstrap\InitParamListener; @@ -14,6 +13,8 @@ /** * Tests Magento\Setup\Mvc\Bootstrap\InitParamListener + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class InitParamListenerTest extends \PHPUnit_Framework_TestCase { @@ -55,8 +56,10 @@ public function testOnBootstrap() ->willReturn($initParams); $serviceManager->expects($this->exactly(2))->method('setService') ->withConsecutive( - ['Magento\Framework\App\Filesystem\DirectoryList', - $this->isInstanceOf('Magento\Framework\App\Filesystem\DirectoryList')], + [ + 'Magento\Framework\App\Filesystem\DirectoryList', + $this->isInstanceOf('Magento\Framework\App\Filesystem\DirectoryList'), + ], ['Magento\Framework\Filesystem', $this->isInstanceOf('Magento\Framework\Filesystem')] ); $mvcApplication->expects($this->any())->method('getServiceManager')->willReturn($serviceManager); @@ -123,10 +126,10 @@ public function testCreateService($zfAppConfig, $env, $cliParam, $expectedArray) $request->expects($this->any()) ->method('getContent') ->willReturn( - $cliParam ? ['install', '--magento-init-params=' . $cliParam ] : ['install'] + $cliParam ? ['install', '--magento-init-params=' . $cliParam] : ['install'] ); $mvcApplication->expects($this->any())->method('getConfig')->willReturn( - $zfAppConfig ? [InitParamListener::BOOTSTRAP_PARAM => $zfAppConfig]:[] + $zfAppConfig ? [InitParamListener::BOOTSTRAP_PARAM => $zfAppConfig] : [] ); $mvcApplication->expects($this->any())->method('getRequest')->willReturn($request); @@ -143,41 +146,55 @@ public function createServiceDataProvider() 'mage_mode App' => [['MAGE_MODE' => 'developer'], [], '', ['MAGE_MODE' => 'developer']], 'mage_mode Env' => [[], ['MAGE_MODE' => 'developer'], '', ['MAGE_MODE' => 'developer']], 'mage_mode CLI' => [[], [], 'MAGE_MODE=developer', ['MAGE_MODE' => 'developer']], - 'one MAGE_DIRS CLI' => [[], [], 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer']], + 'one MAGE_DIRS CLI' => [ + [], + [], + 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2', + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer'], + ], 'two MAGE_DIRS CLI' => [ [], [], 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2&MAGE_DIRS[cache][path]=/tmp/cache', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], - 'MAGE_MODE' => 'developer']], + [ + 'MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], + 'MAGE_MODE' => 'developer', + ], + ], 'mage_mode only' => [[], [], 'MAGE_MODE=developer', ['MAGE_MODE' => 'developer']], 'MAGE_DIRS Env' => [ [], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer'], '', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2']], 'MAGE_MODE' => 'developer'], + ], 'two MAGE_DIRS' => [ [], [], 'MAGE_MODE=developer&MAGE_DIRS[base][path]=/var/www/magento2&MAGE_DIRS[cache][path]=/tmp/cache', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], - 'MAGE_MODE' => 'developer']], + [ + 'MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2'], 'cache' => ['path' => '/tmp/cache']], + 'MAGE_MODE' => 'developer', + ], + ], 'Env overwrites App' => [ ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/App']], 'MAGE_MODE' => 'developer'], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']], 'MAGE_MODE' => 'developer'], '', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']], 'MAGE_MODE' => 'developer']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']], 'MAGE_MODE' => 'developer'], + ], 'CLI overwrites Env' => [ ['MAGE_MODE' => 'developerApp'], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']]], 'MAGE_DIRS[base][path]=/var/www/magento2/CLI', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'developerApp']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'developerApp'], + ], 'CLI overwrites All' => [ ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/App']], 'MAGE_MODE' => 'production'], ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/Env']]], 'MAGE_DIRS[base][path]=/var/www/magento2/CLI', - ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'production']], + ['MAGE_DIRS' => ['base' => ['path' => '/var/www/magento2/CLI']], 'MAGE_MODE' => 'production'], + ], ]; } @@ -218,6 +235,168 @@ private function prepareEventManager() [$this->listener, 'onBootstrap'] )->willReturn($this->callbackHandler); $eventManager->expects($this->once())->method('getSharedManager')->willReturn($sharedManager); + return $eventManager; } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testAuthPreDispatch() + { + $eventMock = $this->getMockBuilder(\Zend\Mvc\MvcEvent::class) + ->disableOriginalConstructor() + ->getMock(); + $routeMatchMock = $this->getMockBuilder(\Zend\Mvc\Router\Http\RouteMatch::class) + ->disableOriginalConstructor() + ->getMock(); + $applicationMock = $this->getMockBuilder(\Zend\Mvc\Application::class) + ->disableOriginalConstructor() + ->getMock(); + $serviceManagerMock = $this->getMockBuilder(\Zend\ServiceManager\ServiceManager::class) + ->disableOriginalConstructor() + ->getMock(); + $deploymentConfigMock = $this->getMockBuilder(\Magento\Framework\App\DeploymentConfig::class) + ->disableOriginalConstructor() + ->getMock(); + $deploymentConfigMock->expects($this->once()) + ->method('isAvailable') + ->willReturn(true); + $omProvider = $this->getMockBuilder(\Magento\Setup\Model\ObjectManagerProvider::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManagerMock = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class); + $adminAppStateMock = $this->getMockBuilder(\Magento\Framework\App\State::class) + ->disableOriginalConstructor() + ->getMock(); + $sessionConfigMock = $this->getMockBuilder(\Magento\Backend\Model\Session\AdminConfig::class) + ->disableOriginalConstructor() + ->getMock(); + $backendAppListMock = $this->getMockBuilder(\Magento\Backend\App\BackendAppList::class) + ->disableOriginalConstructor() + ->getMock(); + $backendAppMock = $this->getMockBuilder(\Magento\Backend\App\BackendApp::class) + ->disableOriginalConstructor() + ->getMock(); + $backendUrlFactoryMock = $this->getMockBuilder(\Magento\Backend\Model\UrlFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $backendUrlMock = $this->getMockBuilder(\Magento\Backend\Model\Url::class) + ->disableOriginalConstructor() + ->getMock(); + $authenticationMock = $this->getMockBuilder(\Magento\Backend\Model\Auth::class) + ->disableOriginalConstructor() + ->getMock(); + $adminSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class) + ->disableOriginalConstructor() + ->getMock(); + $responseMock = $this->getMockBuilder(\Zend\Http\Response::class) + ->disableOriginalConstructor() + ->getMock(); + $headersMock = $this->getMockBuilder(\Zend\Http\Headers::class) + ->disableOriginalConstructor() + ->getMock(); + + $routeMatchMock->expects($this->once()) + ->method('getParam') + ->with('controller') + ->willReturn('testController'); + $eventMock->expects($this->once()) + ->method('getRouteMatch') + ->willReturn($routeMatchMock); + $eventMock->expects($this->once()) + ->method('getApplication') + ->willReturn($applicationMock); + $serviceManagerMock->expects($this->any()) + ->method('get') + ->willReturnMap( + [ + [ + \Magento\Framework\App\DeploymentConfig::class, + true, + $deploymentConfigMock, + ], + [ + \Magento\Setup\Model\ObjectManagerProvider::class, + true, + $omProvider, + ], + ] + ); + $objectManagerMock->expects($this->any()) + ->method('get') + ->willReturnMap( + [ + [ + \Magento\Framework\App\State::class, + $adminAppStateMock, + ], + [ + \Magento\Backend\Model\Session\AdminConfig::class, + $sessionConfigMock, + ], + [ + \Magento\Backend\App\BackendAppList::class, + $backendAppListMock, + ], + [ + \Magento\Backend\Model\UrlFactory::class, + $backendUrlFactoryMock, + ], + [ + \Magento\Backend\Model\Auth::class, + $authenticationMock, + ], + ] + ); + $objectManagerMock->expects($this->any()) + ->method('create') + ->willReturn($adminSessionMock); + $omProvider->expects($this->once()) + ->method('get') + ->willReturn($objectManagerMock); + $adminAppStateMock->expects($this->once()) + ->method('setAreaCode') + ->with(\Magento\Framework\App\Area::AREA_ADMIN); + $applicationMock->expects($this->once()) + ->method('getServiceManager') + ->willReturn($serviceManagerMock); + $backendAppMock->expects($this->once()) + ->method('getCookiePath') + ->willReturn(''); + $backendUrlFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($backendUrlMock); + $backendAppListMock->expects($this->once()) + ->method('getBackendApp') + ->willReturn($backendAppMock); + $authenticationMock->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $adminSessionMock->expects($this->once()) + ->method('isAllowed') + ->with('Magento_Backend::setup_wizard', null) + ->willReturn(false); + $adminSessionMock->expects($this->once()) + ->method('destroy'); + $eventMock->expects($this->once()) + ->method('getResponse') + ->willReturn($responseMock); + $responseMock->expects($this->once()) + ->method('getHeaders') + ->willReturn($headersMock); + $headersMock->expects($this->once()) + ->method('addHeaderLine'); + $responseMock->expects($this->once()) + ->method('setStatusCode') + ->with(302); + $eventMock->expects($this->once()) + ->method('stopPropagation'); + + $this->assertSame( + $this->listener->authPreDispatch($eventMock), + $responseMock + ); + } } diff --git a/setup/view/magento/setup/create-backup.phtml b/setup/view/magento/setup/create-backup.phtml index e19558d043924..47a85a84d2557 100644 --- a/setup/view/magento/setup/create-backup.phtml +++ b/setup/view/magento/setup/create-backup.phtml @@ -46,12 +46,6 @@ role="form" >
-
- - We’ll put your store in maintenance mode to protect your shoppers during the {{type}}. - After this point, you cannot cancel the {{type}}. - -