From 3feda05339677a30590230745619db035781752d Mon Sep 17 00:00:00 2001 From: Mobecls Date: Fri, 8 Jun 2018 17:16:42 +0300 Subject: [PATCH 001/309] 14294 - Fixes 'back' functionality after switching a store view. --- .../Store/view/frontend/templates/switch/languages.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Store/view/frontend/templates/switch/languages.phtml b/app/code/Magento/Store/view/frontend/templates/switch/languages.phtml index 80152dbb9a08f..6d041a9e22c5d 100644 --- a/app/code/Magento/Store/view/frontend/templates/switch/languages.phtml +++ b/app/code/Magento/Store/view/frontend/templates/switch/languages.phtml @@ -27,7 +27,7 @@ getStores() as $_lang): ?> getId() != $block->getCurrentStoreId()): ?>
  • - + escapeHtml($_lang->getName()) ?>
  • From e671008cb2a261842823d64dd4ea20040fafcc5f Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak Date: Tue, 14 Aug 2018 15:26:27 +0300 Subject: [PATCH 002/309] MAGETWO-67269: Gift Options set to no still show up as choices on front end order page - Template fixed: added checking of all gift options. Also added possibility to change order level options visibility from other modules. --- .../GiftMessage/Block/Message/Inline.php | 21 +++++++++++++++++-- .../view/frontend/templates/inline.phtml | 9 +++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/GiftMessage/Block/Message/Inline.php b/app/code/Magento/GiftMessage/Block/Message/Inline.php index e5b80848661fd..1a874cb5ea4ae 100644 --- a/app/code/Magento/GiftMessage/Block/Message/Inline.php +++ b/app/code/Magento/GiftMessage/Block/Message/Inline.php @@ -238,7 +238,7 @@ public function getMessage($entity = null) */ public function getItems() { - if (!$this->getData('items')) { + if (!$this->hasData('items')) { $items = []; $entityItems = $this->getEntity()->getAllItems(); @@ -325,6 +325,23 @@ public function getEscaped($value, $defaultValue = '') return $this->escapeHtml(trim($value) != '' ? $value : $defaultValue); } + /** + * Check availability of order level functionality + * + * @return bool + */ + public function isOrderLevelAvailable() + { + $entity = $this->getEntity(); + if (!$entity->hasIsGiftOptionsAvailable()) { + $this->_eventManager->dispatch('gift_options_prepare', ['entity' => $entity]); + if (!$entity->getIsGiftOptionsAvailable()) { + $entity->setIsGiftOptionsAvailable($this->isMessagesAvailable()); + } + } + return $entity->getIsGiftOptionsAvailable(); + } + /** * Check availability of giftmessages on order level * @@ -355,7 +372,7 @@ public function isItemMessagesAvailable($item) protected function _toHtml() { // render HTML when messages are allowed for order or for items only - if ($this->isItemsAvailable() || $this->isMessagesAvailable()) { + if ($this->isItemsAvailable() || $this->isOrderLevelAvailable()) { return parent::_toHtml(); } return ''; diff --git a/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml b/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml index dec54cfeb9df9..8c58959be15b7 100644 --- a/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml +++ b/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml @@ -18,7 +18,7 @@
    - isMessagesAvailable()): ?> + isOrderLevelAvailable()): ?>
    getEntityHasMessage()): ?> checked="checked" class="checkbox" /> @@ -28,6 +28,7 @@
    + isMessagesAvailable()): ?>
    - + isItemsAvailable()): ?>
    @@ -152,6 +154,7 @@
    + isOrderLevelAvailable()): ?>
    getEntityHasMessage()): ?> checked="checked" class="checkbox" /> @@ -192,7 +195,7 @@
    - + isItemsAvailable()): ?>
    From ddb642c10e70c1acab5683beaf8d7d5b4b5e8ffd Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak Date: Mon, 20 Aug 2018 11:05:50 +0300 Subject: [PATCH 003/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Moved saving toolbar params to session from block to model. Save params before generating layout xml. --- .../Block/Product/ProductList/Toolbar.php | 43 ++++- .../Catalog/Controller/Category/View.php | 12 +- .../Product/ProductList/ToolbarMemorizer.php | 161 ++++++++++++++++++ 3 files changed, 208 insertions(+), 8 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index 0b8d2d6c89e72..0e971dcb5b9f5 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -7,6 +7,7 @@ use Magento\Catalog\Helper\Product\ProductList; use Magento\Catalog\Model\Product\ProductList\Toolbar as ToolbarModel; +use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer; /** * Product list toolbar @@ -77,6 +78,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template /** * @var bool $_paramsMemorizeAllowed + * @deprecated */ protected $_paramsMemorizeAllowed = true; @@ -96,6 +98,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template * Catalog session * * @var \Magento\Catalog\Model\Session + * @deprecated */ protected $_catalogSession; @@ -104,6 +107,11 @@ class Toolbar extends \Magento\Framework\View\Element\Template */ protected $_toolbarModel; + /** + * @var ToolbarMemorizer + */ + private $toolbarMemorizer; + /** * @var ProductList */ @@ -119,6 +127,11 @@ class Toolbar extends \Magento\Framework\View\Element\Template */ protected $_postDataHelper; + /** + * @var \Magento\Framework\App\Http\Context + */ + private $httpContext; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Catalog\Model\Session $catalogSession @@ -127,6 +140,8 @@ class Toolbar extends \Magento\Framework\View\Element\Template * @param \Magento\Framework\Url\EncoderInterface $urlEncoder * @param ProductList $productListHelper * @param \Magento\Framework\Data\Helper\PostHelper $postDataHelper + * @param ToolbarMemorizer|null $toolbarMemorizer + * @param \Magento\Framework\App\Http\Context|null $httpContext * @param array $data */ public function __construct( @@ -137,6 +152,8 @@ public function __construct( \Magento\Framework\Url\EncoderInterface $urlEncoder, ProductList $productListHelper, \Magento\Framework\Data\Helper\PostHelper $postDataHelper, + ToolbarMemorizer $toolbarMemorizer = null, + \Magento\Framework\App\Http\Context $httpContext = null, array $data = [] ) { $this->_catalogSession = $catalogSession; @@ -145,6 +162,12 @@ public function __construct( $this->urlEncoder = $urlEncoder; $this->_productListHelper = $productListHelper; $this->_postDataHelper = $postDataHelper; + $this->toolbarMemorizer = $toolbarMemorizer ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + ToolbarMemorizer::class + ); + $this->httpContext = $httpContext ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Http\Context::class + ); parent::__construct($context, $data); } @@ -152,6 +175,7 @@ public function __construct( * Disable list state params memorizing * * @return $this + * @deprecated */ public function disableParamsMemorizing() { @@ -165,6 +189,7 @@ public function disableParamsMemorizing() * @param string $param parameter name * @param mixed $value parameter value * @return $this + * @deprecated */ protected function _memorizeParam($param, $value) { @@ -244,13 +269,13 @@ public function getCurrentOrder() $defaultOrder = $keys[0]; } - $order = $this->_toolbarModel->getOrder(); + $order = $this->toolbarMemorizer->getOrder(); if (!$order || !isset($orders[$order])) { $order = $defaultOrder; } if ($order != $defaultOrder) { - $this->_memorizeParam('sort_order', $order); + $this->httpContext->setValue(ToolbarModel::ORDER_PARAM_NAME, $order, $defaultOrder); } $this->setData('_current_grid_order', $order); @@ -270,13 +295,13 @@ public function getCurrentDirection() } $directions = ['asc', 'desc']; - $dir = strtolower($this->_toolbarModel->getDirection()); + $dir = strtolower($this->toolbarMemorizer->getDirection()); if (!$dir || !in_array($dir, $directions)) { $dir = $this->_direction; } if ($dir != $this->_direction) { - $this->_memorizeParam('sort_direction', $dir); + $this->httpContext->setValue(ToolbarModel::DIRECTION_PARAM_NAME, $dir, $this->_direction); } $this->setData('_current_grid_direction', $dir); @@ -412,11 +437,15 @@ public function getCurrentMode() return $mode; } $defaultMode = $this->_productListHelper->getDefaultViewMode($this->getModes()); - $mode = $this->_toolbarModel->getMode(); + $mode = $this->toolbarMemorizer->getMode(); if (!$mode || !isset($this->_availableMode[$mode])) { $mode = $defaultMode; } + if ($mode != $defaultMode) { + $this->httpContext->setValue(ToolbarModel::MODE_PARAM_NAME, $mode, $defaultMode); + } + $this->setData('_current_grid_mode', $mode); return $mode; } @@ -568,13 +597,13 @@ public function getLimit() $defaultLimit = $keys[0]; } - $limit = $this->_toolbarModel->getLimit(); + $limit = $this->toolbarMemorizer->getLimit(); if (!$limit || !isset($limits[$limit])) { $limit = $defaultLimit; } if ($limit != $defaultLimit) { - $this->_memorizeParam('limit_page', $limit); + $this->httpContext->setValue(ToolbarModel::LIMIT_PARAM_NAME, $limit, $defaultLimit); } $this->setData('_current_limit', $limit); diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php index 226e572505076..d451357a7c129 100644 --- a/app/code/Magento/Catalog/Controller/Category/View.php +++ b/app/code/Magento/Catalog/Controller/Category/View.php @@ -8,6 +8,7 @@ use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Model\Layer\Resolver; +use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\View\Result\PageFactory; @@ -69,6 +70,11 @@ class View extends \Magento\Framework\App\Action\Action */ protected $categoryRepository; + /** + * @var ToolbarMemorizer + */ + private $toolbarMemorizer; + /** * Constructor * @@ -82,6 +88,7 @@ class View extends \Magento\Framework\App\Action\Action * @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory * @param Resolver $layerResolver * @param CategoryRepositoryInterface $categoryRepository + * @param ToolbarMemorizer|null $toolbarMemorizer * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -94,7 +101,8 @@ public function __construct( PageFactory $resultPageFactory, \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory, Resolver $layerResolver, - CategoryRepositoryInterface $categoryRepository + CategoryRepositoryInterface $categoryRepository, + ToolbarMemorizer $toolbarMemorizer = null ) { parent::__construct($context); $this->_storeManager = $storeManager; @@ -106,6 +114,7 @@ public function __construct( $this->resultForwardFactory = $resultForwardFactory; $this->layerResolver = $layerResolver; $this->categoryRepository = $categoryRepository; + $this->toolbarMemorizer = $toolbarMemorizer ?: $context->getObjectManager()->get(ToolbarMemorizer::class); } /** @@ -130,6 +139,7 @@ protected function _initCategory() } $this->_catalogSession->setLastVisitedCategoryId($category->getId()); $this->_coreRegistry->register('current_category', $category); + $this->toolbarMemorizer->memorizeParams(); try { $this->_eventManager->dispatch( 'catalog_controller_category_init_after', diff --git a/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php new file mode 100644 index 0000000000000..1ff63a0fa3448 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php @@ -0,0 +1,161 @@ +toolbarModel = $toolbarModel; + $this->catalogSession = $catalogSession; + } + + /** + * Get sort order + * + * @return string|bool + */ + public function getOrder() + { + if ($this->order === null) { + $this->order = $this->toolbarModel->getOrder() ?? + $this->catalogSession->getData(Toolbar::ORDER_PARAM_NAME); + } + return $this->order; + } + + /** + * Get sort direction + * + * @return string|bool + */ + public function getDirection() + { + if ($this->direction === null) { + $this->direction = $this->toolbarModel->getDirection() ?? + $this->catalogSession->getData(Toolbar::DIRECTION_PARAM_NAME); + } + return $this->direction; + } + + /** + * Get sort mode + * + * @return string|bool + */ + public function getMode() + { + if ($this->mode === null) { + $this->mode = $this->toolbarModel->getMode() ?? + $this->catalogSession->getData(Toolbar::MODE_PARAM_NAME); + } + return $this->mode; + } + + /** + * Get products per page limit + * + * @return string|bool + */ + public function getLimit() + { + if ($this->limit === null) { + $this->limit = $this->toolbarModel->getLimit() ?? + $this->catalogSession->getData(Toolbar::LIMIT_PARAM_NAME); + } + return $this->limit; + } + + /** + * Disable list state params memorizing + * + * @return $this + */ + public function disableParamsMemorizing() + { + $this->paramsMemorizeAllowed = false; + return $this; + } + + /** + * Method to save all catalog parameters in catalog session + * + * @return void + */ + public function memorizeParams() + { + $this->memorizeParam(Toolbar::ORDER_PARAM_NAME, $this->getOrder()) + ->memorizeParam(Toolbar::DIRECTION_PARAM_NAME, $this->getDirection()) + ->memorizeParam(Toolbar::MODE_PARAM_NAME, $this->getMode()) + ->memorizeParam(Toolbar::LIMIT_PARAM_NAME, $this->getLimit()); + } + + /** + * Memorize parameter value for session + * + * @param string $param parameter name + * @param mixed $value parameter value + * @return $this + */ + private function memorizeParam($param, $value) + { + if ($value && $this->paramsMemorizeAllowed && + !$this->catalogSession->getParamsMemorizeDisabled() && + $this->catalogSession->getData($param) != $value + ) { + $this->catalogSession->setData($param, $value); + } + return $this; + } +} From 9ef1afe6ae278615c23c0deb73b9fdd5f16b1b07 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak Date: Tue, 28 Aug 2018 17:56:51 +0300 Subject: [PATCH 004/309] MAGETWO-91563: Gift wrapping selection does not display in shopping cart - Used right model to observe gift wrapping options. --- .../GiftMessage/view/frontend/web/js/view/gift-message.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/GiftMessage/view/frontend/web/js/view/gift-message.js b/app/code/Magento/GiftMessage/view/frontend/web/js/view/gift-message.js index d025f6974f35e..4c455c83a77a9 100644 --- a/app/code/Magento/GiftMessage/view/frontend/web/js/view/gift-message.js +++ b/app/code/Magento/GiftMessage/view/frontend/web/js/view/gift-message.js @@ -31,8 +31,9 @@ define([ this.itemId = this.itemId || 'orderLevel'; model = new GiftMessage(this.itemId); - giftOptions.addOption(model); this.model = model; + this.isResultBlockVisible(); + giftOptions.addOption(model); this.model.getObservable('isClear').subscribe(function (value) { if (value == true) { //eslint-disable-line eqeqeq @@ -40,8 +41,6 @@ define([ self.model.getObservable('alreadyAdded')(true); } }); - - this.isResultBlockVisible(); }, /** From f2e053a7d6e87252ae526049b2253566d19ee0a7 Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan Date: Tue, 28 Aug 2018 19:23:23 +0400 Subject: [PATCH 005/309] MAGETWO-67269: Gift Options set to no still show up as choices on front end order page - Add automated test --- .../Section/StoreFrontShoppingCartSection.xml | 15 +++++ .../CleanConfigsForGiftOptionsActionGroup.xml | 32 +++++++++++ ...ftWrappingForOrderItemsOnlyActionGroup.xml | 32 +++++++++++ .../Mftf/Section/AdminSalesConfigSection.xml | 16 +++++- .../CheckingGiftOptionsActionGroup.xml | 23 ++++++++ .../Section/GiftOptionsOnFrontSection.xml | 13 +++++ .../StoreFrontCheckingGiftOptionsTest.xml | 57 +++++++++++++++++++ 7 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontShoppingCartSection.xml create mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/CleanConfigsForGiftOptionsActionGroup.xml create mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup.xml create mode 100644 app/code/Magento/GiftMessage/Test/Mftf/ActionGroup/CheckingGiftOptionsActionGroup.xml create mode 100644 app/code/Magento/GiftMessage/Test/Mftf/Section/GiftOptionsOnFrontSection.xml create mode 100644 app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontShoppingCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontShoppingCartSection.xml new file mode 100644 index 0000000000000..303c5e526423f --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontShoppingCartSection.xml @@ -0,0 +1,15 @@ + + + + +
    + + +
    +
    diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/CleanConfigsForGiftOptionsActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/CleanConfigsForGiftOptionsActionGroup.xml new file mode 100644 index 0000000000000..72d88b13736cc --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/CleanConfigsForGiftOptionsActionGroup.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup.xml new file mode 100644 index 0000000000000..8f5b491575e95 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml index 4897e8415c1b8..aefb7c5441c5b 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml @@ -5,9 +5,23 @@ * See COPYING.txt for license details. */ --> - +
    + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/app/code/Magento/GiftMessage/Test/Mftf/ActionGroup/CheckingGiftOptionsActionGroup.xml b/app/code/Magento/GiftMessage/Test/Mftf/ActionGroup/CheckingGiftOptionsActionGroup.xml new file mode 100644 index 0000000000000..05dad757e287f --- /dev/null +++ b/app/code/Magento/GiftMessage/Test/Mftf/ActionGroup/CheckingGiftOptionsActionGroup.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Section/GiftOptionsOnFrontSection.xml b/app/code/Magento/GiftMessage/Test/Mftf/Section/GiftOptionsOnFrontSection.xml new file mode 100644 index 0000000000000..9856f009197c1 --- /dev/null +++ b/app/code/Magento/GiftMessage/Test/Mftf/Section/GiftOptionsOnFrontSection.xml @@ -0,0 +1,13 @@ + + + +
    + + +
    +
    \ No newline at end of file diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml b/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml new file mode 100644 index 0000000000000..7b4bd56ae74e8 --- /dev/null +++ b/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml @@ -0,0 +1,57 @@ + + + + + + + + + + <description value="Admin should be able to control gift options"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94209"/> + <group value="Gift"/> + </annotations> + <before> + <createData stepKey="category" entity="SimpleSubCategory"/> + <createData stepKey="product1" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData stepKey="product2" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="customer"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup" stepKey="configureGiftOptions"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + </before> + + <amOnPage url="$$product1.name$$.html" stepKey="goToProduct1"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct1"> + <argument name="productName" value="$$product1.name$$"/> + </actionGroup> + <amOnPage url="$$product2.name$$.html" stepKey="goToProduct2"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct2"> + <argument name="productName" value="$$product2.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> + <actionGroup ref="CheckingGiftOptionsActionGroup" stepKey="checkGiftOptions"/> + + <after> + <actionGroup ref="CleanConfigsForGiftOptionsActionGroup" stepKey="cleanGiftOptionsConfigs"/> + <deleteData stepKey="deleteCategory" createDataKey="category"/> + <deleteData stepKey="deleteProduct1" createDataKey="product1"/> + <deleteData stepKey="deleteProduct2" createDataKey="product2"/> + <deleteData stepKey="deleteCustomer" createDataKey="customer"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + </test> +</tests> From adb3af29f2fc435b193338a80d873689cac82a1d Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sun, 26 Aug 2018 20:41:13 +0200 Subject: [PATCH 006/309] Optimize code to remove phpmd suppress warnings By making the order of some checks more efficient the suppress warnings in these two files about CyclomaticComplexity and NPathComplexity. --- .../Magento/Customer/Model/Metadata/Form/Text.php | 11 +++++------ app/code/Magento/Eav/Model/Attribute/Data/Text.php | 13 ++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Customer/Model/Metadata/Form/Text.php b/app/code/Magento/Customer/Model/Metadata/Form/Text.php index c8b9a1e46a127..dcd3bc93569a4 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/Text.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/Text.php @@ -52,8 +52,6 @@ public function extractValue(\Magento\Framework\App\RequestInterface $request) /** * @inheritdoc - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) */ public function validateValue($value) { @@ -66,12 +64,12 @@ public function validateValue($value) $value = $this->_value; } - if ($attribute->isRequired() && empty($value) && $value !== '0') { - $errors[] = __('"%1" is a required value.', $label); + if (!$attribute->isRequired() && empty($value)) { + return true; } - if (!$errors && !$attribute->isRequired() && empty($value)) { - return true; + if (empty($value) && $value !== '0') { + $errors[] = __('"%1" is a required value.', $label); } $errors = $this->validateLength($value, $attribute, $errors); @@ -80,6 +78,7 @@ public function validateValue($value) if ($result !== true) { $errors = array_merge($errors, $result); } + if (count($errors) == 0) { return true; } diff --git a/app/code/Magento/Eav/Model/Attribute/Data/Text.php b/app/code/Magento/Eav/Model/Attribute/Data/Text.php index f81fb2affd3b3..a26a92df385f2 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/Text.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/Text.php @@ -55,8 +55,7 @@ public function extractValue(RequestInterface $request) * * @param array|string $value * @return bool|array - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) + * @throws \Magento\Framework\Exception\LocalizedException */ public function validateValue($value) { @@ -68,13 +67,13 @@ public function validateValue($value) $value = $this->getEntity()->getDataUsingMethod($attribute->getAttributeCode()); } - if ($attribute->getIsRequired() && empty($value) && $value !== '0') { - $label = __($attribute->getStoreLabel()); - $errors[] = __('"%1" is a required value.', $label); + if (!$attribute->isRequired() && empty($value)) { + return true; } - if (!$errors && !$attribute->getIsRequired() && empty($value)) { - return true; + if (empty($value) && $value !== '0') { + $label = __($attribute->getStoreLabel()); + $errors[] = __('"%1" is a required value.', $label); } $result = $this->validateLength($attribute, $value); From ea9168570ae0f2d5bcf1911b13b46b760a9cb0eb Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Fri, 31 Aug 2018 14:42:59 +0300 Subject: [PATCH 007/309] MAGETWO-91628: Bundle product price doubled when switching currency - Fixed an issue with quote item options relates to a bundled products. --- .../Magento/Bundle/Model/Product/Type.php | 2 +- .../Plugin/UpdatePriceInQuoteItemOptions.php | 55 +++++++++++++++++++ app/code/Magento/Bundle/etc/di.xml | 3 + 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Bundle/Plugin/UpdatePriceInQuoteItemOptions.php diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php index 17ecba545efad..c5f3aa0a84995 100644 --- a/app/code/Magento/Bundle/Model/Product/Type.php +++ b/app/code/Magento/Bundle/Model/Product/Type.php @@ -735,7 +735,7 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p $price = $product->getPriceModel() ->getSelectionFinalTotalPrice($product, $selection, 0, $qty); $attributes = [ - 'price' => $this->priceCurrency->convert($price), + 'price' => $price, 'qty' => $qty, 'option_label' => $selection->getOption() ->getTitle(), diff --git a/app/code/Magento/Bundle/Plugin/UpdatePriceInQuoteItemOptions.php b/app/code/Magento/Bundle/Plugin/UpdatePriceInQuoteItemOptions.php new file mode 100644 index 0000000000000..d5aafb8ad2b61 --- /dev/null +++ b/app/code/Magento/Bundle/Plugin/UpdatePriceInQuoteItemOptions.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Plugin; + +use Magento\Quote\Model\Quote\Item as OrigQuoteItem; +use Magento\Quote\Model\Quote\Item\AbstractItem; +use Magento\Framework\Serialize\SerializerInterface; + +/** + * Update prices stored in quote item options after calculating quote item's totals + */ +class UpdatePriceInQuoteItemOptions +{ + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @param SerializerInterface $serializer + */ + public function __construct(SerializerInterface $serializer) + { + $this->serializer = $serializer; + } + + /** + * Update price on quote item options level + * + * @param OrigQuoteItem $subject + * @param AbstractItem $result + * @return AbstractItem + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterCalcRowTotal(OrigQuoteItem $subject, AbstractItem $result) + { + $bundleAttributes = $result->getProduct()->getCustomOption('bundle_selection_attributes'); + if ($bundleAttributes !== null) { + $actualPrice = $result->getPrice(); + $parsedValue = $this->serializer->unserialize($bundleAttributes->getValue()); + if (is_array($parsedValue) && array_key_exists('price', $parsedValue)) { + $parsedValue['price'] = $actualPrice; + } + $bundleAttributes->setValue($this->serializer->serialize($parsedValue)); + } + + return $result; + } +} diff --git a/app/code/Magento/Bundle/etc/di.xml b/app/code/Magento/Bundle/etc/di.xml index 733b089dccd4b..6f0cc04790cc2 100644 --- a/app/code/Magento/Bundle/etc/di.xml +++ b/app/code/Magento/Bundle/etc/di.xml @@ -123,6 +123,9 @@ </argument> </arguments> </type> + <type name="Magento\Quote\Model\Quote\Item"> + <plugin name="update_price_for_bundle_in_quote_item_option" type="Magento\Bundle\Plugin\UpdatePriceInQuoteItemOptions"/> + </type> <type name="Magento\Quote\Model\Quote\Item\ToOrderItem"> <plugin name="append_bundle_data_to_order" type="Magento\Bundle\Model\Plugin\QuoteItem"/> </type> From 38d5547049a0f2f6bf83b016b1af55961ccd753c Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Tue, 4 Sep 2018 13:47:33 +0400 Subject: [PATCH 008/309] MAGETWO-67269: Gift Options set to no still show up as choices on front end order page - Updated automated test --- .../CleanConfigsForGiftOptionsActionGroup.xml | 32 --------------- ...ftWrappingForOrderItemsOnlyActionGroup.xml | 32 --------------- .../Test/Mftf/Data/GiftOptionsData.xml | 40 +++++++++++++++++++ .../Test/Mftf/Metadata/gift_options-meta.xml | 31 ++++++++++++++ .../StoreFrontCheckingGiftOptionsTest.xml | 6 ++- 5 files changed, 75 insertions(+), 66 deletions(-) delete mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/CleanConfigsForGiftOptionsActionGroup.xml delete mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup.xml create mode 100644 app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml create mode 100644 app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/CleanConfigsForGiftOptionsActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/CleanConfigsForGiftOptionsActionGroup.xml deleted file mode 100644 index 72d88b13736cc..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/CleanConfigsForGiftOptionsActionGroup.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - <actionGroup name="CleanConfigsForGiftOptionsActionGroup"> - <amOnPage url="{{AdminSalesConfigPage.url('')}}" stepKey="amOnSalesConfigPage"/> - <waitForPageLoad stepKey="waitForConfigPage" time="5"/> - <conditionalClick selector="{{AdminSalesConfigSection.giftOptions}}" - dependentSelector="{{AdminSalesConfigSection.allowGiftWrappingForOrderItems}}" visible="false" - stepKey="openGiftOptionsIfItIsClosed"/> - <waitForPageLoad stepKey="waitForGiftOptions" time="2"/> - <click stepKey="openOrderLevel" selector="{{AdminSalesConfigSection.allowGiftWrappingOnOrderLevel}}"/> - <click stepKey="chooseYesForOrderLevel" selector="{{AdminSalesConfigSection.levelYes}}"/> - <click stepKey="openOrderItems" selector="{{AdminSalesConfigSection.allowGiftWrappingForOrderItems}}"/> - <click stepKey="chooseYesForOrderItems" selector="{{AdminSalesConfigSection.itemsYes}}"/> - <click stepKey="openGiftReceipt" selector="{{AdminSalesConfigSection.allowGiftReceipt}}"/> - <click stepKey="chooseYesForReceipt" selector="{{AdminSalesConfigSection.receiptYes}}"/> - <click stepKey="openPrintedCard" selector="{{AdminSalesConfigSection.allowPrintedCard}}"/> - <click stepKey="chooseYesPrintedCard" selector="{{AdminSalesConfigSection.printedCardYes}}"/> - <click stepKey="closeGiftOptions" selector="{{AdminSalesConfigSection.giftOptions}}"/> - <click stepKey="save" selector="{{AdminConfigSection.saveButton}}"/> - <waitForPageLoad stepKey="waitForSevConfigs" time="5"/> - <see stepKey="seeSuccessMessage" userInput="You saved the configuration."/> - </actionGroup> -</actionGroups> - diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup.xml deleted file mode 100644 index 8f5b491575e95..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - <actionGroup name="ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup"> - <amOnPage url="{{AdminSalesConfigPage.url('')}}" stepKey="amOnSalesConfigPage"/> - <waitForPageLoad stepKey="waitForConfigPage" time="5"/> - <scrollTo stepKey="scrollToGiftOptions" selector="{{AdminSalesConfigSection.giftOptions}}"/> - <conditionalClick selector="{{AdminSalesConfigSection.giftOptions}}" - dependentSelector="{{AdminSalesConfigSection.allowGiftWrappingForOrderItems}}" visible="false" - stepKey="openGiftOptionsIfItIsClosed"/> - <waitForPageLoad stepKey="waitForGiftOptions" time="2"/> - <click stepKey="openOrderLevel" selector="{{AdminSalesConfigSection.allowGiftWrappingOnOrderLevel}}"/> - <click stepKey="chooseNoForOrderLevel" selector="{{AdminSalesConfigSection.levelNo}}"/> - <click stepKey="openOrderItems" selector="{{AdminSalesConfigSection.allowGiftWrappingForOrderItems}}"/> - <click stepKey="chooseYesForOrderItems" selector="{{AdminSalesConfigSection.itemsYes}}"/> - <click stepKey="openGiftReceipt" selector="{{AdminSalesConfigSection.allowGiftReceipt}}"/> - <click stepKey="chooseNoForReceipt" selector="{{AdminSalesConfigSection.receiptNo}}"/> - <click stepKey="openPrintedCard" selector="{{AdminSalesConfigSection.allowPrintedCard}}"/> - <click stepKey="chooseNoPrintedCard" selector="{{AdminSalesConfigSection.printedCardNo}}"/> - <click stepKey="save" selector="{{AdminConfigSection.saveButton}}"/> - <waitForPageLoad stepKey="waitForSevConfigs" time="5"/> - <see stepKey="seeSuccessMessage" userInput="You saved the configuration."/> - </actionGroup> -</actionGroups> - diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml b/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml new file mode 100644 index 0000000000000..b2bc71b5b7a7b --- /dev/null +++ b/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ConfigAllowGiftWrappingForOrderItemsOnly" type="gift_options_config_state"> + <requiredEntity type="wrapping_allow_order">wrappingAllowOrder</requiredEntity> + <requiredEntity type="allow_gift_receipt">allowGiftReceipt</requiredEntity> + <requiredEntity type="allow_printed_card">allowPrintedCard</requiredEntity> + </entity> + <entity name="wrappingAllowOrder" type="wrapping_allow_order"> + <data key="value">0</data> + </entity> + <entity name="allowGiftReceipt" type="allow_gift_receipt"> + <data key="value">0</data> + </entity> + <entity name="allowPrintedCard" type="allow_printed_card"> + <data key="value">0</data> + </entity> + + <entity name="DefaultConfigGiftOptions" type="gift_options_config_state"> + <requiredEntity type="wrapping_allow_order">defaultWrappingAllowOrder</requiredEntity> + <requiredEntity type="allow_gift_receipt">defaultAllowGiftReceipt</requiredEntity> + <requiredEntity type="allow_printed_card">defaultAllowPrintedCard</requiredEntity> + </entity> + <entity name="defaultWrappingAllowOrder" type="wrapping_allow_order"> + <data key="value">1</data> + </entity> + <entity name="defaultAllowGiftReceipt" type="allow_gift_receipt"> + <data key="value">1</data> + </entity> + <entity name="defaultAllowPrintedCard" type="allow_printed_card"> + <data key="value">1</data> + </entity> +</entities> diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml b/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml new file mode 100644 index 0000000000000..a32f5694f2470 --- /dev/null +++ b/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + <operation name="SalesGiftOptionsConfigState" dataType="gift_options_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/sales/" method="POST"> + <object key="groups" dataType="gift_options_config_state"> + <object key="gift_options" dataType="gift_options_config_state"> + <object key="fields" dataType="gift_options_config_state"> + <object key="wrapping_allow_order" dataType="wrapping_allow_order"> + <field key="value">string</field> + </object> + <object key="wrapping_allow_items" dataType="wrapping_allow_items"> + <field key="value">string</field> + </object> + <object key="allow_gift_receipt" dataType="allow_gift_receipt"> + <field key="value">string</field> + </object> + <object key="allow_printed_card" dataType="allow_printed_card"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml b/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml index 7b4bd56ae74e8..0bb5bf0e71fa8 100644 --- a/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml +++ b/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml @@ -27,11 +27,13 @@ <requiredEntity createDataKey="category"/> </createData> <createData entity="Simple_US_Customer" stepKey="customer"/> + <createData entity="ConfigAllowGiftWrappingForOrderItemsOnly" stepKey="configAllowGiftWrappingForOrderItemsOnly"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="ConfigAllowGiftWrappingForOrderItemsOnlyActionGroup" stepKey="configureGiftOptions"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> + </before> <amOnPage url="$$product1.name$$.html" stepKey="goToProduct1"/> @@ -46,7 +48,7 @@ <actionGroup ref="CheckingGiftOptionsActionGroup" stepKey="checkGiftOptions"/> <after> - <actionGroup ref="CleanConfigsForGiftOptionsActionGroup" stepKey="cleanGiftOptionsConfigs"/> + <createData entity="DefaultConfigGiftOptions" stepKey="restoreDefaultConfigGiftOptions"/> <deleteData stepKey="deleteCategory" createDataKey="category"/> <deleteData stepKey="deleteProduct1" createDataKey="product1"/> <deleteData stepKey="deleteProduct2" createDataKey="product2"/> From 0ff4e4ead023aa878823bc6d28aaf74d7851b98d Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan <Karen_Mkhitaryan@epam.com> Date: Fri, 7 Sep 2018 13:10:46 +0400 Subject: [PATCH 009/309] MAGETWO-67269: Gift Options set to no still show up as choices on front end order page - Remove test from CE --- .../Mftf/Section/AdminSalesConfigSection.xml | 10 ---- .../CheckingGiftOptionsActionGroup.xml | 8 +-- .../Test/Mftf/Data/GiftOptionsData.xml | 13 +--- .../Test/Mftf/Metadata/gift_options-meta.xml | 31 ---------- .../StoreFrontCheckingGiftOptionsTest.xml | 59 ------------------- .../Mftf/Section/MultishippingSection.xml} | 5 +- ...AdminShipmentAddressInformationSection.xml | 3 +- 7 files changed, 9 insertions(+), 120 deletions(-) delete mode 100644 app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml delete mode 100644 app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml rename app/code/Magento/{Checkout/Test/Mftf/Section/StoreFrontShoppingCartSection.xml => Multishipping/Test/Mftf/Section/MultishippingSection.xml} (51%) diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml index aefb7c5441c5b..95a9f75768c62 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml @@ -10,18 +10,8 @@ <element name="enableMAPUseSystemValue" type="checkbox" selector="#sales_msrp_enabled_inherit"/> <element name="enableMAPSelect" type="select" selector="#sales_msrp_enabled"/> <element name="giftOptions" type="select" selector="#sales_gift_options-head"/> - <element name="allowGiftWrappingOnOrderLevel" type="select" selector="#sales_gift_options_wrapping_allow_order"/> - <element name="levelNo" type="select" selector="//select[@id='sales_gift_options_wrapping_allow_order']//option[text()='No']"/> - <element name="levelYes" type="select" selector="//select[@id='sales_gift_options_wrapping_allow_order']//option[text()='Yes']"/> - <element name="allowGiftWrappingForOrderItems" type="select" selector="#sales_gift_options_wrapping_allow_items"/> - <element name="itemsNo" type="select" selector="//select[@id='sales_gift_options_wrapping_allow_items']//option[text()='No']"/> - <element name="itemsYes" type="select" selector="//select[@id='sales_gift_options_wrapping_allow_items']//option[text()='Yes']"/> <element name="allowGiftReceipt" type="select" selector="#sales_gift_options_allow_gift_receipt"/> - <element name="receiptNo" type="select" selector="//select[@id='sales_gift_options_allow_gift_receipt']//option[text()='No']"/> - <element name="receiptYes" type="select" selector="//select[@id='sales_gift_options_allow_gift_receipt']//option[text()='Yes']"/> <element name="allowPrintedCard" type="select" selector="#sales_gift_options_allow_printed_card"/> - <element name="printedCardNo" type="select" selector="//select[@id='sales_gift_options_allow_printed_card']//option[text()='No']"/> - <element name="printedCardYes" type="select" selector="//select[@id='sales_gift_options_allow_printed_card']//option[text()='Yes']"/> <element name="go" type="select" selector="//a[@id='sales_gift_options-head']/ancestor::div[@class='entry-edit-head admin__collapsible-block']"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/GiftMessage/Test/Mftf/ActionGroup/CheckingGiftOptionsActionGroup.xml b/app/code/Magento/GiftMessage/Test/Mftf/ActionGroup/CheckingGiftOptionsActionGroup.xml index 05dad757e287f..f81877006c7a6 100644 --- a/app/code/Magento/GiftMessage/Test/Mftf/ActionGroup/CheckingGiftOptionsActionGroup.xml +++ b/app/code/Magento/GiftMessage/Test/Mftf/ActionGroup/CheckingGiftOptionsActionGroup.xml @@ -7,11 +7,11 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <actionGroup name="CheckingGiftOptionsActionGroup"> - <click stepKey="clickOnCheckoutWithMultipleAddresses" selector="{{StoreFrontShoppingCartSection.checkoutWithMultipleAddresses}}"/> - <waitForPageLoad stepKey="waitForShipToMultipleAddresses"/> - <click stepKey="goToShippingInformation" selector="{{StoreFrontShoppingCartSection.goToShippingInformation}}"/> + <click stepKey="clickOnCheckoutWithMultipleAddresses" selector="{{MultishippingSection.checkoutWithMultipleAddresses}}"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click stepKey="goToShippingInformation" selector="{{AdminShipmentAddressInformationSection.goToShippingInformation}}"/> <waitForPageLoad stepKey="waitForGiftOption"/> <click stepKey="thickAddGiftOptions" selector="{{GiftOptionsOnFrontSection.giftOptionCheckbox}}"/> <waitForPageLoad stepKey="waitForOptions"/> diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml b/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml index b2bc71b5b7a7b..951bbd3b787b7 100644 --- a/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml +++ b/app/code/Magento/GiftMessage/Test/Mftf/Data/GiftOptionsData.xml @@ -7,15 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="ConfigAllowGiftWrappingForOrderItemsOnly" type="gift_options_config_state"> - <requiredEntity type="wrapping_allow_order">wrappingAllowOrder</requiredEntity> - <requiredEntity type="allow_gift_receipt">allowGiftReceipt</requiredEntity> - <requiredEntity type="allow_printed_card">allowPrintedCard</requiredEntity> - </entity> - <entity name="wrappingAllowOrder" type="wrapping_allow_order"> - <data key="value">0</data> - </entity> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <entity name="allowGiftReceipt" type="allow_gift_receipt"> <data key="value">0</data> </entity> @@ -28,9 +20,6 @@ <requiredEntity type="allow_gift_receipt">defaultAllowGiftReceipt</requiredEntity> <requiredEntity type="allow_printed_card">defaultAllowPrintedCard</requiredEntity> </entity> - <entity name="defaultWrappingAllowOrder" type="wrapping_allow_order"> - <data key="value">1</data> - </entity> <entity name="defaultAllowGiftReceipt" type="allow_gift_receipt"> <data key="value">1</data> </entity> diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml b/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml deleted file mode 100644 index a32f5694f2470..0000000000000 --- a/app/code/Magento/GiftMessage/Test/Mftf/Metadata/gift_options-meta.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> - <operation name="SalesGiftOptionsConfigState" dataType="gift_options_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/sales/" method="POST"> - <object key="groups" dataType="gift_options_config_state"> - <object key="gift_options" dataType="gift_options_config_state"> - <object key="fields" dataType="gift_options_config_state"> - <object key="wrapping_allow_order" dataType="wrapping_allow_order"> - <field key="value">string</field> - </object> - <object key="wrapping_allow_items" dataType="wrapping_allow_items"> - <field key="value">string</field> - </object> - <object key="allow_gift_receipt" dataType="allow_gift_receipt"> - <field key="value">string</field> - </object> - <object key="allow_printed_card" dataType="allow_printed_card"> - <field key="value">string</field> - </object> - </object> - </object> - </object> - </operation> -</operations> diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml b/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml deleted file mode 100644 index 0bb5bf0e71fa8..0000000000000 --- a/app/code/Magento/GiftMessage/Test/Mftf/Test/StoreFrontCheckingGiftOptionsTest.xml +++ /dev/null @@ -1,59 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="StoreFrontCheckingGiftOptionsTest"> - <annotations> - <features value="Gift"/> - <stories value="Checking Gift Options on front end order page"/> - <title value="Checking Gift Options on front end order page"/> - <description value="Admin should be able to control gift options"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-94209"/> - <group value="Gift"/> - </annotations> - <before> - <createData stepKey="category" entity="SimpleSubCategory"/> - <createData stepKey="product1" entity="SimpleProduct"> - <requiredEntity createDataKey="category"/> - </createData> - <createData stepKey="product2" entity="SimpleProduct"> - <requiredEntity createDataKey="category"/> - </createData> - <createData entity="Simple_US_Customer" stepKey="customer"/> - <createData entity="ConfigAllowGiftWrappingForOrderItemsOnly" stepKey="configAllowGiftWrappingForOrderItemsOnly"/> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - - </before> - - <amOnPage url="$$product1.name$$.html" stepKey="goToProduct1"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct1"> - <argument name="productName" value="$$product1.name$$"/> - </actionGroup> - <amOnPage url="$$product2.name$$.html" stepKey="goToProduct2"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct2"> - <argument name="productName" value="$$product2.name$$"/> - </actionGroup> - <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> - <actionGroup ref="CheckingGiftOptionsActionGroup" stepKey="checkGiftOptions"/> - - <after> - <createData entity="DefaultConfigGiftOptions" stepKey="restoreDefaultConfigGiftOptions"/> - <deleteData stepKey="deleteCategory" createDataKey="category"/> - <deleteData stepKey="deleteProduct1" createDataKey="product1"/> - <deleteData stepKey="deleteProduct2" createDataKey="product2"/> - <deleteData stepKey="deleteCustomer" createDataKey="customer"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> - </after> - </test> -</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontShoppingCartSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml similarity index 51% rename from app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontShoppingCartSection.xml rename to app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml index 303c5e526423f..e7d57af1172c6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StoreFrontShoppingCartSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml @@ -7,9 +7,8 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="StoreFrontShoppingCartSection"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <section name="MultishippingSection"> <element name="checkoutWithMultipleAddresses" type="button" selector="//span[text()='Check Out with Multiple Addresses']"/> - <element name="goToShippingInformation" type="button" selector="//button[@title='Go to Shipping Information']"/> </section> </sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml index ea4dde8190bc7..10878310c262f 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml @@ -7,11 +7,12 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <section name="AdminShipmentAddressInformationSection"> <element name="billingAddress" type="text" selector=".order-billing-address address"/> <element name="billingAddressEdit" type="button" selector=".order-billing-address .actions a"/> <element name="shippingAddress" type="text" selector=".order-shipping-address address"/> <element name="shippingAddressEdit" type="button" selector=".order-shipping-address .actions a"/> + <element name="goToShippingInformation" type="button" selector="//button[@title='Go to Shipping Information']"/> </section> </sections> From 10765ab93c5336dbab89c71f80a0fe14db9ca38a Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Fri, 7 Sep 2018 14:35:54 +0300 Subject: [PATCH 010/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Fixes after discussion. Added configuration to enable/disable toolbar memorizing. --- .../Block/Product/ProductList/Toolbar.php | 19 ++++---- .../Product/ProductList/ToolbarMemorizer.php | 48 +++++++++++-------- .../Magento/Catalog/etc/adminhtml/system.xml | 6 +++ app/code/Magento/Catalog/etc/config.xml | 1 + 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index 0e971dcb5b9f5..e3ed8ef94b74b 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -274,7 +274,7 @@ public function getCurrentOrder() $order = $defaultOrder; } - if ($order != $defaultOrder) { + if ($order != $defaultOrder && $this->toolbarMemorizer->isMemorizingAllowed()) { $this->httpContext->setValue(ToolbarModel::ORDER_PARAM_NAME, $order, $defaultOrder); } @@ -300,7 +300,7 @@ public function getCurrentDirection() $dir = $this->_direction; } - if ($dir != $this->_direction) { + if ($dir != $this->_direction && $this->toolbarMemorizer->isMemorizingAllowed()) { $this->httpContext->setValue(ToolbarModel::DIRECTION_PARAM_NAME, $dir, $this->_direction); } @@ -442,7 +442,7 @@ public function getCurrentMode() $mode = $defaultMode; } - if ($mode != $defaultMode) { + if ($mode != $defaultMode && $this->toolbarMemorizer->isMemorizingAllowed()) { $this->httpContext->setValue(ToolbarModel::MODE_PARAM_NAME, $mode, $defaultMode); } @@ -602,7 +602,7 @@ public function getLimit() $limit = $defaultLimit; } - if ($limit != $defaultLimit) { + if ($limit != $defaultLimit && $this->toolbarMemorizer->isMemorizingAllowed()) { $this->httpContext->setValue(ToolbarModel::LIMIT_PARAM_NAME, $limit, $defaultLimit); } @@ -711,15 +711,18 @@ public function getPagerHtml() public function getWidgetOptionsJson(array $customOptions = []) { $defaultMode = $this->_productListHelper->getDefaultViewMode($this->getModes()); + $defaultDirection = $this->_direction ?: ProductList::DEFAULT_SORT_DIRECTION; + $isMemorizingAllowed = $this->toolbarMemorizer->isMemorizingAllowed(); $options = [ 'mode' => ToolbarModel::MODE_PARAM_NAME, 'direction' => ToolbarModel::DIRECTION_PARAM_NAME, 'order' => ToolbarModel::ORDER_PARAM_NAME, 'limit' => ToolbarModel::LIMIT_PARAM_NAME, - 'modeDefault' => $defaultMode, - 'directionDefault' => $this->_direction ?: ProductList::DEFAULT_SORT_DIRECTION, - 'orderDefault' => $this->getOrderField(), - 'limitDefault' => $this->_productListHelper->getDefaultLimitPerPageValue($defaultMode), + 'modeDefault' => $isMemorizingAllowed ? false : $defaultMode, + 'directionDefault' => $isMemorizingAllowed ? false : $defaultDirection, + 'orderDefault' => $isMemorizingAllowed ? false : $this->getOrderField(), + 'limitDefault' => $isMemorizingAllowed ? false : + $this->_productListHelper->getDefaultLimitPerPageValue($defaultMode), 'url' => $this->getPagerUrl(), ]; $options = array_replace_recursive($options, $customOptions); diff --git a/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php index 1ff63a0fa3448..4e65a4f9853e7 100644 --- a/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php +++ b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php @@ -3,9 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\ProductList; use Magento\Catalog\Model\Session as CatalogSession; +use Magento\Framework\App\Config\ScopeConfigInterface; /** * Class ToolbarMemorizer @@ -14,6 +18,11 @@ */ class ToolbarMemorizer { + /** + * XML PATH to enable/disable saving toolbar parameters to session + */ + const XML_PATH_CATALOG_REMEMBER_PAGINATION = 'catalog/frontend/remember_pagination'; + /** * @var CatalogSession */ @@ -25,9 +34,9 @@ class ToolbarMemorizer private $toolbarModel; /** - * @var bool + * @var ScopeConfigInterface */ - private $paramsMemorizeAllowed = true; + private $scopeConfig; /** * @var string|bool @@ -52,13 +61,16 @@ class ToolbarMemorizer /** * @param Toolbar $toolbarModel * @param CatalogSession $catalogSession + * @param ScopeConfigInterface $scopeConfig */ public function __construct( Toolbar $toolbarModel, - CatalogSession $catalogSession + CatalogSession $catalogSession, + ScopeConfigInterface $scopeConfig ) { $this->toolbarModel = $toolbarModel; $this->catalogSession = $catalogSession; + $this->scopeConfig = $scopeConfig; } /** @@ -118,27 +130,28 @@ public function getLimit() } /** - * Disable list state params memorizing + * Method to save all catalog parameters in catalog session * - * @return $this + * @return void */ - public function disableParamsMemorizing() + public function memorizeParams() { - $this->paramsMemorizeAllowed = false; - return $this; + if (!$this->catalogSession->getParamsMemorizeDisabled() && $this->isMemorizingAllowed()) { + $this->memorizeParam(Toolbar::ORDER_PARAM_NAME, $this->getOrder()) + ->memorizeParam(Toolbar::DIRECTION_PARAM_NAME, $this->getDirection()) + ->memorizeParam(Toolbar::MODE_PARAM_NAME, $this->getMode()) + ->memorizeParam(Toolbar::LIMIT_PARAM_NAME, $this->getLimit()); + } } /** - * Method to save all catalog parameters in catalog session + * Check configuration for enabled/disabled toolbar memorizing * - * @return void + * @return bool */ - public function memorizeParams() + public function isMemorizingAllowed() { - $this->memorizeParam(Toolbar::ORDER_PARAM_NAME, $this->getOrder()) - ->memorizeParam(Toolbar::DIRECTION_PARAM_NAME, $this->getDirection()) - ->memorizeParam(Toolbar::MODE_PARAM_NAME, $this->getMode()) - ->memorizeParam(Toolbar::LIMIT_PARAM_NAME, $this->getLimit()); + return $this->scopeConfig->isSetFlag(self::XML_PATH_CATALOG_REMEMBER_PAGINATION); } /** @@ -150,10 +163,7 @@ public function memorizeParams() */ private function memorizeParam($param, $value) { - if ($value && $this->paramsMemorizeAllowed && - !$this->catalogSession->getParamsMemorizeDisabled() && - $this->catalogSession->getData($param) != $value - ) { + if ($value && $this->catalogSession->getData($param) != $value) { $this->catalogSession->setData($param, $value); } return $this; diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 71a799fd22427..be35ae2e5549d 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -92,6 +92,12 @@ <comment>Whether to show "All" option in the "Show X Per Page" dropdown</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> + <field id="remember_pagination" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <label>Remember Category Pagination</label> + <comment>Note that SEO and performance experience may be negative by this feature enabled. + Also it will increase cache storage consumption.</comment> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> </group> <group id="placeholder" translate="label" sortOrder="300" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Product Image Placeholders</label> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 1d92197e390a1..9ede3885f9a78 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -30,6 +30,7 @@ <flat_catalog_category>0</flat_catalog_category> <default_sort_by>position</default_sort_by> <parse_url_directives>1</parse_url_directives> + <remember_pagination>0</remember_pagination> </frontend> <product> <flat> From 13fa0d74eb5f5fa980d56cead6729c16177e02bc Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Tue, 11 Sep 2018 11:10:13 +0300 Subject: [PATCH 011/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Add plugin to set toolbar parameters to context before controller dispatch --- .../Block/Product/ProductList/Toolbar.php | 8 +- .../Model/App/Action/ContextPlugin.php | 77 +++++++++++++++++++ app/code/Magento/Catalog/etc/frontend/di.xml | 4 + 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/App/Action/ContextPlugin.php diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index e3ed8ef94b74b..073e3f8f6173e 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -274,7 +274,7 @@ public function getCurrentOrder() $order = $defaultOrder; } - if ($order != $defaultOrder && $this->toolbarMemorizer->isMemorizingAllowed()) { + if ($this->toolbarMemorizer->isMemorizingAllowed()) { $this->httpContext->setValue(ToolbarModel::ORDER_PARAM_NAME, $order, $defaultOrder); } @@ -300,7 +300,7 @@ public function getCurrentDirection() $dir = $this->_direction; } - if ($dir != $this->_direction && $this->toolbarMemorizer->isMemorizingAllowed()) { + if ($this->toolbarMemorizer->isMemorizingAllowed()) { $this->httpContext->setValue(ToolbarModel::DIRECTION_PARAM_NAME, $dir, $this->_direction); } @@ -442,7 +442,7 @@ public function getCurrentMode() $mode = $defaultMode; } - if ($mode != $defaultMode && $this->toolbarMemorizer->isMemorizingAllowed()) { + if ($this->toolbarMemorizer->isMemorizingAllowed()) { $this->httpContext->setValue(ToolbarModel::MODE_PARAM_NAME, $mode, $defaultMode); } @@ -602,7 +602,7 @@ public function getLimit() $limit = $defaultLimit; } - if ($limit != $defaultLimit && $this->toolbarMemorizer->isMemorizingAllowed()) { + if ($this->toolbarMemorizer->isMemorizingAllowed()) { $this->httpContext->setValue(ToolbarModel::LIMIT_PARAM_NAME, $limit, $defaultLimit); } diff --git a/app/code/Magento/Catalog/Model/App/Action/ContextPlugin.php b/app/code/Magento/Catalog/Model/App/Action/ContextPlugin.php new file mode 100644 index 0000000000000..c5ed81540e29e --- /dev/null +++ b/app/code/Magento/Catalog/Model/App/Action/ContextPlugin.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\App\Action; + +use Magento\Catalog\Model\Product\ProductList\Toolbar as ToolbarModel; +use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer; +use Magento\Catalog\Model\Session as CatalogSession; +use Magento\Framework\App\Http\Context as HttpContext; + +/** + * Before dispatch plugin for all frontend controllers to update http context. + */ +class ContextPlugin +{ + /** + * @var ToolbarMemorizer + */ + private $toolbarMemorizer; + + /** + * @var CatalogSession + */ + private $catalogSession; + + /** + * @var HttpContext + */ + private $httpContext; + + /** + * @param ToolbarMemorizer $toolbarMemorizer + * @param CatalogSession $catalogSession + * @param HttpContext $httpContext + */ + public function __construct( + ToolbarMemorizer $toolbarMemorizer, + CatalogSession $catalogSession, + HttpContext $httpContext + ) { + $this->toolbarMemorizer = $toolbarMemorizer; + $this->catalogSession = $catalogSession; + $this->httpContext = $httpContext; + } + + /** + * Update http context with catalog sensitive information. + * + * @return void + */ + public function beforeDispatch() + { + if ($this->toolbarMemorizer->isMemorizingAllowed()) { + $order = $this->catalogSession->getData(ToolbarModel::ORDER_PARAM_NAME); + if ($order) { + $this->httpContext->setValue(ToolbarModel::ORDER_PARAM_NAME, $order, false); + } + $direction = $this->catalogSession->getData(ToolbarModel::DIRECTION_PARAM_NAME); + if ($direction) { + $this->httpContext->setValue(ToolbarModel::DIRECTION_PARAM_NAME, $direction, false); + } + $mode = $this->catalogSession->getData(ToolbarModel::MODE_PARAM_NAME); + if ($mode) { + $this->httpContext->setValue(ToolbarModel::MODE_PARAM_NAME, $mode, false); + } + $limit = $this->catalogSession->getData(ToolbarModel::LIMIT_PARAM_NAME); + if ($limit) { + $this->httpContext->setValue(ToolbarModel::LIMIT_PARAM_NAME, $limit, false); + } + } + } +} diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 55098037191e8..a1520b66e83dc 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -116,4 +116,8 @@ <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> <plugin name="get_catalog_product_price_index_table_name" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\TableResolver"/> </type> + <type name="Magento\Framework\App\Action\AbstractAction"> + <plugin name="catalog_app_action_dispatch_controller_context_plugin" + type="Magento\Catalog\Model\App\Action\ContextPlugin" /> + </type> </config> From dc878446ec83d88080c81ad71ef6fc986806f173 Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan <Karen_Mkhitaryan@epam.com> Date: Tue, 11 Sep 2018 19:41:28 +0400 Subject: [PATCH 012/309] MAGETWO-67269: Gift Options set to no still show up as choices on front end order page - Add change related to xsd --- .../GiftMessage/Test/Mftf/Section/GiftOptionsOnFrontSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/GiftMessage/Test/Mftf/Section/GiftOptionsOnFrontSection.xml b/app/code/Magento/GiftMessage/Test/Mftf/Section/GiftOptionsOnFrontSection.xml index 9856f009197c1..63243030682bf 100644 --- a/app/code/Magento/GiftMessage/Test/Mftf/Section/GiftOptionsOnFrontSection.xml +++ b/app/code/Magento/GiftMessage/Test/Mftf/Section/GiftOptionsOnFrontSection.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="GiftOptionsOnFrontSection"> <element name="giftOptionCheckbox" type="text" selector="//span[text()='Add Gift Options']"/> <element name="addGiftOptionsForIndividualItems" type="text" selector="//dt[@class='order-title individual']"/> From ca7932ba5e813967018a5052a763bbe65c9abd66 Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan <karen_mkhitaryan@epam.com> Date: Fri, 7 Sep 2018 12:10:46 +0300 Subject: [PATCH 013/309] MAGETWO-67269: Gift Options set to no still show up as choices on front end order page - Remove test from CE --- .../Mftf/Section/AdminShipmentAddressInformationSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml index 3268a6f288371..10878310c262f 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentAddressInformationSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <section name="AdminShipmentAddressInformationSection"> <element name="billingAddress" type="text" selector=".order-billing-address address"/> <element name="billingAddressEdit" type="button" selector=".order-billing-address .actions a"/> From 6d068c79b5bb8fcad3a4b18df2a394fb7beb84f6 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Tue, 11 Sep 2018 18:11:30 +0300 Subject: [PATCH 014/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Post toolbar parameters when using toolbar saving. --- .../Block/Product/ProductList/Toolbar.php | 29 +++++++++---- .../frontend/web/js/product/list/toolbar.js | 41 ++++++++++++++++--- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index 073e3f8f6173e..9f1514aab9d6f 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -8,6 +8,7 @@ use Magento\Catalog\Helper\Product\ProductList; use Magento\Catalog\Model\Product\ProductList\Toolbar as ToolbarModel; use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer; +use Magento\Framework\App\ObjectManager; /** * Product list toolbar @@ -132,6 +133,11 @@ class Toolbar extends \Magento\Framework\View\Element\Template */ private $httpContext; + /** + * @var \Magento\Framework\Data\Form\FormKey + */ + private $formKey; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Catalog\Model\Session $catalogSession @@ -142,6 +148,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template * @param \Magento\Framework\Data\Helper\PostHelper $postDataHelper * @param ToolbarMemorizer|null $toolbarMemorizer * @param \Magento\Framework\App\Http\Context|null $httpContext + * @param \Magento\Framework\Data\Form\FormKey|null $formKey * @param array $data */ public function __construct( @@ -154,6 +161,7 @@ public function __construct( \Magento\Framework\Data\Helper\PostHelper $postDataHelper, ToolbarMemorizer $toolbarMemorizer = null, \Magento\Framework\App\Http\Context $httpContext = null, + \Magento\Framework\Data\Form\FormKey $formKey = null, array $data = [] ) { $this->_catalogSession = $catalogSession; @@ -162,12 +170,15 @@ public function __construct( $this->urlEncoder = $urlEncoder; $this->_productListHelper = $productListHelper; $this->_postDataHelper = $postDataHelper; - $this->toolbarMemorizer = $toolbarMemorizer ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + $this->toolbarMemorizer = $toolbarMemorizer ?: ObjectManager::getInstance()->get( ToolbarMemorizer::class ); - $this->httpContext = $httpContext ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + $this->httpContext = $httpContext ?: ObjectManager::getInstance()->get( \Magento\Framework\App\Http\Context::class ); + $this->formKey = $formKey ?: ObjectManager::getInstance()->get( + \Magento\Framework\Data\Form\FormKey::class + ); parent::__construct($context, $data); } @@ -711,19 +722,19 @@ public function getPagerHtml() public function getWidgetOptionsJson(array $customOptions = []) { $defaultMode = $this->_productListHelper->getDefaultViewMode($this->getModes()); - $defaultDirection = $this->_direction ?: ProductList::DEFAULT_SORT_DIRECTION; - $isMemorizingAllowed = $this->toolbarMemorizer->isMemorizingAllowed(); $options = [ 'mode' => ToolbarModel::MODE_PARAM_NAME, 'direction' => ToolbarModel::DIRECTION_PARAM_NAME, 'order' => ToolbarModel::ORDER_PARAM_NAME, 'limit' => ToolbarModel::LIMIT_PARAM_NAME, - 'modeDefault' => $isMemorizingAllowed ? false : $defaultMode, - 'directionDefault' => $isMemorizingAllowed ? false : $defaultDirection, - 'orderDefault' => $isMemorizingAllowed ? false : $this->getOrderField(), - 'limitDefault' => $isMemorizingAllowed ? false : - $this->_productListHelper->getDefaultLimitPerPageValue($defaultMode), + 'modeDefault' => $defaultMode, + 'directionDefault' => $this->_direction ?: ProductList::DEFAULT_SORT_DIRECTION, + 'orderDefault' => $this->getOrderField(), + 'limitDefault' => $this->_productListHelper->getDefaultLimitPerPageValue($defaultMode), 'url' => $this->getPagerUrl(), + 'formKey' => $this->formKey->getFormKey(), + 'pageParam' => ToolbarModel::PAGE_PARM_NAME, + 'post' => $this->toolbarMemorizer->isMemorizingAllowed() ? true : false ]; $options = array_replace_recursive($options, $customOptions); return json_encode(['productListToolbarForm' => $options]); diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js index 88be03a04e71a..aee8933194ab1 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js @@ -27,7 +27,10 @@ define([ directionDefault: 'asc', orderDefault: 'position', limitDefault: '9', - url: '' + url: '', + formKey: '', + pageParam: 'p', + post: false }, /** @inheritdoc */ @@ -99,12 +102,38 @@ define([ } paramData[paramName] = paramValue; - if (paramValue == defaultValue) { //eslint-disable-line eqeqeq - delete paramData[paramName]; - } - paramData = $.param(paramData); + if (this.options.post) { + var form = document.createElement('form'); + + var params = [this.options.mode, this.options.direction, this.options.order, this.options.limit]; + for (var key in paramData) { + if (params.indexOf(key) !== -1) { + var input = document.createElement('input'); + input.name = key; + input.value = paramData[key]; + form.appendChild(input); + delete paramData[key]; + } + } + var formKey = document.createElement('input'); + formKey.name = 'form_key'; + formKey.value = this.options.formKey; + form.appendChild(formKey); + + paramData = $.param(paramData); + baseUrl = baseUrl + (paramData.length ? '?' + paramData : ''); - location.href = baseUrl + (paramData.length ? '?' + paramData : ''); + form.action = baseUrl; + form.method = 'POST'; + document.body.appendChild(form); + form.submit(); + } else { + if (paramValue == defaultValue) { //eslint-disable-line eqeqeq + delete paramData[paramName]; + } + paramData = $.param(paramData); + location.href = baseUrl + (paramData.length ? '?' + paramData : ''); + } } }); From b2b611107a9988f69ea95db1b09dbfbfa3770c12 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Wed, 12 Sep 2018 19:20:52 +0300 Subject: [PATCH 015/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Fix tests. --- .../Block/Product/ProductList/Toolbar.php | 1 - .../Block/Product/ProductList/ToolbarTest.php | 25 ++++++++++++++----- .../frontend/web/js/product/list/toolbar.js | 1 - 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index 9f1514aab9d6f..3e3f86cd7de01 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -733,7 +733,6 @@ public function getWidgetOptionsJson(array $customOptions = []) 'limitDefault' => $this->_productListHelper->getDefaultLimitPerPageValue($defaultMode), 'url' => $this->getPagerUrl(), 'formKey' => $this->formKey->getFormKey(), - 'pageParam' => ToolbarModel::PAGE_PARM_NAME, 'post' => $this->toolbarMemorizer->isMemorizingAllowed() ? true : false ]; $options = array_replace_recursive($options, $customOptions); diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php index ac963326dbfa1..8282ffa409f9c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php @@ -18,6 +18,11 @@ class ToolbarTest extends \PHPUnit\Framework\TestCase */ protected $model; + /** + * @var \Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer | \PHPUnit_Framework_MockObject_MockObject + */ + private $memorizer; + /** * @var \Magento\Framework\Url | \PHPUnit_Framework_MockObject_MockObject */ @@ -62,6 +67,13 @@ protected function setUp() 'getLimit', 'getCurrentPage' ]); + $this->memorizer = $this->createPartialMock(\Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer::class, [ + 'getDirection', + 'getOrder', + 'getMode', + 'getLimit', + 'isMemorizingAllowed' + ]); $this->layout = $this->createPartialMock(\Magento\Framework\View\Layout::class, ['getChildName', 'getBlock']); $this->pagerBlock = $this->createPartialMock(\Magento\Theme\Block\Html\Pager::class, [ 'setUseContainer', @@ -116,6 +128,7 @@ protected function setUp() 'context' => $context, 'catalogConfig' => $this->catalogConfig, 'toolbarModel' => $this->model, + 'toolbarMemorizer' => $this->memorizer, 'urlEncoder' => $this->urlEncoder, 'productListHelper' => $this->productListHelper ] @@ -155,7 +168,7 @@ public function testGetPagerEncodedUrl() public function testGetCurrentOrder() { $order = 'price'; - $this->model->expects($this->once()) + $this->memorizer->expects($this->once()) ->method('getOrder') ->will($this->returnValue($order)); $this->catalogConfig->expects($this->once()) @@ -169,7 +182,7 @@ public function testGetCurrentDirection() { $direction = 'desc'; - $this->model->expects($this->once()) + $this->memorizer->expects($this->once()) ->method('getDirection') ->will($this->returnValue($direction)); @@ -183,7 +196,7 @@ public function testGetCurrentMode() $this->productListHelper->expects($this->once()) ->method('getAvailableViewMode') ->will($this->returnValue(['list' => 'List'])); - $this->model->expects($this->once()) + $this->memorizer->expects($this->once()) ->method('getMode') ->will($this->returnValue($mode)); @@ -232,11 +245,11 @@ public function testGetLimit() $mode = 'list'; $limit = 10; - $this->model->expects($this->once()) + $this->memorizer->expects($this->once()) ->method('getMode') ->will($this->returnValue($mode)); - $this->model->expects($this->once()) + $this->memorizer->expects($this->once()) ->method('getLimit') ->will($this->returnValue($limit)); $this->productListHelper->expects($this->once()) @@ -266,7 +279,7 @@ public function testGetPagerHtml() $this->productListHelper->expects($this->exactly(2)) ->method('getAvailableLimit') ->will($this->returnValue([10 => 10, 20 => 20])); - $this->model->expects($this->once()) + $this->memorizer->expects($this->once()) ->method('getLimit') ->will($this->returnValue($limit)); $this->pagerBlock->expects($this->once()) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js index aee8933194ab1..7c47f5bd04d5a 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js @@ -29,7 +29,6 @@ define([ limitDefault: '9', url: '', formKey: '', - pageParam: 'p', post: false }, From 00cb15e4f59e6a71dd51b08b3553633eebfebeaf Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Fri, 14 Sep 2018 09:14:40 +0300 Subject: [PATCH 016/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Fix bug when memorizing getting disabled after we have params in session already. --- .../Product/ProductList/ToolbarMemorizer.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php index 4e65a4f9853e7..9c1a781d594f7 100644 --- a/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php +++ b/app/code/Magento/Catalog/Model/Product/ProductList/ToolbarMemorizer.php @@ -58,6 +58,11 @@ class ToolbarMemorizer */ private $limit; + /** + * @var bool + */ + private $isMemorizingAllowed; + /** * @param Toolbar $toolbarModel * @param CatalogSession $catalogSession @@ -82,7 +87,7 @@ public function getOrder() { if ($this->order === null) { $this->order = $this->toolbarModel->getOrder() ?? - $this->catalogSession->getData(Toolbar::ORDER_PARAM_NAME); + ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::ORDER_PARAM_NAME) : null); } return $this->order; } @@ -96,7 +101,7 @@ public function getDirection() { if ($this->direction === null) { $this->direction = $this->toolbarModel->getDirection() ?? - $this->catalogSession->getData(Toolbar::DIRECTION_PARAM_NAME); + ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::DIRECTION_PARAM_NAME) : null); } return $this->direction; } @@ -110,7 +115,7 @@ public function getMode() { if ($this->mode === null) { $this->mode = $this->toolbarModel->getMode() ?? - $this->catalogSession->getData(Toolbar::MODE_PARAM_NAME); + ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::MODE_PARAM_NAME) : null); } return $this->mode; } @@ -124,7 +129,7 @@ public function getLimit() { if ($this->limit === null) { $this->limit = $this->toolbarModel->getLimit() ?? - $this->catalogSession->getData(Toolbar::LIMIT_PARAM_NAME); + ($this->isMemorizingAllowed() ? $this->catalogSession->getData(Toolbar::LIMIT_PARAM_NAME) : null); } return $this->limit; } @@ -151,7 +156,10 @@ public function memorizeParams() */ public function isMemorizingAllowed() { - return $this->scopeConfig->isSetFlag(self::XML_PATH_CATALOG_REMEMBER_PAGINATION); + if ($this->isMemorizingAllowed === null) { + $this->isMemorizingAllowed = $this->scopeConfig->isSetFlag(self::XML_PATH_CATALOG_REMEMBER_PAGINATION); + } + return $this->isMemorizingAllowed; } /** From 0400e32d120bc8dc5b94818f76209b789ac6623d Mon Sep 17 00:00:00 2001 From: eugene-shab <dev.eugene.shab@gmail.com> Date: Fri, 14 Sep 2018 10:53:07 +0300 Subject: [PATCH 017/309] Added ProductImage reslover to images. --- .../Model/Resolver/Product/ProductImage.php | 97 +++++++++++++++++++ .../CatalogGraphQl/etc/schema.graphqls | 13 ++- 2 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php new file mode 100644 index 0000000000000..2e6133f1e32cd --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Product; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Helper\ImageFactory as CatalogImageHelperFactory; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Returns product image. + */ +class ProductImage implements ResolverInterface +{ + /** + * @var CatalogImageHelperFactory + */ + private $catalogImageHelperFactory; + + /** + * @var ValueFactory + */ + private $valueFactory; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @param ValueFactory $valueFactory + * @param CatalogImageHelperFactory $catalogImageHelperFactory, + * @param StoreManagerInterface $storeManager + */ + public function __construct( + ValueFactory $valueFactory, + CatalogImageHelperFactory $catalogImageHelperFactory, + StoreManagerInterface $storeManager + ) + { + $this->valueFactory = $valueFactory; + $this->catalogImageHelperFactory = $catalogImageHelperFactory; + $this->storeManager = $storeManager; + } + + /** + * Get product's image by type. + * + * {@inheritdoc} + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ): Value + { + if (!isset($value['model'])) { + $result = function () { + return null; + }; + return $this->valueFactory->create($result); + } + /** @var Product $product */ + $product = $value['model']; + $imageType = $field->getName(); + + $catalogImageHelper = $this->catalogImageHelperFactory->create(); + + $imageUrl = $catalogImageHelper->init( + $product, + 'product_' . $imageType, + ['type' => $imageType] + )->getUrl(); + + $imageData = [ + 'url' => $imageUrl, + 'path' => $product->getData($imageType) + ]; + + $result = function () use ($imageData) { + return $imageData; + }; + + return $this->valueFactory->create($result); + } +} \ No newline at end of file diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 9235ec271a3c2..683dfb8de1f26 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -257,9 +257,9 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ meta_title: String @doc(description: "A string that is displayed in the title bar and tab of the browser and in search results lists") meta_keyword: String @doc(description: "A comma-separated list of keywords that are visible only to search engines") meta_description: String @doc(description: "A brief overview of the product for search results listings, maximum 255 characters") - image: String @doc(description: "The relative path to the main image on the product page") - small_image: String @doc(description: "The relative path to the small image, which is used on catalog pages") - thumbnail: String @doc(description: "The relative path to the product's thumbnail image") + image: ProductImage @doc(description: "The relative path to the main image on the product page") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage") + small_image: ProductImage @doc(description: "The relative path to the small image, which is used on catalog pages") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage") + thumbnail: ProductImage @doc(description: "The relative path to the product's thumbnail image") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage") new_from_date: String @doc(description: "The beginning date for new product listings, and determines if the product is featured as a new product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\NewFromTo") new_to_date: String @doc(description: "The end date for new product listings") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\NewFromTo") tier_price: Float @doc(description: "The price when tier pricing is in effect and the items purchased threshold has been reached") @@ -352,6 +352,11 @@ type CustomizableFileValue @doc(description: "CustomizableFileValue defines the image_size_y: Int @doc(description: "The maximum height of an image") } +type ProductImage @doc(description: "Product image information. Contains image relative path and URL") { + url: String + path: String +} + interface CustomizableOptionInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\CustomizableOptionTypeResolver") @doc(description: "The CustomizableOptionInterface contains basic information about a customizable option. It can be implemented by several types of configurable options.") { title: String @doc(description: "The display name for this option") required: Boolean @doc(description: "Indicates whether the option is required") @@ -548,6 +553,6 @@ type SortField { } type SortFields @doc(description: "SortFields contains a default value for sort fields and all available sort fields") { - default: String @doc(description: "Default value of sort fields") + default: String @doc(description: "Default value of sort fields") options: [SortField] @doc(description: "Available sort fields") } From 277c81fe005884724cfe1298f4f7cdeda3d5901a Mon Sep 17 00:00:00 2001 From: eugene-shab <dev.eugene.shab@gmail.com> Date: Fri, 14 Sep 2018 11:23:03 +0300 Subject: [PATCH 018/309] Updates --- .../Model/Resolver/Product/ProductImage.php | 29 ++++--------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php index 2e6133f1e32cd..811bf08fc1c18 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php @@ -10,14 +10,13 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Helper\ImageFactory as CatalogImageHelperFactory; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\Resolver\Value; -use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Store\Model\StoreManagerInterface; /** - * Returns product image. + * Return product image paths by image type. */ class ProductImage implements ResolverInterface { @@ -26,28 +25,20 @@ class ProductImage implements ResolverInterface */ private $catalogImageHelperFactory; - /** - * @var ValueFactory - */ - private $valueFactory; - /** * @var StoreManagerInterface */ private $storeManager; /** - * @param ValueFactory $valueFactory * @param CatalogImageHelperFactory $catalogImageHelperFactory, * @param StoreManagerInterface $storeManager */ public function __construct( - ValueFactory $valueFactory, CatalogImageHelperFactory $catalogImageHelperFactory, StoreManagerInterface $storeManager ) { - $this->valueFactory = $valueFactory; $this->catalogImageHelperFactory = $catalogImageHelperFactory; $this->storeManager = $storeManager; } @@ -63,13 +54,9 @@ public function resolve( ResolveInfo $info, array $value = null, array $args = null - ): Value - { + ) { if (!isset($value['model'])) { - $result = function () { - return null; - }; - return $this->valueFactory->create($result); + throw new GraphQlInputException(__('"model" value should be specified')); } /** @var Product $product */ $product = $value['model']; @@ -88,10 +75,6 @@ public function resolve( 'path' => $product->getData($imageType) ]; - $result = function () use ($imageData) { - return $imageData; - }; - - return $this->valueFactory->create($result); + return $imageData; } -} \ No newline at end of file +} From 819b1178eca59bb530b6cca9fd7a1a7cbde28f4f Mon Sep 17 00:00:00 2001 From: eugene-shab <dev.eugene.shab@gmail.com> Date: Fri, 14 Sep 2018 14:44:34 +0300 Subject: [PATCH 019/309] Update code style. --- .../Model/Resolver/Product/ProductImage.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php index 811bf08fc1c18..3cf961c0f4225 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php @@ -31,14 +31,13 @@ class ProductImage implements ResolverInterface private $storeManager; /** - * @param CatalogImageHelperFactory $catalogImageHelperFactory, - * @param StoreManagerInterface $storeManager + * @param CatalogImageHelperFactory $catalogImageHelperFactory + * @param StoreManagerInterface $storeManager */ public function __construct( CatalogImageHelperFactory $catalogImageHelperFactory, StoreManagerInterface $storeManager - ) - { + ) { $this->catalogImageHelperFactory = $catalogImageHelperFactory; $this->storeManager = $storeManager; } @@ -46,7 +45,7 @@ public function __construct( /** * Get product's image by type. * - * {@inheritdoc} + * @inheritdoc */ public function resolve( Field $field, @@ -62,6 +61,7 @@ public function resolve( $product = $value['model']; $imageType = $field->getName(); + /** @var \Magento\Catalog\Helper\Image $catalogImageHelper */ $catalogImageHelper = $this->catalogImageHelperFactory->create(); $imageUrl = $catalogImageHelper->init( From 6c168dd9ced1f3eb141b01dc8091319b474811a6 Mon Sep 17 00:00:00 2001 From: Bartosz Kubicki <bartosz.kubicki@lizardmedia.pl> Date: Tue, 11 Sep 2018 19:38:22 +0200 Subject: [PATCH 020/309] Adding trimming sku value function to sku backend model. Adding trimming sku value function to sku backend model. Adding frontend validation for sku field. --- .../Model/Product/Attribute/Backend/Sku.php | 14 ++++++++++++++ .../DataProvider/Product/Form/Modifier/General.php | 3 ++- app/code/Magento/Ui/i18n/en_US.csv | 1 + .../Ui/view/base/web/js/lib/validation/rules.js | 6 ++++++ lib/web/i18n/en_US.csv | 1 + lib/web/mage/validation.js | 6 ++++++ 6 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php index a652d0ef90213..2ea3b9aaf10a8 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php @@ -97,6 +97,7 @@ protected function _generateUniqueSku($object) public function beforeSave($object) { $this->_generateUniqueSku($object); + $this->trimValue($object); return parent::beforeSave($object); } @@ -127,4 +128,17 @@ protected function _getLastSimilarAttributeValueIncrement($attribute, $object) $data = $connection->fetchOne($select, $bind); return abs((int)str_replace($value, '', $data)); } + + /** + * @param Product $object + * @return void + */ + private function trimValue($object) + { + $attrCode = $this->getAttribute()->getAttributeCode(); + $value = $object->getData($attrCode); + if ($value) { + $object->setData($attrCode, trim($value)); + } + } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index 98de8ea347671..e0b0d066c1c1d 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -361,7 +361,8 @@ protected function customizeNameListeners(array $meta) $skuPath . static::META_CONFIG_PATH, $meta, [ - 'autoImportIfEmpty' => true + 'autoImportIfEmpty' => true, + 'validation' => ['no-marginal-whitespace' => true] ] ); diff --git a/app/code/Magento/Ui/i18n/en_US.csv b/app/code/Magento/Ui/i18n/en_US.csv index 106526f66e708..2731d4be182a4 100644 --- a/app/code/Magento/Ui/i18n/en_US.csv +++ b/app/code/Magento/Ui/i18n/en_US.csv @@ -58,6 +58,7 @@ Keyword,Keyword "Letters, numbers, spaces or underscores only please","Letters, numbers, spaces or underscores only please" "Letters only please","Letters only please" "No white space please","No white space please" +"No marginal white space please","No marginal white space please" "Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx","Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx" "A positive or negative non-decimal number please","A positive or negative non-decimal number please" "The specified vehicle identification number (VIN) is invalid.","The specified vehicle identification number (VIN) is invalid." diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 1103f3e54a4cf..ced16e01b2bc4 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -116,6 +116,12 @@ define([ }, $.mage.__('No white space please') ], + 'no-marginal-whitespace': [ + function (value) { + return !/^\s+|\s+$/i.test(value); + }, + $.mage.__('No marginal white space please') + ], 'zip-range': [ function (value) { return /^90[2-5]-\d{2}-\d{4}$/.test(value); diff --git a/lib/web/i18n/en_US.csv b/lib/web/i18n/en_US.csv index 5c63a191420a4..bca42473e2f0b 100644 --- a/lib/web/i18n/en_US.csv +++ b/lib/web/i18n/en_US.csv @@ -27,6 +27,7 @@ Submit,Submit "Letters, numbers, spaces or underscores only please","Letters, numbers, spaces or underscores only please" "Letters only please","Letters only please" "No white space please","No white space please" +"No marginal white space please","No marginal white space please" "Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx","Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx" "A positive or negative non-decimal number please","A positive or negative non-decimal number please" "The specified vehicle identification number (VIN) is invalid.","The specified vehicle identification number (VIN) is invalid." diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index d08819ebe94aa..dcfe9c41d311f 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -253,6 +253,12 @@ }, $.mage.__('No white space please') ], + 'no-marginal-whitespace': [ + function (value, element) { + return this.optional(element) || !/^\s+|\s+$/i.test(value); + }, + $.mage.__('No marginal white space please') + ], 'zip-range': [ function (value, element) { return this.optional(element) || /^90[2-5]-\d{2}-\d{4}$/.test(value); From ae42612217947d75aa09b300a34e23487b8954c3 Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Sun, 16 Sep 2018 13:58:19 -0300 Subject: [PATCH 021/309] Fix date format for different locales --- .../Magento/Framework/Stdlib/DateTime/Timezone.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 3c2f20fcc186f..242c551c984f9 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -309,11 +309,23 @@ public function formatDateTime( */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') { + $formatter = new \IntlDateFormatter( + $this->_localeResolver->getLocale(), + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::MEDIUM, + $this->getConfigTimezone(), + null, + null + ); + $unixTime = $formatter->parse($date); + $dateTime = new DateTime($this); + if (!($date instanceof \DateTimeInterface)) { if ($date instanceof \DateTimeImmutable) { $date = new \DateTime($date->format('Y-m-d H:i:s'), new \DateTimeZone($this->getConfigTimezone())); } else { - $date = new \DateTime($date, new \DateTimeZone($this->getConfigTimezone())); + $dateUniversal = $dateTime->gmtDate(null, $unixTime); + $date = new \DateTime($dateUniversal, new \DateTimeZone($this->getConfigTimezone())); } } else { if ($date->getTimezone()->getName() !== $this->getConfigTimezone()) { From c4fa85bb59e9006f12fa79854da78af4e3fd601e Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Sun, 16 Sep 2018 14:49:06 -0300 Subject: [PATCH 022/309] Move formatter into string conversion block --- app/code/Magento/Newsletter/Model/Queue.php | 3 ++- .../Framework/Stdlib/DateTime/Timezone.php | 22 +++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/Queue.php b/app/code/Magento/Newsletter/Model/Queue.php index efb68fd4243d1..83c80e1e5afce 100644 --- a/app/code/Magento/Newsletter/Model/Queue.php +++ b/app/code/Magento/Newsletter/Model/Queue.php @@ -196,7 +196,8 @@ public function setQueueStartAtByString($startAt) if ($startAt === null || $startAt == '') { $this->setQueueStartAt(null); } else { - $this->setQueueStartAt($this->timezone->convertConfigTimeToUtc($startAt)); + $startAt = $this->timezone->convertConfigTimeToUtc($startAt, null); + $this->setQueueStartAt($startAt); } return $this; } diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 242c551c984f9..54e6e9e1d3caf 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -309,21 +309,21 @@ public function formatDateTime( */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') { - $formatter = new \IntlDateFormatter( - $this->_localeResolver->getLocale(), - \IntlDateFormatter::MEDIUM, - \IntlDateFormatter::MEDIUM, - $this->getConfigTimezone(), - null, - null - ); - $unixTime = $formatter->parse($date); - $dateTime = new DateTime($this); - if (!($date instanceof \DateTimeInterface)) { if ($date instanceof \DateTimeImmutable) { $date = new \DateTime($date->format('Y-m-d H:i:s'), new \DateTimeZone($this->getConfigTimezone())); } else { + $formatter = new \IntlDateFormatter( + $this->_localeResolver->getLocale(), + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::MEDIUM, + $this->getConfigTimezone(), + null, + $format + ); + $unixTime = $formatter->parse($date); + $dateTime = new DateTime($this); + $dateUniversal = $dateTime->gmtDate(null, $unixTime); $date = new \DateTime($dateUniversal, new \DateTimeZone($this->getConfigTimezone())); } From 6bda779e40699ae1e4b16920374cfd398dea28a8 Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Sun, 16 Sep 2018 15:21:20 -0300 Subject: [PATCH 023/309] Convert internationalized date into DateTime object --- app/code/Magento/Newsletter/Model/Queue.php | 2 +- .../Framework/Stdlib/DateTime/Timezone.php | 20 ++++++++++++++++++- .../Stdlib/DateTime/TimezoneInterface.php | 9 +++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/Queue.php b/app/code/Magento/Newsletter/Model/Queue.php index 83c80e1e5afce..67f8cadbfaed4 100644 --- a/app/code/Magento/Newsletter/Model/Queue.php +++ b/app/code/Magento/Newsletter/Model/Queue.php @@ -196,7 +196,7 @@ public function setQueueStartAtByString($startAt) if ($startAt === null || $startAt == '') { $this->setQueueStartAt(null); } else { - $startAt = $this->timezone->convertConfigTimeToUtc($startAt, null); + $startAt = $this->timezone->convertConfigTimeToUtcWithPattern($startAt, 'Y-m-d H:i:s', null); $this->setQueueStartAt($startAt); } return $this; diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 54e6e9e1d3caf..a083b7c6fa986 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -306,8 +306,25 @@ public function formatDateTime( * @param string $format * @throws LocalizedException * @return string + * @deprecated */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') + { + return $this->convertConfigTimeToUtcWithPattern($date, $format); + } + + /** + * Convert date from config timezone to Utc. + * If pass \DateTime object as argument be sure that timezone is the same with config timezone + * + * @param string|\DateTimeInterface $date + * @param string $format + * @param string $pattern + * @throws LocalizedException + * @return string + * @deprecated + */ + public function convertConfigTimeToUtcWithPattern($date, $format = 'Y-m-d H:i:s', $pattern = 'Y-M-dd HH:mm:ss') { if (!($date instanceof \DateTimeInterface)) { if ($date instanceof \DateTimeImmutable) { @@ -319,7 +336,7 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') \IntlDateFormatter::MEDIUM, $this->getConfigTimezone(), null, - $format + $pattern ); $unixTime = $formatter->parse($date); $dateTime = new DateTime($this); @@ -343,6 +360,7 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') return $date->format($format); } + /** * Retrieve date with time * diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php index a8b3fb1a81ffe..5a4b15d6cca0c 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php @@ -143,6 +143,15 @@ public function formatDateTime( * @param string $format * @return string * @since 100.1.0 + * @deprecated */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s'); + + /** + * @param $date + * @param string $format + * @param string $pattern + * @return mixed + */ + public function convertConfigTimeToUtcWithPattern($date, $format = 'Y-m-d H:i:s', $pattern = 'Y-m-d H:i:s'); } From a4f80a9d93c455328a08718b1c7319fde0dfc32f Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Sun, 16 Sep 2018 15:50:33 -0300 Subject: [PATCH 024/309] Force pattern if locale not found --- .../Framework/Stdlib/DateTime/Timezone.php | 8 +++-- .../Test/Unit/DateTime/TimezoneTest.php | 29 +++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index a083b7c6fa986..59b299d176bd1 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -324,14 +324,18 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') * @return string * @deprecated */ - public function convertConfigTimeToUtcWithPattern($date, $format = 'Y-m-d H:i:s', $pattern = 'Y-M-dd HH:mm:ss') + public function convertConfigTimeToUtcWithPattern($date, $format = 'Y-m-d H:i:s', $pattern = null) { if (!($date instanceof \DateTimeInterface)) { if ($date instanceof \DateTimeImmutable) { $date = new \DateTime($date->format('Y-m-d H:i:s'), new \DateTimeZone($this->getConfigTimezone())); } else { + $locale = $this->_localeResolver->getLocale(); + if ($locale === null) { + $pattern = 'Y-M-dd HH:mm:ss'; + } $formatter = new \IntlDateFormatter( - $this->_localeResolver->getLocale(), + $locale, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::MEDIUM, $this->getConfigTimezone(), diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php index d57525590b9e8..ea761db902674 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php @@ -124,11 +124,15 @@ public function dateIncludeTimeDataProvider() * @param string $expectedResult * @dataProvider getConvertConfigTimeToUtcFixtures */ - public function testConvertConfigTimeToUtc($date, $configuredTimezone, $expectedResult) + public function testConvertConfigTimeToUtc($date, $configuredTimezone, $configuredLocale, $expectedResult) { + $this->localeResolver + ->method('getLocale') + ->willReturn($configuredLocale); + $this->scopeConfigWillReturnConfiguredTimezone($configuredTimezone); - $this->assertEquals($expectedResult, $this->getTimezone()->convertConfigTimeToUtc($date)); + $this->assertEquals($expectedResult, $this->getTimezone()->convertConfigTimeToUtcWithPattern($date)); } /** @@ -141,16 +145,37 @@ public function getConvertConfigTimeToUtcFixtures() 'string' => [ '2016-10-10 10:00:00', 'UTC', + null, '2016-10-10 10:00:00' ], + 'string-en_US' => [ + 'Sep 29, 2018, 6:07:38 PM', + 'UTC', + 'en_US', + '2018-09-29 18:07:38' + ], + 'string-pt_BR' => [ + '29 de set de 2018 18:07:38', + 'UTC', + 'pt_BR', + '2018-09-29 18:07:38' + ], + 'string-tr_TR' => [ + '29 Eyl 2018 18:07:38', + 'UTC', + 'tr_TR', + '2018-09-29 18:07:38' + ], 'datetime' => [ new \DateTime('2016-10-10 10:00:00', new \DateTimeZone('UTC')), 'UTC', + null, '2016-10-10 10:00:00' ], 'datetimeimmutable' => [ new \DateTimeImmutable('2016-10-10 10:00:00', new \DateTimeZone('UTC')), 'UTC', + null, '2016-10-10 10:00:00' ] ]; From a44eb5dd5fd2a95f79b617a480b9221704495887 Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Sun, 16 Sep 2018 17:14:58 -0300 Subject: [PATCH 025/309] #17744 Adding logic to get default billing address --- .../web/js/model/checkout-data-resolver.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 73f4df567903c..6586f647e2404 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -219,15 +219,26 @@ define([ */ applyBillingAddress: function () { var shippingAddress; + var isBillingAddressInitialized; if (quote.billingAddress()) { selectBillingAddress(quote.billingAddress()); return; } - shippingAddress = quote.shippingAddress(); + shippingAddress = quote.billingAddress(); + if(quote.isVirtual()) { + isBillingAddressInitialized = addressList.some(function (addrs) { + if (addrs.isDefaultBilling()) { + selectBillingAddress(addrs); + return true; + } + return false; + }); + } - if (shippingAddress && + if (!isBillingAddressInitialized && + shippingAddress && shippingAddress.canUseForBilling() && (shippingAddress.isDefaultShipping() || !quote.isVirtual()) ) { From 8aedb958f43bf714c934b677c90d4b74b9d1922b Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Sun, 16 Sep 2018 20:24:39 +0000 Subject: [PATCH 026/309] GraphQl-44: prototype of format strategy Signed-off-by: vitaliyboyko <v.boyko@atwix.com> --- ...ibute.php => ProductTextareaAttribute.php} | 25 ++++++---- .../FormatFactory.php | 43 +++++++++++++++++ .../FormatInterface.php | 23 +++++++++ .../Product/ProductTextareaAttribute/Html.php | 47 +++++++++++++++++++ .../CatalogGraphQl/etc/schema.graphqls | 41 +++++++++------- 5 files changed, 152 insertions(+), 27 deletions(-) rename app/code/Magento/CatalogGraphQl/Model/Resolver/Product/{ProductHtmlAttribute.php => ProductTextareaAttribute.php} (67%) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/FormatFactory.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/FormatInterface.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/Html.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductHtmlAttribute.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute.php similarity index 67% rename from app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductHtmlAttribute.php rename to app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute.php index 18d15088e6656..f04609f91c86d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductHtmlAttribute.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute.php @@ -8,38 +8,40 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Catalog\Model\Product; +use Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute\FormatFactory; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\Resolver\Value; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Catalog\Helper\Output as OutputHelper; /** * Resolve rendered content for attributes where HTML content is allowed */ -class ProductHtmlAttribute implements ResolverInterface +class ProductTextareaAttribute implements ResolverInterface { + const DEFAULT_CONTENT_FORMAT_IDENTIFIER = 'html'; + /** * @var ValueFactory */ private $valueFactory; /** - * @var OutputHelper + * @var FormatFactory */ - private $outputHelper; + private $formatFactory; /** * @param ValueFactory $valueFactory - * @param OutputHelper $outputHelper + * @param FormatFactory $formatFactory */ public function __construct( ValueFactory $valueFactory, - OutputHelper $outputHelper + FormatFactory $formatFactory ) { $this->valueFactory = $valueFactory; - $this->outputHelper = $outputHelper; + $this->formatFactory = $formatFactory; } /** @@ -62,9 +64,12 @@ public function resolve( /* @var $product Product */ $product = $value['model']; $fieldName = $field->getName(); - $renderedValue = $this->outputHelper->productAttribute($product, $product->getData($fieldName), $fieldName); - $result = function () use ($renderedValue) { - return $renderedValue; + $formatIdentifier = $args['format'] ?? self::DEFAULT_CONTENT_FORMAT_IDENTIFIER; + $format = $this->formatFactory->create($formatIdentifier); + $attribute = ['content' => $format->getContent($product, $fieldName)]; + + $result = function () use ($attribute) { + return $attribute; }; return $this->valueFactory->create($result); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/FormatFactory.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/FormatFactory.php new file mode 100644 index 0000000000000..2aab2d5816083 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/FormatFactory.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute; + +use Magento\Framework\ObjectManagerInterface; + +class FormatFactory +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @param ObjectManagerInterface $objectManager + */ + public function __construct(ObjectManagerInterface $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * @param string $formatIdentifier + * @param array $data + * @return FormatInterface + */ + public function create(string $formatIdentifier, $data = []) : FormatInterface + { + $formatClassName = 'Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute\\' . ucfirst($formatIdentifier); + $formatInstance = $this->objectManager->create($formatClassName, $data); + if (false == $formatInstance instanceof FormatInterface) { + throw new \InvalidArgumentException( + $formatInstance . ' is not instance of \Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute\FormatInterface' + ); + } + return $formatInstance; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/FormatInterface.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/FormatInterface.php new file mode 100644 index 0000000000000..fae685b75c060 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/FormatInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute; + +use Magento\Catalog\Model\Product as ModelProduct; + +interface FormatInterface +{ + /** + * @param ModelProduct $product + * @param string $fieldName + * @return string + */ + public function getContent( + ModelProduct $product, + string $fieldName + ): string; +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/Html.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/Html.php new file mode 100644 index 0000000000000..8201c40427dc9 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextareaAttribute/Html.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute; + +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Catalog\Helper\Output as OutputHelper; +use Magento\Catalog\Model\Product as ModelProduct; + +class Html implements FormatInterface +{ + /** + * @var ValueFactory + */ + private $valueFactory; + + /** + * @var OutputHelper + */ + private $outputHelper; + + /** + * @param ValueFactory $valueFactory + * @param OutputHelper $outputHelper + */ + public function __construct( + ValueFactory $valueFactory, + OutputHelper $outputHelper + ) { + $this->valueFactory = $valueFactory; + $this->outputHelper = $outputHelper; + } + + /** + * @inheritdoc + */ + public function getContent( + ModelProduct $product, + string $fieldName + ): string { + return $this->outputHelper->productAttribute($product, $product->getData($fieldName), $fieldName); + } +} \ No newline at end of file diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index f63c1a96762c2..82bc262ecf3c3 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -3,16 +3,16 @@ type Query { products ( - search: String @doc(description: "Performs a full-text search using the specified key words."), - filter: ProductFilterInput @doc(description: "Identifies which product attributes to search for and return."), - pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), - currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), - sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") - ): Products - @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes") + search: String @doc(description: "Performs a full-text search using the specified key words."), + filter: ProductFilterInput @doc(description: "Identifies which product attributes to search for and return."), + pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), + currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), + sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") +): Products + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes") category ( - id: Int @doc(description: "Id of the category") - ): CategoryTree + id: Int @doc(description: "Id of the category") +): CategoryTree @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") } @@ -248,8 +248,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ id: Int @doc(description: "The ID number assigned to the product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\EntityIdToId") name: String @doc(description: "The product name. Customers use this name to identify the product.") sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") - description: String @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductHtmlAttribute") - short_description: String @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductHtmlAttribute") + description: ProductTextareaAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") + short_description: ProductTextareaAttribute @doc(description: "A short description of the product. Its use depends on the theme.") special_price: Float @doc(description: "The discounted price of the product") special_from_date: String @doc(description: "The beginning date that a product has a special price") special_to_date: String @doc(description: "The end date that a product has a special price") @@ -377,10 +377,10 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model product_count: Int @doc(description: "The number of products in the category") default_sort_by: String @doc(description: "The attribute to use for sorting") products( - pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), - currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), - sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") - ): CategoryProducts @doc(description: "The list of products assigned to the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products") + pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), + currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), + sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") +): CategoryProducts @doc(description: "The list of products assigned to the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products") breadcrumbs: [Breadcrumb] @doc(description: "Breadcrumbs, parent categories info") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Breadcrumbs") } @@ -408,8 +408,8 @@ type VirtualProduct implements ProductInterface, CustomizableProductInterface @d } type SimpleProduct implements ProductInterface, PhysicalProductInterface, CustomizableProductInterface @doc(description: "A simple product is tangible and are usually sold as single units or in fixed quantities") -{ -} + { + } type Products @doc(description: "The Products object is the top-level object returned in a product search") { items: [ProductInterface] @doc(description: "An array of products that match the specified search criteria") @@ -551,3 +551,10 @@ type SortFields @doc(description: "SortFields contains a default value for sort default: String @doc(description: "Default value of sort fields") options: [SortField] @doc(description: "Available sort fields") } + +type ProductTextareaAttribute { + content ( + format: String! @doc(description: "The format of content") +): String + @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextareaAttribute") +} From cde04fc296154d8ade7c390b3eb3def76dcc4c02 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Sun, 16 Sep 2018 20:38:50 +0000 Subject: [PATCH 027/309] GraphQl-45: pretified schema Signed-off-by: vitaliyboyko <v.boyko@atwix.com> --- .../CatalogGraphQl/etc/schema.graphqls | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 82bc262ecf3c3..a9c4a1b4bd8ae 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -3,16 +3,16 @@ type Query { products ( - search: String @doc(description: "Performs a full-text search using the specified key words."), - filter: ProductFilterInput @doc(description: "Identifies which product attributes to search for and return."), - pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), - currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), - sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") -): Products - @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes") + search: String @doc(description: "Performs a full-text search using the specified key words."), + filter: ProductFilterInput @doc(description: "Identifies which product attributes to search for and return."), + pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), + currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), + sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") + ): Products + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes") category ( - id: Int @doc(description: "Id of the category") -): CategoryTree + id: Int @doc(description: "Id of the category") + ): CategoryTree @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") } @@ -377,10 +377,10 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model product_count: Int @doc(description: "The number of products in the category") default_sort_by: String @doc(description: "The attribute to use for sorting") products( - pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), - currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), - sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") -): CategoryProducts @doc(description: "The list of products assigned to the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products") + pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), + currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), + sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") + ): CategoryProducts @doc(description: "The list of products assigned to the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products") breadcrumbs: [Breadcrumb] @doc(description: "Breadcrumbs, parent categories info") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Breadcrumbs") } @@ -408,8 +408,8 @@ type VirtualProduct implements ProductInterface, CustomizableProductInterface @d } type SimpleProduct implements ProductInterface, PhysicalProductInterface, CustomizableProductInterface @doc(description: "A simple product is tangible and are usually sold as single units or in fixed quantities") - { - } +{ +} type Products @doc(description: "The Products object is the top-level object returned in a product search") { items: [ProductInterface] @doc(description: "An array of products that match the specified search criteria") From 57fe7c19716fbc248fb7679063f2c4cd6281b7e6 Mon Sep 17 00:00:00 2001 From: "Hakobyan, Lusine" <Lusine_Hakobyan@epam.com> Date: Mon, 17 Sep 2018 17:24:41 +0400 Subject: [PATCH 028/309] MAGETWO-91704: Can't cancel/change payment method after chosen Store Credit - Add locator for automated test --- .../Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml index e4d329bc85057..8aaf626edab33 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml @@ -9,9 +9,10 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormPaymentSection"> + <element name="useCustomerBalance" type="checkbox" selector="#p_method_use_customerbalance"/> <element name="header" type="text" selector="#order-methods span.title"/> <element name="getShippingMethods" type="text" selector="#order-shipping_method a.action-default" timeout="30"/> <element name="flatRateOption" type="radio" selector="#s_method_flatrate_flatrate" timeout="30"/> <element name="shippingError" type="text" selector="#order[has_shipping]-error"/> </section> -</sections> \ No newline at end of file +</sections> From 0890139741aa73a06f81223e0793c0994e171f4d Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Mon, 17 Sep 2018 11:05:54 -0300 Subject: [PATCH 029/309] #17744 Reorganizing code --- .../view/frontend/web/js/model/checkout-data-resolver.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 6586f647e2404..7a1ba9513ec42 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -226,7 +226,7 @@ define([ return; } - shippingAddress = quote.billingAddress(); + if(quote.isVirtual()) { isBillingAddressInitialized = addressList.some(function (addrs) { if (addrs.isDefaultBilling()) { @@ -237,6 +237,7 @@ define([ }); } + shippingAddress = quote.shippingAddress(); if (!isBillingAddressInitialized && shippingAddress && shippingAddress.canUseForBilling() && From 5372c7df5e16601f2cce687e15ff25ecdd0b82ce Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Mon, 17 Sep 2018 17:18:04 -0300 Subject: [PATCH 030/309] #17744 Fixing syntax pattern --- .../view/frontend/web/js/model/checkout-data-resolver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 7a1ba9513ec42..6f4c99438610b 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -227,7 +227,7 @@ define([ return; } - if(quote.isVirtual()) { + if (quote.isVirtual()) { isBillingAddressInitialized = addressList.some(function (addrs) { if (addrs.isDefaultBilling()) { selectBillingAddress(addrs); From b871ea91610bd14c57ee99aa520ed7350c65b584 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <strpwebstudio@gmail.com> Date: Tue, 18 Sep 2018 10:34:27 +0300 Subject: [PATCH 031/309] #17744 Adding logic to get default billing address Fixed var declaration --- .../view/frontend/web/js/model/checkout-data-resolver.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 6f4c99438610b..142f6af91cc29 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -218,8 +218,8 @@ define([ * Apply resolved billing address to quote */ applyBillingAddress: function () { - var shippingAddress; - var isBillingAddressInitialized; + var shippingAddress, + isBillingAddressInitialized; if (quote.billingAddress()) { selectBillingAddress(quote.billingAddress()); From 540397d5f61fa76d1f6bc64c0c36dc4baaeebb40 Mon Sep 17 00:00:00 2001 From: Dave Macaulay <dmacaulay@magento.com> Date: Tue, 18 Sep 2018 12:32:24 +0200 Subject: [PATCH 032/309] MC-4189: IE11 - PageBuilder Does Not Load - Fix IE 11 loading of TinyMCE --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js index 223c6d6ab04ea..06943b25de55e 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js @@ -30,7 +30,7 @@ define([ _.bindAll(this, 'openFileBrowser'); - config = Object.assign({}, baseConfig, config || {}); + config = _.extend({}, baseConfig, config || {}); this.wysiwygInstance = new WysiwygInstancePrototype(htmlId, config); this.wysiwygInstance.eventBus = this.eventBus = new window.varienEvents(); }, From beb1f0cd690209a7d71c01482c8627775944363e Mon Sep 17 00:00:00 2001 From: Dave Macaulay <dmacaulay@magento.com> Date: Tue, 18 Sep 2018 13:24:21 +0200 Subject: [PATCH 033/309] MC-4189: IE11 - PageBuilder Does Not Load - Update WeakMap polyfill to resolve stack memory errors in IE 11 --- lib/web/es6-collections.js | 273 +++++++------------------------------ 1 file changed, 46 insertions(+), 227 deletions(-) diff --git a/lib/web/es6-collections.js b/lib/web/es6-collections.js index 50ff560e53a8a..ed8e80c1fd07e 100644 --- a/lib/web/es6-collections.js +++ b/lib/web/es6-collections.js @@ -1,227 +1,46 @@ -(function (exports) {'use strict'; - //shared pointer - var i; - //shortcuts - var defineProperty = Object.defineProperty, is = function(a,b) { return isNaN(a)? isNaN(b): a === b; }; - - - //Polyfill global objects - if (typeof WeakMap == 'undefined') { - exports.WeakMap = createCollection({ - // WeakMap#delete(key:void*):boolean - 'delete': sharedDelete, - // WeakMap#clear(): - clear: sharedClear, - // WeakMap#get(key:void*):void* - get: sharedGet, - // WeakMap#has(key:void*):boolean - has: mapHas, - // WeakMap#set(key:void*, value:void*):void - set: sharedSet - }, true); - } - - if (typeof Map == 'undefined' || typeof ((new Map).values) !== 'function' || !(new Map).values().next) { - exports.Map = createCollection({ - // WeakMap#delete(key:void*):boolean - 'delete': sharedDelete, - //:was Map#get(key:void*[, d3fault:void*]):void* - // Map#has(key:void*):boolean - has: mapHas, - // Map#get(key:void*):boolean - get: sharedGet, - // Map#set(key:void*, value:void*):void - set: sharedSet, - // Map#keys(void):Iterator - keys: sharedKeys, - // Map#values(void):Iterator - values: sharedValues, - // Map#entries(void):Iterator - entries: mapEntries, - // Map#forEach(callback:Function, context:void*):void ==> callback.call(context, key, value, mapObject) === not in specs` - forEach: sharedForEach, - // Map#clear(): - clear: sharedClear - }); - } - - if (typeof Set == 'undefined' || typeof ((new Set).values) !== 'function' || !(new Set).values().next) { - exports.Set = createCollection({ - // Set#has(value:void*):boolean - has: setHas, - // Set#add(value:void*):boolean - add: sharedAdd, - // Set#delete(key:void*):boolean - 'delete': sharedDelete, - // Set#clear(): - clear: sharedClear, - // Set#keys(void):Iterator - keys: sharedValues, // specs actually say "the same function object as the initial value of the values property" - // Set#values(void):Iterator - values: sharedValues, - // Set#entries(void):Iterator - entries: setEntries, - // Set#forEach(callback:Function, context:void*):void ==> callback.call(context, value, index) === not in specs - forEach: sharedForEach - }); - } - - if (typeof WeakSet == 'undefined') { - exports.WeakSet = createCollection({ - // WeakSet#delete(key:void*):boolean - 'delete': sharedDelete, - // WeakSet#add(value:void*):boolean - add: sharedAdd, - // WeakSet#clear(): - clear: sharedClear, - // WeakSet#has(value:void*):boolean - has: setHas - }, true); - } - - - /** - * ES6 collection constructor - * @return {Function} a collection class - */ - function createCollection(proto, objectOnly){ - function Collection(a){ - if (!this || this.constructor !== Collection) return new Collection(a); - this._keys = []; - this._values = []; - this._itp = []; // iteration pointers - this.objectOnly = objectOnly; - - //parse initial iterable argument passed - if (a) init.call(this, a); - } - - //define size for non object-only collections - if (!objectOnly) { - defineProperty(proto, 'size', { - get: sharedSize - }); - } - - //set prototype - proto.constructor = Collection; - Collection.prototype = proto; - - return Collection; - } - - - /** parse initial iterable argument passed */ - function init(a){ - var i; - //init Set argument, like `[1,2,3,{}]` - if (this.add) - a.forEach(this.add, this); - //init Map argument like `[[1,2], [{}, 4]]` - else - a.forEach(function(a){this.set(a[0],a[1])}, this); - } - - - /** delete */ - function sharedDelete(key) { - if (this.has(key)) { - this._keys.splice(i, 1); - this._values.splice(i, 1); - // update iteration pointers - this._itp.forEach(function(p) { if (i < p[0]) p[0]--; }); - } - // Aurora here does it while Canary doesn't - return -1 < i; - }; - - function sharedGet(key) { - return this.has(key) ? this._values[i] : undefined; - } - - function has(list, key) { - if (this.objectOnly && key !== Object(key)) - throw new TypeError("Invalid value used as weak collection key"); - //NaN or 0 passed - if (key != key || key === 0) for (i = list.length; i-- && !is(list[i], key);){} - else i = list.indexOf(key); - return -1 < i; - } - - function setHas(value) { - return has.call(this, this._values, value); - } - - function mapHas(value) { - return has.call(this, this._keys, value); - } - - /** @chainable */ - function sharedSet(key, value) { - this.has(key) ? - this._values[i] = value - : - this._values[this._keys.push(key) - 1] = value - ; - return this; - } - - /** @chainable */ - function sharedAdd(value) { - if (!this.has(value)) this._values.push(value); - return this; - } - - function sharedClear() { - this._values.length = 0; - } - - /** keys, values, and iterate related methods */ - function sharedKeys() { - return sharedIterator(this._itp, this._keys); - } - - function sharedValues() { - return sharedIterator(this._itp, this._values); - } - - function mapEntries() { - return sharedIterator(this._itp, this._keys, this._values); - } - - function setEntries() { - return sharedIterator(this._itp, this._values, this._values); - } - - function sharedIterator(itp, array, array2) { - var p = [0], done = false; - itp.push(p); - return { - next: function() { - var v, k = p[0]; - if (!done && k < array.length) { - v = array2 ? [array[k], array2[k]]: array[k]; - p[0]++; - } else { - done = true; - itp.splice(itp.indexOf(p), 1); - } - return { done: done, value: v }; - } - }; - } - - function sharedSize() { - return this._values.length; - } - - function sharedForEach(callback, context) { - var it = this.entries(); - for (;;) { - var r = it.next(); - if (r.done) break; - callback.call(context, r.value[1], r.value[0], this); - } - } - -})(typeof exports != 'undefined' && typeof global != 'undefined' ? global : window ); \ No newline at end of file +/* + * Copyright 2012 The Polymer Authors. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +if (typeof WeakMap === 'undefined') { + (function() { + var defineProperty = Object.defineProperty; + var counter = Date.now() % 1e9; + + var WeakMap = function() { + this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); + }; + + WeakMap.prototype = { + set: function(key, value) { + var entry = key[this.name]; + if (entry && entry[0] === key) + entry[1] = value; + else + defineProperty(key, this.name, {value: [key, value], writable: true}); + return this; + }, + get: function(key) { + var entry; + return (entry = key[this.name]) && entry[0] === key ? + entry[1] : undefined; + }, + delete: function(key) { + var entry = key[this.name]; + if (!entry) return false; + var hasValue = entry[0] === key; + entry[0] = entry[1] = undefined; + return hasValue; + }, + has: function(key) { + var entry = key[this.name]; + if (!entry) return false; + return entry[0] === key; + } + }; + + window.WeakMap = WeakMap; + })(); +} \ No newline at end of file From 02ab61cc1a9ac377aca9d7b2d488fcd2a3858da6 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Sat, 15 Sep 2018 13:32:00 +0300 Subject: [PATCH 034/309] MAGETWO-59265: Import doesn't allow to set default value per store view (dropdown fields) - Changed logic of setting default value of attributes type "select" --- .../Model/Import/Product/Type/AbstractType.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index afd018f077d20..8b04b7ec3532f 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -195,6 +195,8 @@ public function __construct( } /** + * Initialize template for error message. + * * @param array $templateCollection * @return $this */ @@ -377,6 +379,8 @@ public function retrieveAttributeFromCache($attributeCode) } /** + * Adding attribute option. + * * In case we've dynamically added new attribute option during import we need to add it to our cache * in order to keep it up to date. * @@ -508,8 +512,10 @@ public function isSuitable() } /** - * Prepare attributes values for save: exclude non-existent, static or with empty values attributes; - * set default values if needed + * Adding default attribute to product before save. + * + * Prepare attributes values for save: exclude non-existent, static or with empty values attributes, + * set default values if needed. * * @param array $rowData * @param bool $withDefaultValue @@ -539,7 +545,8 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe } } elseif (array_key_exists($attrCode, $rowData)) { $resultAttrs[$attrCode] = $rowData[$attrCode]; - } elseif ($withDefaultValue && null !== $attrParams['default_value']) { + } elseif ($withDefaultValue && null !== $attrParams['default_value'] + && 'select' == $attrParams['type'] && null === $rowData['_store']) { $resultAttrs[$attrCode] = $attrParams['default_value']; } } @@ -611,7 +618,8 @@ protected function getProductEntityLinkField() } /** - * Clean cached values + * Clean cached values. + * * @since 100.2.0 */ public function __destruct() From 1b9ae1174da8a34ac898ea2bc69cfa39f75eb252 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Tue, 18 Sep 2018 15:32:37 +0300 Subject: [PATCH 035/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Fixes according to code review. --- .../Block/Product/ProductList/Toolbar.php | 2 ++ .../Framework}/App/Action/ContextPlugin.php | 28 ++++++++----------- .../Magento/Catalog/etc/adminhtml/system.xml | 3 +- app/code/Magento/Catalog/etc/frontend/di.xml | 2 +- 4 files changed, 16 insertions(+), 19 deletions(-) rename app/code/Magento/Catalog/{Model => Plugin/Framework}/App/Action/ContextPlugin.php (60%) diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index 3e3f86cd7de01..12c691d1365c3 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -150,6 +150,8 @@ class Toolbar extends \Magento\Framework\View\Element\Template * @param \Magento\Framework\App\Http\Context|null $httpContext * @param \Magento\Framework\Data\Form\FormKey|null $formKey * @param array $data + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, diff --git a/app/code/Magento/Catalog/Model/App/Action/ContextPlugin.php b/app/code/Magento/Catalog/Plugin/Framework/App/Action/ContextPlugin.php similarity index 60% rename from app/code/Magento/Catalog/Model/App/Action/ContextPlugin.php rename to app/code/Magento/Catalog/Plugin/Framework/App/Action/ContextPlugin.php index c5ed81540e29e..6add542b15554 100644 --- a/app/code/Magento/Catalog/Model/App/Action/ContextPlugin.php +++ b/app/code/Magento/Catalog/Plugin/Framework/App/Action/ContextPlugin.php @@ -6,7 +6,7 @@ declare(strict_types=1); -namespace Magento\Catalog\Model\App\Action; +namespace Magento\Catalog\Plugin\Framework\App\Action; use Magento\Catalog\Model\Product\ProductList\Toolbar as ToolbarModel; use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer; @@ -56,21 +56,17 @@ public function __construct( public function beforeDispatch() { if ($this->toolbarMemorizer->isMemorizingAllowed()) { - $order = $this->catalogSession->getData(ToolbarModel::ORDER_PARAM_NAME); - if ($order) { - $this->httpContext->setValue(ToolbarModel::ORDER_PARAM_NAME, $order, false); - } - $direction = $this->catalogSession->getData(ToolbarModel::DIRECTION_PARAM_NAME); - if ($direction) { - $this->httpContext->setValue(ToolbarModel::DIRECTION_PARAM_NAME, $direction, false); - } - $mode = $this->catalogSession->getData(ToolbarModel::MODE_PARAM_NAME); - if ($mode) { - $this->httpContext->setValue(ToolbarModel::MODE_PARAM_NAME, $mode, false); - } - $limit = $this->catalogSession->getData(ToolbarModel::LIMIT_PARAM_NAME); - if ($limit) { - $this->httpContext->setValue(ToolbarModel::LIMIT_PARAM_NAME, $limit, false); + $params = [ + ToolbarModel::ORDER_PARAM_NAME, + ToolbarModel::DIRECTION_PARAM_NAME, + ToolbarModel::MODE_PARAM_NAME, + ToolbarModel::LIMIT_PARAM_NAME + ]; + foreach ($params as $param) { + $paramValue = $this->catalogSession->getData($param); + if ($paramValue) { + $this->httpContext->setValue($param, $paramValue, false); + } } } } diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index be35ae2e5549d..adfe4c735201c 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -94,8 +94,7 @@ </field> <field id="remember_pagination" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Remember Category Pagination</label> - <comment>Note that SEO and performance experience may be negative by this feature enabled. - Also it will increase cache storage consumption.</comment> + <comment>Changing may affect SEO and cache storage consumption.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index a1520b66e83dc..ee9c5b29da894 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -118,6 +118,6 @@ </type> <type name="Magento\Framework\App\Action\AbstractAction"> <plugin name="catalog_app_action_dispatch_controller_context_plugin" - type="Magento\Catalog\Model\App\Action\ContextPlugin" /> + type="Magento\Catalog\Plugin\Framework\App\Action\ContextPlugin" /> </type> </config> From 0c7edc5a71a02bcc6df929ac0d0f7518b18a376b Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan <Karen_Mkhitaryan@epam.com> Date: Tue, 18 Sep 2018 20:35:30 +0400 Subject: [PATCH 036/309] MAGETWO-91617: Ordered Products Report Error for restricted scope admin - Add some helpers for automated test --- .../ActionGroup/AdminProductActionGroup.xml | 17 +++++++- ...omerWithWebsiteAndStoreViewActionGroup.xml | 43 +++++++++++++++++++ .../AdminDeleteCustomerActionGroup.xml | 23 ++++++++++ .../Customer/Test/Mftf/Data/AddressData.xml | 1 + ...AdminCustomerAccountInformationSection.xml | 1 + .../Section/AdminCustomerAddressesSection.xml | 24 +++++++++++ .../AdminCustomerGridMainActionsSection.xml | 2 + .../AdminReviewOrderActionGroup.xml | 24 +++++++++++ .../Mftf/Section/OrderedProductsSection.xml | 17 ++++++++ .../ActionGroup/AdminOrderActionGroup.xml | 24 +++++++++++ .../AdminInvoicePaymentShippingSection.xml | 2 + .../AdminOrderStoreScopeTreeSection.xml | 1 + .../Mftf/Section/AdminOrdersGridSection.xml | 2 + .../Test/Mftf/Section/OrdersGridSection.xml | 3 +- .../Store/Test/Mftf/Data/StoreData.xml | 12 ++++++ .../AdminCreateUserActionGroup.xml | 23 ++++++++++ .../AdminDeleteCreatedUserActionGroup.xml | 23 ++++++++++ 17 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressesSection.xml create mode 100644 app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminReviewOrderActionGroup.xml create mode 100644 app/code/Magento/Reports/Test/Mftf/Section/OrderedProductsSection.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedUserActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 9f8d827b20849..91141a573cf5b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -276,5 +276,20 @@ <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessage"/> <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> - + <actionGroup name="CreatedProductConnectToWebsite"> + <arguments> + <argument name="website"/> + <argument name="product"/> + </arguments> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="navigateToProductPage"/> + <waitForPageLoad stepKey="waitForProductsList"/> + <click stepKey="openProduct" selector="{{AdminProductGridActionSection.productName(product.name)}}"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ScrollToWebsites"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openWebsitesList"/> + <waitForPageLoad stepKey="waitForWebsitesList"/> + <click selector="{{ProductInWebsitesSection.website(website.name)}}" stepKey="SelectWebsite"/> + <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct"/> + <waitForPageLoad stepKey="waitForSave"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml new file mode 100644 index 0000000000000..0071acf2ef1e0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCustomerWithWebsiteAndStoreViewActionGroup"> + <arguments> + <argument name="customerData"/> + <argument name="address"/> + <argument name="website" type="string"/> + <argument name="storeView" type="string"/> + </arguments> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersPage"/> + <click stepKey="addNewCustomer" selector="{{AdminCustomerGridMainActionsSection.addNewCustomer}}"/> + <selectOption stepKey="selectWebSite" selector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" userInput="{{website}}"/> + <fillField stepKey="FillFirstName" selector="{{AdminCustomerAccountInformationSection.firstName}}" userInput="{{customerData.firstname}}"/> + <fillField stepKey="FillLastName" selector="{{AdminCustomerAccountInformationSection.lastName}}" userInput="{{customerData.lastname}}"/> + <fillField stepKey="FillEmail" selector="{{AdminCustomerAccountInformationSection.email}}" userInput="{{customerData.email}}"/> + <selectOption stepKey="selectStoreView" selector="{{AdminCustomerAccountInformationSection.storeView}}" userInput="{{storeView}}"/> + <scrollToTopOfPage stepKey="scrollToTopOfThePage"/> + <click stepKey="goToAddresses" selector="{{AdminCustomerAccountInformationSection.addressesButton}}"/> + <waitForPageLoad stepKey="waitForAddresses"/> + <click stepKey="clickOnAddNewAddress" selector="{{AdminCustomerAddressesSection.addNewAddress}}"/> + <waitForPageLoad stepKey="waitForAddressFields"/> + <click stepKey="thickBillingAddress" selector="{{AdminCustomerAddressesSection.defaultBillingAddress}}"/> + <click stepKey="thickShippingAddress" selector="{{AdminCustomerAddressesSection.defaultShippingAddress}}"/> + <fillField stepKey="fillFirstNameForAddress" selector="{{AdminCustomerAddressesSection.firstNameForAddress}}" userInput="{{address.firstname}}"/> + <fillField stepKey="fillLastNameForAddress" selector="{{AdminCustomerAddressesSection.lastNameForAddress}}" userInput="{{address.lastname}}"/> + <fillField stepKey="fillStreetAddress" selector="{{AdminCustomerAddressesSection.streetAddress}}" userInput="{{address.street[0]}}"/> + <fillField stepKey="fillCity" selector="{{AdminCustomerAddressesSection.city}}" userInput="{{address.city}}"/> + <selectOption stepKey="selectCountry" selector="{{AdminCustomerAddressesSection.country}}" userInput="{{address.country}}"/> + <selectOption stepKey="selectState" selector="{{AdminCustomerAddressesSection.state}}" userInput="{{address.state}}"/> + <fillField stepKey="fillZip" selector="{{AdminCustomerAddressesSection.zip}}" userInput="{{address.postcode}}"/> + <fillField stepKey="fillPhoneNumber" selector="{{AdminCustomerAddressesSection.phoneNumber}}" userInput="{{address.telephone}}"/> + <click stepKey="save" selector="{{AdminCustomerAccountInformationSection.saveCustomer}}"/> + <waitForPageLoad stepKey="waitForCustomersPage"/> + <see stepKey="seeSuccessMessage" userInput="You saved the customer."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml new file mode 100644 index 0000000000000..68f6b49e80996 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteCustomerActionGroup"> + <arguments> + <argument name="customerEmail"/> + </arguments> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomersPage"/> + <click stepKey="chooseCustomer" selector="{{AdminCustomerGridMainActionsSection.customerCheckbox(customerEmail)}}"/> + <click stepKey="openActions" selector="{{AdminCustomerGridMainActionsSection.actions}}"/> + <waitForPageLoad stepKey="waitActions"/> + <click stepKey="delete" selector="{{AdminCustomerGridMainActionsSection.delete}}"/> + <waitForPageLoad stepKey="waitForConfirmationAlert"/> + <click stepKey="accept" selector="{{AdminCustomerGridMainActionsSection.ok}}"/> + <see stepKey="seeSuccessMessage" userInput="were deleted."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index d090620145105..033add209a4c2 100755 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -65,6 +65,7 @@ <data key="default_billing">Yes</data> <data key="default_shipping">Yes</data> <requiredEntity type="region">RegionNY</requiredEntity> + <data key="country">United States</data> </entity> <entity name="US_Address_CA" type="address"> <data key="firstname">John</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml index 32746a66621eb..8510fd385f92b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml @@ -11,6 +11,7 @@ <section name="AdminCustomerAccountInformationSection"> <element name="accountInformationTitle" type="text" selector=".admin__page-nav-title"/> <element name="accountInformationButton" type="text" selector="//a/span[text()='Account Information']"/> + <element name="addressesButton" type="select" selector="//a//span[contains(text(), 'Addresses')]"/> <element name="firstName" type="input" selector="input[name='customer[firstname]']"/> <element name="lastName" type="input" selector="input[name='customer[lastname]']"/> <element name="email" type="input" selector="input[name='customer[email]']"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressesSection.xml new file mode 100644 index 0000000000000..175c7ab573d37 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressesSection.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCustomerAddressesSection"> + <element name="addNewAddress" type="button" selector="//span[text()='Add New Addresses']"/> + <element name="defaultBillingAddress" type="button" selector="//label[text()='Default Billing Address']"/> + <element name="defaultShippingAddress" type="button" selector="//label[text()='Default Shipping Address']"/> + <element name="firstNameForAddress" type="button" selector="//input[contains(@name, 'address')][contains(@name, 'firstname')]"/> + <element name="lastNameForAddress" type="button" selector="//input[contains(@name, 'address')][contains(@name, 'lastname')]"/> + <element name="streetAddress" type="button" selector="//input[contains(@name, 'street')]"/> + <element name="city" type="input" selector="//input[contains(@name, 'city')]"/> + <element name="country" type="select" selector="//select[contains(@name, 'country_id')]"/> + <element name="state" type="select" selector="//select[contains(@name, 'address[new_0][region_id]')]"/> + <element name="zip" type="input" selector="//input[contains(@name, 'postcode')]"/> + <element name="phoneNumber" type="input" selector="//input[contains(@name, 'telephone')]"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridMainActionsSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridMainActionsSection.xml index 1f0c4b4998a9f..d644b581088bc 100755 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridMainActionsSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridMainActionsSection.xml @@ -13,5 +13,7 @@ <element name="multicheck" type="checkbox" selector="#container>div>div.admin__data-grid-wrap>table>thead>tr>th.data-grid-multicheck-cell>div>label"/> <element name="delete" type="button" selector="//*[contains(@class, 'admin__data-grid-header')]//span[contains(@class,'action-menu-item') and text()='Delete']"/> <element name="actions" type="text" selector=".action-select"/> + <element name="customerCheckbox" type="button" selector="//*[contains(text(),'{{arg}}')]/parent::td/preceding-sibling::td/label[@class='data-grid-checkbox-cell-inner']//input" parameterized="true"/> + <element name="ok" type="button" selector="//button[@data-role='action']//span[text()='OK']"/> </section> </sections> diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminReviewOrderActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminReviewOrderActionGroup.xml new file mode 100644 index 0000000000000..003a5e6655f34 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/AdminReviewOrderActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminReviewOrderActionGroup"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <click stepKey="openReports" selector="{{OrderedProductsSection.reports}}"/> + <waitForPageLoad stepKey="waitForReports" time="5"/> + <click stepKey="openOrdered" selector="{{OrderedProductsSection.ordered}}"/> + <waitForPageLoad stepKey="waitForOrdersPage" time="5"/> + <click stepKey="refresh" selector="{{OrderedProductsSection.refresh}}"/> + <waitForPageLoad stepKey="waitForOrderList" time="5"/> + <scrollTo stepKey="scrollTo" selector="{{OrderedProductsSection.total}}"/> + <see stepKey="seeOrder" userInput="{{productName}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderedProductsSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderedProductsSection.xml new file mode 100644 index 0000000000000..89e8497dddcea --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/OrderedProductsSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="OrderedProductsSection"> + <element name="reports" type="button" selector="//li[@data-ui-id='menu-magento-reports-report']"/> + <element name="ordered" type="button" selector="//li[@data-ui-id='menu-magento-reports-report-products-sold']"/> + <element name="refresh" type="button" selector="//button[@title='Refresh']" timeout="30"/> + <element name="total" type="text" selector="//tfoot//th[contains(text(), 'Total')]"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index c82623632d782..ffa6a2a5f2d99 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -268,4 +268,28 @@ <see selector="{{AdminMessagesSection.success}}" userInput="You canceled the order." stepKey="seeCancelSuccessMessage"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Canceled" stepKey="seeOrderStatusCanceled"/> </actionGroup> + + <actionGroup name="CreateOrderInStoreActionGroup"> + <arguments> + <argument name="product"/> + <argument name="customer"/> + <argument name="storeView"/> + </arguments> + <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> + <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> + <waitForPageLoad stepKey="waitForStoresPageOpened"/> + <click stepKey="chooseStore" selector="{{AdminOrderStoreScopeTreeSection.storeForOrder(storeView.name)}}"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{OrdersGridSection.addProducts}}" stepKey="clickOnAddProducts"/> + <waitForPageLoad stepKey="waitForProductsListForOrder"/> + <click selector="{{AdminOrdersGridSection.productForOrder(product.sku)}}" stepKey="chooseTheProduct"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="addSelectedProductToOrder"/> + <waitForPageLoad stepKey="waitForProductAddedInOrder"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethodsThickened"/> + <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> + <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml index 918a8e814b555..8d7c64733972e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml @@ -15,5 +15,7 @@ <element name="ShippingMethod" type="text" selector=".order-shipping-address .shipping-description-title"/> <element name="ShippingPrice" type="text" selector=".order-shipping-address .shipping-description-content .price"/> <element name="CreateShipment" type="checkbox" selector=".order-shipping-address input[name='invoice[do_shipment]']"/> + <element name="getShippingMethodAndRates" type="button" selector="//span[text()='Get shipping methods and rates']"/> + <element name="shippingMethod" type="button" selector="//label[contains(text(), 'Fixed')]"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml index 050e1ba8b2df4..cbe17499319f9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStoreScopeTreeSection.xml @@ -11,5 +11,6 @@ <section name="AdminOrderStoreScopeTreeSection"> <element name="storeTree" type="text" selector="div.tree-store-scope"/> <element name="storeOption" type="radio" selector="//div[contains(@class, 'tree-store-scope')]//label[contains(text(), '{{name}}')]/preceding-sibling::input" parameterized="true" timeout="30"/> + <element name="storeForOrder" type="radio" selector="//div[contains(@class, 'tree-store-scope')]//label[contains(text(), '{{arg}}')]" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index 7ece18fb863b7..53a6cbffcdac6 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -29,5 +29,7 @@ <element name="viewBookmark" type="button" selector="//div[contains(@class, 'admin__data-grid-action-bookmarks')]/ul/li/div/a[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="columnsDropdown" type="button" selector="div.admin__data-grid-action-columns button" timeout="30"/> <element name="viewColumnCheckbox" type="checkbox" selector="//div[contains(@class,'admin__data-grid-action-columns')]//div[contains(@class, 'admin__field-option')]//label[text() = '{{column}}']/preceding-sibling::input" parameterized="true"/> + <element name="customerInOrdersSection" type="button" selector="(//td[contains(text(),'{{customer}}')])[1]" parameterized="true"/> + <element name="productForOrder" type="button" selector="//td[contains(text(),'{{var}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml index 8d99bf4872d0a..717022322698f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/OrdersGridSection.xml @@ -18,7 +18,7 @@ <element name="createNewOrder" type="button" selector="button[title='Create New Order'"/> <element name="website" type="radio" selector="//label[contains(text(), '{{arg}}')]" parameterized="true"/> - <element name="addProducts" type="button" selector="//span[text()='Add Products']"/> + <element name="addProducts" type="button" selector="#add_products"/> <element name="selectProduct" type="checkbox" selector="//td[contains(text(), '{{arg}}')]/following-sibling::td[contains(@class, 'col-select col-in_products')]" parameterized="true"/> <element name="setQuantity" type="checkbox" selector="//td[contains(text(), '{{arg}}')]/following-sibling::td[contains(@class, 'col-qty')]/input" parameterized="true"/> <element name="addProductsToOrder" type="button" selector="//span[text()='Add Selected Product(s) to Order']"/> @@ -29,5 +29,6 @@ <element name="productPrice" type="text" selector="//span[text()='{{arg}}']/parent::td/following-sibling::td[@class='col-price col-row-subtotal']/span" parameterized="true"/> <element name="removeItems" type="select" selector="//span[text()='{{arg}}']/parent::td/following-sibling::td/select[@class='admin__control-select']" parameterized="true"/> <element name="applyCoupon" type="input" selector="#coupons:code"/> + <element name="submitOrder" type="button" selector="#submit_order_top_button"/> </section> </sections> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 2bff20e05435a..b0f04dbe703a9 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -49,4 +49,16 @@ <data key="store_type">store</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> + <entity name="NewStoreData" type="store"> + <data key="name" unique="suffix">Store</data> + <data key="code" unique="suffix">StoreCode</data> + </entity> + <entity name="NewWebSiteData" type="webSite"> + <data key="name" unique="suffix">WebSite</data> + <data key="code" unique="suffix">WebSiteCode</data> + </entity> + <entity name="NewStoreViewData"> + <data key="name" unique="suffix">StoreView</data> + <data key="code" unique="suffix">StoreViewCode</data> + </entity> </entities> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml index de887d2de6704..9a0fa4a205799 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml @@ -31,4 +31,27 @@ <waitForPageLoad stepKey="waitForPageLoad2" /> <see userInput="You saved the user." stepKey="seeSuccessMessage" /> </actionGroup> + + <!--Create new user with role--> + <actionGroup name="AdminCreateUserWithRoleActionGroup"> + <arguments> + <argument name="role"/> + <argument name="user" defaultValue="newAdmin"/> + </arguments> + <amOnPage url="{{AdminEditUserPage.url}}" stepKey="navigateToNewUser"/> + <waitForPageLoad stepKey="waitForUsersPage" /> + <fillField selector="{{AdminCreateUserSection.usernameTextField}}" userInput="{{user.username}}" stepKey="enterUserName" /> + <fillField selector="{{AdminCreateUserSection.firstNameTextField}}" userInput="{{user.firstName}}" stepKey="enterFirstName" /> + <fillField selector="{{AdminCreateUserSection.lastNameTextField}}" userInput="{{user.lastName}}" stepKey="enterLastName" /> + <fillField selector="{{AdminCreateUserSection.emailTextField}}" userInput="{{user.username}}@magento.com" stepKey="enterEmail" /> + <fillField selector="{{AdminCreateUserSection.passwordTextField}}" userInput="{{user.password}}" stepKey="enterPassword" /> + <fillField selector="{{AdminCreateUserSection.pwConfirmationTextField}}" userInput="{{user.password}}" stepKey="confirmPassword" /> + <fillField selector="{{AdminCreateUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword" /> + <scrollToTopOfPage stepKey="scrollToTopOfPage" /> + <click stepKey="clickUserRole" selector="{{AdminCreateUserSection.userRoleTab}}"/> + <click stepKey="chooseRole" selector="{{AdminStoreSection.createdRoleInUserPage(role.name)}}"/> + <click selector="{{AdminCreateUserSection.saveButton}}" stepKey="clickSaveUser" /> + <waitForPageLoad stepKey="waitForSaveTheUser" /> + <see userInput="You saved the user." stepKey="seeSuccessMessage" /> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedUserActionGroup.xml new file mode 100644 index 0000000000000..7f1ed3be1ca57 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedUserActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteCreatedUserActionGroup"> + <arguments> + <argument name="user"/> + </arguments> + <amOnPage stepKey="amOnAdminUsersPage" url="{{AdminUsersPage.url}}"/> + <click stepKey="openTheUser" selector="{{AdminDeleteUserSection.role(user.username)}}"/> + <fillField stepKey="TypeCurrentPassword" selector="{{AdminDeleteUserSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click stepKey="clickToDeleteUser" selector="{{AdminDeleteUserSection.delete}}"/> + <waitForPageLoad stepKey="waitForConfirmationPopup"/> + <click stepKey="clickToConfirm" selector="{{AdminDeleteUserSection.confirm}}"/> + <see stepKey="seeDeleteMessageForUser" userInput="You deleted the user."/> + </actionGroup> +</actionGroups> From a6369144904e0503e21d80ef96e14396c2a3308f Mon Sep 17 00:00:00 2001 From: eugene-shab <dev.eugene.shab@gmail.com> Date: Tue, 18 Sep 2018 23:23:10 +0300 Subject: [PATCH 037/309] Fixed tests --- .../testsuite/Magento/GraphQl/Catalog/CategoryTest.php | 6 +++--- .../testsuite/Magento/GraphQl/Catalog/ProductViewTest.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 8841fab75790c..5f0be1bdb8a86 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -141,7 +141,7 @@ public function testCategoryProducts() available_sort_by level } - image + image { url, path } image_label meta_description meta_keyword @@ -225,13 +225,13 @@ public function testCategoryProducts() } short_description sku - small_image + small_image { url, path } small_image_label special_from_date special_price special_to_date swatch_image - thumbnail + thumbnail { url, path } thumbnail_label tier_price tier_prices { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index 34cebde64d03a..fdf1bfc35408b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -53,7 +53,7 @@ public function testQueryAllFieldsSimpleProduct() available_sort_by level } - image + image { url, path } image_label meta_description meta_keyword @@ -205,13 +205,13 @@ public function testQueryAllFieldsSimpleProduct() } short_description sku - small_image + small_image { url, path } small_image_label special_from_date special_price special_to_date swatch_image - thumbnail + thumbnail { url, path } thumbnail_label tier_price tier_prices From f017676c873418b4e87b306e8666f9db39e82b99 Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan <Karen_Mkhitaryan@epam.com> Date: Wed, 19 Sep 2018 15:45:48 +0400 Subject: [PATCH 038/309] MAGETWO-91617: Ordered Products Report Error for restricted scope admin - Add fixed code --- .../ActionGroup/AdminOrderActionGroup.xml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index ffa6a2a5f2d99..aa6c10400d48b 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -259,16 +259,6 @@ <see selector="{{AdminOrderItemsOrderedSection.productSkuColumn}}" userInput="{{product.sku}}" stepKey="seeSkuInItemsOrdered"/> </actionGroup> - <!--Cancel order that is in pending status--> - <actionGroup name="cancelPendingOrder"> - <click selector="{{AdminOrderDetailsMainActionsSection.cancel}}" stepKey="clickCancelOrder"/> - <waitForElement selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForCancelConfirmation"/> - <see selector="{{AdminConfirmationModalSection.message}}" userInput="Are you sure you want to cancel this order?" stepKey="seeConfirmationMessage"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmOrderCancel"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You canceled the order." stepKey="seeCancelSuccessMessage"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Canceled" stepKey="seeOrderStatusCanceled"/> - </actionGroup> - <actionGroup name="CreateOrderInStoreActionGroup"> <arguments> <argument name="product"/> @@ -292,4 +282,14 @@ <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> </actionGroup> + + <!--Cancel order that is in pending status--> + <actionGroup name="cancelPendingOrder"> + <click selector="{{AdminOrderDetailsMainActionsSection.cancel}}" stepKey="clickCancelOrder"/> + <waitForElement selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForCancelConfirmation"/> + <see selector="{{AdminConfirmationModalSection.message}}" userInput="Are you sure you want to cancel this order?" stepKey="seeConfirmationMessage"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmOrderCancel"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You canceled the order." stepKey="seeCancelSuccessMessage"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Canceled" stepKey="seeOrderStatusCanceled"/> + </actionGroup> </actionGroups> From 5333ca582fcc97c6926df798c96eba3378b7c3e2 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Wed, 19 Sep 2018 15:13:26 +0300 Subject: [PATCH 039/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Fixed statics. --- .../Test/Unit/Block/Product/ProductList/ToolbarTest.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php index 8282ffa409f9c..884f4c543c8b8 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php @@ -67,13 +67,16 @@ protected function setUp() 'getLimit', 'getCurrentPage' ]); - $this->memorizer = $this->createPartialMock(\Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer::class, [ + $this->memorizer = $this->createPartialMock( + \Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer::class, + [ 'getDirection', 'getOrder', 'getMode', 'getLimit', 'isMemorizingAllowed' - ]); + ] + ); $this->layout = $this->createPartialMock(\Magento\Framework\View\Layout::class, ['getChildName', 'getBlock']); $this->pagerBlock = $this->createPartialMock(\Magento\Theme\Block\Html\Pager::class, [ 'setUseContainer', From 949aa6b9ae92153fa7cd0d0453742259c33ee54c Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Wed, 19 Sep 2018 16:17:22 +0300 Subject: [PATCH 040/309] MAGETWO-67269: Gift Options set to no still show up as choices on front end order page - Fixes according to code review. --- app/code/Magento/GiftMessage/Block/Message/Inline.php | 7 ++----- .../GiftMessage/view/frontend/templates/inline.phtml | 8 +++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/GiftMessage/Block/Message/Inline.php b/app/code/Magento/GiftMessage/Block/Message/Inline.php index 1a874cb5ea4ae..f44f967cefdc6 100644 --- a/app/code/Magento/GiftMessage/Block/Message/Inline.php +++ b/app/code/Magento/GiftMessage/Block/Message/Inline.php @@ -330,14 +330,11 @@ public function getEscaped($value, $defaultValue = '') * * @return bool */ - public function isOrderLevelAvailable() + public function isMessagesOrderAvailable() { $entity = $this->getEntity(); if (!$entity->hasIsGiftOptionsAvailable()) { $this->_eventManager->dispatch('gift_options_prepare', ['entity' => $entity]); - if (!$entity->getIsGiftOptionsAvailable()) { - $entity->setIsGiftOptionsAvailable($this->isMessagesAvailable()); - } } return $entity->getIsGiftOptionsAvailable(); } @@ -372,7 +369,7 @@ public function isItemMessagesAvailable($item) protected function _toHtml() { // render HTML when messages are allowed for order or for items only - if ($this->isItemsAvailable() || $this->isOrderLevelAvailable()) { + if ($this->isItemsAvailable() || $this->isMessagesAvailable() || $this->isMessagesOrderAvailable()) { return parent::_toHtml(); } return ''; diff --git a/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml b/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml index 8c58959be15b7..640ef1ba16486 100644 --- a/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml +++ b/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml @@ -18,7 +18,7 @@ </div> <dl class="options-items" id="allow-gift-options-container"> - <?php if ($block->isOrderLevelAvailable()): ?> + <?php if ($block->isMessagesAvailable()): ?> <dt id="add-gift-options-for-order-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="order-title"> <div class="field choice"> <input type="checkbox" name="allow_gift_messages_for_order" id="allow_gift_options_for_order" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-for-order-container"}'<?php if ($block->getEntityHasMessage()): ?> checked="checked"<?php endif; ?> class="checkbox" /> @@ -28,7 +28,6 @@ <dd id="allow-gift-options-for-order-container" class="order-options"> <div class="options-order-container" id="options-order-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"></div> - <?php if ($block->isMessagesAvailable()): ?> <button class="action action-gift" data-mage-init='{"toggleAdvanced": {"selectorsToggleClass":"hidden", "toggleContainers":"#allow-gift-messages-for-order-container"}}'> <span><?= /* @escapeNotVerified */ __('Gift Message') ?></span> @@ -63,9 +62,8 @@ }); </script> </div> - <?php endif; ?> </dd> - <?php endif; ?> + <?php endif ?> <?php if ($block->isItemsAvailable()): ?> <dt id="add-gift-options-for-items-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="order-title individual"> <div class="field choice"> @@ -154,7 +152,7 @@ </div> <dl class="options-items" id="allow-gift-options-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"> - <?php if ($block->isOrderLevelAvailable()): ?> + <?php if ($block->isMessagesOrderAvailable() || $block->isMessagesAvailable()): ?> <dt id="add-gift-options-for-order-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="order-title"> <div class="field choice"> <input type="checkbox" name="allow_gift_options_for_order_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" id="allow_gift_options_for_order_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-for-order-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"}'<?php if ($block->getEntityHasMessage()): ?> checked="checked"<?php endif; ?> class="checkbox" /> From 8024b4c1c2a5c3a28c42805c3d52edea536f7501 Mon Sep 17 00:00:00 2001 From: "Hakobyan, Lusine" <Lusine_Hakobyan@epam.com> Date: Thu, 20 Sep 2018 11:24:47 +0400 Subject: [PATCH 041/309] MAGETWO-91704: Can't cancel/change payment method after chosen Store Credit - Update automated test according to review --- .../Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml index 8aaf626edab33..24ff30071830f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml @@ -9,7 +9,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormPaymentSection"> - <element name="useCustomerBalance" type="checkbox" selector="#p_method_use_customerbalance"/> <element name="header" type="text" selector="#order-methods span.title"/> <element name="getShippingMethods" type="text" selector="#order-shipping_method a.action-default" timeout="30"/> <element name="flatRateOption" type="radio" selector="#s_method_flatrate_flatrate" timeout="30"/> From 0d191b7ad858f0c4ef0e468b0a288861e5d2d5f2 Mon Sep 17 00:00:00 2001 From: eugene-shab <dev.eugene.shab@gmail.com> Date: Thu, 20 Sep 2018 11:57:51 +0300 Subject: [PATCH 042/309] Add label param to the image. --- .../Model/Resolver/Product/ProductImage.php | 10 ++++++---- app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 6 ++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php index 3cf961c0f4225..9dc0dd68ee457 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php @@ -64,15 +64,17 @@ public function resolve( /** @var \Magento\Catalog\Helper\Image $catalogImageHelper */ $catalogImageHelper = $this->catalogImageHelperFactory->create(); - $imageUrl = $catalogImageHelper->init( + /** @var \Magento\Catalog\Helper\Image $image */ + $image = $catalogImageHelper->init( $product, 'product_' . $imageType, ['type' => $imageType] - )->getUrl(); + ); $imageData = [ - 'url' => $imageUrl, - 'path' => $product->getData($imageType) + 'url' => $image->getUrl(), + 'path' => $product->getData($imageType), + 'label' => $image->getLabel() ]; return $imageData; diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 683dfb8de1f26..4a382e16447a4 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -264,9 +264,6 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ new_to_date: String @doc(description: "The end date for new product listings") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\NewFromTo") tier_price: Float @doc(description: "The price when tier pricing is in effect and the items purchased threshold has been reached") options_container: String @doc(description: "If the product has multiple options, determines where they appear on the product page") - image_label: String @doc(description: "The label assigned to a product image") - small_image_label: String @doc(description: "The label assigned to a product's small image") - thumbnail_label: String @doc(description: "The label assigned to a product's thumbnail image") created_at: String @doc(description: "Timestamp indicating when the product was created") updated_at: String @doc(description: "Timestamp indicating when the product was updated") country_of_manufacture: String @doc(description: "The product's country of origin") @@ -352,9 +349,10 @@ type CustomizableFileValue @doc(description: "CustomizableFileValue defines the image_size_y: Int @doc(description: "The maximum height of an image") } -type ProductImage @doc(description: "Product image information. Contains image relative path and URL") { +type ProductImage @doc(description: "Product image information. Contains image relative path, URL and label") { url: String path: String + label: String } interface CustomizableOptionInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\CustomizableOptionTypeResolver") @doc(description: "The CustomizableOptionInterface contains basic information about a customizable option. It can be implemented by several types of configurable options.") { From d3df40b11e75968eb78acee4cae47d27a3a5716c Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Thu, 20 Sep 2018 12:52:22 +0300 Subject: [PATCH 043/309] MAGETWO-94429: [2.3] Files and folders symlinked in pub/media cannot be deleted from media gallery browser --- .../Framework/Filesystem/Driver/FileTest.php | 75 ++++++++++++++++--- .../Framework/Filesystem/Driver/File.php | 15 +++- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php index 26401c782efc4..5f53e62165502 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filesystem/Driver/FileTest.php @@ -7,22 +7,27 @@ */ namespace Magento\Framework\Filesystem\Driver; -use Magento\Framework\Filesystem\DriverInterface; +use Magento\Framework\Exception\FileSystemException; class FileTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\Filesystem\Driver\File + * @var File */ - protected $driver; + private $driver; /** - * @var string + * @var String */ - protected $absolutePath; + private $absolutePath; /** - * get relative path for test + * @var String + */ + private $generatedPath; + + /** + * Returns relative path for the test. * * @param $relativePath * @return string @@ -33,16 +38,26 @@ protected function getTestPath($relativePath) } /** - * Set up + * @inheritdoc */ public function setUp() { - $this->driver = new \Magento\Framework\Filesystem\Driver\File(); + $this->driver = new File(); $this->absolutePath = dirname(__DIR__) . '/_files/'; + $this->generatedPath = $this->getTestPath('generated'); + $this->removeGeneratedDirectory(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->removeGeneratedDirectory(); } /** - * test read recursively read + * Tests directory recursive read. */ public function testReadDirectoryRecursively() { @@ -60,7 +75,7 @@ public function testReadDirectoryRecursively() } /** - * test exception + * Tests directory reading exception. * * @expectedException \Magento\Framework\Exception\FileSystemException */ @@ -69,6 +84,11 @@ public function testReadDirectoryRecursivelyFailure() $this->driver->readDirectoryRecursively($this->getTestPath('not-existing-directory')); } + /** + * Tests of directory creating. + * + * @throws FileSystemException + */ public function testCreateDirectory() { $generatedPath = $this->getTestPath('generated/roo/bar/baz/foo'); @@ -80,4 +100,39 @@ public function testCreateDirectory() $this->assertTrue($this->driver->createDirectory($generatedPath)); $this->assertTrue(is_dir($generatedPath)); } + + /** + * Tests creation and removing of symlinks. + * + * @throws FileSystemException + * @return void + */ + public function testSymlinks(): void + { + $sourceDirectory = $this->generatedPath . '/source'; + $destinationDirectory = $this->generatedPath . '/destination'; + + $this->driver->createDirectory($sourceDirectory); + $this->driver->createDirectory($destinationDirectory); + + $linkName = $destinationDirectory . '/link'; + + self::assertTrue($this->driver->isWritable($destinationDirectory)); + self::assertTrue($this->driver->symlink($sourceDirectory, $linkName)); + self::assertTrue($this->driver->isExists($linkName)); + self::assertTrue($this->driver->deleteDirectory($linkName)); + } + + /** + * Remove generated directories. + * + * @throws FileSystemException + * @return void + */ + private function removeGeneratedDirectory(): void + { + if (is_dir($this->generatedPath)) { + $this->driver->deleteDirectory($this->generatedPath); + } + } } diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/File.php b/lib/internal/Magento/Framework/Filesystem/Driver/File.php index b54b02bd6de98..499d1f5adc9f3 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/File.php @@ -14,6 +14,7 @@ /** * Class File + * * @package Magento\Framework\Filesystem\Driver * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ @@ -409,7 +410,14 @@ public function deleteDirectory($path) $this->deleteFile($entity->getPathname()); } } - $result = @rmdir($this->getScheme() . $path); + + $fullPath = $this->getScheme() . $path; + if (is_link($fullPath)) { + $result = @unlink($fullPath); + } else { + $result = @rmdir($fullPath); + } + if (!$result) { throw new FileSystemException( new \Magento\Framework\Phrase( @@ -843,6 +851,8 @@ public function fileUnlock($resource) } /** + * Returns an absolute path for the given one. + * * @param string $basePath * @param string $path * @param string|null $scheme @@ -879,7 +889,8 @@ public function getRelativePath($basePath, $path = null) } /** - * Fixes path separator + * Fixes path separator. + * * Utility method. * * @param string $path From 1148f85e42022e77817e33a901bef5153a7cf108 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Fri, 21 Sep 2018 15:11:30 +0300 Subject: [PATCH 044/309] MAGETWO-59265: Import doesn't allow to set default value per store view (dropdown fields) - Changed logic of setting default value of attributes by store view during import --- .../Model/Import/Product/Type/AbstractType.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index 8b04b7ec3532f..3b6caef66ce6c 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -543,10 +543,9 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe } else { $resultAttrs[$attrCode] = $rowData[$attrCode]; } - } elseif (array_key_exists($attrCode, $rowData)) { + } elseif (array_key_exists($attrCode, $rowData) && empty($rowData['_store'])) { $resultAttrs[$attrCode] = $rowData[$attrCode]; - } elseif ($withDefaultValue && null !== $attrParams['default_value'] - && 'select' == $attrParams['type'] && null === $rowData['_store']) { + } elseif ($withDefaultValue && null !== $attrParams['default_value'] && empty($rowData['_store'])) { $resultAttrs[$attrCode] = $attrParams['default_value']; } } From bb949b4f3a0322f1ab73b19c2680d12859294038 Mon Sep 17 00:00:00 2001 From: Aleksey Tsoy <aleksey_tsoy@epam.com> Date: Fri, 21 Sep 2018 20:50:29 +0600 Subject: [PATCH 045/309] MAGETWO-91745: Product pages load blank page - Added automated test --- .../Mftf/Data/CatalogAttributeSetData.xml | 16 ++++++++++++ .../Catalog/Test/Mftf/Data/ProductData.xml | 14 +++++++++++ .../Metadata/catalog_attribute_set-meta.xml | 25 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml new file mode 100644 index 0000000000000..d78c03a51dd75 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="CatalogAttributeSet" type="CatalogAttributeSet"> + <data key="attribute_set_name" unique="suffix">test_set_</data> + <data key="attributeGroupId">7</data> + <data key="skeletonId">4</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 9ae551b69d388..3da3272567c9d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -301,6 +301,20 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> + <entity name="SimpleProductWithCustomAttributeSet" type="product"> + <data key="sku" unique="suffix">testSku</data> + <data key="type_id">simple</data> + <var key="attribute_set_id" entityKey="attribute_set_id" entityType="CatalogAttributeSet"/> + <data key="visibility">4</data> + <data key="name" unique="suffix">testProductName</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">testurlkey</data> + <data key="status">1</data> + <data key="weight">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> <entity name="productWithOptions" type="product"> <var key="sku" entityType="product" entityKey="sku" /> <data key="file">magento.jpg</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml new file mode 100644 index 0000000000000..9ef7b507812a0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="AddCatalogAttributeToAttributeSet" dataType="CatalogAttributeSet" type="create" auth="adminOauth" url="/V1/products/attribute-sets" method="POST"> + <contentType>application/json</contentType> + <object key="attributeSet" dataType="CatalogAttributeSet"> + <field key="attribute_set_name">string</field> + <field key="sort_order">integer</field> + </object> + <field key="skeletonId">integer</field> + </operation> + <operation name="DeleteCatalogAttributeFromAttributeSet" dataType="CatalogAttributeSet" type="delete" auth="adminOauth" url="/V1/products/attribute-sets/{attribute_set_id}" method="DELETE"> + <contentType>application/json</contentType> + </operation> + <operation name="GetCatalogAttributesFromDefaultSet" dataType="CatalogAttributeSet" type="get" auth="adminOauth" url="/V1/products/attribute-sets/{attribute_set_id}" method="GET"> + <contentType>application/json</contentType> + </operation> +</operations> \ No newline at end of file From b35b68130265163f2ed151ff94e0d9f7fc63d44c Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Fri, 21 Sep 2018 18:57:25 +0300 Subject: [PATCH 046/309] MAGETWO-91702: Shipping method Table Rates settings gets from wrong store - Get store id from quote for shipping rates --- .../Magento/Quote/Model/Quote/Address.php | 90 +++++++++---------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 3eb5d68885035..11beffe5711b9 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -965,6 +965,7 @@ public function collectShippingRates() /** * Request shipping rates for entire address or specified address item + * * Returns true if current selected shipping method code corresponds to one of the found rates * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item @@ -1002,7 +1003,7 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte /** * Store and website identifiers specified from StoreManager */ - $request->setStoreId($this->storeManager->getStore()->getId()); + $request->setStoreId($this->getQuote()->getStoreId() ?? $this->storeManager->getStore()->getId()); $request->setWebsiteId($this->storeManager->getWebsite()->getId()); $request->setFreeShipping($this->getFreeShipping()); /** @@ -1348,7 +1349,7 @@ public function getAllBaseTotalAmounts() /******************************* End Total Collector Interface *******************************************/ /** - * {@inheritdoc} + * @inheritdoc */ protected function _getValidationRulesBeforeSave() { @@ -1356,7 +1357,7 @@ protected function _getValidationRulesBeforeSave() } /** - * {@inheritdoc} + * @inheritdoc */ public function getCountryId() { @@ -1364,7 +1365,7 @@ public function getCountryId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCountryId($countryId) { @@ -1372,7 +1373,7 @@ public function setCountryId($countryId) } /** - * {@inheritdoc} + * @inheritdoc */ public function getStreet() { @@ -1381,7 +1382,7 @@ public function getStreet() } /** - * {@inheritdoc} + * @inheritdoc */ public function setStreet($street) { @@ -1389,7 +1390,7 @@ public function setStreet($street) } /** - * {@inheritdoc} + * @inheritdoc */ public function getCompany() { @@ -1397,7 +1398,7 @@ public function getCompany() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCompany($company) { @@ -1405,7 +1406,7 @@ public function setCompany($company) } /** - * {@inheritdoc} + * @inheritdoc */ public function getTelephone() { @@ -1413,7 +1414,7 @@ public function getTelephone() } /** - * {@inheritdoc} + * @inheritdoc */ public function setTelephone($telephone) { @@ -1421,7 +1422,7 @@ public function setTelephone($telephone) } /** - * {@inheritdoc} + * @inheritdoc */ public function getFax() { @@ -1429,7 +1430,7 @@ public function getFax() } /** - * {@inheritdoc} + * @inheritdoc */ public function setFax($fax) { @@ -1437,7 +1438,7 @@ public function setFax($fax) } /** - * {@inheritdoc} + * @inheritdoc */ public function getPostcode() { @@ -1445,7 +1446,7 @@ public function getPostcode() } /** - * {@inheritdoc} + * @inheritdoc */ public function setPostcode($postcode) { @@ -1453,7 +1454,7 @@ public function setPostcode($postcode) } /** - * {@inheritdoc} + * @inheritdoc */ public function getCity() { @@ -1461,7 +1462,7 @@ public function getCity() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCity($city) { @@ -1469,7 +1470,7 @@ public function setCity($city) } /** - * {@inheritdoc} + * @inheritdoc */ public function getFirstname() { @@ -1477,7 +1478,7 @@ public function getFirstname() } /** - * {@inheritdoc} + * @inheritdoc */ public function setFirstname($firstname) { @@ -1485,7 +1486,7 @@ public function setFirstname($firstname) } /** - * {@inheritdoc} + * @inheritdoc */ public function getLastname() { @@ -1493,7 +1494,7 @@ public function getLastname() } /** - * {@inheritdoc} + * @inheritdoc */ public function setLastname($lastname) { @@ -1501,7 +1502,7 @@ public function setLastname($lastname) } /** - * {@inheritdoc} + * @inheritdoc */ public function getMiddlename() { @@ -1509,7 +1510,7 @@ public function getMiddlename() } /** - * {@inheritdoc} + * @inheritdoc */ public function setMiddlename($middlename) { @@ -1517,7 +1518,7 @@ public function setMiddlename($middlename) } /** - * {@inheritdoc} + * @inheritdoc */ public function getPrefix() { @@ -1525,7 +1526,7 @@ public function getPrefix() } /** - * {@inheritdoc} + * @inheritdoc */ public function setPrefix($prefix) { @@ -1533,7 +1534,7 @@ public function setPrefix($prefix) } /** - * {@inheritdoc} + * @inheritdoc */ public function getSuffix() { @@ -1541,7 +1542,7 @@ public function getSuffix() } /** - * {@inheritdoc} + * @inheritdoc */ public function setSuffix($suffix) { @@ -1549,7 +1550,7 @@ public function setSuffix($suffix) } /** - * {@inheritdoc} + * @inheritdoc */ public function getVatId() { @@ -1557,7 +1558,7 @@ public function getVatId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setVatId($vatId) { @@ -1565,7 +1566,7 @@ public function setVatId($vatId) } /** - * {@inheritdoc} + * @inheritdoc */ public function getCustomerId() { @@ -1573,7 +1574,7 @@ public function getCustomerId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCustomerId($customerId) { @@ -1581,7 +1582,7 @@ public function setCustomerId($customerId) } /** - * {@inheritdoc} + * @inheritdoc */ public function getEmail() { @@ -1594,7 +1595,7 @@ public function getEmail() } /** - * {@inheritdoc} + * @inheritdoc */ public function setEmail($email) { @@ -1602,7 +1603,7 @@ public function setEmail($email) } /** - * {@inheritdoc} + * @inheritdoc */ public function setRegion($region) { @@ -1610,7 +1611,7 @@ public function setRegion($region) } /** - * {@inheritdoc} + * @inheritdoc */ public function setRegionId($regionId) { @@ -1618,7 +1619,7 @@ public function setRegionId($regionId) } /** - * {@inheritdoc} + * @inheritdoc */ public function setRegionCode($regionCode) { @@ -1626,7 +1627,7 @@ public function setRegionCode($regionCode) } /** - * {@inheritdoc} + * @inheritdoc */ public function getSameAsBilling() { @@ -1634,7 +1635,7 @@ public function getSameAsBilling() } /** - * {@inheritdoc} + * @inheritdoc */ public function setSameAsBilling($sameAsBilling) { @@ -1642,7 +1643,7 @@ public function setSameAsBilling($sameAsBilling) } /** - * {@inheritdoc} + * @inheritdoc */ public function getCustomerAddressId() { @@ -1650,7 +1651,7 @@ public function getCustomerAddressId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setCustomerAddressId($customerAddressId) { @@ -1681,9 +1682,7 @@ public function setSaveInAddressBook($saveInAddressBook) //@codeCoverageIgnoreEnd /** - * {@inheritdoc} - * - * @return \Magento\Quote\Api\Data\AddressExtensionInterface|null + * @inheritdoc */ public function getExtensionAttributes() { @@ -1691,10 +1690,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} - * - * @param \Magento\Quote\Api\Data\AddressExtensionInterface $extensionAttributes - * @return $this + * @inheritdoc */ public function setExtensionAttributes(\Magento\Quote\Api\Data\AddressExtensionInterface $extensionAttributes) { @@ -1712,7 +1708,7 @@ public function getShippingMethod() } /** - * {@inheritdoc} + * @inheritdoc */ protected function getCustomAttributesCodes() { From 14d9528852bd30b6f7545b3a22ac2d555a196543 Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Fri, 21 Sep 2018 15:14:11 -0300 Subject: [PATCH 047/309] Replaced {@inheritdoc} with the actual documentation in order to fix code style violations --- .../Framework/Stdlib/DateTime/Timezone.php | 71 +++++++++++++++---- .../Stdlib/DateTime/TimezoneInterface.php | 18 ++++- 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 59b299d176bd1..2bfa465c8268f 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -85,7 +85,9 @@ public function __construct( } /** - * {@inheritdoc} + * Return path to default timezone + * + * @return string */ public function getDefaultTimezonePath() { @@ -93,7 +95,9 @@ public function getDefaultTimezonePath() } /** - * {@inheritdoc} + * Retrieve timezone code + * + * @return string */ public function getDefaultTimezone() { @@ -101,7 +105,11 @@ public function getDefaultTimezone() } /** - * {@inheritdoc} + * Gets the scope config timezone + * + * @param string $scopeType + * @param string $scopeCode + * @return string */ public function getConfigTimezone($scopeType = null, $scopeCode = null) { @@ -113,7 +121,10 @@ public function getConfigTimezone($scopeType = null, $scopeCode = null) } /** - * {@inheritdoc} + * Retrieve ISO date format + * + * @param int $type + * @return string */ public function getDateFormat($type = \IntlDateFormatter::SHORT) { @@ -125,7 +136,9 @@ public function getDateFormat($type = \IntlDateFormatter::SHORT) } /** - * {@inheritdoc} + * Retrieve short date format with 4-digit year + * + * @return string */ public function getDateFormatWithLongYear() { @@ -137,7 +150,10 @@ public function getDateFormatWithLongYear() } /** - * {@inheritdoc} + * Retrieve ISO time format + * + * @param string $type + * @return string */ public function getTimeFormat($type = \IntlDateFormatter::SHORT) { @@ -149,7 +165,10 @@ public function getTimeFormat($type = \IntlDateFormatter::SHORT) } /** - * {@inheritdoc} + * Retrieve ISO datetime format + * + * @param string $type + * @return string */ public function getDateTimeFormat($type) { @@ -157,7 +176,13 @@ public function getDateTimeFormat($type) } /** - * {@inheritdoc} + * Create \DateTime object for current locale + * + * @param mixed $date + * @param string $locale + * @param bool $useTimezone + * @param bool $includeTime + * @return \DateTime */ public function date($date = null, $locale = null, $useTimezone = true, $includeTime = true) { @@ -191,7 +216,12 @@ public function date($date = null, $locale = null, $useTimezone = true, $include } /** - * {@inheritdoc} + * Create \DateTime object with date converted to scope timezone and scope Locale + * + * @param mixed $scope Information about scope + * @param string|integer|\DateTime|array|null $date date in UTC + * @param boolean $includeTime flag for including time to date + * @return \DateTime */ public function scopeDate($scope = null, $date = null, $includeTime = false) { @@ -204,7 +234,12 @@ public function scopeDate($scope = null, $date = null, $includeTime = false) } /** - * {@inheritdoc} + * Format date using current locale options and time zone. + * + * @param \DateTime|null $date + * @param int $format + * @param bool $showTime + * @return string */ public function formatDate($date = null, $format = \IntlDateFormatter::SHORT, $showTime = false) { @@ -218,7 +253,12 @@ public function formatDate($date = null, $format = \IntlDateFormatter::SHORT, $s } /** - * {@inheritdoc} + * Get scope timestamp + * + * Timestamp will be built with scope timezone settings + * + * @param mixed $scope + * @return int */ public function scopeTimeStamp($scope = null) { @@ -231,7 +271,12 @@ public function scopeTimeStamp($scope = null) } /** - * {@inheritdoc} + * Checks if current date of the given scope (in the scope timezone) is within the range + * + * @param int|string|\Magento\Framework\App\ScopeInterface $scope + * @param string|null $dateFrom + * @param string|null $dateTo + * @return bool */ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null) { @@ -300,6 +345,7 @@ public function formatDateTime( /** * Convert date from config timezone to Utc. + * * If pass \DateTime object as argument be sure that timezone is the same with config timezone * * @param string|\DateTimeInterface $date @@ -315,6 +361,7 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') /** * Convert date from config timezone to Utc. + * * If pass \DateTime object as argument be sure that timezone is the same with config timezone * * @param string|\DateTimeInterface $date diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php index 5a4b15d6cca0c..17ba759a4d763 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php @@ -80,6 +80,7 @@ public function scopeDate($scope = null, $date = null, $includeTime = false); /** * Get scope timestamp + * * Timestamp will be built with scope timezone settings * * @param mixed $scope @@ -121,6 +122,7 @@ public function getConfigTimezone($scopeType = null, $scopeCode = null); public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null); /** + * * @param string|\DateTimeInterface $date * @param int $dateType * @param int $timeType @@ -139,19 +141,29 @@ public function formatDateTime( ); /** + * Convert date from config timezone to Utc. + * + * If pass \DateTime object as argument be sure that timezone is the same with config timezone + * * @param string|\DateTimeInterface $date * @param string $format + * @throws LocalizedException * @return string - * @since 100.1.0 * @deprecated */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s'); /** - * @param $date + * Convert date from config timezone to Utc. + * + * If pass \DateTime object as argument be sure that timezone is the same with config timezone + * + * @param string|\DateTimeInterface $date * @param string $format * @param string $pattern - * @return mixed + * @throws LocalizedException + * @return string + * @deprecated */ public function convertConfigTimeToUtcWithPattern($date, $format = 'Y-m-d H:i:s', $pattern = 'Y-m-d H:i:s'); } From 79a968ba6bbb54ce8c3e5714bba164f924fc74f3 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Sat, 22 Sep 2018 10:28:58 +0000 Subject: [PATCH 048/309] graphQl-44: refactoring of format strategy Signed-off-by: vitaliyboyko <v.boyko@atwix.com> --- .../Resolver/Product/ProductTextAttribute.php | 33 +++++++++---------- .../ProductTextAttribute/FormatInterface.php | 2 +- .../ProductTextAttribute/FormatList.php | 28 +++++++++------- .../Product/ProductTextAttribute/Html.php | 2 +- .../Magento/CatalogGraphQl/etc/graphql/di.xml | 7 ++++ .../CatalogGraphQl/etc/schema.graphqls | 12 +++---- 6 files changed, 46 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php index f04609f91c86d..9c1212667acf3 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php @@ -8,7 +8,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Catalog\Model\Product; -use Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute\FormatFactory; +use Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute\FormatList; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\Resolver\Value; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; @@ -18,9 +18,12 @@ /** * Resolve rendered content for attributes where HTML content is allowed */ -class ProductTextareaAttribute implements ResolverInterface +class ProductTextAttribute implements ResolverInterface { - const DEFAULT_CONTENT_FORMAT_IDENTIFIER = 'html'; + /** + * @var FormatList + */ + private $formatList; /** * @var ValueFactory @@ -28,20 +31,20 @@ class ProductTextareaAttribute implements ResolverInterface private $valueFactory; /** - * @var FormatFactory + * @var string */ - private $formatFactory; + private $defaultFormat = 'html'; /** * @param ValueFactory $valueFactory - * @param FormatFactory $formatFactory + * @param FormatList $formatFactory */ public function __construct( ValueFactory $valueFactory, - FormatFactory $formatFactory + FormatList $formatFactory ) { $this->valueFactory = $valueFactory; - $this->formatFactory = $formatFactory; + $this->formatList = $formatFactory; } /** @@ -55,22 +58,16 @@ public function resolve( array $args = null ): Value { if (!isset($value['model'])) { - $result = function () { - return null; - }; + $result = []; return $this->valueFactory->create($result); } /* @var $product Product */ $product = $value['model']; $fieldName = $field->getName(); - $formatIdentifier = $args['format'] ?? self::DEFAULT_CONTENT_FORMAT_IDENTIFIER; - $format = $this->formatFactory->create($formatIdentifier); - $attribute = ['content' => $format->getContent($product, $fieldName)]; - - $result = function () use ($attribute) { - return $attribute; - }; + $formatIdentifier = $args['format'] ?? $this->defaultFormat; + $format = $this->formatList->create($formatIdentifier); + $result = ['content' => $format->getContent($product, $fieldName)]; return $this->valueFactory->create($result); } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatInterface.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatInterface.php index fae685b75c060..2cf702bf18466 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatInterface.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatInterface.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute; +namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute; use Magento\Catalog\Model\Product as ModelProduct; diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatList.php index 97e0be3763f1d..2e2f21d643092 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatList.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatList.php @@ -9,35 +9,39 @@ use Magento\Framework\ObjectManagerInterface; -class FormatFactory +class FormatList { /** * @var ObjectManagerInterface */ private $objectManager; + /** + * @var string + */ + private $formats; + /** * @param ObjectManagerInterface $objectManager + * @param array $formats */ - public function __construct(ObjectManagerInterface $objectManager) - { + public function __construct( + ObjectManagerInterface $objectManager, + array $formats + ) { $this->objectManager = $objectManager; + $this->formats = $formats; } /** * @param string $formatIdentifier - * @param array $data * @return FormatInterface */ - public function create(string $formatIdentifier, $data = []) : FormatInterface + public function create(string $formatIdentifier) : FormatInterface { - $formatClassName = 'Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute\\' . ucfirst($formatIdentifier); - $formatInstance = $this->objectManager->create($formatClassName, $data); - if (false == $formatInstance instanceof FormatInterface) { - throw new \InvalidArgumentException( - $formatInstance . ' is not instance of \Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute\FormatInterface' - ); - } + $formatClassName = 'Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute\\' . ucfirst($formatIdentifier); + $formatInstance = $this->objectManager->get($formatClassName); + return $formatInstance; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php index 8201c40427dc9..75c29a3f78fac 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextareaAttribute; +namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; use Magento\Catalog\Helper\Output as OutputHelper; diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml index 68a292ede6b4a..8d4ca97001d3d 100644 --- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml @@ -63,6 +63,13 @@ <argument name="collectionProcessor" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\ProductCollectionProcessor</argument> </arguments> </type> + <type name="Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute\FormatList"> + <arguments> + <argument name="formats" xsi:type="array"> + <item name="html" xsi:type="string">Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute\Html</item> + </argument> + </arguments> + </type> <virtualType name="Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ProductFilterProcessor" type="Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor"> <arguments> <argument name="customFilters" xsi:type="array"> diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index a9c4a1b4bd8ae..3f3282ed5f529 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -248,8 +248,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ id: Int @doc(description: "The ID number assigned to the product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\EntityIdToId") name: String @doc(description: "The product name. Customers use this name to identify the product.") sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") - description: ProductTextareaAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") - short_description: ProductTextareaAttribute @doc(description: "A short description of the product. Its use depends on the theme.") + description: ProductTextAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") + short_description: ProductTextAttribute @doc(description: "A short description of the product. Its use depends on the theme.") special_price: Float @doc(description: "The discounted price of the product") special_from_date: String @doc(description: "The beginning date that a product has a special price") special_to_date: String @doc(description: "The end date that a product has a special price") @@ -552,9 +552,9 @@ type SortFields @doc(description: "SortFields contains a default value for sort options: [SortField] @doc(description: "Available sort fields") } -type ProductTextareaAttribute { +type ProductTextAttribute { content ( - format: String! @doc(description: "The format of content") -): String - @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextareaAttribute") + format: String @doc(description: "The format of content") +): String @doc(description: "The format of content") + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") } From aef18a56e212eb0f55c5393c5a814b6bb15c9510 Mon Sep 17 00:00:00 2001 From: Aleksey Tsoy <aleksey_tsoy@epam.com> Date: Mon, 24 Sep 2018 19:04:00 +0600 Subject: [PATCH 049/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Added automated test --- .../StorefrontCategoryActionGroup.xml | 15 +++ .../Mftf/Data/CatalogStorefrontConfigData.xml | 41 ++++++ .../Metadata/catalog_configuration-meta.xml | 118 ++++++++++++++++++ .../Section/StorefrontCategoryMainSection.xml | 3 + ...orefrontRememberCategoryPaginationTest.xml | 68 ++++++++++ 5 files changed, 245 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml index c980c43b8f3af..e215aa81b3384 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml @@ -21,6 +21,21 @@ <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> + <actionGroup name="VerifyCategoryPageParameters"> + <arguments> + <argument name="category"/> + <argument name="mode" type="string"/> + <argument name="numOfProductsPerPage" type="string"/> + <argument name="sortBy" type="string" defaultValue="position"/> + </arguments> + <seeInCurrentUrl url="/{{category.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> + <seeInTitle userInput="{{category.name}}" stepKey="assertCategoryNameInTitle"/> + <see userInput="{{category.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> + <see userInput="{{mode}}" selector="{{StorefrontCategoryMainSection.modeGridIsActive}}" stepKey="assertViewMode"/> + <see userInput="{{numOfProductsPerPage}}" selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="assertNumberOfProductsPerPage"/> + <see userInput="{{sortBy}}" selector="{{StorefrontCategoryMainSection.sortedBy}}" stepKey="assertSortedBy"/> + </actionGroup> + <!-- Check the category page --> <actionGroup name="StorefrontCheckCategoryActionGroup"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml new file mode 100644 index 0000000000000..d8ec84013d93b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="RememberPaginationCatalogStorefrontConfig" type="catalog_storefront_config"> + <requiredEntity type="grid_per_page_values">GridPerPageValues</requiredEntity> + <requiredEntity type="remember_pagination">RememberCategoryPagination</requiredEntity> + </entity> + + <entity name="GridPerPageValues" type="grid_per_page_values"> + <data key="value">9,12,20,24</data> + </entity> + + <entity name="RememberCategoryPagination" type="remember_pagination"> + <data key="value">1</data> + </entity> + + <entity name="DefaultCatalogStorefrontConfiguration" type="default_catalog_storefront_config"> + <requiredEntity type="catalogStorefrontFlagZero">DefaultCatalogStorefrontFlagZero</requiredEntity> + <data key="list_allow_all">DefaultListAllowAll</data> + <data key="flat_catalog_product">DefaultFlatCatalogProduct</data> + </entity> + + <entity name="DefaultCatalogStorefrontFlagZero" type="catalogStorefrontFlagZero"> + <data key="value">0</data> + </entity> + + <entity name="DefaultListAllowAll" type="list_allow_all"> + <data key="value">0</data> + </entity> + + <entity name="DefaultFlatCatalogProduct" type="flat_catalog_product"> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml new file mode 100644 index 0000000000000..b1f2b43220b36 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_configuration-meta.xml @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CatalogStorefrontConfiguration" dataType="catalog_storefront_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/catalog/" method="POST"> + <object key="groups" dataType="catalog_storefront_config"> + <object key="frontend" dataType="catalog_storefront_config"> + <object key="fields" dataType="catalog_storefront_config"> + <object key="list_mode" dataType="list_mode"> + <field key="value">string</field> + </object> + <object key="grid_per_page_values" dataType="grid_per_page_values"> + <field key="value">string</field> + </object> + <object key="grid_per_page" dataType="grid_per_page"> + <field key="value">string</field> + </object> + <object key="list_per_page_values" dataType="list_per_page_values"> + <field key="value">string</field> + </object> + <object key="list_per_page" dataType="list_per_page"> + <field key="value">string</field> + </object> + <object key="default_sort_by" dataType="default_sort_by"> + <field key="value">string</field> + </object> + <object key="list_allow_all" dataType="list_allow_all"> + <field key="value">integer</field> + </object> + <object key="remember_pagination" dataType="remember_pagination"> + <field key="value">integer</field> + </object> + <object key="flat_catalog_category" dataType="flat_catalog_category"> + <field key="value">integer</field> + </object> + <object key="flat_catalog_product" dataType="flat_catalog_product"> + <field key="value">integer</field> + </object> + <object key="swatches_per_product" dataType="swatches_per_product"> + <field key="value">string</field> + </object> + <object key="show_swatches_in_product_list" dataType="show_swatches_in_product_list"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </operation> + + <operation name="DefaultCatalogStorefrontConfiguration" dataType="default_catalog_storefront_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/catalog/" method="POST"> + <object key="groups" dataType="default_catalog_storefront_config"> + <object key="frontend" dataType="default_catalog_storefront_config"> + <object key="fields" dataType="default_catalog_storefront_config"> + <object key="list_mode" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="grid_per_page_values" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="grid_per_page" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="list_per_page_values" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="list_per_page" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="default_sort_by" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="remember_pagination" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="flat_catalog_category" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="swatches_per_product" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="show_swatches_in_product_list" dataType="default_catalog_storefront_config"> + <object key="inherit" dataType="catalogStorefrontFlagZero"> + <field key="value">integer</field> + </object> + </object> + <object key="list_allow_all" dataType="list_allow_all"> + <field key="value">integer</field> + </object> + <object key="flat_catalog_product" dataType="flat_catalog_product"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index d484abf2069ff..03566be55ad2f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -9,6 +9,9 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategoryMainSection"> + <element name="perPage" type="select" selector="//*[@id='authenticationPopup']/following-sibling::div[3]//*[@id='limiter']"/> + <element name="sortedBy" type="select" selector="//*[@id='authenticationPopup']/following-sibling::div[1]//*[@id='sorter']"/> + <element name="modeGridIsActive" type="text" selector="//*[@id='authenticationPopup']/following-sibling::div[1]//*[@class='modes']/strong[@class='modes-mode active mode-grid']/span"/> <element name="modeListButton" type="button" selector="#mode-list"/> <element name="CategoryTitle" type="text" selector="#page-title-heading span"/> <element name="ProductItemInfo" type="button" selector=".product-item-info"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml new file mode 100644 index 0000000000000..0ed61b8636c4f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontRememberCategoryPaginationTest"> + <annotations> + <title value="Verify that Number of Products per page retained when visiting a different category"/> + <stories value="MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category"/> + <description value="Verify that Number of Products per page retained when visiting a different category"/> + <features value="Catalog"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94210"/> + <group value="Catalog"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="defaultCategory1"/> + <createData entity="SimpleProduct" stepKey="simpleProduct1"> + <requiredEntity createDataKey="defaultCategory1"/> + </createData> + + <createData entity="_defaultCategory" stepKey="defaultCategory2"/> + <createData entity="SimpleProduct" stepKey="simpleProduct2"> + <requiredEntity createDataKey="defaultCategory2"/> + </createData> + + <createData entity="RememberPaginationCatalogStorefrontConfig" stepKey="setRememberPaginationCatalogStorefrontConfig"/> + </before> + + <actionGroup ref="GoToStorefrontCategoryPageByParameters" stepKey="GoToStorefrontCategory1Page"> + <argument name="category" value="$$defaultCategory1.custom_attributes[url_key]$$"/> + <argument name="mode" value="grid"/> + <argument name="numOfProductsPerPage" value="12"/> + </actionGroup> + + <actionGroup ref="VerifyCategoryPageParameters" stepKey="verifyCategory1PageParameters"> + <argument name="category" value="$$defaultCategory1$$"/> + <argument name="mode" value="grid"/> + <argument name="numOfProductsPerPage" value="12"/> + </actionGroup> + + <amOnPage url="{{StorefrontCategoryPage.url($$defaultCategory2.name$$)}}" stepKey="navigateToCategory2Page"/> + <waitForPageLoad stepKey="waitForCategory2PageToLoad"/> + + <actionGroup ref="VerifyCategoryPageParameters" stepKey="verifyCategory2PageParameters"> + <argument name="category" value="$$defaultCategory2$$"/> + <argument name="mode" value="grid"/> + <argument name="numOfProductsPerPage" value="12"/> + </actionGroup> + + <after> + <createData entity="DefaultCatalogStorefrontConfiguration" stepKey="setDefaultCatalogStorefrontConfiguration"/> + + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="defaultCategory1" stepKey="deleteCategory1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="defaultCategory2" stepKey="deleteCategory2"/> + + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + </after> + </test> +</tests> From 68933c1b128f09b74298f81822879e2bee63d68a Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <mikalai_shostka@epam.com> Date: Mon, 24 Sep 2018 17:35:47 +0300 Subject: [PATCH 050/309] MAGETWO-70303: [GITHUB] Anchor categories are showing products of disabled subcategories #9002 - Change index table and temporary table --- .../Category/Product/AbstractAction.php | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php index 6a499883ac723..d312b78e74059 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php @@ -499,7 +499,7 @@ protected function createAnchorSelect(Store $store) 'cc2.parent_id = cc.entity_id AND cc.entity_id NOT IN (' . implode( ',', $rootCatIds - ) . ')', + ) . ') AND cc2.store_id = ' . $store->getId(), [] )->joinInner( ['ccp' => $this->getTable('catalog_category_product')], @@ -586,6 +586,8 @@ protected function createAnchorSelect(Store $store) } /** + * Get temporary table name + * * Get temporary table name for concurrent indexing in persistent connection * Temp table name is NOT shared between action instances and each action has it's own temp tree index * @@ -629,6 +631,12 @@ protected function makeTempCategoryTreeIndex() null, ['nullable' => false, 'unsigned' => true] ); + $temporaryTable->addColumn( + 'store_id', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['nullable' => false, 'unsigned' => true] + ); // Each entry will be unique. $temporaryTable->addIndex( 'idx_primary', @@ -642,6 +650,11 @@ protected function makeTempCategoryTreeIndex() ['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_INDEX] ); + $temporaryTable->addIndex( + 'store_id', + ['store_id'], + ['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_INDEX] + ); // Drop the temporary table in case it already exists on this (persistent?) connection. $this->connection->dropTemporaryTable($temporaryName); $this->connection->createTemporaryTable($temporaryTable); @@ -659,11 +672,23 @@ protected function makeTempCategoryTreeIndex() */ protected function fillTempCategoryTreeIndex($temporaryName) { + $isActiveAttributeId = $this->config->getAttribute( + \Magento\Catalog\Model\Category::ENTITY, + 'is_active' + )->getId(); + $categoryMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\CategoryInterface::class); + $categoryLinkField = $categoryMetadata->getLinkField(); $selects = $this->prepareSelectsByRange( $this->connection->select() ->from( ['c' => $this->getTable('catalog_category_entity')], ['entity_id', 'path'] + )->joinInner( + ['ccei' => $this->getTable('catalog_category_entity_int')], + 'ccei.' . $categoryLinkField . ' = c.' . $categoryLinkField . + ' AND ccei.attribute_id = ' . $isActiveAttributeId . + ' AND ccei.value = 1', + ['store_id'] ), 'entity_id' ); @@ -674,13 +699,13 @@ protected function fillTempCategoryTreeIndex($temporaryName) foreach ($this->connection->fetchAll($select) as $category) { foreach (explode('/', $category['path']) as $parentId) { if ($parentId !== $category['entity_id']) { - $values[] = [$parentId, $category['entity_id']]; + $values[] = [$parentId, $category['entity_id'], $category['store_id']]; } } } if (count($values) > 0) { - $this->connection->insertArray($temporaryName, ['parent_id', 'child_id'], $values); + $this->connection->insertArray($temporaryName, ['parent_id', 'child_id', 'store_id'], $values); } } } From 2034958405344006414e44804b914dd4cbdb9e46 Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan <Karen_Mkhitaryan@epam.com> Date: Tue, 25 Sep 2018 12:58:19 +0400 Subject: [PATCH 051/309] MAGETWO-91628: Bundle product price doubled when switching currency - Add automation test --- ...tAddProductToCartFromBundleActionGroup.xml | 24 ++++++ .../Mftf/Section/StorefrontBundledSection.xml | 5 +- ...urrencyChangingBundleProductInCartTest.xml | 77 +++++++++++++++++++ .../Mftf/Page/ConfigCurrencySetupPage.xml | 13 ++++ .../Mftf/Section/CurrencySetupSection.xml | 14 ++++ 5 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Page/ConfigCurrencySetupPage.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Section/CurrencySetupSection.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml new file mode 100644 index 0000000000000..441303e8f1b84 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StoreFrontAddProductToCartFromBundleWithCurrencyActionGroup"> + <arguments> + <argument name="product"/> + <argument name="currency" type="string" defaultValue="US Dollar"/> + </arguments> + <click selector="{{StorefrontBundledSection.currencyTrigger}}" stepKey="openCurrencyTrigger"/> + <click selector="{{StorefrontBundledSection.currency(currency)}}" stepKey="chooseCurrency"/> + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> + <waitForPageLoad stepKey="waitForBundleOpen"/> + <checkOption selector="{{StorefrontBundledSection.productInBundle(product.name)}}" stepKey="chooseProduct"/> + <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="addToCartProduct"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml index f724f9bbfe1bd..5646ecf812a3f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontBundledSection"> <element name="nthBundledOption" type="input" selector=".option:nth-of-type({{numOption}}) .choice:nth-of-type({{numOptionSelect}}) input" parameterized="true"/> <element name="addToCart" type="button" selector="#bundle-slide" timeout="30"/> @@ -24,10 +24,13 @@ <element name="bundleProductName" type="text" selector="//*[@id='maincontent']//span[@itemprop='name']"/> <element name="pageNotFound" type="text" selector="//h1[@class='page-title']//span[contains(., 'Whoops, our bad...')]"/> <element name="dropDownOptionOneProducts" type="select" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//select" parameterized="true"/> + <element name="productInBundle" type="select" selector="//label//span[contains(text(), '{{productName}}')]" parameterized="true"/> <element name="dropDownOptionOneQuantity" type="input" selector="//span[contains(text(), '{{productName}}')]/../..//input" parameterized="true"/> <element name="radioButtonOptionTwoProducts" type="checkbox" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//div[@class='field choice'][{{productNumber}}]/input" parameterized="true"/> <element name="radioButtonOptionTwoQuantity" type="input" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//div[@class='field qty qty-holder']//input" parameterized="true"/> <element name="checkboxOptionThreeProducts" type="checkbox" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//div[@class='field choice'][{{productNumber}}]/input" parameterized="true"/> <element name="multiselectOptionFourProducts" type="multiselect" selector="//label//span[contains(text(), '{{productName}}')]/../..//select[@multiple='multiple']" parameterized="true"/> + <element name="currencyTrigger" type="select" selector="#switcher-currency-trigger" timeout="30"/> + <element name="currency" type="select" selector="//a[text()='{{arg}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml new file mode 100644 index 0000000000000..b7e716dc15ece --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CurrencyChangingBundleProductInCartTest"> + <annotations> + <features value="Bundle"/> + <stories value="Check that after changing currency price of cart is correct when the bundle product added to the cart"/> + <title value="User should be able change the currency and get right price in cart when the bundle product added to the cart"/> + <description value="User should be able change the currency and add one more product in cart and get right price in previous currency"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94467"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <!-- Delete the bundled product --> + <actionGroup stepKey="deleteBundle" ref="deleteProductUsingProductGrid"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="ClearFiltersAfter"/> + <!--Clear Configs--> + <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> + <waitForPageLoad stepKey="waitForAdminLoginPageLoad"/> + <amOnPage url="{{ConfigCurrencySetupPage.url}}" stepKey="navigateToConfigCurrencySetupPage"/> + <waitForPageLoad stepKey="waitForConfigCurrencySetupPageForUnselectEuroCurrency"/> + <unselectOption selector="{{CurrencySetupSection.allowCurrencies}}" userInput="Euro" stepKey="unselectEuro"/> + <click stepKey="saveUnselectedConfigs" selector="{{AdminConfigSection.saveButton}}"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + <!--Go to bundle product creation page--> + <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage"/> + <waitForPageLoad stepKey="waitForBundleProductCreatePageToLoad"/> + <actionGroup ref="fillMainBundleProductForm" stepKey="fillMainFieldsForBundle"/> + <!-- Add Option, a "Radio Buttons" type option --> + <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts2"> + <argument name="x" value="0"/> + <argument name="n" value="1"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="optionTitle" value="Option"/> + <argument name="inputType" value="radio"/> + </actionGroup> + <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '0')}}" stepKey="userDefinedQuantitiyOptionProduct0"/> + <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '1')}}" stepKey="userDefinedQuantitiyOptionProduct1"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <amOnPage url="{{ConfigCurrencySetupPage.url}}" stepKey="navigateToConfigCurrencySetupPage"/> + <waitForPageLoad stepKey="waitForConfigCurrencySetupPage"/> + <selectOption selector="{{CurrencySetupSection.allowCurrencies}}" parameterArray="['Euro', 'US Dollar']" stepKey="selectCurrencies"/> + <click stepKey="saveConfigs" selector="{{AdminConfigSection.saveButton}}"/> + <!-- Go to storefront BundleProduct --> + <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + <actionGroup ref="StoreFrontAddProductToCartFromBundleWithCurrencyActionGroup" stepKey="addProduct1ToCartAndChangeCurrencyToEuro"> + <argument name="product" value="$$simpleProduct1$$"/> + <argument name="currency" value="EUR - Euro"/> + </actionGroup> + <actionGroup ref="StoreFrontAddProductToCartFromBundleWithCurrencyActionGroup" stepKey="addProduct2ToCartAndChangeCurrencyToUSD"> + <argument name="product" value="$$simpleProduct2$$"/> + <argument name="currency" value="USD - US Dollar"/> + </actionGroup> + <click stepKey="openMiniCart" selector="{{StorefrontMinicartSection.showCart}}"/> + <waitForPageLoad stepKey="waitForMiniCart"/> + <see stepKey="seeCartSubtotal" userInput="$12,300.00"/> + </test> +</tests> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Page/ConfigCurrencySetupPage.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Page/ConfigCurrencySetupPage.xml new file mode 100644 index 0000000000000..f523cb58d3bb6 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Page/ConfigCurrencySetupPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="ConfigCurrencySetupPage" url="admin/system_config/edit/section/currency" area="admin" module="Magento_Config"> + </page> +</pages> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/CurrencySetupSection.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/CurrencySetupSection.xml new file mode 100644 index 0000000000000..16101409ad7e8 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/CurrencySetupSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CurrencySetupSection"> + <element name="allowCurrencies" type="select" selector="#currency_options_allow"/> + </section> +</sections> \ No newline at end of file From 25cedef8c15ead70195ec7117f4995fbcd5bddf2 Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan <Karen_Mkhitaryan@epam.com> Date: Tue, 25 Sep 2018 13:03:19 +0400 Subject: [PATCH 052/309] MAGETWO-91628: Bundle product price doubled when switching currency - Revert xsi --- .../Bundle/Test/Mftf/Section/StorefrontBundledSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml index 5646ecf812a3f..ad74cfff54bd9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="StorefrontBundledSection"> <element name="nthBundledOption" type="input" selector=".option:nth-of-type({{numOption}}) .choice:nth-of-type({{numOptionSelect}}) input" parameterized="true"/> <element name="addToCart" type="button" selector="#bundle-slide" timeout="30"/> From 1b8cd089461f20131a8b5e5784238918138e13ae Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <mikalai_shostka@epam.com> Date: Tue, 25 Sep 2018 15:25:55 +0300 Subject: [PATCH 053/309] MAGETWO-70303: [GITHUB] Anchor categories are showing products of disabled subcategories #9002 - Change index table and temporary table --- .../Category/Product/AbstractAction.php | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php index d312b78e74059..a1358c02ce9fc 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php @@ -123,6 +123,11 @@ abstract class AbstractAction */ private $queryGenerator; + /** + * @var int + */ + private $currentStoreId = 0; + /** * @param ResourceConnection $resource * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -164,6 +169,7 @@ protected function reindex() { foreach ($this->storeManager->getStores() as $store) { if ($this->getPathFromCategoryId($store->getRootCategoryId())) { + $this->currentStoreId = $store->getId(); $this->reindexRootCategory($store); $this->reindexAnchorCategories($store); $this->reindexNonAnchorCategories($store); @@ -499,7 +505,7 @@ protected function createAnchorSelect(Store $store) 'cc2.parent_id = cc.entity_id AND cc.entity_id NOT IN (' . implode( ',', $rootCatIds - ) . ') AND cc2.store_id = ' . $store->getId(), + ) . ')', [] )->joinInner( ['ccp' => $this->getTable('catalog_category_product')], @@ -631,12 +637,6 @@ protected function makeTempCategoryTreeIndex() null, ['nullable' => false, 'unsigned' => true] ); - $temporaryTable->addColumn( - 'store_id', - \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, - null, - ['nullable' => false, 'unsigned' => true] - ); // Each entry will be unique. $temporaryTable->addIndex( 'idx_primary', @@ -649,12 +649,6 @@ protected function makeTempCategoryTreeIndex() ['child_id'], ['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_INDEX] ); - - $temporaryTable->addIndex( - 'store_id', - ['store_id'], - ['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_INDEX] - ); // Drop the temporary table in case it already exists on this (persistent?) connection. $this->connection->dropTemporaryTable($temporaryName); $this->connection->createTemporaryTable($temporaryTable); @@ -684,11 +678,19 @@ protected function fillTempCategoryTreeIndex($temporaryName) ['c' => $this->getTable('catalog_category_entity')], ['entity_id', 'path'] )->joinInner( - ['ccei' => $this->getTable('catalog_category_entity_int')], - 'ccei.' . $categoryLinkField . ' = c.' . $categoryLinkField . - ' AND ccei.attribute_id = ' . $isActiveAttributeId . - ' AND ccei.value = 1', - ['store_id'] + ['ccacd' => $this->getTable('catalog_category_entity_int')], + 'ccacd.' . $categoryLinkField . ' = c.' . $categoryLinkField . ' AND ccacd.store_id = 0' . + ' AND ccacd.attribute_id = ' . $isActiveAttributeId, + [] + )->joinLeft( + ['ccacs' => $this->getTable('catalog_category_entity_int')], + 'ccacs.' . $categoryLinkField . ' = c.' . $categoryLinkField + . ' AND ccacs.attribute_id = ccacd.attribute_id AND ccacs.store_id = ' . + $this->currentStoreId, + [] + )->where( + $this->connection->getIfNullSql('ccacs.value', 'ccacd.value') . ' = ?', + 1 ), 'entity_id' ); @@ -699,13 +701,13 @@ protected function fillTempCategoryTreeIndex($temporaryName) foreach ($this->connection->fetchAll($select) as $category) { foreach (explode('/', $category['path']) as $parentId) { if ($parentId !== $category['entity_id']) { - $values[] = [$parentId, $category['entity_id'], $category['store_id']]; + $values[] = [$parentId, $category['entity_id']]; } } } if (count($values) > 0) { - $this->connection->insertArray($temporaryName, ['parent_id', 'child_id', 'store_id'], $values); + $this->connection->insertArray($temporaryName, ['parent_id', 'child_id'], $values); } } } From ca3f8ba315ec2c810ee077ba1bacf7bcf371026a Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Tue, 25 Sep 2018 17:07:29 +0300 Subject: [PATCH 054/309] MAGETWO-91628: Bundle product price doubled when switching currency - Fixed unit tests; --- .../Bundle/Test/Unit/Model/Product/TypeTest.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php index 4bbf5641c55d3..9819abf62a273 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php @@ -513,10 +513,6 @@ function ($key) use ($optionCollection, $selectionCollection) { ->method('getSelectionId') ->willReturn(314); - $this->priceCurrency->expects($this->once()) - ->method('convert') - ->willReturn(3.14); - $result = $this->model->prepareForCartAdvanced($buyRequest, $product); $this->assertEquals([$product, $productType], $result); } @@ -737,10 +733,6 @@ function ($key) use ($optionCollection, $selectionCollection) { ->method('prepareForCart') ->willReturn([]); - $this->priceCurrency->expects($this->once()) - ->method('convert') - ->willReturn(3.14); - $result = $this->model->prepareForCartAdvanced($buyRequest, $product); $this->assertEquals('We can\'t add this item to your shopping cart right now.', $result); } @@ -961,10 +953,6 @@ function ($key) use ($optionCollection, $selectionCollection) { ->method('prepareForCart') ->willReturn('string'); - $this->priceCurrency->expects($this->once()) - ->method('convert') - ->willReturn(3.14); - $result = $this->model->prepareForCartAdvanced($buyRequest, $product); $this->assertEquals('string', $result); } From 9a1775623fd7cfb3d5e9204033bdb70e1f2483aa Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Wed, 26 Sep 2018 16:02:54 +0300 Subject: [PATCH 055/309] MAGETWO-94991: [2.3] Magnifier does not work with Windows Chrome/FF --- lib/web/magnifier/magnify.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/web/magnifier/magnify.js b/lib/web/magnifier/magnify.js index 1fb73ea28bff1..9d673092b806c 100644 --- a/lib/web/magnifier/magnify.js +++ b/lib/web/magnifier/magnify.js @@ -35,13 +35,6 @@ define([ allowZoomOut = false, allowZoomIn = true; - if (isTouchEnabled) { - $(element).on('fotorama:showend fotorama:load', function () { - $(magnifierSelector).remove(); - $(magnifierZoomSelector).remove(); - }); - } - (function () { var style = document.documentElement.style, transitionEnabled = style.transition !== undefined || From 75ccb2485aa5543f02e8ea2aac8e7f3c93d8c2c8 Mon Sep 17 00:00:00 2001 From: vtymchynskyi <vtymchynskyi@magento.com> Date: Wed, 26 Sep 2018 15:07:58 +0300 Subject: [PATCH 056/309] MAGETWO-93394: Table rate shipment is taken from wrong website ID when creating an order through the admin --- .../Magento/Sales/Model/AdminOrder/Create.php | 17 ++- .../_files/tablerates_second_website.php | 40 +++++ .../tablerates_second_website_rollback.php | 13 ++ .../Controller/Adminhtml/Order/CreateTest.php | 137 +++++++++++++++++- .../_files/websites_different_countries.php | 14 +- 5 files changed, 215 insertions(+), 6 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website.php create mode 100644 dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website_rollback.php diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index b690395ebab7c..088ad5a61f6c3 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -14,6 +14,7 @@ use Magento\Quote\Model\Quote\Item; use Magento\Sales\Api\Data\OrderAddressInterface; use Magento\Sales\Model\Order; +use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; /** @@ -242,6 +243,11 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ */ private $dataObjectConverter; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -273,6 +279,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ * @param array $data * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @param ExtensibleDataObjectConverter|null $dataObjectConverter + * @param StoreManagerInterface $storeManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -305,7 +312,8 @@ public function __construct( \Magento\Quote\Model\QuoteFactory $quoteFactory, array $data = [], \Magento\Framework\Serialize\Serializer\Json $serializer = null, - ExtensibleDataObjectConverter $dataObjectConverter = null + ExtensibleDataObjectConverter $dataObjectConverter = null, + StoreManagerInterface $storeManager = null ) { $this->_objectManager = $objectManager; $this->_eventManager = $eventManager; @@ -339,6 +347,7 @@ public function __construct( parent::__construct($data); $this->dataObjectConverter = $dataObjectConverter ?: ObjectManager::getInstance() ->get(ExtensibleDataObjectConverter::class); + $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class); } /** @@ -416,7 +425,8 @@ public function setRecollect($flag) /** * Recollect totals for customer cart. - * Set recollect totals flag for quote + * + * Set recollect totals flag for quote. * * @return $this */ @@ -1333,6 +1343,7 @@ protected function _createCustomerForm(\Magento\Customer\Api\Data\CustomerInterf /** * Set and validate Quote address + * * All errors added to _errors * * @param \Magento\Quote\Model\Quote\Address $address @@ -1536,6 +1547,8 @@ public function resetShippingMethod() */ public function collectShippingRates() { + $store = $this->getQuote()->getStore(); + $this->storeManager->setCurrentStore($store); $this->getQuote()->getShippingAddress()->setCollectShippingRates(true); $this->collectRates(); diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website.php new file mode 100644 index 0000000000000..52551e6dc96d8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\ResourceConnection; +use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$website = $websiteRepository->get('test'); + +/** @var ResourceConnection $resource */ +$resource = $objectManager->get(ResourceConnection::class); +$connection = $resource->getConnection(); +$resourceModel = $objectManager->create(Tablerate::class); +$entityTable = $resourceModel->getTable('shipping_tablerate'); +$data = + [ + 'website_id' => $website->getId(), + 'dest_country_id' => 'US', + 'dest_region_id' => 0, + 'dest_zip' => '*', + 'condition_name' => 'package_qty', + 'condition_value' => 1, + 'price' => 20, + 'cost' => 20 + ]; +$connection->query( + "INSERT INTO {$entityTable} (`website_id`, `dest_country_id`, `dest_region_id`, `dest_zip`, `condition_name`," + . "`condition_value`, `price`, `cost`) VALUES (:website_id, :dest_country_id, :dest_region_id, :dest_zip," + . " :condition_name, :condition_value, :price, :cost);", + $data +); diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website_rollback.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website_rollback.php new file mode 100644 index 0000000000000..9606b0eb605fd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website_rollback.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$resource = $objectManager->get(\Magento\Framework\App\ResourceConnection::class); +$connection = $resource->getConnection(); +$resourceModel = $objectManager->create(\Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate::class); +$entityTable = $resourceModel->getTable('shipping_tablerate'); +$connection->query("DELETE FROM {$entityTable};"); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php index efb1b12fc60ed..a07616474a410 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php @@ -6,15 +6,26 @@ namespace Magento\Sales\Controller\Adminhtml\Order; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Backend\Model\Session\Quote; +use Magento\Backend\Model\Session\Quote as SessionQuote; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Quote\Model\Quote; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Api\Data\WebsiteInterface; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\ScopeInterface; /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class CreateTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -70,6 +81,57 @@ public function testLoadBlockActionData() } /** + * Tests that shipping method 'Table rates' shows rates according to selected website. + * + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Quote/Fixtures/quote_sec_website.php + * @magentoDataFixture Magento/OfflineShipping/_files/tablerates_second_website.php + * @magentoDbIsolation disabled + */ + public function testLoadBlockShippingMethod() + { + $store = $this->getStore('fixture_second_store'); + + /** @var MutableScopeConfigInterface $mutableScopeConfig */ + $mutableScopeConfig = $this->_objectManager->get(MutableScopeConfigInterface::class); + $mutableScopeConfig->setValue( + 'carriers/tablerate/active', + 1, + ScopeInterface::SCOPE_STORE, + $store->getCode() + ); + $mutableScopeConfig->setValue( + 'carriers/tablerate/condition_name', + 'package_qty', + ScopeInterface::SCOPE_STORE, + $store->getCode() + ); + + $website = $this->getWebsite('test'); + $customer = $this->getCustomer('customer.web@example.com', (int)$website->getId()); + $quote = $this->getQuoteById('0000032134'); + $session = $this->_objectManager->get(SessionQuote::class); + $session->setQuoteId($quote->getId()); + + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue( + [ + 'customer_id' => $customer->getId(), + 'collect_shipping_rates' => 1, + 'store_id' => $store->getId(), + 'json' => true + ] + ); + $this->dispatch('backend/sales/order_create/loadBlock/block/shipping_method'); + $body = $this->getResponse()->getBody(); + $expectedTableRatePrice = '<span class=\"price\">$20.00<\/span>'; + + $this->assertContains($expectedTableRatePrice, $body, ''); + } + + /** + * Tests LoadBlock actions. + * * @param string $block Block name. * @param string $expected Contains HTML. * @@ -100,6 +162,8 @@ public function loadBlockActionsDataProvider() } /** + * Tests action items. + * * @magentoDataFixture Magento/Catalog/_files/product_simple.php */ public function testLoadBlockActionItems() @@ -174,6 +238,8 @@ public function testIndexAction() } /** + * Tests ACL. + * * @param string $actionName * @param boolean $reordered * @param string $expectedResult @@ -183,7 +249,7 @@ public function testIndexAction() */ public function testGetAclResource($actionName, $reordered, $expectedResult) { - $this->_objectManager->get(Quote::class)->setReordered($reordered); + $this->_objectManager->get(SessionQuote::class)->setReordered($reordered); $orderController = $this->_objectManager->get( \Magento\Sales\Controller\Adminhtml\Order\Stub\OrderCreateStub::class ); @@ -278,7 +344,7 @@ public function testSyncBetweenQuoteAddresses() $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); $quote = $quoteRepository->getActiveForCustomer($customer->getId()); - $session = $this->_objectManager->get(Quote::class); + $session = $this->_objectManager->get(SessionQuote::class); $session->setQuoteId($quote->getId()); $data = [ @@ -314,4 +380,69 @@ public function testSyncBetweenQuoteAddresses() self::assertEquals($data['city'], $shippingAddress->getCity()); self::assertEquals($data['street'], $shippingAddress->getStreet()); } + + /** + * Gets quote entity by reserved order id. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuoteById(string $reservedOrderId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $repository */ + $repository = $this->_objectManager->get(CartRepositoryInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Gets website entity. + * + * @param string $code + * @return WebsiteInterface + * @throws NoSuchEntityException + */ + private function getWebsite(string $code): WebsiteInterface + { + /** @var WebsiteRepositoryInterface $repository */ + $repository = $this->_objectManager->get(WebsiteRepositoryInterface::class); + return $repository->get($code); + } + + /** + * Gets customer entity. + * + * @param string $email + * @param int $websiteId + * @return CustomerInterface + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getCustomer(string $email, int $websiteId): CustomerInterface + { + /** @var CustomerRepositoryInterface $repository */ + $repository = $this->_objectManager->get(CustomerRepositoryInterface::class); + return $repository->get($email, $websiteId); + } + + /** + * Gets store by code. + * + * @param string $code + * @return StoreInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getStore(string $code): StoreInterface + { + /** @var StoreRepositoryInterface $repository */ + $repository = $this->_objectManager->get(StoreRepositoryInterface::class); + return $repository->get($code); + } } diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/websites_different_countries.php b/dev/tests/integration/testsuite/Magento/Store/_files/websites_different_countries.php index 04223d1353314..970b1619f0214 100644 --- a/dev/tests/integration/testsuite/Magento/Store/_files/websites_different_countries.php +++ b/dev/tests/integration/testsuite/Magento/Store/_files/websites_different_countries.php @@ -10,6 +10,7 @@ use Magento\Store\Model\Store; use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndex; use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Store\Model\Group; $objectManager = Bootstrap::getObjectManager(); //Creating second website with a store. @@ -21,12 +22,23 @@ $website->setData([ 'code' => 'test', 'name' => 'Test Website', - 'default_group_id' => '1', 'is_default' => '0', ]); $website->save(); } +/** + * @var Group $storeGroup + */ +$storeGroup = $objectManager->create(Group::class); +$storeGroup->setCode('some_group') + ->setName('custom store group') + ->setWebsite($website); +$storeGroup->save($storeGroup); + +$website->setDefaultGroupId($storeGroup->getId()); +$website->save($website); + $websiteId = $website->getId(); $store = $objectManager->create(Store::class); $store->load('fixture_second_store', 'code'); From 476f14ff89d6be14d1b3d8c950713c00d5802ec0 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Wed, 26 Sep 2018 16:35:20 +0000 Subject: [PATCH 057/309] graphQl-44: refactoring of resolver Signed-off-by: vitaliyboyko <v.boyko@atwix.com> --- .../Resolver/Product/ProductTextAttribute.php | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php index 9c1212667acf3..7649464e2e1ca 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php @@ -10,8 +10,7 @@ use Magento\Catalog\Model\Product; use Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute\FormatList; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\Resolver\Value; -use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -25,26 +24,18 @@ class ProductTextAttribute implements ResolverInterface */ private $formatList; - /** - * @var ValueFactory - */ - private $valueFactory; - /** * @var string */ private $defaultFormat = 'html'; /** - * @param ValueFactory $valueFactory - * @param FormatList $formatFactory + * @param FormatList $formatList */ public function __construct( - ValueFactory $valueFactory, - FormatList $formatFactory + FormatList $formatList ) { - $this->valueFactory = $valueFactory; - $this->formatList = $formatFactory; + $this->formatList = $formatList; } /** @@ -56,10 +47,9 @@ public function resolve( ResolveInfo $info, array $value = null, array $args = null - ): Value { + ) { if (!isset($value['model'])) { - $result = []; - return $this->valueFactory->create($result); + throw new GraphQlInputException(__('"model" value should be specified')); } /* @var $product Product */ @@ -69,6 +59,6 @@ public function resolve( $format = $this->formatList->create($formatIdentifier); $result = ['content' => $format->getContent($product, $fieldName)]; - return $this->valueFactory->create($result); + return $result; } } From 5d5ae032b48280118d8681a30d9d4abd1d2027fb Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Thu, 27 Sep 2018 15:16:51 +0300 Subject: [PATCH 058/309] MAGETWO-91679: Bundled SKUs are being assembled based on the product ID number - Fixed an issue with incorrect genereation of dynamic SKU for bundle products; --- app/code/Magento/Bundle/Model/Product/Type.php | 7 +++++-- .../Bundle/Test/Unit/Model/Product/TypeTest.php | 11 +++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php index 92bada8094c7e..67441cd944c4f 100644 --- a/app/code/Magento/Bundle/Model/Product/Type.php +++ b/app/code/Magento/Bundle/Model/Product/Type.php @@ -308,8 +308,11 @@ public function getSku($product) $selectionIds = $this->serializer->unserialize($customOption->getValue()); if (!empty($selectionIds)) { $selections = $this->getSelectionsByIds($selectionIds, $product); - foreach ($selections->getItems() as $selection) { - $skuParts[] = $selection->getSku(); + foreach ($selectionIds as $selectionId) { + $entity = $selections->getItemByColumnValue('selection_id', $selectionId); + if ($entity->getEntityId()) { + $skuParts[] = $entity->getSku(); + } } } } diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php index 4bbf5641c55d3..1b9cafbe8c198 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php @@ -1595,7 +1595,7 @@ public function testGetSkuWithoutType() ->disableOriginalConstructor() ->getMock(); $selectionItemMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) - ->setMethods(['getSku', '__wakeup']) + ->setMethods(['getSku', 'getEntityId', '__wakeup']) ->disableOriginalConstructor() ->getMock(); @@ -1623,9 +1623,12 @@ public function testGetSkuWithoutType() ->will($this->returnValue($serializeIds)); $selectionMock = $this->getSelectionsByIdsMock($selectionIds, $productMock, 5, 6); $selectionMock->expects(($this->any())) - ->method('getItems') - ->will($this->returnValue([$selectionItemMock])); - $selectionItemMock->expects($this->any()) + ->method('getItemByColumnValue') + ->will($this->returnValue($selectionItemMock)); + $selectionItemMock->expects($this->at(0)) + ->method('getEntityId') + ->will($this->returnValue(1)); + $selectionItemMock->expects($this->once()) ->method('getSku') ->will($this->returnValue($itemSku)); From 3d34a3c74d78929d76bdaa3944b6e371be147d09 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Thu, 27 Sep 2018 17:03:01 +0300 Subject: [PATCH 059/309] MAGETWO-94989: [2.3] Magnifier function does not disappear after mouse-off the image from the bottom --- lib/web/magnifier/magnifier.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/web/magnifier/magnifier.js b/lib/web/magnifier/magnifier.js index 150c8adf0b22b..55646aa9b4af2 100644 --- a/lib/web/magnifier/magnifier.js +++ b/lib/web/magnifier/magnifier.js @@ -554,6 +554,15 @@ thumbObj.src = thumb.src; } + /** + * Hide magnifier when mouse exceeds image bounds. + */ + function onMouseLeave() { + onThumbLeave(); + isOverThumb = false; + $magnifierPreview.addClass(MagnifyCls.magnifyHidden); + } + function onMousemove(e) { pos.x = e.clientX; pos.y = e.clientY; @@ -564,20 +573,13 @@ isOverThumb = inBounds; } - if (inBounds && isOverThumb) { - if(gMode === 'outside'){ - $magnifierPreview.removeClass(MagnifyCls.magnifyHidden); - } + if (inBounds && isOverThumb && gMode === 'outside') { + $magnifierPreview.removeClass(MagnifyCls.magnifyHidden); move(); - } else { - onThumbLeave(); - isOverThumb = false; - $magnifierPreview.addClass(MagnifyCls.magnifyHidden); } } function onScroll() { - if (curThumb !== null) { setThumbData(curThumb, magnifierOptions); } @@ -589,6 +591,8 @@ }); $box.on('mousemove', onMousemove); + $box.on('mouseleave', onMouseLeave); + _init($box, customUserOptions); } }(jQuery)); From 32058c748731348e7d38cffa7a6c56113e17b3d8 Mon Sep 17 00:00:00 2001 From: David Alger <davidmalger@gmail.com> Date: Thu, 27 Sep 2018 10:44:27 -0500 Subject: [PATCH 060/309] Update colinmollenhour/cache-backend-redis from version 1.10.5 to 1.10.6 See colinmollenhour/Cm_Cache_Backend_Redis#134 for details on updates to library --- composer.json | 2 +- composer.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index e2a646275d98b..dfd7bfb5aa9ec 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "lib-libxml": "*", "braintree/braintree_php": "3.35.0", "colinmollenhour/cache-backend-file": "~1.4.1", - "colinmollenhour/cache-backend-redis": "1.10.5", + "colinmollenhour/cache-backend-redis": "1.10.6", "colinmollenhour/credis": "1.10.0", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", diff --git a/composer.lock b/composer.lock index 2550f70f0be81..849366434dbf4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "18982aa4d36bcfd22cf073dfb578efdb", + "content-hash": "dcecc6ef5197bb32c59c7ba1f5d136ac", "packages": [ { "name": "braintree/braintree_php", @@ -88,16 +88,16 @@ }, { "name": "colinmollenhour/cache-backend-redis", - "version": "1.10.5", + "version": "1.10.6", "source": { "type": "git", "url": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis.git", - "reference": "91d949e28d939e607484a4bbf9307cff5afa689b" + "reference": "cc941a5f4cc017e11d3eab9061811ba9583ed6bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_Redis/zipball/91d949e28d939e607484a4bbf9307cff5afa689b", - "reference": "91d949e28d939e607484a4bbf9307cff5afa689b", + "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_Redis/zipball/cc941a5f4cc017e11d3eab9061811ba9583ed6bf", + "reference": "cc941a5f4cc017e11d3eab9061811ba9583ed6bf", "shasum": "" }, "require": { @@ -120,7 +120,7 @@ ], "description": "Zend_Cache backend using Redis with full support for tags.", "homepage": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis", - "time": "2018-05-15T16:02:25+00:00" + "time": "2018-09-24T16:02:07+00:00" }, { "name": "colinmollenhour/credis", From 5aab5421697039e56993f84d88e2b6db4a2242da Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Wed, 26 Sep 2018 21:12:12 +0300 Subject: [PATCH 061/309] MAGETWO-91517: Cancel and Return link removes billing and shipping address - Provide information from database to set to browser local storage if local storage is empty. --- .../Checkout/Model/DefaultConfigProvider.php | 53 +++++++++++++++++-- .../web/js/model/checkout-data-resolver.js | 13 ++++- .../web/js/view/form/element/email.js | 7 +++ 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php index ea6cdd2e51b4a..fd120f0a37a4b 100644 --- a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php +++ b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php @@ -14,6 +14,7 @@ use Magento\Customer\Model\Session as CustomerSession; use Magento\Customer\Model\Url as CustomerUrlManager; use Magento\Eav\Api\AttributeOptionManagementInterface; +use Magento\Framework\Api\CustomAttributesDataInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Http\Context as HttpContext; use Magento\Framework\App\ObjectManager; @@ -22,9 +23,11 @@ use Magento\Framework\UrlInterface; use Magento\Quote\Api\CartItemRepositoryInterface as QuoteItemRepository; use Magento\Quote\Api\CartTotalRepositoryInterface; +use Magento\Quote\Api\Data\AddressInterface; use Magento\Quote\Api\ShippingMethodManagementInterface as ShippingMethodManager; use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Store\Model\ScopeInterface; +use Magento\Ui\Component\Form\Element\Multiline; /** * Default Config Provider @@ -272,16 +275,28 @@ public function __construct( * * @return array|mixed * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\LocalizedException */ public function getConfig() { - $quoteId = $this->checkoutSession->getQuote()->getId(); + $quote = $this->checkoutSession->getQuote(); + $quoteId = $quote->getId(); + $email = $quote->getShippingAddress()->getEmail(); $output['formKey'] = $this->formKey->getFormKey(); $output['customerData'] = $this->getCustomerData(); $output['quoteData'] = $this->getQuoteData(); $output['quoteItemData'] = $this->getQuoteItemData(); $output['isCustomerLoggedIn'] = $this->isCustomerLoggedIn(); $output['selectedShippingMethod'] = $this->getSelectedShippingMethod(); + if ($email && !$this->isCustomerLoggedIn()) { + $shippingAddressFromData = $this->getAddressFromData($quote->getShippingAddress()); + $billingAddressFromData = $this->getAddressFromData($quote->getBillingAddress()); + $output['shippingAddressFromData'] = $shippingAddressFromData; + if ($shippingAddressFromData != $billingAddressFromData) { + $output['billingAddressFromData'] = $billingAddressFromData; + } + $output['validatedEmailValue'] = $email; + } $output['storeCode'] = $this->getStoreCode(); $output['isGuestCheckoutAllowed'] = $this->isGuestCheckoutAllowed(); $output['isCustomerLoginRequired'] = $this->isCustomerLoginRequired(); @@ -293,11 +308,11 @@ public function getConfig() $output['staticBaseUrl'] = $this->getStaticBaseUrl(); $output['priceFormat'] = $this->localeFormat->getPriceFormat( null, - $this->checkoutSession->getQuote()->getQuoteCurrencyCode() + $quote->getQuoteCurrencyCode() ); $output['basePriceFormat'] = $this->localeFormat->getPriceFormat( null, - $this->checkoutSession->getQuote()->getBaseCurrencyCode() + $quote->getBaseCurrencyCode() ); $output['postCodes'] = $this->postCodesConfig->getPostCodes(); $output['imageData'] = $this->imageProvider->getImages($quoteId); @@ -528,6 +543,38 @@ private function getSelectedShippingMethod() return $shippingMethodData; } + /** + * Create address data appropriate to fill checkout address form + * + * @param AddressInterface $address + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getAddressFromData(AddressInterface $address) + { + $addressData = []; + $attributesMetadata = $this->addressMetadata->getAllAttributesMetadata(); + foreach ($attributesMetadata as $attributeMetadata) { + if (!$attributeMetadata->isVisible()) { + continue; + } + $attributeCode = $attributeMetadata->getAttributeCode(); + $attributeData = $address->getData($attributeCode); + if ($attributeData) { + if ($attributeMetadata->getFrontendInput() === Multiline::NAME) { + $attributeData = \is_array($attributeData) ? $attributeData : explode("\n", $attributeData); + $attributeData = (object)$attributeData; + } + if ($attributeMetadata->isUserDefined()) { + $addressData[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES][$attributeCode] = $attributeData; + continue; + } + $addressData[$attributeCode] = $attributeData; + } + } + return $addressData; + } + /** * Retrieve store code * diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 73f4df567903c..25abe3c4ed3a0 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -60,13 +60,18 @@ define([ this.resolveBillingAddress(); } } - }, /** * Resolve shipping address. Used local storage */ resolveShippingAddress: function () { + if (!checkoutData.getShippingAddressFromData() && + window.checkoutConfig.shippingAddressFromData + ) { + checkoutData.setShippingAddressFromData(window.checkoutConfig.shippingAddressFromData); + } + var newCustomerShippingAddress = checkoutData.getNewCustomerShippingAddress(); if (newCustomerShippingAddress) { @@ -196,6 +201,12 @@ define([ * Resolve billing address. Used local storage */ resolveBillingAddress: function () { + if (!checkoutData.getBillingAddressFromData() && + window.checkoutConfig.billingAddressFromData + ) { + checkoutData.setBillingAddressFromData(window.checkoutConfig.billingAddressFromData); + } + var selectedBillingAddress = checkoutData.getSelectedBillingAddress(), newCustomerBillingAddressData = checkoutData.getNewCustomerBillingAddress(); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js index 4a25778e754c7..6c0075fca6d51 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js @@ -17,6 +17,13 @@ define([ ], function ($, Component, ko, customer, checkEmailAvailability, loginAction, quote, checkoutData, fullScreenLoader) { 'use strict'; + if (!checkoutData.getValidatedEmailValue() && + window.checkoutConfig.validatedEmailValue + ) { + checkoutData.setInputFieldEmailValue(window.checkoutConfig.validatedEmailValue); + checkoutData.setValidatedEmailValue(window.checkoutConfig.validatedEmailValue); + } + var validatedEmail = checkoutData.getValidatedEmailValue(); if (validatedEmail && !customer.isLoggedIn()) { From 77ec759a758eb80aceb28a67ab65d46846e38d78 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Thu, 27 Sep 2018 21:14:23 +0300 Subject: [PATCH 062/309] MAGETWO-91640: Scheduled Import of Products fails on error when errors should be skipped - Changed behaviour for "skip-errors" processing during import shceduling --- .../CatalogImportExport/Model/Import/Product.php | 7 +++++-- app/code/Magento/ImportExport/Model/Import.php | 13 +++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index b755d91e403ff..d85c64327be60 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1623,8 +1623,11 @@ protected function _saveProducts() continue; } if ($this->getErrorAggregator()->hasToBeTerminated()) { - $this->getErrorAggregator()->addRowToSkip($rowNum); - continue; + $validationStrategy = $this->_parameters[Import::FIELD_NAME_VALIDATION_STRATEGY]; + if (ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_SKIP_ERRORS !== $validationStrategy) { + $this->getErrorAggregator()->addRowToSkip($rowNum); + continue; + } } $rowScope = $this->getRowScope($rowData); diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index b5e8220e0e9b0..064c696ad0a84 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -181,7 +181,7 @@ class Import extends \Magento\ImportExport\Model\AbstractModel * @param Source\Import\Behavior\Factory $behaviorFactory * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry * @param History $importHistoryModel - * @param \Magento\Framework\Stdlib\DateTime\DateTime + * @param \Magento\Framework\Stdlib\DateTime\DateTime $localeDate * @param array $data * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -443,6 +443,8 @@ public function importSource() } /** + * Processing of import. + * * @return bool * @throws \Magento\Framework\Exception\LocalizedException */ @@ -462,6 +464,8 @@ public function isImportAllowed() } /** + * Get error aggregator instance. + * * @return ProcessingErrorAggregatorInterface * @throws \Magento\Framework\Exception\LocalizedException */ @@ -585,6 +589,11 @@ public function validateSource(\Magento\ImportExport\Model\Import\AbstractSource $this->addLogComment($messages); $result = !$errorAggregator->getErrorsCount(); + $validationStrategy = $this->getData(self::FIELD_NAME_VALIDATION_STRATEGY); + if ($validationStrategy === ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_SKIP_ERRORS) { + $result = true; + } + if ($result) { $this->addLogComment(__('Import data validation is complete.')); } @@ -710,9 +719,9 @@ public function isReportEntityType($entity = null) /** * Create history report * + * @param string $sourceFileRelative * @param string $entity * @param string $extension - * @param string $sourceFileRelative * @param array $result * @return $this * @throws \Magento\Framework\Exception\LocalizedException From defc2c525c5e1f6df965f5b8460648b3bff5653a Mon Sep 17 00:00:00 2001 From: Yevhen Sentiabov <isentiabov@magento.com> Date: Fri, 28 Sep 2018 11:49:52 +0300 Subject: [PATCH 063/309] MAGETWO-91610: [2.3] Billing Address in Braintree PayPal response is absent - Added check if a billing address is returned by Braintree --- .../Model/Paypal/Helper/QuoteUpdater.php | 2 +- .../Model/Paypal/Helper/QuoteUpdaterTest.php | 214 +++++++++--------- 2 files changed, 107 insertions(+), 109 deletions(-) diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php index fe5895541543d..aa23fa767d1ed 100644 --- a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php +++ b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php @@ -148,7 +148,7 @@ private function updateBillingAddress(Quote $quote, array $details) { $billingAddress = $quote->getBillingAddress(); - if ($this->config->isRequiredBillingAddress()) { + if ($this->config->isRequiredBillingAddress() && !empty($details['billingAddress'])) { $this->updateAddressData($billingAddress, $details['billingAddress']); } else { $this->updateAddressData($billingAddress, $details['shippingAddress']); diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php index 62452228b6186..a2b5380d2884b 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php @@ -3,23 +3,24 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Braintree\Test\Unit\Model\Paypal\Helper; -use Magento\Quote\Model\Quote; -use Magento\Quote\Model\Quote\Address; -use Magento\Quote\Model\Quote\Payment; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Braintree\Model\Ui\PayPal\ConfigProvider; -use Magento\Braintree\Observer\DataAssignObserver; use Magento\Braintree\Gateway\Config\PayPal\Config; use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater; +use Magento\Braintree\Model\Ui\PayPal\ConfigProvider; +use Magento\Braintree\Observer\DataAssignObserver; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartExtensionInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\Quote\Payment; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class QuoteUpdaterTest * - * @see \Magento\Braintree\Model\Paypal\Helper\QuoteUpdater - * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class QuoteUpdaterTest extends \PHPUnit\Framework\TestCase @@ -27,24 +28,24 @@ class QuoteUpdaterTest extends \PHPUnit\Framework\TestCase const TEST_NONCE = '3ede7045-2aea-463e-9754-cd658ffeeb48'; /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ - private $configMock; + private $config; /** - * @var CartRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CartRepositoryInterface|MockObject */ - private $quoteRepositoryMock; + private $quoteRepository; /** - * @var Address|\PHPUnit_Framework_MockObject_MockObject + * @var Address|MockObject */ - private $billingAddressMock; + private $billingAddress; /** - * @var Address|\PHPUnit_Framework_MockObject_MockObject + * @var Address|MockObject */ - private $shippingAddressMock; + private $shippingAddress; /** * @var QuoteUpdater @@ -52,17 +53,17 @@ class QuoteUpdaterTest extends \PHPUnit\Framework\TestCase private $quoteUpdater; /** - * @return void + * @inheritdoc */ protected function setUp() { - $this->configMock = $this->getMockBuilder(Config::class) + $this->config = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - $this->quoteRepositoryMock = $this->getMockBuilder(CartRepositoryInterface::class) + $this->quoteRepository = $this->getMockBuilder(CartRepositoryInterface::class) ->getMockForAbstractClass(); - $this->billingAddressMock = $this->getMockBuilder(Address::class) + $this->billingAddress = $this->getMockBuilder(Address::class) ->setMethods( [ 'setLastname', @@ -77,9 +78,10 @@ protected function setUp() 'setShouldIgnoreValidation', 'getEmail' ] - )->disableOriginalConstructor() + ) + ->disableOriginalConstructor() ->getMock(); - $this->shippingAddressMock = $this->getMockBuilder(Address::class) + $this->shippingAddress = $this->getMockBuilder(Address::class) ->setMethods( [ 'setLastname', @@ -93,61 +95,61 @@ protected function setUp() 'setPostcode', 'setShouldIgnoreValidation' ] - )->disableOriginalConstructor() + ) + ->disableOriginalConstructor() ->getMock(); $this->quoteUpdater = new QuoteUpdater( - $this->configMock, - $this->quoteRepositoryMock + $this->config, + $this->quoteRepository ); } /** - * @return void + * Checks if quote details can be update by the response from Braintree. + * * @throws \Magento\Framework\Exception\LocalizedException */ - public function testExecute() + public function testExecute(): void { $details = $this->getDetails(); - $quoteMock = $this->getQuoteMock(); - $paymentMock = $this->getPaymentMock(); + $quote = $this->getQuoteMock(); + $payment = $this->getPaymentMock(); - $quoteMock->expects(self::once()) - ->method('getPayment') - ->willReturn($paymentMock); + $quote->method('getPayment') + ->willReturn($payment); - $paymentMock->expects(self::once()) - ->method('setMethod') + $payment->method('setMethod') ->with(ConfigProvider::PAYPAL_CODE); - $paymentMock->expects(self::once()) - ->method('setAdditionalInformation') + $payment->method('setAdditionalInformation') ->with(DataAssignObserver::PAYMENT_METHOD_NONCE, self::TEST_NONCE); - $this->updateQuoteStep($quoteMock, $details); + $this->updateQuoteStep($quote, $details); - $this->quoteUpdater->execute(self::TEST_NONCE, $details, $quoteMock); + $this->quoteUpdater->execute(self::TEST_NONCE, $details, $quote); } /** + * Disables quote's addresses validation. + * * @return void */ - private function disabledQuoteAddressValidationStep() + private function disabledQuoteAddressValidationStep(): void { - $this->billingAddressMock->expects(self::once()) - ->method('setShouldIgnoreValidation') + $this->billingAddress->method('setShouldIgnoreValidation') ->with(true); - $this->shippingAddressMock->expects(self::once()) - ->method('setShouldIgnoreValidation') + $this->shippingAddress->method('setShouldIgnoreValidation') ->with(true); - $this->billingAddressMock->expects(self::once()) - ->method('getEmail') + $this->billingAddress->method('getEmail') ->willReturn('bt_buyer_us@paypal.com'); } /** + * Gets quote's details. + * * @return array */ - private function getDetails() + private function getDetails(): array { return [ 'email' => 'bt_buyer_us@paypal.com', @@ -177,54 +179,51 @@ private function getDetails() } /** + * Updates shipping address details. + * * @param array $details */ - private function updateShippingAddressStep(array $details) + private function updateShippingAddressStep(array $details): void { - $this->shippingAddressMock->expects(self::once()) - ->method('setLastname') + $this->shippingAddress->method('setLastname') ->with($details['lastName']); - $this->shippingAddressMock->expects(self::once()) - ->method('setFirstname') + $this->shippingAddress->method('setFirstname') ->with($details['firstName']); - $this->shippingAddressMock->expects(self::once()) - ->method('setEmail') + $this->shippingAddress->method('setEmail') ->with($details['email']); - $this->shippingAddressMock->expects(self::once()) - ->method('setCollectShippingRates') + $this->shippingAddress->method('setCollectShippingRates') ->with(true); - $this->updateAddressDataStep($this->shippingAddressMock, $details['shippingAddress']); + $this->updateAddressDataStep($this->shippingAddress, $details['shippingAddress']); } /** - * @param \PHPUnit_Framework_MockObject_MockObject $addressMock + * Updates address details. + * + * @param MockObject $address * @param array $addressData */ - private function updateAddressDataStep(\PHPUnit_Framework_MockObject_MockObject $addressMock, array $addressData) + private function updateAddressDataStep(MockObject $address, array $addressData): void { - $addressMock->expects(self::once()) - ->method('setStreet') + $address->method('setStreet') ->with([$addressData['streetAddress'], $addressData['extendedAddress']]); - $addressMock->expects(self::once()) - ->method('setCity') + $address->method('setCity') ->with($addressData['locality']); - $addressMock->expects(self::once()) - ->method('setRegionCode') + $address->method('setRegionCode') ->with($addressData['region']); - $addressMock->expects(self::once()) - ->method('setCountryId') + $address->method('setCountryId') ->with($addressData['countryCodeAlpha2']); - $addressMock->expects(self::once()) - ->method('setPostcode') + $address->method('setPostcode') ->with($addressData['postalCode']); } /** - * @param \PHPUnit_Framework_MockObject_MockObject $quoteMock + * Updates quote's address details. + * + * @param MockObject $quoteMock * @param array $details */ - private function updateQuoteAddressStep(\PHPUnit_Framework_MockObject_MockObject $quoteMock, array $details) + private function updateQuoteAddressStep(MockObject $quoteMock, array $details): void { $quoteMock->expects(self::exactly(2)) ->method('getIsVirtual') @@ -235,64 +234,61 @@ private function updateQuoteAddressStep(\PHPUnit_Framework_MockObject_MockObject } /** + * Updates billing address details. + * * @param array $details */ - private function updateBillingAddressStep(array $details) + private function updateBillingAddressStep(array $details): void { - $this->configMock->expects(self::once()) - ->method('isRequiredBillingAddress') + $this->config->method('isRequiredBillingAddress') ->willReturn(true); - $this->updateAddressDataStep($this->billingAddressMock, $details['billingAddress']); + $this->updateAddressDataStep($this->billingAddress, $details['billingAddress']); - $this->billingAddressMock->expects(self::once()) - ->method('setLastname') + $this->billingAddress->method('setLastname') ->with($details['lastName']); - $this->billingAddressMock->expects(self::once()) - ->method('setFirstname') + $this->billingAddress->method('setFirstname') ->with($details['firstName']); - $this->billingAddressMock->expects(self::once()) - ->method('setEmail') + $this->billingAddress->method('setEmail') ->with($details['email']); } /** - * @param \PHPUnit_Framework_MockObject_MockObject $quoteMock + * Updates quote details. + * + * @param MockObject $quote * @param array $details */ - private function updateQuoteStep(\PHPUnit_Framework_MockObject_MockObject $quoteMock, array $details) + private function updateQuoteStep(MockObject $quote, array $details): void { - $quoteMock->expects(self::once()) - ->method('setMayEditShippingAddress') + $quote->method('setMayEditShippingAddress') ->with(false); - $quoteMock->expects(self::once()) - ->method('setMayEditShippingMethod') + $quote->method('setMayEditShippingMethod') ->with(true); - $quoteMock->expects(self::exactly(2)) - ->method('getShippingAddress') - ->willReturn($this->shippingAddressMock); - $quoteMock->expects(self::exactly(2)) + $quote->method('getShippingAddress') + ->willReturn($this->shippingAddress); + $quote->expects(self::exactly(2)) ->method('getBillingAddress') - ->willReturn($this->billingAddressMock); + ->willReturn($this->billingAddress); - $this->updateQuoteAddressStep($quoteMock, $details); + $this->updateQuoteAddressStep($quote, $details); $this->disabledQuoteAddressValidationStep(); - $quoteMock->expects(self::once()) - ->method('collectTotals'); + $quote->method('collectTotals'); - $this->quoteRepositoryMock->expects(self::once()) - ->method('save') - ->with($quoteMock); + $this->quoteRepository->method('save') + ->with($quote); } /** - * @return Quote|\PHPUnit_Framework_MockObject_MockObject + * Creates a mock for Quote object. + * + * @return Quote|MockObject */ - private function getQuoteMock() + private function getQuoteMock(): MockObject { - $quoteMock = $this->getMockBuilder(Quote::class) + $quote = $this->getMockBuilder(Quote::class) ->setMethods( [ 'getIsVirtual', @@ -304,25 +300,27 @@ private function getQuoteMock() 'getBillingAddress', 'getExtensionAttributes' ] - )->disableOriginalConstructor() + ) + ->disableOriginalConstructor() ->getMock(); - $cartExtensionMock = $this->getMockBuilder(CartExtensionInterface::class) + $cartExtension = $this->getMockBuilder(CartExtensionInterface::class) ->setMethods(['setShippingAssignments']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $quoteMock->expects(self::any()) - ->method('getExtensionAttributes') - ->willReturn($cartExtensionMock); + $quote->method('getExtensionAttributes') + ->willReturn($cartExtension); - return $quoteMock; + return $quote; } /** - * @return Payment|\PHPUnit_Framework_MockObject_MockObject + * Creates a mock for Payment object. + * + * @return Payment|MockObject */ - private function getPaymentMock() + private function getPaymentMock(): MockObject { return $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() From cc00197d13d6244e32201e4c6bf00287e54747c5 Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Mon, 1 Oct 2018 13:49:26 +0300 Subject: [PATCH 064/309] MAGETWO-91702: Shipping method Table Rates settings gets from wrong store - Fix website id for shipping --- app/code/Magento/Quote/Model/Quote/Address.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 11beffe5711b9..140ace42afae1 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -1003,8 +1003,14 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte /** * Store and website identifiers specified from StoreManager */ - $request->setStoreId($this->getQuote()->getStoreId() ?? $this->storeManager->getStore()->getId()); - $request->setWebsiteId($this->storeManager->getWebsite()->getId()); + if ($this->getQuote()->getStoreId()) { + $storeId = $this->getQuote()->getStoreId(); + $request->setStoreId($storeId); + $request->setWebsiteId($this->storeManager->getStore($storeId)->getWebsiteId()); + } else { + $request->setStoreId($this->storeManager->getStore()->getId()); + $request->setWebsiteId($this->storeManager->getWebsite()->getId()); + } $request->setFreeShipping($this->getFreeShipping()); /** * Currencies need to convert in free shipping From 16a7f0babc8cfd4420dfea32c35449745449791b Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 1 Oct 2018 09:05:26 -0300 Subject: [PATCH 065/309] Use @inheritdoc and revert back code change --- .../Framework/Stdlib/DateTime/Timezone.php | 125 +++--------------- 1 file changed, 15 insertions(+), 110 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 2bfa465c8268f..fbda76a8e3616 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -85,9 +85,7 @@ public function __construct( } /** - * Return path to default timezone - * - * @return string + * @inheritdoc */ public function getDefaultTimezonePath() { @@ -95,9 +93,7 @@ public function getDefaultTimezonePath() } /** - * Retrieve timezone code - * - * @return string + * @inheritdoc */ public function getDefaultTimezone() { @@ -105,11 +101,7 @@ public function getDefaultTimezone() } /** - * Gets the scope config timezone - * - * @param string $scopeType - * @param string $scopeCode - * @return string + * @inheritdoc */ public function getConfigTimezone($scopeType = null, $scopeCode = null) { @@ -121,10 +113,7 @@ public function getConfigTimezone($scopeType = null, $scopeCode = null) } /** - * Retrieve ISO date format - * - * @param int $type - * @return string + * @inheritdoc */ public function getDateFormat($type = \IntlDateFormatter::SHORT) { @@ -136,9 +125,7 @@ public function getDateFormat($type = \IntlDateFormatter::SHORT) } /** - * Retrieve short date format with 4-digit year - * - * @return string + * @inheritdoc */ public function getDateFormatWithLongYear() { @@ -150,10 +137,7 @@ public function getDateFormatWithLongYear() } /** - * Retrieve ISO time format - * - * @param string $type - * @return string + * @inheritdoc */ public function getTimeFormat($type = \IntlDateFormatter::SHORT) { @@ -165,10 +149,7 @@ public function getTimeFormat($type = \IntlDateFormatter::SHORT) } /** - * Retrieve ISO datetime format - * - * @param string $type - * @return string + * @inheritdoc */ public function getDateTimeFormat($type) { @@ -176,13 +157,7 @@ public function getDateTimeFormat($type) } /** - * Create \DateTime object for current locale - * - * @param mixed $date - * @param string $locale - * @param bool $useTimezone - * @param bool $includeTime - * @return \DateTime + * @inheritdoc */ public function date($date = null, $locale = null, $useTimezone = true, $includeTime = true) { @@ -216,12 +191,7 @@ public function date($date = null, $locale = null, $useTimezone = true, $include } /** - * Create \DateTime object with date converted to scope timezone and scope Locale - * - * @param mixed $scope Information about scope - * @param string|integer|\DateTime|array|null $date date in UTC - * @param boolean $includeTime flag for including time to date - * @return \DateTime + * @inheritdoc */ public function scopeDate($scope = null, $date = null, $includeTime = false) { @@ -234,12 +204,7 @@ public function scopeDate($scope = null, $date = null, $includeTime = false) } /** - * Format date using current locale options and time zone. - * - * @param \DateTime|null $date - * @param int $format - * @param bool $showTime - * @return string + * @inheritdoc */ public function formatDate($date = null, $format = \IntlDateFormatter::SHORT, $showTime = false) { @@ -253,12 +218,7 @@ public function formatDate($date = null, $format = \IntlDateFormatter::SHORT, $s } /** - * Get scope timestamp - * - * Timestamp will be built with scope timezone settings - * - * @param mixed $scope - * @return int + * @inheritdoc */ public function scopeTimeStamp($scope = null) { @@ -271,12 +231,7 @@ public function scopeTimeStamp($scope = null) } /** - * Checks if current date of the given scope (in the scope timezone) is within the range - * - * @param int|string|\Magento\Framework\App\ScopeInterface $scope - * @param string|null $dateFrom - * @param string|null $dateTo - * @return bool + * @inheritdoc */ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null) { @@ -302,13 +257,7 @@ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null) } /** - * @param string|\DateTimeInterface $date - * @param int $dateType - * @param int $timeType - * @param string|null $locale - * @param string|null $timezone - * @param string|null $pattern - * @return string + * @inheritdoc */ public function formatDateTime( $date, @@ -344,56 +293,15 @@ public function formatDateTime( } /** - * Convert date from config timezone to Utc. - * - * If pass \DateTime object as argument be sure that timezone is the same with config timezone - * - * @param string|\DateTimeInterface $date - * @param string $format - * @throws LocalizedException - * @return string - * @deprecated + * @inheritdoc */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') - { - return $this->convertConfigTimeToUtcWithPattern($date, $format); - } - - /** - * Convert date from config timezone to Utc. - * - * If pass \DateTime object as argument be sure that timezone is the same with config timezone - * - * @param string|\DateTimeInterface $date - * @param string $format - * @param string $pattern - * @throws LocalizedException - * @return string - * @deprecated - */ - public function convertConfigTimeToUtcWithPattern($date, $format = 'Y-m-d H:i:s', $pattern = null) { if (!($date instanceof \DateTimeInterface)) { if ($date instanceof \DateTimeImmutable) { $date = new \DateTime($date->format('Y-m-d H:i:s'), new \DateTimeZone($this->getConfigTimezone())); } else { - $locale = $this->_localeResolver->getLocale(); - if ($locale === null) { - $pattern = 'Y-M-dd HH:mm:ss'; - } - $formatter = new \IntlDateFormatter( - $locale, - \IntlDateFormatter::MEDIUM, - \IntlDateFormatter::MEDIUM, - $this->getConfigTimezone(), - null, - $pattern - ); - $unixTime = $formatter->parse($date); - $dateTime = new DateTime($this); - - $dateUniversal = $dateTime->gmtDate(null, $unixTime); - $date = new \DateTime($dateUniversal, new \DateTimeZone($this->getConfigTimezone())); + $date = new \DateTime($date, new \DateTimeZone($this->getConfigTimezone())); } } else { if ($date->getTimezone()->getName() !== $this->getConfigTimezone()) { @@ -405,13 +313,10 @@ public function convertConfigTimeToUtcWithPattern($date, $format = 'Y-m-d H:i:s' ); } } - $date->setTimezone(new \DateTimeZone('UTC')); - return $date->format($format); } - /** * Retrieve date with time * From 9315dd6963834ec6299eed8395add8bdb7bb7895 Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 1 Oct 2018 09:07:53 -0300 Subject: [PATCH 066/309] Revert back code change to fix BiC --- .../Framework/Stdlib/DateTime/Timezone.php | 16 ++++++++++++++-- .../Stdlib/DateTime/TimezoneInterface.php | 14 -------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index fbda76a8e3616..5dbaf093698a1 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -257,7 +257,13 @@ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null) } /** - * @inheritdoc + * @param string|\DateTimeInterface $date + * @param int $dateType + * @param int $timeType + * @param string|null $locale + * @param string|null $timezone + * @param string|null $pattern + * @return string */ public function formatDateTime( $date, @@ -293,7 +299,13 @@ public function formatDateTime( } /** - * @inheritdoc + * Convert date from config timezone to Utc. + * If pass \DateTime object as argument be sure that timezone is the same with config timezone + * + * @param string|\DateTimeInterface $date + * @param string $format + * @throws LocalizedException + * @return string */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') { diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php index 17ba759a4d763..e8e20070c55de 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php @@ -152,18 +152,4 @@ public function formatDateTime( * @deprecated */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s'); - - /** - * Convert date from config timezone to Utc. - * - * If pass \DateTime object as argument be sure that timezone is the same with config timezone - * - * @param string|\DateTimeInterface $date - * @param string $format - * @param string $pattern - * @throws LocalizedException - * @return string - * @deprecated - */ - public function convertConfigTimeToUtcWithPattern($date, $format = 'Y-m-d H:i:s', $pattern = 'Y-m-d H:i:s'); } From 42cd2282007b0fc191413f8d483c58c6729823fc Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 1 Oct 2018 09:08:38 -0300 Subject: [PATCH 067/309] Revert back @deprecated tag --- .../Magento/Framework/Stdlib/DateTime/TimezoneInterface.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php index e8e20070c55de..11e9a22d8cea5 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php @@ -149,7 +149,6 @@ public function formatDateTime( * @param string $format * @throws LocalizedException * @return string - * @deprecated */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s'); } From ddeaf36c942f17f720cc720c840aeb844e93efbf Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 1 Oct 2018 09:09:24 -0300 Subject: [PATCH 068/309] Revert back new lines --- lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 5dbaf093698a1..2bc0b36cb6ef7 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -325,7 +325,9 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') ); } } + $date->setTimezone(new \DateTimeZone('UTC')); + return $date->format($format); } From 6cc903442a668370898ee352871510f48a72bc21 Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 1 Oct 2018 09:10:48 -0300 Subject: [PATCH 069/309] Revert back documentation change in API interface --- .../Magento/Framework/Stdlib/DateTime/Timezone.php | 2 +- .../Framework/Stdlib/DateTime/TimezoneInterface.php | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 2bc0b36cb6ef7..8534e798481cb 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -327,7 +327,7 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') } $date->setTimezone(new \DateTimeZone('UTC')); - + return $date->format($format); } diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php index 11e9a22d8cea5..217bf5b7f010d 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php @@ -104,7 +104,6 @@ public function formatDate( /** * Gets the scope config timezone - * * @param string $scopeType * @param string $scopeCode * @return string @@ -122,7 +121,6 @@ public function getConfigTimezone($scopeType = null, $scopeCode = null); public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null); /** - * * @param string|\DateTimeInterface $date * @param int $dateType * @param int $timeType @@ -141,14 +139,10 @@ public function formatDateTime( ); /** - * Convert date from config timezone to Utc. - * - * If pass \DateTime object as argument be sure that timezone is the same with config timezone - * * @param string|\DateTimeInterface $date * @param string $format - * @throws LocalizedException * @return string + * @since 100.1.0 */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s'); } From 28617c01e62941eaa68df8b6ea72a958c6f5367c Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 1 Oct 2018 09:11:51 -0300 Subject: [PATCH 070/309] Fix documentation change for code smell --- .../Magento/Framework/Stdlib/DateTime/TimezoneInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php index 217bf5b7f010d..c2ef294daf0c8 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php @@ -104,6 +104,7 @@ public function formatDate( /** * Gets the scope config timezone + * * @param string $scopeType * @param string $scopeCode * @return string From fd0ab037a74cc5d78d02c603adbd5cf10cb92c88 Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 1 Oct 2018 09:12:38 -0300 Subject: [PATCH 071/309] Fix documentation --- .../Magento/Framework/Stdlib/DateTime/TimezoneInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php index c2ef294daf0c8..4aff80161ef80 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php @@ -104,7 +104,7 @@ public function formatDate( /** * Gets the scope config timezone - * + * * @param string $scopeType * @param string $scopeCode * @return string From 5f1b9a0f4ec9d6276d811c166b5a0345f4e74222 Mon Sep 17 00:00:00 2001 From: Dave Macaulay <dmacaulay@magento.com> Date: Mon, 1 Oct 2018 18:16:06 +0200 Subject: [PATCH 072/309] MC-4189: IE11 - PageBuilder Does Not Load - Revert changes to es6-collections.js --- lib/web/es6-collections.js | 273 ++++++++++++++++++++++++++++++------- 1 file changed, 227 insertions(+), 46 deletions(-) diff --git a/lib/web/es6-collections.js b/lib/web/es6-collections.js index ed8e80c1fd07e..50ff560e53a8a 100644 --- a/lib/web/es6-collections.js +++ b/lib/web/es6-collections.js @@ -1,46 +1,227 @@ -/* - * Copyright 2012 The Polymer Authors. All rights reserved. - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -if (typeof WeakMap === 'undefined') { - (function() { - var defineProperty = Object.defineProperty; - var counter = Date.now() % 1e9; - - var WeakMap = function() { - this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); - }; - - WeakMap.prototype = { - set: function(key, value) { - var entry = key[this.name]; - if (entry && entry[0] === key) - entry[1] = value; - else - defineProperty(key, this.name, {value: [key, value], writable: true}); - return this; - }, - get: function(key) { - var entry; - return (entry = key[this.name]) && entry[0] === key ? - entry[1] : undefined; - }, - delete: function(key) { - var entry = key[this.name]; - if (!entry) return false; - var hasValue = entry[0] === key; - entry[0] = entry[1] = undefined; - return hasValue; - }, - has: function(key) { - var entry = key[this.name]; - if (!entry) return false; - return entry[0] === key; - } - }; - - window.WeakMap = WeakMap; - })(); -} \ No newline at end of file +(function (exports) {'use strict'; + //shared pointer + var i; + //shortcuts + var defineProperty = Object.defineProperty, is = function(a,b) { return isNaN(a)? isNaN(b): a === b; }; + + + //Polyfill global objects + if (typeof WeakMap == 'undefined') { + exports.WeakMap = createCollection({ + // WeakMap#delete(key:void*):boolean + 'delete': sharedDelete, + // WeakMap#clear(): + clear: sharedClear, + // WeakMap#get(key:void*):void* + get: sharedGet, + // WeakMap#has(key:void*):boolean + has: mapHas, + // WeakMap#set(key:void*, value:void*):void + set: sharedSet + }, true); + } + + if (typeof Map == 'undefined' || typeof ((new Map).values) !== 'function' || !(new Map).values().next) { + exports.Map = createCollection({ + // WeakMap#delete(key:void*):boolean + 'delete': sharedDelete, + //:was Map#get(key:void*[, d3fault:void*]):void* + // Map#has(key:void*):boolean + has: mapHas, + // Map#get(key:void*):boolean + get: sharedGet, + // Map#set(key:void*, value:void*):void + set: sharedSet, + // Map#keys(void):Iterator + keys: sharedKeys, + // Map#values(void):Iterator + values: sharedValues, + // Map#entries(void):Iterator + entries: mapEntries, + // Map#forEach(callback:Function, context:void*):void ==> callback.call(context, key, value, mapObject) === not in specs` + forEach: sharedForEach, + // Map#clear(): + clear: sharedClear + }); + } + + if (typeof Set == 'undefined' || typeof ((new Set).values) !== 'function' || !(new Set).values().next) { + exports.Set = createCollection({ + // Set#has(value:void*):boolean + has: setHas, + // Set#add(value:void*):boolean + add: sharedAdd, + // Set#delete(key:void*):boolean + 'delete': sharedDelete, + // Set#clear(): + clear: sharedClear, + // Set#keys(void):Iterator + keys: sharedValues, // specs actually say "the same function object as the initial value of the values property" + // Set#values(void):Iterator + values: sharedValues, + // Set#entries(void):Iterator + entries: setEntries, + // Set#forEach(callback:Function, context:void*):void ==> callback.call(context, value, index) === not in specs + forEach: sharedForEach + }); + } + + if (typeof WeakSet == 'undefined') { + exports.WeakSet = createCollection({ + // WeakSet#delete(key:void*):boolean + 'delete': sharedDelete, + // WeakSet#add(value:void*):boolean + add: sharedAdd, + // WeakSet#clear(): + clear: sharedClear, + // WeakSet#has(value:void*):boolean + has: setHas + }, true); + } + + + /** + * ES6 collection constructor + * @return {Function} a collection class + */ + function createCollection(proto, objectOnly){ + function Collection(a){ + if (!this || this.constructor !== Collection) return new Collection(a); + this._keys = []; + this._values = []; + this._itp = []; // iteration pointers + this.objectOnly = objectOnly; + + //parse initial iterable argument passed + if (a) init.call(this, a); + } + + //define size for non object-only collections + if (!objectOnly) { + defineProperty(proto, 'size', { + get: sharedSize + }); + } + + //set prototype + proto.constructor = Collection; + Collection.prototype = proto; + + return Collection; + } + + + /** parse initial iterable argument passed */ + function init(a){ + var i; + //init Set argument, like `[1,2,3,{}]` + if (this.add) + a.forEach(this.add, this); + //init Map argument like `[[1,2], [{}, 4]]` + else + a.forEach(function(a){this.set(a[0],a[1])}, this); + } + + + /** delete */ + function sharedDelete(key) { + if (this.has(key)) { + this._keys.splice(i, 1); + this._values.splice(i, 1); + // update iteration pointers + this._itp.forEach(function(p) { if (i < p[0]) p[0]--; }); + } + // Aurora here does it while Canary doesn't + return -1 < i; + }; + + function sharedGet(key) { + return this.has(key) ? this._values[i] : undefined; + } + + function has(list, key) { + if (this.objectOnly && key !== Object(key)) + throw new TypeError("Invalid value used as weak collection key"); + //NaN or 0 passed + if (key != key || key === 0) for (i = list.length; i-- && !is(list[i], key);){} + else i = list.indexOf(key); + return -1 < i; + } + + function setHas(value) { + return has.call(this, this._values, value); + } + + function mapHas(value) { + return has.call(this, this._keys, value); + } + + /** @chainable */ + function sharedSet(key, value) { + this.has(key) ? + this._values[i] = value + : + this._values[this._keys.push(key) - 1] = value + ; + return this; + } + + /** @chainable */ + function sharedAdd(value) { + if (!this.has(value)) this._values.push(value); + return this; + } + + function sharedClear() { + this._values.length = 0; + } + + /** keys, values, and iterate related methods */ + function sharedKeys() { + return sharedIterator(this._itp, this._keys); + } + + function sharedValues() { + return sharedIterator(this._itp, this._values); + } + + function mapEntries() { + return sharedIterator(this._itp, this._keys, this._values); + } + + function setEntries() { + return sharedIterator(this._itp, this._values, this._values); + } + + function sharedIterator(itp, array, array2) { + var p = [0], done = false; + itp.push(p); + return { + next: function() { + var v, k = p[0]; + if (!done && k < array.length) { + v = array2 ? [array[k], array2[k]]: array[k]; + p[0]++; + } else { + done = true; + itp.splice(itp.indexOf(p), 1); + } + return { done: done, value: v }; + } + }; + } + + function sharedSize() { + return this._values.length; + } + + function sharedForEach(callback, context) { + var it = this.entries(); + for (;;) { + var r = it.next(); + if (r.done) break; + callback.call(context, r.value[1], r.value[0], this); + } + } + +})(typeof exports != 'undefined' && typeof global != 'undefined' ? global : window ); \ No newline at end of file From ec5d9b7f171e76c0285c6e683a4b2da5902c2f78 Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Mon, 1 Oct 2018 15:31:32 -0300 Subject: [PATCH 073/309] #17744 Adding isVirtual mock to quote instance && fixs --- .../view/frontend/web/js/model/checkout-data-resolver.js | 3 +++ .../frontend/js/view/payment/method-renderer/paypal.test.js | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 6f4c99438610b..d75da01b5ded5 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -231,13 +231,16 @@ define([ isBillingAddressInitialized = addressList.some(function (addrs) { if (addrs.isDefaultBilling()) { selectBillingAddress(addrs); + return true; } + return false; }); } shippingAddress = quote.shippingAddress(); + if (!isBillingAddressInitialized && shippingAddress && shippingAddress.canUseForBilling() && diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js index 6fabc513da9af..f9475d78607f4 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js @@ -24,7 +24,10 @@ define([ paymentMethod: ko.observable(), totals: ko.observable({ 'base_grand_total': 0 - }) + }), + isVirtual: function () { + return false; + } }, 'Magento_Braintree/js/view/payment/adapter': { config: {}, From f3bad34fe9156f1fe2d2e2d4a4a7d8a244107085 Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Mon, 1 Oct 2018 16:55:30 -0300 Subject: [PATCH 074/309] #17744 Adding others isVirtual mock and annotations --- .../js/view/payment/method-renderer/cc-form.test.js | 7 ++++++- .../js/view/payment/method-renderer/paypal.test.js | 2 ++ .../method-renderer/paypal-express-abstract.test.js | 6 +++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js index 84db685c02987..8fdef2cbaadbb 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js @@ -19,7 +19,12 @@ define([ billingAddress: ko.observable(), shippingAddress: ko.observable(), paymentMethod: ko.observable(), - totals: ko.observable({}) + totals: ko.observable({}), + + /** Stub */ + isVirtual: function () { + return false; + } }, 'Magento_Braintree/js/view/payment/validator-handler': jasmine.createSpyObj( 'validator-handler', diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js index f9475d78607f4..4fc73caf7e14b 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js @@ -25,6 +25,8 @@ define([ totals: ko.observable({ 'base_grand_total': 0 }), + + /** Stub */ isVirtual: function () { return false; } diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js index f05338ad1e7d2..9950c89ce3c9d 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js @@ -28,8 +28,12 @@ define([ billingAddress: ko.observable(), shippingAddress: ko.observable(), paymentMethod: ko.observable(), - totals: ko.observable({}) + totals: ko.observable({}), + /** Stub */ + isVirtual: function () { + return false; + } }, 'Magento_Checkout/js/action/set-payment-information': setPaymentMock, 'Magento_Checkout/js/model/payment/additional-validators': { From 2c651382be48db9916469d286227a400c5af1ef5 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Mon, 1 Oct 2018 19:36:56 +0300 Subject: [PATCH 075/309] MAGETWO-95315: [2.3] Joins to grid collections causes MySQL exception due to ambiguous where clause --- .../ResourceModel/Order/Grid/Collection.php | 15 +++++++ .../Order/Grid/CollectionTest.php | 45 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/Grid/Collection.php b/app/code/Magento/Sales/Model/ResourceModel/Order/Grid/Collection.php index f6dd8f8527a53..82c612c1a781d 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Order/Grid/Collection.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Grid/Collection.php @@ -35,4 +35,19 @@ public function __construct( ) { parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $mainTable, $resourceModel); } + + /** + * @inheritdoc + */ + protected function _initSelect() + { + parent::_initSelect(); + + $tableDescription = $this->getConnection()->describeTable($this->getMainTable()); + foreach ($tableDescription as $columnInfo) { + $this->addFilterToMap($columnInfo['COLUMN_NAME'], 'main_table.' . $columnInfo['COLUMN_NAME']); + } + + return $this; + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php new file mode 100644 index 0000000000000..1649706a51f6b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\ResourceModel\Order\Grid; + +use Magento\TestFramework\Helper\Bootstrap; + +class CollectionTest extends \PHPUnit\Framework\TestCase +{ + /** + * Tests collection properties. + * + * @throws \ReflectionException + * @return void + */ + public function testCollectionCreate(): void + { + $objectManager = Bootstrap::getObjectManager(); + + /** @var Collection $gridCollection */ + $gridCollection = $objectManager->get(Collection::class); + $tableDescription = $gridCollection->getConnection() + ->describeTable($gridCollection->getMainTable()); + + $mapper = new \ReflectionMethod( + Collection::class, + '_getMapper' + ); + $mapper->setAccessible(true); + $map = $mapper->invoke($gridCollection); + + self::assertInternalType('array', $map); + self::assertArrayHasKey('fields', $map); + self::assertInternalType('array', $map['fields']); + self::assertCount(count($tableDescription), $map['fields']); + + foreach ($map['fields'] as $mappedName) { + self::assertContains('main_table.', $mappedName); + } + } +} From 4333c92bf37af3c9e50fd953a46e904f0a1c2b05 Mon Sep 17 00:00:00 2001 From: vtymchynskyi <vtymchynskyi@magento.com> Date: Tue, 2 Oct 2018 14:24:20 +0300 Subject: [PATCH 076/309] MAGETWO-95311: [2.3] Removed products are still exist in wishlist_item_option table in database --- .../Patch/Schema/AddProductIdConstraint.php | 79 +++++++++++++++++++ .../Wishlist/etc/db_schema_whitelist.json | 6 +- 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Wishlist/Setup/Patch/Schema/AddProductIdConstraint.php diff --git a/app/code/Magento/Wishlist/Setup/Patch/Schema/AddProductIdConstraint.php b/app/code/Magento/Wishlist/Setup/Patch/Schema/AddProductIdConstraint.php new file mode 100644 index 0000000000000..5c65fce10ccd2 --- /dev/null +++ b/app/code/Magento/Wishlist/Setup/Patch/Schema/AddProductIdConstraint.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Setup\Patch\Schema; + +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Setup\Patch\SchemaPatchInterface; +use Magento\Framework\Setup\SchemaSetupInterface; + +/** + * Class AddProductIdConstraint + */ +class AddProductIdConstraint implements SchemaPatchInterface +{ + /** + * @var SchemaSetupInterface + */ + private $schemaSetup; + + /** + * @param SchemaSetupInterface $schemaSetup + */ + public function __construct( + SchemaSetupInterface $schemaSetup + ) { + $this->schemaSetup = $schemaSetup; + } + + /** + * Run code inside patch. + * + * @return void + */ + public function apply() + { + $this->schemaSetup->startSetup(); + + $this->schemaSetup->getConnection()->addForeignKey( + $this->schemaSetup->getConnection()->getForeignKeyName( + $this->schemaSetup->getTable('wishlist_item_option'), + 'product_id', + $this->schemaSetup->getTable('catalog_product_entity'), + 'entity_id' + ), + $this->schemaSetup->getTable('wishlist_item_option'), + 'product_id', + $this->schemaSetup->getTable('catalog_product_entity'), + 'entity_id', + AdapterInterface::FK_ACTION_CASCADE, + true + ); + + $this->schemaSetup->endSetup(); + } + + /** + * Get array of patches that have to be executed prior to this. + * + * @return string[] + */ + public static function getDependencies() + { + return []; + } + + /** + * Get aliases (previous names) for the patch. + * + * @return string[] + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Wishlist/etc/db_schema_whitelist.json b/app/code/Magento/Wishlist/etc/db_schema_whitelist.json index beaab64280a76..36a294f26e145 100644 --- a/app/code/Magento/Wishlist/etc/db_schema_whitelist.json +++ b/app/code/Magento/Wishlist/etc/db_schema_whitelist.json @@ -35,8 +35,7 @@ "PRIMARY": true, "WISHLIST_ITEM_WISHLIST_ID_WISHLIST_WISHLIST_ID": true, "WISHLIST_ITEM_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, - "WISHLIST_ITEM_STORE_ID_STORE_STORE_ID": true, - "WISHLIST_ITEM_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true + "WISHLIST_ITEM_STORE_ID_STORE_STORE_ID": true } }, "wishlist_item_option": { @@ -49,7 +48,8 @@ }, "constraint": { "PRIMARY": true, - "FK_A014B30B04B72DD0EAB3EECD779728D6": true + "FK_A014B30B04B72DD0EAB3EECD779728D6": true, + "WISHLIST_ITEM_OPTION_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true } } } \ No newline at end of file From ce0cb8c76c31545d3527243cbef1bd77947caf49 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Thu, 4 Oct 2018 13:39:59 +0300 Subject: [PATCH 077/309] MAGETWO-67269: Gift Options set to no still show up as choices on front end order page - Fix statics. --- app/code/Magento/GiftMessage/Block/Message/Inline.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/GiftMessage/Block/Message/Inline.php b/app/code/Magento/GiftMessage/Block/Message/Inline.php index f44f967cefdc6..4a9311c1b4ba2 100644 --- a/app/code/Magento/GiftMessage/Block/Message/Inline.php +++ b/app/code/Magento/GiftMessage/Block/Message/Inline.php @@ -139,7 +139,7 @@ public function getType() /** * Define checkout type * - * @param $type string + * @param string $type * @return $this * @codeCoverageIgnore */ @@ -278,6 +278,8 @@ public function countItems() } /** + * Call method getItemsHasMessages + * * @deprecated Misspelled method * @see getItemsHasMessages */ From 8cf76a56cb7d4eb43489e223240f17868a2b8ca4 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Fri, 5 Oct 2018 16:30:57 +0300 Subject: [PATCH 078/309] MAGETWO-91517: Cancel and Return link removes billing and shipping address - Fix statics. --- .../frontend/web/js/model/checkout-data-resolver.js | 13 ++++++++----- .../view/frontend/web/js/view/form/element/email.js | 5 +++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 25abe3c4ed3a0..4abb4c4d61700 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -66,14 +66,15 @@ define([ * Resolve shipping address. Used local storage */ resolveShippingAddress: function () { + var newCustomerShippingAddress; + if (!checkoutData.getShippingAddressFromData() && window.checkoutConfig.shippingAddressFromData ) { checkoutData.setShippingAddressFromData(window.checkoutConfig.shippingAddressFromData); } - var newCustomerShippingAddress = checkoutData.getNewCustomerShippingAddress(); - + newCustomerShippingAddress = checkoutData.getNewCustomerShippingAddress(); if (newCustomerShippingAddress) { createShippingAddress(newCustomerShippingAddress); } @@ -201,15 +202,17 @@ define([ * Resolve billing address. Used local storage */ resolveBillingAddress: function () { + var selectedBillingAddress, + newCustomerBillingAddressData; + if (!checkoutData.getBillingAddressFromData() && window.checkoutConfig.billingAddressFromData ) { checkoutData.setBillingAddressFromData(window.checkoutConfig.billingAddressFromData); } - var selectedBillingAddress = checkoutData.getSelectedBillingAddress(), - newCustomerBillingAddressData = checkoutData.getNewCustomerBillingAddress(); - + selectedBillingAddress = checkoutData.getSelectedBillingAddress(); + newCustomerBillingAddressData = checkoutData.getNewCustomerBillingAddress(); if (selectedBillingAddress) { if (selectedBillingAddress == 'new-customer-address' && newCustomerBillingAddressData) { //eslint-disable-line selectBillingAddress(createBillingAddress(newCustomerBillingAddressData)); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js index 6c0075fca6d51..37a785e292365 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js @@ -17,6 +17,8 @@ define([ ], function ($, Component, ko, customer, checkEmailAvailability, loginAction, quote, checkoutData, fullScreenLoader) { 'use strict'; + var validatedEmail; + if (!checkoutData.getValidatedEmailValue() && window.checkoutConfig.validatedEmailValue ) { @@ -24,8 +26,7 @@ define([ checkoutData.setValidatedEmailValue(window.checkoutConfig.validatedEmailValue); } - var validatedEmail = checkoutData.getValidatedEmailValue(); - + validatedEmail = checkoutData.getValidatedEmailValue(); if (validatedEmail && !customer.isLoggedIn()) { quote.guestEmail = validatedEmail; } From 2bee3660f183af1439b111557b3b958b3c812b25 Mon Sep 17 00:00:00 2001 From: Peter O'Callaghan <contact@peterocallaghan.co.uk> Date: Sat, 6 Oct 2018 10:28:25 +0100 Subject: [PATCH 079/309] Fixes #18357 - SQL error when table prefix used. --- .../Model/ResourceModel/Agreement/Grid/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php b/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php index 70794d24a64eb..78ad7b32330ad 100644 --- a/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php +++ b/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php @@ -64,7 +64,7 @@ private function getStoresForAgreements() if (!empty($agreementId)) { $select = $this->getConnection()->select()->from( - ['agreement_store' => 'checkout_agreement_store'] + ['agreement_store' => $this->getResource()->getTable('checkout_agreement_store')] )->where( 'agreement_store.agreement_id IN (?)', $agreementId From 8f36157cae89483469d567710aaba1d728328dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Eisenf=C3=BChrer?= <m.eisenfuehrer@techdivision.com> Date: Sat, 6 Oct 2018 16:57:46 +0200 Subject: [PATCH 080/309] fix issue 12399 --- .../Controller/Adminhtml/Promo/Catalog/Save.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index bad1118e3ae72..4d3b36c7fe69c 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -61,6 +61,17 @@ public function execute() ['request' => $this->getRequest()] ); $data = $this->getRequest()->getPostValue(); + + $filterValues = ['from_date' => $this->_dateFilter]; + if ($this->getRequest()->getParam('to_date')) { + $filterValues['to_date'] = $this->_dateFilter; + } + $inputFilter = new \Zend_Filter_Input( + $filterValues, + [], + $data + ); + $data = $inputFilter->getUnescaped(); $id = $this->getRequest()->getParam('rule_id'); if ($id) { $model = $ruleRepository->get($id); From 0d97a1600e3ccb3b3cf5c024a971b6f7b3597a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Eisenf=C3=BChrer?= <m.eisenfuehrer@techdivision.com> Date: Sun, 7 Oct 2018 10:41:26 +0200 Subject: [PATCH 081/309] fix documentation for phpcs --- .../Controller/Adminhtml/Promo/Catalog/Save.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 4d3b36c7fe69c..b09598d4fd76f 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -14,7 +14,8 @@ use Magento\Framework\App\Request\DataPersistorInterface; /** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * Save action for catalog rule + * @package Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog */ class Save extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog implements HttpPostActionInterface { @@ -40,7 +41,9 @@ public function __construct( } /** - * @return void + * Execute save action from catalog rule + * + * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() From ba703d7d22923239c4519616b6c6ab21124ec8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Eisenf=C3=BChrer?= <m.eisenfuehrer@techdivision.com> Date: Sun, 7 Oct 2018 14:24:52 +0200 Subject: [PATCH 082/309] fix one line for static test --- .../CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index b09598d4fd76f..d770d674bb3e6 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -15,6 +15,7 @@ /** * Save action for catalog rule + * * @package Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog */ class Save extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog implements HttpPostActionInterface From d11ef56535da099158cc302eff104c2b466f2af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Eisenf=C3=BChrer?= <m.eisenfuehrer@techdivision.com> Date: Mon, 8 Oct 2018 08:09:14 +0200 Subject: [PATCH 083/309] fix: add SuppressWarnings again --- .../CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index d770d674bb3e6..0ff12faf54cbf 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -15,8 +15,8 @@ /** * Save action for catalog rule - * - * @package Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog implements HttpPostActionInterface { From 65b4bf1be923344c575effb445444ac542f00712 Mon Sep 17 00:00:00 2001 From: Vincent MARMIESSE <vincent.marmiesse@ph2m.com> Date: Thu, 4 Oct 2018 19:32:37 +0200 Subject: [PATCH 084/309] Add class into image builder --- .../Magento/Catalog/Block/Product/Image.php | 3 +++ .../Catalog/Block/Product/ImageFactory.php | 24 +++++++++++++++---- .../Unit/Block/Product/ImageFactoryTest.php | 7 ++++-- .../product/image_with_borders.phtml | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/Image.php b/app/code/Magento/Catalog/Block/Product/Image.php index 20a556ab41451..7a7f9c0affc7d 100644 --- a/app/code/Magento/Catalog/Block/Product/Image.php +++ b/app/code/Magento/Catalog/Block/Product/Image.php @@ -6,6 +6,8 @@ namespace Magento\Catalog\Block\Product; /** + * Product image block + * * @api * @method string getImageUrl() * @method string getWidth() @@ -13,6 +15,7 @@ * @method string getLabel() * @method float getRatio() * @method string getCustomAttributes() + * @method string getClass() * @since 100.0.2 */ class Image extends \Magento\Framework\View\Element\Template diff --git a/app/code/Magento/Catalog/Block/Product/ImageFactory.php b/app/code/Magento/Catalog/Block/Product/ImageFactory.php index f9a576367ddeb..aa303af656a5b 100644 --- a/app/code/Magento/Catalog/Block/Product/ImageFactory.php +++ b/app/code/Magento/Catalog/Block/Product/ImageFactory.php @@ -77,16 +77,29 @@ private function getStringCustomAttributes(array $attributes): string { $result = []; foreach ($attributes as $name => $value) { - $result[] = $name . '="' . $value . '"'; + if ($name != 'class') { + $result[] = $name . '="' . $value . '"'; + } } return !empty($result) ? implode(' ', $result) : ''; } + /** + * Retrieve image class for HTML element + * + * @param array $attributes + * @return string + */ + private function getClass(array $attributes): string + { + return $attributes['class'] ?? 'product-image-photo'; + } + /** * Calculate image ratio * - * @param $width - * @param $height + * @param int $width + * @param int $height * @return float */ private function getRatio(int $width, int $height): float @@ -98,8 +111,9 @@ private function getRatio(int $width, int $height): float } /** - * @param Product $product + * Get image label * + * @param Product $product * @param string $imageType * @return string */ @@ -114,6 +128,7 @@ private function getLabel(Product $product, string $imageType): string /** * Create image block from product + * * @param Product $product * @param string $imageId * @param array|null $attributes @@ -154,6 +169,7 @@ public function create(Product $product, string $imageId, array $attributes = nu 'label' => $this->getLabel($product, $imageMiscParams['image_type']), 'ratio' => $this->getRatio($imageMiscParams['image_width'], $imageMiscParams['image_height']), 'custom_attributes' => $this->getStringCustomAttributes($attributes), + 'class' => $this->getClass($attributes), 'product_id' => $product->getId() ], ]; diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php index 8a42865a3fe4d..95b06e40602bf 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageFactoryTest.php @@ -145,7 +145,8 @@ private function getTestDataWithoutAttributes(): array 'label' => 'test_image_label', 'ratio' => 1, 'custom_attributes' => '', - 'product_id' => null + 'product_id' => null, + 'class' => 'product-image-photo' ], ], ]; @@ -190,6 +191,7 @@ private function getTestDataWithAttributes(): array 'custom_attributes' => [ 'name_1' => 'value_1', 'name_2' => 'value_2', + 'class' => 'my-class' ], ], 'expected' => [ @@ -201,7 +203,8 @@ private function getTestDataWithAttributes(): array 'label' => 'test_product_name', 'ratio' => 0.5, // <== 'custom_attributes' => 'name_1="value_1" name_2="value_2"', - 'product_id' => null + 'product_id' => null, + 'class' => 'my-class' ], ], ]; diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml index 74a0b2d7cf1a3..8a907bd54aa6a 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml @@ -10,7 +10,7 @@ style="width:<?= /* @escapeNotVerified */ $block->getWidth() ?>px;"> <span class="product-image-wrapper" style="padding-bottom: <?= /* @escapeNotVerified */ ($block->getRatio() * 100) ?>%;"> - <img class="product-image-photo" + <img class="<?= /* @escapeNotVerified */ $block->getClass() ?>" <?= /* @escapeNotVerified */ $block->getCustomAttributes() ?> src="<?= /* @escapeNotVerified */ $block->getImageUrl() ?>" max-width="<?= /* @escapeNotVerified */ $block->getWidth() ?>" From d921849d060864994249f387487d743dce85fc06 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Mon, 8 Oct 2018 14:12:59 +0300 Subject: [PATCH 085/309] MAGETWO-91517: Cancel and Return link removes billing and shipping address - Fix statics. --- .../view/frontend/web/js/model/checkout-data-resolver.js | 1 + .../Checkout/view/frontend/web/js/view/form/element/email.js | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 4abb4c4d61700..cd4c59df51187 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -75,6 +75,7 @@ define([ } newCustomerShippingAddress = checkoutData.getNewCustomerShippingAddress(); + if (newCustomerShippingAddress) { createShippingAddress(newCustomerShippingAddress); } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js index 37a785e292365..4d883fb1373bd 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js @@ -27,6 +27,7 @@ define([ } validatedEmail = checkoutData.getValidatedEmailValue(); + if (validatedEmail && !customer.isLoggedIn()) { quote.guestEmail = validatedEmail; } From e258878dbf07fcb14449b8253adb2f8f35b6ebcf Mon Sep 17 00:00:00 2001 From: Valentin Boyanov <val.rusev@gmail.com> Date: Mon, 8 Oct 2018 14:02:48 +0200 Subject: [PATCH 086/309] MAGETWO-34709 Changing empty label value for first option. --- app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php index dc928b4c7942d..4f4ed9de03e43 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php @@ -250,7 +250,7 @@ protected function _getSelectHtmlWithValue(Attribute $attribute, $value) if ('' === $firstOption['value']) { $options[key($options)]['label'] = ''; } else { - array_unshift($options, ['value' => '', 'label' => '']); + array_unshift($options, ['value' => '', 'label' => __('-- Not Selected --')]); } $arguments = [ 'name' => $this->getFilterElementName($attribute->getAttributeCode()), From 1b03c19021957d85e625eeb5bb61cd3921b31937 Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 8 Oct 2018 09:16:07 -0300 Subject: [PATCH 087/309] Revert back change --- app/code/Magento/Newsletter/Model/Queue.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/Queue.php b/app/code/Magento/Newsletter/Model/Queue.php index 67f8cadbfaed4..efb68fd4243d1 100644 --- a/app/code/Magento/Newsletter/Model/Queue.php +++ b/app/code/Magento/Newsletter/Model/Queue.php @@ -196,8 +196,7 @@ public function setQueueStartAtByString($startAt) if ($startAt === null || $startAt == '') { $this->setQueueStartAt(null); } else { - $startAt = $this->timezone->convertConfigTimeToUtcWithPattern($startAt, 'Y-m-d H:i:s', null); - $this->setQueueStartAt($startAt); + $this->setQueueStartAt($this->timezone->convertConfigTimeToUtc($startAt)); } return $this; } From c6c2ffe214f45366e81e3644b7ddadb2b2fce383 Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 8 Oct 2018 09:17:39 -0300 Subject: [PATCH 088/309] Rever-back unit test --- .../Test/Unit/DateTime/TimezoneTest.php | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php index ea761db902674..d57525590b9e8 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php @@ -124,15 +124,11 @@ public function dateIncludeTimeDataProvider() * @param string $expectedResult * @dataProvider getConvertConfigTimeToUtcFixtures */ - public function testConvertConfigTimeToUtc($date, $configuredTimezone, $configuredLocale, $expectedResult) + public function testConvertConfigTimeToUtc($date, $configuredTimezone, $expectedResult) { - $this->localeResolver - ->method('getLocale') - ->willReturn($configuredLocale); - $this->scopeConfigWillReturnConfiguredTimezone($configuredTimezone); - $this->assertEquals($expectedResult, $this->getTimezone()->convertConfigTimeToUtcWithPattern($date)); + $this->assertEquals($expectedResult, $this->getTimezone()->convertConfigTimeToUtc($date)); } /** @@ -145,37 +141,16 @@ public function getConvertConfigTimeToUtcFixtures() 'string' => [ '2016-10-10 10:00:00', 'UTC', - null, '2016-10-10 10:00:00' ], - 'string-en_US' => [ - 'Sep 29, 2018, 6:07:38 PM', - 'UTC', - 'en_US', - '2018-09-29 18:07:38' - ], - 'string-pt_BR' => [ - '29 de set de 2018 18:07:38', - 'UTC', - 'pt_BR', - '2018-09-29 18:07:38' - ], - 'string-tr_TR' => [ - '29 Eyl 2018 18:07:38', - 'UTC', - 'tr_TR', - '2018-09-29 18:07:38' - ], 'datetime' => [ new \DateTime('2016-10-10 10:00:00', new \DateTimeZone('UTC')), 'UTC', - null, '2016-10-10 10:00:00' ], 'datetimeimmutable' => [ new \DateTimeImmutable('2016-10-10 10:00:00', new \DateTimeZone('UTC')), 'UTC', - null, '2016-10-10 10:00:00' ] ]; From cc16df90455a510083f4d4bebda27566c23e221e Mon Sep 17 00:00:00 2001 From: Thiago Lima <thiagolimaufrj@gmail.com> Date: Mon, 8 Oct 2018 14:27:56 +0200 Subject: [PATCH 089/309] fix issue Fatal Error when save configurable product in Magento 2.2.5 #18082 --- .../Product/Initialization/Helper/Plugin/Configurable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5cd8b6a7d0b95..556939ec112f1 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 @@ -158,7 +158,7 @@ protected function getVariationMatrix() $configurableMatrix = json_decode($configurableMatrix, true); foreach ($configurableMatrix as $item) { - if ($item['newProduct']) { + if (isset($item['newProduct'])) { $result[$item['variationKey']] = $this->mapData($item); if (isset($item['qty'])) { From 502e5ac6941a10e39ef1e559c91565bdb07a4d14 Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 8 Oct 2018 09:34:26 -0300 Subject: [PATCH 090/309] New class and interface created for time format convertion --- app/code/Magento/Store/etc/di.xml | 6 ++ app/etc/di.xml | 1 + .../Timezone/LocalizedDateToUtcConverter.php | 86 +++++++++++++++++++ .../LocalizedDateToUtcConverterInterface.php | 17 ++++ 4 files changed, 110 insertions(+) create mode 100644 lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php create mode 100644 lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index be005264b7bbf..f9e003aca69af 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -294,6 +294,12 @@ <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> </arguments> </type> + <type name="Magento\Framework\Stdlib\DateTime\Timezone\LocalizedDateToUtcConverter"> + <arguments> + <argument name="defaultTimezonePath" xsi:type="const">Magento\Directory\Helper\Data::XML_PATH_DEFAULT_TIMEZONE</argument> + <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> + </arguments> + </type> <type name="Magento\Framework\Locale\Resolver"> <arguments> <argument name="defaultLocalePath" xsi:type="const">Magento\Directory\Helper\Data::XML_PATH_DEFAULT_LOCALE</argument> diff --git a/app/etc/di.xml b/app/etc/di.xml index db979f9b76382..282a0af4f46a5 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -146,6 +146,7 @@ <preference for="Magento\Framework\Locale\FormatInterface" type="Magento\Framework\Locale\Format" /> <preference for="Magento\Framework\Locale\ResolverInterface" type="Magento\Framework\Locale\Resolver" /> <preference for="Magento\Framework\Stdlib\DateTime\TimezoneInterface" type="Magento\Framework\Stdlib\DateTime\Timezone" /> + <preference for="Magento\Framework\Stdlib\DateTime\Timezone\LocalizedDateToUtcConverterInterface" type="Magento\Framework\Stdlib\DateTime\Timezone\LocalizedDateToUtcConverter" /> <preference for="Magento\Framework\Communication\ConfigInterface" type="Magento\Framework\Communication\Config" /> <preference for="Magento\Framework\Module\ResourceInterface" type="Magento\Framework\Module\ModuleResource" /> <preference for="Magento\Framework\Pricing\Amount\AmountInterface" type="Magento\Framework\Pricing\Amount\Base" /> diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php new file mode 100644 index 0000000000000..23dadc11cc6ef --- /dev/null +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Stdlib\DateTime\Timezone; + +use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; + +/** + * Class LocalizedDateToUtcConverter + */ +class LocalizedDateToUtcConverter implements LocalizedDateToUtcConverterInterface +{ + /** + * Contains default date format + * + * @var string + */ + private $defaultFormat = 'Y-m-d H:i:s'; + + /** + * @var ResolverInterface + */ + private $localeResolver; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var string + */ + private $scopeType; + + /** + * @var string + */ + private $defaultTimezonePath; + + /** + * LocalizedDateToUtcConverter constructor. + * + * @param ResolverInterface $localeResolver + */ + public function __construct( + ResolverInterface $localeResolver, + ScopeConfigInterface $scopeConfig, + $scopeType, + $defaultTimezonePath + ) + { + $this->localeResolver = $localeResolver; + $this->scopeConfig = $scopeConfig; + $this->scopeType = $scopeType; + $this->defaultTimezonePath = $defaultTimezonePath; + } + + /** + * @inheritdoc + */ + public function convertLocalizedDateToUtc($date) + { + $locale = $this->localeResolver->getLocale(); + $formatter = new \IntlDateFormatter( + $locale, + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::MEDIUM, + $this->getConfigTimezone(), + null, + null + ); + $unixTime = $formatter->parse($date); + $dateTime = new DateTime($this); + $dateUniversal = $dateTime->gmtDate(null, $unixTime); + $date = new \DateTime($dateUniversal, new \DateTimeZone($this->getConfigTimezone())); + + $date->setTimezone(new \DateTimeZone('UTC')); + + return $date->format($this->defaultFormat); + } +} \ No newline at end of file diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php new file mode 100644 index 0000000000000..277900c46badf --- /dev/null +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Stdlib\DateTime\Timezone; + +interface LocalizedDateToUtcConverterInterface +{ + /** + * @param string $data + * @return string + */ + public function convertLocalizedDateToUtc($date); +} \ No newline at end of file From 51e8fe7118e6734e3ee250483d9877eda2d1388a Mon Sep 17 00:00:00 2001 From: Bunyamin <inanbunyamin90@gmail.com> Date: Mon, 8 Oct 2018 10:07:17 -0300 Subject: [PATCH 091/309] Convert local timestamp into GMT timestamp --- app/code/Magento/Newsletter/Model/Queue.php | 18 +++++--- app/code/Magento/Store/etc/di.xml | 6 --- .../Timezone/LocalizedDateToUtcConverter.php | 44 +++++++------------ 3 files changed, 29 insertions(+), 39 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/Queue.php b/app/code/Magento/Newsletter/Model/Queue.php index efb68fd4243d1..58c5ba8a7e04a 100644 --- a/app/code/Magento/Newsletter/Model/Queue.php +++ b/app/code/Magento/Newsletter/Model/Queue.php @@ -7,6 +7,7 @@ use Magento\Framework\App\TemplateTypesInterface; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\Stdlib\DateTime\Timezone\LocalizedDateToUtcConverterInterface; /** * Newsletter queue model. @@ -117,6 +118,11 @@ class Queue extends \Magento\Framework\Model\AbstractModel implements TemplateTy */ private $timezone; + /** + * @var LocalizedDateToUtcConverterInterface + */ + private $utcConverter; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -144,7 +150,8 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - TimezoneInterface $timezone = null + TimezoneInterface $timezone = null, + LocalizedDateToUtcConverterInterface $utcConverter = null ) { parent::__construct( $context, @@ -159,9 +166,10 @@ public function __construct( $this->_problemFactory = $problemFactory; $this->_subscribersCollection = $subscriberCollectionFactory->create(); $this->_transportBuilder = $transportBuilder; - $this->timezone = $timezone ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - TimezoneInterface::class - ); + + $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $this->timezone = $timezone ?: $objectManager->get(TimezoneInterface::class); + $this->utcConverter = $utcConverter ?? $objectManager->get(LocalizedDateToUtcConverterInterface::class); } /** @@ -196,7 +204,7 @@ public function setQueueStartAtByString($startAt) if ($startAt === null || $startAt == '') { $this->setQueueStartAt(null); } else { - $this->setQueueStartAt($this->timezone->convertConfigTimeToUtc($startAt)); + $this->setQueueStartAt($this->utcConverter->convertLocalizedDateToUtc($startAt)); } return $this; } diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index f9e003aca69af..be005264b7bbf 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -294,12 +294,6 @@ <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> </arguments> </type> - <type name="Magento\Framework\Stdlib\DateTime\Timezone\LocalizedDateToUtcConverter"> - <arguments> - <argument name="defaultTimezonePath" xsi:type="const">Magento\Directory\Helper\Data::XML_PATH_DEFAULT_TIMEZONE</argument> - <argument name="scopeType" xsi:type="const">Magento\Store\Model\ScopeInterface::SCOPE_STORE</argument> - </arguments> - </type> <type name="Magento\Framework\Locale\Resolver"> <arguments> <argument name="defaultLocalePath" xsi:type="const">Magento\Directory\Helper\Data::XML_PATH_DEFAULT_LOCALE</argument> diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php index 23dadc11cc6ef..721ed9a384dad 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php @@ -7,6 +7,7 @@ namespace Magento\Framework\Stdlib\DateTime\Timezone; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\Locale\ResolverInterface; use Magento\Framework\App\Config\ScopeConfigInterface; @@ -23,24 +24,14 @@ class LocalizedDateToUtcConverter implements LocalizedDateToUtcConverterInterfac private $defaultFormat = 'Y-m-d H:i:s'; /** - * @var ResolverInterface - */ - private $localeResolver; - - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * @var string + * @var TimezoneInterface */ - private $scopeType; + private $timezone; /** - * @var string + * @var ResolverInterface */ - private $defaultTimezonePath; + private $localeResolver; /** * LocalizedDateToUtcConverter constructor. @@ -48,16 +39,12 @@ class LocalizedDateToUtcConverter implements LocalizedDateToUtcConverterInterfac * @param ResolverInterface $localeResolver */ public function __construct( - ResolverInterface $localeResolver, - ScopeConfigInterface $scopeConfig, - $scopeType, - $defaultTimezonePath + TimezoneInterface $timezone, + ResolverInterface $localeResolver ) { + $this->timezone = $timezone; $this->localeResolver = $localeResolver; - $this->scopeConfig = $scopeConfig; - $this->scopeType = $scopeType; - $this->defaultTimezonePath = $defaultTimezonePath; } /** @@ -65,20 +52,21 @@ public function __construct( */ public function convertLocalizedDateToUtc($date) { + $configTimezone = $this->timezone->getConfigTimezone(); $locale = $this->localeResolver->getLocale(); + $formatter = new \IntlDateFormatter( $locale, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::MEDIUM, - $this->getConfigTimezone(), - null, - null + $configTimezone ); - $unixTime = $formatter->parse($date); - $dateTime = new DateTime($this); - $dateUniversal = $dateTime->gmtDate(null, $unixTime); - $date = new \DateTime($dateUniversal, new \DateTimeZone($this->getConfigTimezone())); + $localTimestamp = $formatter->parse($date); + $gmtTimestamp = $this->timezone->date($localTimestamp)->getTimestamp(); + $formattedUniversalTime = date($this->defaultFormat, $gmtTimestamp); + + $date = new \DateTime($formattedUniversalTime, new \DateTimeZone($configTimezone)); $date->setTimezone(new \DateTimeZone('UTC')); return $date->format($this->defaultFormat); From 9d824af97af6d6ac34cf18b1c6086dc59ba58cdc Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 8 Oct 2018 16:39:07 +0300 Subject: [PATCH 092/309] ENGCOM-2998: Adding trimming sku value function to sku backend model. #18019. Fix static tests. --- .../Catalog/Model/Product/Attribute/Backend/Sku.php | 10 +++++----- .../Ui/DataProvider/Product/Form/Modifier/General.php | 10 ++++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php index 2ea3b9aaf10a8..98738e055ca8f 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php @@ -4,15 +4,13 @@ * See COPYING.txt for license details. */ -/** - * Catalog product SKU backend attribute model - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Catalog\Model\Product\Attribute\Backend; use Magento\Catalog\Model\Product; +/** + * Catalog product SKU backend attribute model. + */ class Sku extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend { /** @@ -130,6 +128,8 @@ protected function _getLastSimilarAttributeValueIncrement($attribute, $object) } /** + * Remove extra spaces from attribute value before save. + * * @param Product $object * @return void */ diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index e0b0d066c1c1d..6ec1cc6c46d9d 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -58,8 +58,11 @@ public function __construct( } /** - * {@inheritdoc} + * Customize number fields for advanced price and weight fields. + * * @since 101.0.0 + * @param array $data + * @return array * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function modifyData(array $data) @@ -130,8 +133,11 @@ protected function customizeAdvancedPriceFormat(array $data) } /** - * {@inheritdoc} + * Customize product form fields. + * * @since 101.0.0 + * @param array $meta + * @return array */ public function modifyMeta(array $meta) { From 02bdadd38cd023840ed85cca43b6091c3457d94a Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Tue, 9 Oct 2018 11:33:27 +0300 Subject: [PATCH 093/309] MAGETWO-91517: Cancel and Return link removes billing and shipping address - Fix statics. --- .../view/frontend/web/js/model/checkout-data-resolver.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index cd4c59df51187..1c2438889c170 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -214,6 +214,7 @@ define([ selectedBillingAddress = checkoutData.getSelectedBillingAddress(); newCustomerBillingAddressData = checkoutData.getNewCustomerBillingAddress(); + if (selectedBillingAddress) { if (selectedBillingAddress == 'new-customer-address' && newCustomerBillingAddressData) { //eslint-disable-line selectBillingAddress(createBillingAddress(newCustomerBillingAddressData)); From 152bbd9a0900257c6cf73381b8bc5a81fa2457d3 Mon Sep 17 00:00:00 2001 From: Yevhen Sentiabov <isentiabov@magento.com> Date: Tue, 9 Oct 2018 15:01:53 +0300 Subject: [PATCH 094/309] MAGETWO-95512: [2.3] Unable to create invoice for an order via Braintree PayPal - Added an exception if Payment Token is not available --- .../Request/PayPal/VaultDataBuilder.php | 2 + .../Request/VaultCaptureDataBuilder.php | 5 + .../Request/VaultCaptureDataBuilderTest.php | 95 +++++++++++++------ app/code/Magento/Braintree/i18n/en_US.csv | 1 + 4 files changed, 72 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php index a035c84b4cafd..4d63ee4125b74 100644 --- a/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php +++ b/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php @@ -49,6 +49,8 @@ public function build(array $buildSubject) $payment = $paymentDO->getPayment(); $data = $payment->getAdditionalInformation(); + // the payment token could be stored only if a customer checks the Vault flow on storefront + // see https://developers.braintreepayments.com/guides/paypal/vault/javascript/v2#invoking-the-vault-flow if (!empty($data[VaultConfigProvider::IS_ACTIVE_CODE])) { $result[self::$optionsKey] = [ self::$storeInVaultOnSuccess => true diff --git a/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php index 4280663178efb..950634ba2d9e2 100644 --- a/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php +++ b/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php @@ -6,6 +6,8 @@ namespace Magento\Braintree\Gateway\Request; use Magento\Braintree\Gateway\SubjectReader; +use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Gateway\Command\CommandException; use Magento\Payment\Gateway\Request\BuilderInterface; use Magento\Payment\Helper\Formatter; @@ -41,6 +43,9 @@ public function build(array $buildSubject) $payment = $paymentDO->getPayment(); $extensionAttributes = $payment->getExtensionAttributes(); $paymentToken = $extensionAttributes->getVaultPaymentToken(); + if ($paymentToken === null) { + throw new CommandException(__('The Payment Token is not available to perform the request.')); + } return [ 'amount' => $this->formatPrice($this->subjectReader->readAmount($buildSubject)), 'paymentMethodToken' => $paymentToken->getGatewayToken() diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php index 25ccd8b32d10e..d4e1f2745e3f3 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php @@ -3,10 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Braintree\Test\Unit\Gateway\Request; -use Magento\Braintree\Gateway\SubjectReader; use Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder; +use Magento\Braintree\Gateway\SubjectReader; use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; use Magento\Sales\Api\Data\OrderPaymentExtension; use Magento\Sales\Model\Order\Payment; @@ -26,47 +28,46 @@ class VaultCaptureDataBuilderTest extends \PHPUnit\Framework\TestCase /** * @var PaymentDataObjectInterface|MockObject */ - private $paymentDOMock; + private $paymentDO; /** * @var Payment|MockObject */ - private $paymentMock; + private $payment; /** - * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject + * @var SubjectReader|MockObject */ - private $subjectReaderMock; + private $subjectReader; /** * @inheritdoc */ - protected function setUp() + protected function setUp(): void { - $this->paymentDOMock = $this->createMock(PaymentDataObjectInterface::class); - $this->paymentMock = $this->getMockBuilder(Payment::class) + $this->paymentDO = $this->createMock(PaymentDataObjectInterface::class); + $this->payment = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() ->getMock(); - $this->paymentDOMock->expects(static::once()) - ->method('getPayment') - ->willReturn($this->paymentMock); + $this->paymentDO->method('getPayment') + ->willReturn($this->payment); - $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) ->disableOriginalConstructor() ->getMock(); - $this->builder = new VaultCaptureDataBuilder($this->subjectReaderMock); + $this->builder = new VaultCaptureDataBuilder($this->subjectReader); } /** - * \Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder::build + * Checks the result after builder execution. */ - public function testBuild() + public function testBuild(): void { $amount = 30.00; $token = '5tfm4c'; $buildSubject = [ - 'payment' => $this->paymentDOMock, + 'payment' => $this->paymentDO, 'amount' => $amount, ]; @@ -75,36 +76,68 @@ public function testBuild() 'paymentMethodToken' => $token, ]; - $this->subjectReaderMock->expects(self::once()) - ->method('readPayment') + $this->subjectReader->method('readPayment') ->with($buildSubject) - ->willReturn($this->paymentDOMock); - $this->subjectReaderMock->expects(self::once()) - ->method('readAmount') + ->willReturn($this->paymentDO); + $this->subjectReader->method('readAmount') ->with($buildSubject) ->willReturn($amount); - $paymentExtensionMock = $this->getMockBuilder(OrderPaymentExtension::class) + /** @var OrderPaymentExtension|MockObject $paymentExtension */ + $paymentExtension = $this->getMockBuilder(OrderPaymentExtension::class) ->setMethods(['getVaultPaymentToken']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $paymentTokenMock = $this->getMockBuilder(PaymentToken::class) + /** @var PaymentToken|MockObject $paymentToken */ + $paymentToken = $this->getMockBuilder(PaymentToken::class) ->disableOriginalConstructor() ->getMock(); - $paymentExtensionMock->expects(static::once()) - ->method('getVaultPaymentToken') - ->willReturn($paymentTokenMock); - $this->paymentMock->expects(static::once()) - ->method('getExtensionAttributes') - ->willReturn($paymentExtensionMock); + $paymentExtension->method('getVaultPaymentToken') + ->willReturn($paymentToken); + $this->payment->method('getExtensionAttributes') + ->willReturn($paymentExtension); - $paymentTokenMock->expects(static::once()) - ->method('getGatewayToken') + $paymentToken->method('getGatewayToken') ->willReturn($token); $result = $this->builder->build($buildSubject); self::assertEquals($expected, $result); } + + /** + * Checks a builder execution if Payment Token doesn't exist. + * + * @expectedException \Magento\Payment\Gateway\Command\CommandException + * @expectedExceptionMessage The Payment Token is not available to perform the request. + */ + public function testBuildWithoutPaymentToken(): void + { + $amount = 30.00; + $buildSubject = [ + 'payment' => $this->paymentDO, + 'amount' => $amount, + ]; + + $this->subjectReader->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDO); + $this->subjectReader->method('readAmount') + ->with($buildSubject) + ->willReturn($amount); + + /** @var OrderPaymentExtension|MockObject $paymentExtension */ + $paymentExtension = $this->getMockBuilder(OrderPaymentExtension::class) + ->setMethods(['getVaultPaymentToken']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->payment->method('getExtensionAttributes') + ->willReturn($paymentExtension); + $paymentExtension->method('getVaultPaymentToken') + ->willReturn(null); + + $this->builder->build($buildSubject); + } } diff --git a/app/code/Magento/Braintree/i18n/en_US.csv b/app/code/Magento/Braintree/i18n/en_US.csv index 6bf677151ed0d..7bd305f546dc6 100644 --- a/app/code/Magento/Braintree/i18n/en_US.csv +++ b/app/code/Magento/Braintree/i18n/en_US.csv @@ -192,3 +192,4 @@ Currency,Currency "Too many concurrent attempts to refund this transaction. Try again later.","Too many concurrent attempts to refund this transaction. Try again later." "Too many concurrent attempts to void this transaction. Try again later.","Too many concurrent attempts to void this transaction. Try again later." "Braintree Settlement","Braintree Settlement" +"The Payment Token is not available to perform the request.","The Payment Token is not available to perform the request." From 3370c8110ef6c479a1886722c447593b0437340a Mon Sep 17 00:00:00 2001 From: Karen_Mkhitaryan <Karen_Mkhitaryan@epam.com> Date: Tue, 9 Oct 2018 17:28:12 +0400 Subject: [PATCH 095/309] MAGETWO-91628: Bundle product price doubled when switching currency - Add minor changes in test --- .../Mftf/Test/CurrencyChangingBundleProductInCartTest.xml | 6 ++++++ .../Test/Mftf/Section/CurrencySetupSection.xml | 1 + 2 files changed, 7 insertions(+) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml index b7e716dc15ece..ded8bb3c83337 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml @@ -28,12 +28,16 @@ <argument name="product" value="BundleProduct"/> </actionGroup> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="ClearFiltersAfter"/> + <waitForPageLoad stepKey="waitForClearFilter"/> <!--Clear Configs--> <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> <waitForPageLoad stepKey="waitForAdminLoginPageLoad"/> <amOnPage url="{{ConfigCurrencySetupPage.url}}" stepKey="navigateToConfigCurrencySetupPage"/> <waitForPageLoad stepKey="waitForConfigCurrencySetupPageForUnselectEuroCurrency"/> <unselectOption selector="{{CurrencySetupSection.allowCurrencies}}" userInput="Euro" stepKey="unselectEuro"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{CurrencySetupSection.currencyOptions}}" stepKey="closeOptions"/> + <waitForPageLoad stepKey="waitForCloseOptions"/> <click stepKey="saveUnselectedConfigs" selector="{{AdminConfigSection.saveButton}}"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> @@ -57,6 +61,8 @@ <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <amOnPage url="{{ConfigCurrencySetupPage.url}}" stepKey="navigateToConfigCurrencySetupPage"/> <waitForPageLoad stepKey="waitForConfigCurrencySetupPage"/> + <conditionalClick selector="{{CurrencySetupSection.currencyOptions}}" dependentSelector="{{CurrencySetupSection.allowCurrencies}}" visible="false" stepKey="openOptions"/> + <waitForPageLoad stepKey="waitForOptions"/> <selectOption selector="{{CurrencySetupSection.allowCurrencies}}" parameterArray="['Euro', 'US Dollar']" stepKey="selectCurrencies"/> <click stepKey="saveConfigs" selector="{{AdminConfigSection.saveButton}}"/> <!-- Go to storefront BundleProduct --> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/CurrencySetupSection.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/CurrencySetupSection.xml index 16101409ad7e8..20fcd1e89360c 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/CurrencySetupSection.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/CurrencySetupSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CurrencySetupSection"> <element name="allowCurrencies" type="select" selector="#currency_options_allow"/> + <element name="currencyOptions" type="select" selector="#currency_options-head"/> </section> </sections> \ No newline at end of file From dd560f3c985e890fd21cb97b3fd6b5f5b190a312 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Tue, 9 Oct 2018 16:32:45 +0300 Subject: [PATCH 096/309] MAGETWO-91640: Scheduled Import of Products fails on error when errors should be skipped - Integration test fix --- .../integration/testsuite/Magento/Catalog/_files/categories.php | 2 +- .../Magento/CatalogImportExport/Model/Import/ProductTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php index a903274793c34..9a1a6f24dffc8 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php @@ -84,7 +84,7 @@ $category = $objectManager->create(\Magento\Catalog\Model\Category::class); $category->isObjectNew(true); $category->setId(6) - ->setName('Category 2') + ->setName('Category 2.1') ->setParentId(2) ->setPath('1/2/6') ->setLevel(2) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 237d7660da9bd..296c36caed5a0 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -958,6 +958,7 @@ public function testInvalidSkuLink() $errors = $this->_model->setParameters( [ 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + Import::FIELD_NAME_VALIDATION_STRATEGY => null, 'entity' => 'catalog_product' ] )->setSource( From 77111659b520ebad13b53389f67144fddd5dfad6 Mon Sep 17 00:00:00 2001 From: Thiago Lima <thiagolimaufrj@gmail.com> Date: Wed, 10 Oct 2018 11:28:17 +0200 Subject: [PATCH 097/309] 18082 fix unit test --- .../Product/Initialization/Helper/Plugin/Configurable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 556939ec112f1..b5940e36aa792 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 @@ -158,7 +158,7 @@ protected function getVariationMatrix() $configurableMatrix = json_decode($configurableMatrix, true); foreach ($configurableMatrix as $item) { - if (isset($item['newProduct'])) { + if (isset($item['newProduct']) && $item['newProduct']) { $result[$item['variationKey']] = $this->mapData($item); if (isset($item['qty'])) { From b0d7a268162d57f3f30a3488fca1afac104ae322 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Wed, 10 Oct 2018 13:53:30 +0300 Subject: [PATCH 098/309] MAGETWO-91679: Bundled SKUs are being assembled based on the product ID number - Integration test fix --- app/code/Magento/Bundle/Model/Product/Type.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php index 67441cd944c4f..3729cd945758f 100644 --- a/app/code/Magento/Bundle/Model/Product/Type.php +++ b/app/code/Magento/Bundle/Model/Product/Type.php @@ -310,7 +310,7 @@ public function getSku($product) $selections = $this->getSelectionsByIds($selectionIds, $product); foreach ($selectionIds as $selectionId) { $entity = $selections->getItemByColumnValue('selection_id', $selectionId); - if ($entity->getEntityId()) { + if (isset($entity) && $entity->getEntityId()) { $skuParts[] = $entity->getSku(); } } From 4c9166f6bbce8949e8ce57b56b2a232134fe3c64 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Wed, 10 Oct 2018 14:52:44 -0500 Subject: [PATCH 099/309] MC-4323: Cannot Add Slider WYSIWYG Image From Gallery or Link to Image in Gallery After Page Has Been Saved - Fixed editor callback to use the global editor --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 760e0785a7893..bd47391e2ed2e 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -117,7 +117,7 @@ define([ jQuery.when.apply(jQuery, deferreds).done(function () { tinyMCE4.init(settings); this.getPluginButtons().hide(); - this.eventBus.attachEventHandler('open_browser_callback', this.openFileBrowser); + this.eventBus.attachEventHandler('open_browser_callback', tinyMceEditors.get(self.id).openFileBrowser); }.bind(this)); }, From 41cb4685fc24b296de837ddf8c39ab377e7347a9 Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <Mikalai_Shostka@epam.com> Date: Thu, 11 Oct 2018 10:24:11 +0300 Subject: [PATCH 100/309] MAGETWO-91563: Gift wrapping selection does not display in shopping cart - Add GET interface --- app/code/Magento/Checkout/Controller/Cart/UpdatePost.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php index 9f0dcc6d9c18f..b49635a951e39 100644 --- a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php +++ b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php @@ -8,8 +8,9 @@ use Magento\Checkout\Model\Cart\RequestQuantityProcessor; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; -class UpdatePost extends \Magento\Checkout\Controller\Cart implements HttpPostActionInterface +class UpdatePost extends \Magento\Checkout\Controller\Cart implements HttpGetActionInterface, HttpPostActionInterface { /** * @var RequestQuantityProcessor From e210b7044a7bfd503082249a72a95a6c0ae03eb5 Mon Sep 17 00:00:00 2001 From: Joseph Maxwell <joseph@swiftotter.com> Date: Wed, 6 Jun 2018 10:40:48 -0500 Subject: [PATCH 101/309] Bundle checkboxes now are checked when item edited from cart --- .../Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php index 5d326e7c01d19..ed6a4d1ad7209 100644 --- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php +++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php @@ -167,7 +167,9 @@ protected function _getSelectedOptions() */ protected function assignSelection(\Magento\Bundle\Model\Option $option, $selectionId) { - if ($selectionId && $option->getSelectionById($selectionId)) { + if (is_array($selectionId)) { + $this->_selectedOptions = $selectionId; + } else if ($selectionId && $option->getSelectionById($selectionId)) { $this->_selectedOptions = $selectionId; } elseif (!$option->getRequired()) { $this->_selectedOptions = 'None'; From 53f59424c0b049e2f275c50bbced1fc9b42a6916 Mon Sep 17 00:00:00 2001 From: Joseph Maxwell <joseph@swiftotter.com> Date: Wed, 6 Jun 2018 10:44:38 -0500 Subject: [PATCH 102/309] #4942: bundled products now retain customer selections when editing from cart --- .../Block/Catalog/Product/View/Type/Bundle.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php index 62a2fa1c47e1e..cab923da49215 100644 --- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php +++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php @@ -193,6 +193,18 @@ public function getJsonConfig() $options[$optionId]['selections'][$configValue]['qty'] = $configQty; } } + + + $preConfiguredQtys = $preConfiguredValues->getData("bundle_option_qty/${optionId}") ?? []; + $selections = $options[$optionId]['selections']; + array_walk($selections, function(&$selection, $selectionId) use ($preConfiguredQtys) { + if (is_array($preConfiguredQtys) && isset($preConfiguredQtys[$selectionId])) { + $selection['qty'] = $preConfiguredQtys[$selectionId]; + } else if ((int)$preConfiguredQtys > 0) { + $selection['qty'] = $preConfiguredQtys; + } + }); + $options[$optionId]['selections'] = $selections; } $position++; } From 8050d5014037c9ae845be1fd1338b4f4ce7b8d19 Mon Sep 17 00:00:00 2001 From: David Manners <dmanners87@gmail.com> Date: Tue, 18 Sep 2018 09:05:00 +0000 Subject: [PATCH 103/309] magento/magento2#4942: fix code styles in the app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php file --- .../Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php index cab923da49215..1cd0c41abb528 100644 --- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php +++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php @@ -194,10 +194,9 @@ public function getJsonConfig() } } - $preConfiguredQtys = $preConfiguredValues->getData("bundle_option_qty/${optionId}") ?? []; $selections = $options[$optionId]['selections']; - array_walk($selections, function(&$selection, $selectionId) use ($preConfiguredQtys) { + array_walk($selections, function (&$selection, $selectionId) use ($preConfiguredQtys) { if (is_array($preConfiguredQtys) && isset($preConfiguredQtys[$selectionId])) { $selection['qty'] = $preConfiguredQtys[$selectionId]; } else if ((int)$preConfiguredQtys > 0) { From 0bd75c823e506e8099cfa4dbb5877be5662bcebb Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 4 Oct 2018 16:16:01 +0300 Subject: [PATCH 104/309] =?UTF-8?q?NGCOM-2997:=20#4942=20and=20bundle=20ch?= =?UTF-8?q?eckbox=20bug=20#15905.=20Fix=20functional=20and=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Catalog/Product/View/Type/Bundle.php | 43 +++++++++++++------ .../Test/StorefrontEditBundleProductTest.xml | 5 +-- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php index 1cd0c41abb528..fa488b073f515 100644 --- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php +++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php @@ -7,6 +7,7 @@ use Magento\Bundle\Model\Option; use Magento\Catalog\Model\Product; +use Magento\Framework\DataObject; /** * Catalog bundle product info block @@ -170,7 +171,7 @@ public function getJsonConfig() $defaultValues = []; $preConfiguredFlag = $currentProduct->hasPreconfiguredValues(); - /** @var \Magento\Framework\DataObject|null $preConfiguredValues */ + /** @var DataObject|null $preConfiguredValues */ $preConfiguredValues = $preConfiguredFlag ? $currentProduct->getPreconfiguredValues() : null; $position = 0; @@ -193,23 +194,13 @@ public function getJsonConfig() $options[$optionId]['selections'][$configValue]['qty'] = $configQty; } } - - $preConfiguredQtys = $preConfiguredValues->getData("bundle_option_qty/${optionId}") ?? []; - $selections = $options[$optionId]['selections']; - array_walk($selections, function (&$selection, $selectionId) use ($preConfiguredQtys) { - if (is_array($preConfiguredQtys) && isset($preConfiguredQtys[$selectionId])) { - $selection['qty'] = $preConfiguredQtys[$selectionId]; - } else if ((int)$preConfiguredQtys > 0) { - $selection['qty'] = $preConfiguredQtys; - } - }); - $options[$optionId]['selections'] = $selections; + $options = $this->processOptions($optionId, $options, $preConfiguredValues); } $position++; } $config = $this->getConfigData($currentProduct, $options); - $configObj = new \Magento\Framework\DataObject( + $configObj = new DataObject( [ 'config' => $config, ] @@ -414,4 +405,30 @@ private function getConfigData(Product $product, array $options) ]; return $config; } + + /** + * Set preconfigured quantities and selections to options. + * + * @param string $optionId + * @param array $options + * @param DataObject $preConfiguredValues + * @return array + */ + private function processOptions(string $optionId, array $options, DataObject $preConfiguredValues) + { + $preConfiguredQtys = $preConfiguredValues->getData("bundle_option_qty/${optionId}") ?? []; + $selections = $options[$optionId]['selections']; + array_walk($selections, function (&$selection, $selectionId) use ($preConfiguredQtys) { + if (is_array($preConfiguredQtys) && isset($preConfiguredQtys[$selectionId])) { + $selection['qty'] = $preConfiguredQtys[$selectionId]; + } else { + if ((int)$preConfiguredQtys > 0) { + $selection['qty'] = $preConfiguredQtys; + } + } + }); + $options[$optionId]['selections'] = $selections; + + return $options; + } } diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml index f94cd83f4e7d7..4945308c26c4a 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml @@ -94,9 +94,8 @@ <click stepKey="clickEdit" selector="{{CheckoutCartProductSection.nthEditButton('1')}}"/> <waitForPageLoad stepKey="waitForStorefront2"/> - <!-- Choose both of the options on the storefront --> - <click stepKey="selectFirstBundleOption2" selector="{{StorefrontBundledSection.nthBundledOption('1','1')}}"/> - <click stepKey="selectSecondBundleOption2" selector="{{StorefrontBundledSection.nthBundledOption('1','2')}}"/> + <!-- Check second one option to choose both of the options on the storefront --> + <click selector="{{StorefrontBundledSection.bundleOption('1','2')}}" stepKey="selectSecondBundleOption2"/> <waitForPageLoad stepKey="waitForPriceUpdate3"/> <see stepKey="seeDoublePrice" selector="{{StorefrontBundledSection.configuredPrice}}" userInput="2,460.00"/> From 38b91b77bef7604d747780a3cad2d342d05453d3 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 11 Oct 2018 11:52:56 +0300 Subject: [PATCH 105/309] ENGCOM-1928: 14294 - Fixes 'back' functionality after switching a store view. #15961. Fix functional tests. --- app/code/Magento/Store/Block/Switcher.php | 20 +++++++++++++++++++ .../frontend/templates/switch/languages.phtml | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Block/Switcher.php b/app/code/Magento/Store/Block/Switcher.php index f15349f11066d..e162a2a0abe16 100644 --- a/app/code/Magento/Store/Block/Switcher.php +++ b/app/code/Magento/Store/Block/Switcher.php @@ -265,4 +265,24 @@ public function getTargetStorePostData(Store $store, $data = []) $data ); } + + /** + * Returns target store redirect url. + * + * @param Store $store + * @return string + */ + public function getTargetStoreRedirectUrl(Store $store) + { + return $this->getUrl( + 'stores/store/redirect', + [ + '___store' => $store->getCode(), + '___from_store' => $this->getCurrentStoreCode(), + ActionInterface::PARAM_NAME_URL_ENCODED => $this->urlHelper->getEncodedUrl( + $store->getCurrentUrl(false) + ), + ] + ); + } } diff --git a/app/code/Magento/Store/view/frontend/templates/switch/languages.phtml b/app/code/Magento/Store/view/frontend/templates/switch/languages.phtml index 6d041a9e22c5d..411a1ef3c9a32 100644 --- a/app/code/Magento/Store/view/frontend/templates/switch/languages.phtml +++ b/app/code/Magento/Store/view/frontend/templates/switch/languages.phtml @@ -27,7 +27,7 @@ <?php foreach ($block->getStores() as $_lang): ?> <?php if ($_lang->getId() != $block->getCurrentStoreId()): ?> <li class="view-<?= $block->escapeHtml($_lang->getCode()) ?> switcher-option"> - <a href="<?= $block->escapeUrl($_lang->getCurrentUrl(true)) ?>"> + <a href="<?= $block->escapeUrl($block->getTargetStoreRedirectUrl($_lang)) ?>"> <?= $block->escapeHtml($_lang->getName()) ?> </a> </li> From f03ed7a2e6292f58cc5859422d660e85ec9d5cdb Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Thu, 11 Oct 2018 15:00:48 +0300 Subject: [PATCH 106/309] MAGETWO-91563: Gift wrapping selection does not display in shopping cart - Fix static test --- app/code/Magento/Checkout/Controller/Cart/UpdatePost.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php index b49635a951e39..bfc408d920ad3 100644 --- a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php +++ b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -10,6 +9,9 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Action\HttpGetActionInterface; +/** + * Post update shopping cart. + */ class UpdatePost extends \Magento\Checkout\Controller\Cart implements HttpGetActionInterface, HttpPostActionInterface { /** From d3a62b1c8bcdad89d564b0a41f53a08cc81a4fa0 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Thu, 11 Oct 2018 15:32:13 +0300 Subject: [PATCH 107/309] MAGETWO-91640: Scheduled Import of Products fails on error when errors should be skipped - Fix integration test --- .../integration/testsuite/Magento/Catalog/_files/categories.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php index 9a1a6f24dffc8..a903274793c34 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php @@ -84,7 +84,7 @@ $category = $objectManager->create(\Magento\Catalog\Model\Category::class); $category->isObjectNew(true); $category->setId(6) - ->setName('Category 2.1') + ->setName('Category 2') ->setParentId(2) ->setPath('1/2/6') ->setLevel(2) From de9603e744996a3bd2de090ddb1613e597edec0e Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 11 Oct 2018 13:27:58 -0500 Subject: [PATCH 108/309] MQE-1304: MFTF test failures between 5pm PDT and midnight PDT --- .../Test/Mftf/Section/AdminCartPriceRulesFormSection.xml | 2 ++ .../SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml | 1 + .../Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml | 1 + .../Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml | 1 + .../Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml | 1 + .../Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml | 1 + .../Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml | 1 + .../SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml | 1 + .../Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml | 1 + .../Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml | 1 + .../SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml | 1 + .../Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml | 1 + 12 files changed, 13 insertions(+) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index d8253505c42e4..54f7aa97053cb 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -21,6 +21,8 @@ <element name="coupon" type="select" selector="select[name='coupon_type']"/> <element name="couponCode" type="input" selector="input[name='coupon_code']"/> <element name="useAutoGeneration" type="checkbox" selector="input[name='use_auto_generation']"/> + <element name="fromDate" type="input" selector="input[name='from_date']"/> + <element name="toDate" type="input" selector="input[name='to_date']"/> <element name="userPerCoupon" type="input" selector="//input[@name='uses_per_coupon']"/> <element name="userPerCustomer" type="input" selector="//input[@name='uses_per_customer']"/> <element name="priority" type="input" selector="//*[@name='sort_order']"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml index c69fa97efc034..51062da6f2efe 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml @@ -40,6 +40,7 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Buy X get Y free (discount amount is Y)" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="1" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index 49233cb4b42f5..44145f9290457 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -42,6 +42,7 @@ <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> <fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{_defaultCoupon.code}}" stepKey="fillCouponCode"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="5" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index c1aeebfca520e..a9083cb320ccb 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -42,6 +42,7 @@ <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> <checkOption selector="{{AdminCartPriceRulesFormSection.useAutoGeneration}}" stepKey="tickAutoGeneration"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="0.99" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml index 30aa26b26ed39..9d95b01b2f81d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml @@ -40,6 +40,7 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="10" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml index 7ac69f82f79da..1adf2d22be4db 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml @@ -40,6 +40,7 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="19.99" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml index eb04ce04f0718..42d84f13b9acf 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml @@ -40,6 +40,7 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Percent of product price discount" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="50" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml index ca897bffe8b79..e8b8c6cd9e683 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml @@ -43,6 +43,7 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml index 83854c4a767ca..ae7d0eed739ff 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml @@ -43,6 +43,7 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml index 60a19074317fc..c43aaf8d3c97c 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml @@ -43,6 +43,7 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml index f98f7b408436f..4e1944bed9b42 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml @@ -43,6 +43,7 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml index 6567beba198eb..8fb27a9dd1ef3 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml @@ -43,6 +43,7 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> From 8574dac6e8eaef7cb0b0da7f0aca347d3e82fe02 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 11 Oct 2018 15:23:32 -0500 Subject: [PATCH 109/309] MQE-1303: AdminCreateCustomerTestCest test sequence failure --- .../Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml index 9dd2127fa28b2..6bde63d900c17 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml @@ -27,17 +27,17 @@ <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> <waitForPageLoad stepKey="waitForLoad1"/> <click selector="{{AdminCustomerGridMainActionsSection.addNewCustomer}}" stepKey="clickCreateCustomer"/> - <waitForElement selector="{{AdminCustomerAccountInformationSection.firstName}}" stepKey="wait1"/> <fillField userInput="{{CustomerEntityOne.firstname}}" selector="{{AdminCustomerAccountInformationSection.firstName}}" stepKey="fillFirstName"/> <fillField userInput="{{CustomerEntityOne.lastname}}" selector="{{AdminCustomerAccountInformationSection.lastName}}" stepKey="fillLastName"/> <fillField userInput="{{CustomerEntityOne.email}}" selector="{{AdminCustomerAccountInformationSection.email}}" stepKey="fillEmail"/> <click selector="{{AdminCustomerMainActionsSection.saveButton}}" stepKey="saveCustomer"/> - <waitForElementNotVisible selector="div [data-role='spinner']" time="10" stepKey="waitForSpinner1"/> <seeElement selector="{{AdminCustomerMessagesSection.successMessage}}" stepKey="assertSuccessMessage"/> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <reloadPage stepKey="reloadPage"/> + <waitForPageLoad stepKey="waitForLoad2"/> <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="openFilter"/> <fillField userInput="{{CustomerEntityOne.email}}" selector="{{AdminCustomerFiltersSection.emailInput}}" stepKey="filterEmail"/> <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="applyFilter"/> - <waitForElementNotVisible selector="div [data-role='spinner']" time="10" stepKey="waitForSpinner2"/> <see userInput="{{CustomerEntityOne.firstname}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertFirstName"/> <see userInput="{{CustomerEntityOne.lastname}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertLastName"/> <see userInput="{{CustomerEntityOne.email}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertEmail"/> From 93b8f297f0890a88deb52e8baefb09af1bed8abf Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Fri, 12 Oct 2018 14:03:11 +0300 Subject: [PATCH 110/309] MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category - Fix static tests --- .../Block/Product/ProductList/Toolbar.php | 14 ++++++++++++++ .../view/frontend/web/js/product/list/toolbar.js | 16 ++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index 12c691d1365c3..c7a9fcce7bf7a 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -430,6 +430,8 @@ public function getPagerUrl($params = []) } /** + * Get pager encoded url. + * * @param array $params * @return string */ @@ -624,6 +626,8 @@ public function getLimit() } /** + * Check if limit is current used in toolbar. + * * @param int $limit * @return bool */ @@ -633,6 +637,8 @@ public function isLimitCurrent($limit) } /** + * Pager number of items from which products started on current page. + * * @return int */ public function getFirstNum() @@ -642,6 +648,8 @@ public function getFirstNum() } /** + * Pager number of items products finished on current page. + * * @return int */ public function getLastNum() @@ -651,6 +659,8 @@ public function getLastNum() } /** + * Total number of products in current category. + * * @return int */ public function getTotalNum() @@ -659,6 +669,8 @@ public function getTotalNum() } /** + * Check if current page is the first. + * * @return bool */ public function isFirstPage() @@ -667,6 +679,8 @@ public function isFirstPage() } /** + * Return last page number. + * * @return int */ public function getLastPageNum() diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js index 7c47f5bd04d5a..b8b6ff65be2b4 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js @@ -91,7 +91,7 @@ define([ baseUrl = urlPaths[0], urlParams = urlPaths[1] ? urlPaths[1].split('&') : [], paramData = {}, - parameters, i; + parameters, i, form, params, key, input, formKey; for (i = 0; i < urlParams.length; i++) { parameters = urlParams[i].split('='); @@ -102,25 +102,25 @@ define([ paramData[paramName] = paramValue; if (this.options.post) { - var form = document.createElement('form'); + form = document.createElement('form'); + params = [this.options.mode, this.options.direction, this.options.order, this.options.limit]; - var params = [this.options.mode, this.options.direction, this.options.order, this.options.limit]; - for (var key in paramData) { - if (params.indexOf(key) !== -1) { - var input = document.createElement('input'); + for (key in paramData) { + if (params.indexOf(key) !== -1) { //eslint-disable-line max-depth + input = document.createElement('input'); input.name = key; input.value = paramData[key]; form.appendChild(input); delete paramData[key]; } } - var formKey = document.createElement('input'); + formKey = document.createElement('input'); formKey.name = 'form_key'; formKey.value = this.options.formKey; form.appendChild(formKey); paramData = $.param(paramData); - baseUrl = baseUrl + (paramData.length ? '?' + paramData : ''); + baseUrl += paramData.length ? '?' + paramData : ''; form.action = baseUrl; form.method = 'POST'; From 12753a6f9c73ed4ba3b14a6f94b43c81ea5e2ece Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Fri, 12 Oct 2018 09:55:49 -0500 Subject: [PATCH 111/309] MQE-1304: MFTF test failures between 5pm PDT and midnight PDT - Use generateDate -1 day instead of 01/01/2018 --- .../SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml | 3 ++- .../Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml | 3 ++- .../Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml | 3 ++- .../Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml | 3 ++- .../Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml | 3 ++- .../Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml | 3 ++- .../Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml | 3 ++- .../Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml | 3 ++- .../Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml | 3 ++- .../SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml | 3 ++- .../Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml | 3 ++- 11 files changed, 22 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml index 51062da6f2efe..92d221de9e157 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml @@ -40,7 +40,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Buy X get Y free (discount amount is Y)" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="1" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index 44145f9290457..3deb688de9c34 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -42,7 +42,8 @@ <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> <fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{_defaultCoupon.code}}" stepKey="fillCouponCode"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="5" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index a9083cb320ccb..ec835384a52f8 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -42,7 +42,8 @@ <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> <checkOption selector="{{AdminCartPriceRulesFormSection.useAutoGeneration}}" stepKey="tickAutoGeneration"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="0.99" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml index 9d95b01b2f81d..08a08275ee07a 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml @@ -40,7 +40,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="10" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml index 1adf2d22be4db..a39530f7607e4 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml @@ -40,7 +40,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="19.99" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml index 42d84f13b9acf..1f7d849ac02b0 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml @@ -40,7 +40,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Percent of product price discount" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="50" stepKey="fillDiscountAmount"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml index e8b8c6cd9e683..3eec020e26347 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml @@ -43,7 +43,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml index ae7d0eed739ff..73b5d2546f439 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml @@ -43,7 +43,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml index c43aaf8d3c97c..51d11b4e5cb1c 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml @@ -43,7 +43,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml index 4e1944bed9b42..9395e87c20edb 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml @@ -43,7 +43,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml index 8fb27a9dd1ef3..7c9c52e1c02ac 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml @@ -43,7 +43,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> - <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="01/1/2018" stepKey="fillFromDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> <!-- Scroll down to fix some flaky behavior... --> <scrollTo selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="scrollToConditionsTab"/> From 4e382cc735a883c9dcd8eaeeedc185d7faa06a7f Mon Sep 17 00:00:00 2001 From: hiren pandya <infinitehdp@gmail.com> Date: Sat, 13 Oct 2018 11:52:27 +0530 Subject: [PATCH 112/309] Special price date from issue resolve --- .../Controller/Adminhtml/Product/Initialization/Helper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index d82f4a04fb252..e14916f731ed8 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -463,6 +463,7 @@ private function fillProductOptions(Product $product, array $productOptions) private function convertSpecialFromDateStringToObject($productData) { if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { + $productData['special_from_date'] = $this->dateFilter->filter($productData['special_from_date']); $productData['special_from_date'] = new \DateTime($productData['special_from_date']); } From fd8a5a90b32431fbdd275000624723b106c81841 Mon Sep 17 00:00:00 2001 From: hiren pandya <hiren.pandya@krishtechnolabs.com> Date: Sat, 13 Oct 2018 13:00:58 +0530 Subject: [PATCH 113/309] Revert "Special price date from issue resolve" This reverts commit 4e382cc735a883c9dcd8eaeeedc185d7faa06a7f. --- .../Controller/Adminhtml/Product/Initialization/Helper.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index e14916f731ed8..d82f4a04fb252 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -463,7 +463,6 @@ private function fillProductOptions(Product $product, array $productOptions) private function convertSpecialFromDateStringToObject($productData) { if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { - $productData['special_from_date'] = $this->dateFilter->filter($productData['special_from_date']); $productData['special_from_date'] = new \DateTime($productData['special_from_date']); } From 88592facd5d34872ac2f2051dda246703091cacc Mon Sep 17 00:00:00 2001 From: hiren pandya <hiren.pandya@krishtechnolabs.com> Date: Sat, 13 Oct 2018 13:03:07 +0530 Subject: [PATCH 114/309] Special price date from issue resolve --- .../Controller/Adminhtml/Product/Initialization/Helper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index d82f4a04fb252..8999b03b647a3 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -463,6 +463,7 @@ private function fillProductOptions(Product $product, array $productOptions) private function convertSpecialFromDateStringToObject($productData) { if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { + $productData['special_from_date'] = $this->dateFilter->filter($productData['special_from_date']); $productData['special_from_date'] = new \DateTime($productData['special_from_date']); } From 96507df2629a67616931f8aa404b5b2cf6f5440a Mon Sep 17 00:00:00 2001 From: Luuk Schakenraad <luuk@chessweb.eu> Date: Sat, 13 Oct 2018 13:56:47 +0200 Subject: [PATCH 115/309] Fix disappearing navigation arrows in fotorama zoom --- lib/web/fotorama/fotorama.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js index a16e437c0d7ff..9f756696008cc 100644 --- a/lib/web/fotorama/fotorama.js +++ b/lib/web/fotorama/fotorama.js @@ -2098,7 +2098,7 @@ fotoramaVersion = '4.6.4'; o_navTop = opts.navposition === 'top'; classes.remove.push(selectClass); - $arrs.toggle(opts.arrows); + $arrs.toggle(!!opts.arrows); } else { o_nav = false; $arrs.hide(); From f5b496c2a033b48e8669db81b421f16240c359d0 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sat, 13 Oct 2018 15:05:44 +0200 Subject: [PATCH 116/309] Use the correct method to validate if attribute is required --- app/code/Magento/Eav/Model/Attribute/Data/Text.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Attribute/Data/Text.php b/app/code/Magento/Eav/Model/Attribute/Data/Text.php index a26a92df385f2..a919a54cf6289 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/Text.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/Text.php @@ -67,7 +67,7 @@ public function validateValue($value) $value = $this->getEntity()->getDataUsingMethod($attribute->getAttributeCode()); } - if (!$attribute->isRequired() && empty($value)) { + if (!$attribute->getIsRequired() && empty($value)) { return true; } From 623a6b01330545582c3337813d803691faefef87 Mon Sep 17 00:00:00 2001 From: Luuk Schakenraad <luuk@chessweb.eu> Date: Sun, 14 Oct 2018 19:25:42 +0200 Subject: [PATCH 117/309] Fix disappearing navigation arrows in fotorama zoom also in fotorama.min.js --- lib/web/fotorama/fotorama.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/fotorama/fotorama.min.js b/lib/web/fotorama/fotorama.min.js index 9842f20cdc2af..0a0cbd9db7e74 100644 --- a/lib/web/fotorama/fotorama.min.js +++ b/lib/web/fotorama/fotorama.min.js @@ -1 +1 @@ -fotoramaVersion="4.6.4";(function(window,document,location,$,undefined){"use strict";var _fotoramaClass="fotorama",_fullscreenClass="fotorama__fullscreen",wrapClass=_fotoramaClass+"__wrap",wrapCss2Class=wrapClass+"--css2",wrapCss3Class=wrapClass+"--css3",wrapVideoClass=wrapClass+"--video",wrapFadeClass=wrapClass+"--fade",wrapSlideClass=wrapClass+"--slide",wrapNoControlsClass=wrapClass+"--no-controls",wrapNoShadowsClass=wrapClass+"--no-shadows",wrapPanYClass=wrapClass+"--pan-y",wrapRtlClass=wrapClass+"--rtl",wrapOnlyActiveClass=wrapClass+"--only-active",wrapNoCaptionsClass=wrapClass+"--no-captions",wrapToggleArrowsClass=wrapClass+"--toggle-arrows",stageClass=_fotoramaClass+"__stage",stageFrameClass=stageClass+"__frame",stageFrameVideoClass=stageFrameClass+"--video",stageShaftClass=stageClass+"__shaft",grabClass=_fotoramaClass+"__grab",pointerClass=_fotoramaClass+"__pointer",arrClass=_fotoramaClass+"__arr",arrDisabledClass=arrClass+"--disabled",arrPrevClass=arrClass+"--prev",arrNextClass=arrClass+"--next",navClass=_fotoramaClass+"__nav",navWrapClass=navClass+"-wrap",navShaftClass=navClass+"__shaft",navShaftVerticalClass=navWrapClass+"--vertical",navShaftListClass=navWrapClass+"--list",navShafthorizontalClass=navWrapClass+"--horizontal",navDotsClass=navClass+"--dots",navThumbsClass=navClass+"--thumbs",navFrameClass=navClass+"__frame",fadeClass=_fotoramaClass+"__fade",fadeFrontClass=fadeClass+"-front",fadeRearClass=fadeClass+"-rear",shadowClass=_fotoramaClass+"__shadow",shadowsClass=shadowClass+"s",shadowsLeftClass=shadowsClass+"--left",shadowsRightClass=shadowsClass+"--right",shadowsTopClass=shadowsClass+"--top",shadowsBottomClass=shadowsClass+"--bottom",activeClass=_fotoramaClass+"__active",selectClass=_fotoramaClass+"__select",hiddenClass=_fotoramaClass+"--hidden",fullscreenClass=_fotoramaClass+"--fullscreen",fullscreenIconClass=_fotoramaClass+"__fullscreen-icon",errorClass=_fotoramaClass+"__error",loadingClass=_fotoramaClass+"__loading",loadedClass=_fotoramaClass+"__loaded",loadedFullClass=loadedClass+"--full",loadedImgClass=loadedClass+"--img",grabbingClass=_fotoramaClass+"__grabbing",imgClass=_fotoramaClass+"__img",imgFullClass=imgClass+"--full",thumbClass=_fotoramaClass+"__thumb",thumbArrLeft=thumbClass+"__arr--left",thumbArrRight=thumbClass+"__arr--right",thumbBorderClass=thumbClass+"-border",htmlClass=_fotoramaClass+"__html",videoContainerClass=_fotoramaClass+"-video-container",videoClass=_fotoramaClass+"__video",videoPlayClass=videoClass+"-play",videoCloseClass=videoClass+"-close",horizontalImageClass=_fotoramaClass+"_horizontal_ratio",verticalImageClass=_fotoramaClass+"_vertical_ratio",fotoramaSpinnerClass=_fotoramaClass+"__spinner",spinnerShowClass=fotoramaSpinnerClass+"--show";var JQUERY_VERSION=$&&$.fn.jquery.split(".");if(!JQUERY_VERSION||JQUERY_VERSION[0]<1||JQUERY_VERSION[0]==1&&JQUERY_VERSION[1]<8){throw"Fotorama requires jQuery 1.8 or later and will not run without it."}var _={};var Modernizr=function(window,document,undefined){var version="2.8.3",Modernizr={},docElement=document.documentElement,mod="modernizr",modElem=document.createElement(mod),mStyle=modElem.style,inputElem,toString={}.toString,prefixes=" -webkit- -moz- -o- -ms- ".split(" "),omPrefixes="Webkit Moz O ms",cssomPrefixes=omPrefixes.split(" "),domPrefixes=omPrefixes.toLowerCase().split(" "),tests={},inputs={},attrs={},classes=[],slice=classes.slice,featureName,injectElementWithStyles=function(rule,callback,nodes,testnames){var style,ret,node,docOverflow,div=document.createElement("div"),body=document.body,fakeBody=body||document.createElement("body");if(parseInt(nodes,10)){while(nodes--){node=document.createElement("div");node.id=testnames?testnames[nodes]:mod+(nodes+1);div.appendChild(node)}}style=["­",'<style id="s',mod,'">',rule,"</style>"].join("");div.id=mod;(body?div:fakeBody).innerHTML+=style;fakeBody.appendChild(div);if(!body){fakeBody.style.background="";fakeBody.style.overflow="hidden";docOverflow=docElement.style.overflow;docElement.style.overflow="hidden";docElement.appendChild(fakeBody)}ret=callback(div,rule);if(!body){fakeBody.parentNode.removeChild(fakeBody);docElement.style.overflow=docOverflow}else{div.parentNode.removeChild(div)}return!!ret},_hasOwnProperty={}.hasOwnProperty,hasOwnProp;if(!is(_hasOwnProperty,"undefined")&&!is(_hasOwnProperty.call,"undefined")){hasOwnProp=function(object,property){return _hasOwnProperty.call(object,property)}}else{hasOwnProp=function(object,property){return property in object&&is(object.constructor.prototype[property],"undefined")}}if(!Function.prototype.bind){Function.prototype.bind=function bind(that){var target=this;if(typeof target!="function"){throw new TypeError}var args=slice.call(arguments,1),bound=function(){if(this instanceof bound){var F=function(){};F.prototype=target.prototype;var self=new F;var result=target.apply(self,args.concat(slice.call(arguments)));if(Object(result)===result){return result}return self}else{return target.apply(that,args.concat(slice.call(arguments)))}};return bound}}function setCss(str){mStyle.cssText=str}function setCssAll(str1,str2){return setCss(prefixes.join(str1+";")+(str2||""))}function is(obj,type){return typeof obj===type}function contains(str,substr){return!!~(""+str).indexOf(substr)}function testProps(props,prefixed){for(var i in props){var prop=props[i];if(!contains(prop,"-")&&mStyle[prop]!==undefined){return prefixed=="pfx"?prop:true}}return false}function testDOMProps(props,obj,elem){for(var i in props){var item=obj[props[i]];if(item!==undefined){if(elem===false)return props[i];if(is(item,"function")){return item.bind(elem||obj)}return item}}return false}function testPropsAll(prop,prefixed,elem){var ucProp=prop.charAt(0).toUpperCase()+prop.slice(1),props=(prop+" "+cssomPrefixes.join(ucProp+" ")+ucProp).split(" ");if(is(prefixed,"string")||is(prefixed,"undefined")){return testProps(props,prefixed)}else{props=(prop+" "+domPrefixes.join(ucProp+" ")+ucProp).split(" ");return testDOMProps(props,prefixed,elem)}}tests["touch"]=function(){var bool;if("ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch){bool=true}else{injectElementWithStyles(["@media (",prefixes.join("touch-enabled),("),mod,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(node){bool=node.offsetTop===9})}return bool};tests["csstransforms3d"]=function(){var ret=!!testPropsAll("perspective");if(ret&&"webkitPerspective"in docElement.style){injectElementWithStyles("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(node,rule){ret=node.offsetLeft===9&&node.offsetHeight===3})}return ret};tests["csstransitions"]=function(){return testPropsAll("transition")};for(var feature in tests){if(hasOwnProp(tests,feature)){featureName=feature.toLowerCase();Modernizr[featureName]=tests[feature]();classes.push((Modernizr[featureName]?"":"no-")+featureName)}}Modernizr.addTest=function(feature,test){if(typeof feature=="object"){for(var key in feature){if(hasOwnProp(feature,key)){Modernizr.addTest(key,feature[key])}}}else{feature=feature.toLowerCase();if(Modernizr[feature]!==undefined){return Modernizr}test=typeof test=="function"?test():test;if(typeof enableClasses!=="undefined"&&enableClasses){docElement.className+=" "+(test?"":"no-")+feature}Modernizr[feature]=test}return Modernizr};setCss("");modElem=inputElem=null;Modernizr._version=version;Modernizr._prefixes=prefixes;Modernizr._domPrefixes=domPrefixes;Modernizr._cssomPrefixes=cssomPrefixes;Modernizr.testProp=function(prop){return testProps([prop])};Modernizr.testAllProps=testPropsAll;Modernizr.testStyles=injectElementWithStyles;Modernizr.prefixed=function(prop,obj,elem){if(!obj){return testPropsAll(prop,"pfx")}else{return testPropsAll(prop,obj,elem)}};return Modernizr}(window,document);var fullScreenApi={ok:false,is:function(){return false},request:function(){},cancel:function(){},event:"",prefix:""},browserPrefixes="webkit moz o ms khtml".split(" ");if(typeof document.cancelFullScreen!="undefined"){fullScreenApi.ok=true}else{for(var i=0,il=browserPrefixes.length;i<il;i++){fullScreenApi.prefix=browserPrefixes[i];if(typeof document[fullScreenApi.prefix+"CancelFullScreen"]!="undefined"){fullScreenApi.ok=true;break}}}if(fullScreenApi.ok){fullScreenApi.event=fullScreenApi.prefix+"fullscreenchange";fullScreenApi.is=function(){switch(this.prefix){case"":return document.fullScreen;case"webkit":return document.webkitIsFullScreen;default:return document[this.prefix+"FullScreen"]}};fullScreenApi.request=function(el){return this.prefix===""?el.requestFullScreen():el[this.prefix+"RequestFullScreen"]()};fullScreenApi.cancel=function(el){return this.prefix===""?document.cancelFullScreen():document[this.prefix+"CancelFullScreen"]()}}function bez(coOrdArray){var encodedFuncName="bez_"+$.makeArray(arguments).join("_").replace(".","p");if(typeof $["easing"][encodedFuncName]!=="function"){var polyBez=function(p1,p2){var A=[null,null],B=[null,null],C=[null,null],bezCoOrd=function(t,ax){C[ax]=3*p1[ax];B[ax]=3*(p2[ax]-p1[ax])-C[ax];A[ax]=1-C[ax]-B[ax];return t*(C[ax]+t*(B[ax]+t*A[ax]))},xDeriv=function(t){return C[0]+t*(2*B[0]+3*A[0]*t)},xForT=function(t){var x=t,i=0,z;while(++i<14){z=bezCoOrd(x,0)-t;if(Math.abs(z)<.001)break;x-=z/xDeriv(x)}return x};return function(t){return bezCoOrd(xForT(t),1)}};$["easing"][encodedFuncName]=function(x,t,b,c,d){return c*polyBez([coOrdArray[0],coOrdArray[1]],[coOrdArray[2],coOrdArray[3]])(t/d)+b}}return encodedFuncName}var $WINDOW=$(window),$DOCUMENT=$(document),$HTML,$BODY,QUIRKS_FORCE=location.hash.replace("#","")==="quirks",TRANSFORMS3D=Modernizr.csstransforms3d,CSS3=TRANSFORMS3D&&!QUIRKS_FORCE,COMPAT=TRANSFORMS3D||document.compatMode==="CSS1Compat",FULLSCREEN=fullScreenApi.ok,MOBILE=navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i),SLOW=!CSS3||MOBILE,MS_POINTER=navigator.msPointerEnabled,WHEEL="onwheel"in document.createElement("div")?"wheel":document.onmousewheel!==undefined?"mousewheel":"DOMMouseScroll",TOUCH_TIMEOUT=250,TRANSITION_DURATION=300,SCROLL_LOCK_TIMEOUT=1400,AUTOPLAY_INTERVAL=5e3,MARGIN=2,THUMB_SIZE=64,WIDTH=500,HEIGHT=333,STAGE_FRAME_KEY="$stageFrame",NAV_DOT_FRAME_KEY="$navDotFrame",NAV_THUMB_FRAME_KEY="$navThumbFrame",AUTO="auto",BEZIER=bez([.1,0,.25,1]),MAX_WIDTH=1200,thumbsPerSlide=1,OPTIONS={width:null,minwidth:null,maxwidth:"100%",height:null,minheight:null,maxheight:null,ratio:null,margin:MARGIN,nav:"dots",navposition:"bottom",navwidth:null,thumbwidth:THUMB_SIZE,thumbheight:THUMB_SIZE,thumbmargin:MARGIN,thumbborderwidth:MARGIN,allowfullscreen:false,transition:"slide",clicktransition:null,transitionduration:TRANSITION_DURATION,captions:true,startindex:0,loop:false,autoplay:false,stopautoplayontouch:true,keyboard:false,arrows:true,click:true,swipe:false,trackpad:false,shuffle:false,direction:"ltr",shadows:true,showcaption:true,navdir:"horizontal",navarrows:true,navtype:"thumbs"},KEYBOARD_OPTIONS={left:true,right:true,down:true,up:true,space:false,home:false,end:false};function noop(){}function minMaxLimit(value,min,max){return Math.max(isNaN(min)?-Infinity:min,Math.min(isNaN(max)?Infinity:max,value))}function readTransform(css,dir){return css.match(/ma/)&&css.match(/-?\d+(?!d)/g)[css.match(/3d/)?dir==="vertical"?13:12:dir==="vertical"?5:4]}function readPosition($el,dir){if(CSS3){return+readTransform($el.css("transform"),dir)}else{return+$el.css(dir==="vertical"?"top":"left").replace("px","")}}function getTranslate(pos,direction){var obj={};if(CSS3){switch(direction){case"vertical":obj.transform="translate3d(0, "+pos+"px,0)";break;case"list":break;default:obj.transform="translate3d("+pos+"px,0,0)";break}}else{direction==="vertical"?obj.top=pos:obj.left=pos}return obj}function getDuration(time){return{"transition-duration":time+"ms"}}function unlessNaN(value,alternative){return isNaN(value)?alternative:value}function numberFromMeasure(value,measure){return unlessNaN(+String(value).replace(measure||"px",""))}function numberFromPercent(value){return/%$/.test(value)?numberFromMeasure(value,"%"):undefined}function numberFromWhatever(value,whole){return unlessNaN(numberFromPercent(value)/100*whole,numberFromMeasure(value))}function measureIsValid(value){return(!isNaN(numberFromMeasure(value))||!isNaN(numberFromMeasure(value,"%")))&&value}function getPosByIndex(index,side,margin,baseIndex){return(index-(baseIndex||0))*(side+(margin||0))}function getIndexByPos(pos,side,margin,baseIndex){return-Math.round(pos/(side+(margin||0))-(baseIndex||0))}function bindTransitionEnd($el){var elData=$el.data();if(elData.tEnd)return;var el=$el[0],transitionEndEvent={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",msTransition:"MSTransitionEnd",transition:"transitionend"};addEvent(el,transitionEndEvent[Modernizr.prefixed("transition")],function(e){elData.tProp&&e.propertyName.match(elData.tProp)&&elData.onEndFn()});elData.tEnd=true}function afterTransition($el,property,fn,time){var ok,elData=$el.data();if(elData){elData.onEndFn=function(){if(ok)return;ok=true;clearTimeout(elData.tT);fn()};elData.tProp=property;clearTimeout(elData.tT);elData.tT=setTimeout(function(){elData.onEndFn()},time*1.5);bindTransitionEnd($el)}}function stop($el,pos){var dir=$el.navdir||"horizontal";if($el.length){var elData=$el.data();if(CSS3){$el.css(getDuration(0));elData.onEndFn=noop;clearTimeout(elData.tT)}else{$el.stop()}var lockedPos=getNumber(pos,function(){return readPosition($el,dir)});$el.css(getTranslate(lockedPos,dir));return lockedPos}}function getNumber(){var number;for(var _i=0,_l=arguments.length;_i<_l;_i++){number=_i?arguments[_i]():arguments[_i];if(typeof number==="number"){break}}return number}function edgeResistance(pos,edge){return Math.round(pos+(edge-pos)/1.5)}function getProtocol(){getProtocol.p=getProtocol.p||(location.protocol==="https:"?"https://":"http://");return getProtocol.p}function parseHref(href){var a=document.createElement("a");a.href=href;return a}function findVideoId(href,forceVideo){if(typeof href!=="string")return href;href=parseHref(href);var id,type;if(href.host.match(/youtube\.com/)&&href.search){id=href.search.split("v=")[1];if(id){var ampersandPosition=id.indexOf("&");if(ampersandPosition!==-1){id=id.substring(0,ampersandPosition)}type="youtube"}}else if(href.host.match(/youtube\.com|youtu\.be/)){id=href.pathname.replace(/^\/(embed\/|v\/)?/,"").replace(/\/.*/,"");type="youtube"}else if(href.host.match(/vimeo\.com/)){type="vimeo";id=href.pathname.replace(/^\/(video\/)?/,"").replace(/\/.*/,"")}if((!id||!type)&&forceVideo){id=href.href;type="custom"}return id?{id:id,type:type,s:href.search.replace(/^\?/,""),p:getProtocol()}:false}function getVideoThumbs(dataFrame,data,fotorama){var img,thumb,video=dataFrame.video;if(video.type==="youtube"){thumb=getProtocol()+"img.youtube.com/vi/"+video.id+"/default.jpg";img=thumb.replace(/\/default.jpg$/,"/hqdefault.jpg");dataFrame.thumbsReady=true}else if(video.type==="vimeo"){$.ajax({url:getProtocol()+"vimeo.com/api/v2/video/"+video.id+".json",dataType:"jsonp",success:function(json){dataFrame.thumbsReady=true;updateData(data,{img:json[0].thumbnail_large,thumb:json[0].thumbnail_small},dataFrame.i,fotorama)}})}else{dataFrame.thumbsReady=true}return{img:img,thumb:thumb}}function updateData(data,_dataFrame,i,fotorama){for(var _i=0,_l=data.length;_i<_l;_i++){var dataFrame=data[_i];if(dataFrame.i===i&&dataFrame.thumbsReady){var clear={videoReady:true};clear[STAGE_FRAME_KEY]=clear[NAV_THUMB_FRAME_KEY]=clear[NAV_DOT_FRAME_KEY]=false;fotorama.splice(_i,1,$.extend({},dataFrame,clear,_dataFrame));break}}}function getDataFromHtml($el){var data=[];function getDataFromImg($img,imgData,checkVideo){var $child=$img.children("img").eq(0),_imgHref=$img.attr("href"),_imgSrc=$img.attr("src"),_thumbSrc=$child.attr("src"),_video=imgData.video,video=checkVideo?findVideoId(_imgHref,_video===true):false;if(video){_imgHref=false}else{video=_video}getDimensions($img,$child,$.extend(imgData,{video:video,img:imgData.img||_imgHref||_imgSrc||_thumbSrc,thumb:imgData.thumb||_thumbSrc||_imgSrc||_imgHref}))}function getDimensions($img,$child,imgData){var separateThumbFLAG=imgData.thumb&&imgData.img!==imgData.thumb,width=numberFromMeasure(imgData.width||$img.attr("width")),height=numberFromMeasure(imgData.height||$img.attr("height"));$.extend(imgData,{width:width,height:height,thumbratio:getRatio(imgData.thumbratio||numberFromMeasure(imgData.thumbwidth||$child&&$child.attr("width")||separateThumbFLAG||width)/numberFromMeasure(imgData.thumbheight||$child&&$child.attr("height")||separateThumbFLAG||height))})}$el.children().each(function(){var $this=$(this),dataFrame=optionsToLowerCase($.extend($this.data(),{id:$this.attr("id")}));if($this.is("a, img")){getDataFromImg($this,dataFrame,true)}else if(!$this.is(":empty")){getDimensions($this,null,$.extend(dataFrame,{html:this,_html:$this.html()}))}else return;data.push(dataFrame)});return data}function isHidden(el){return el.offsetWidth===0&&el.offsetHeight===0}function isDetached(el){return!$.contains(document.documentElement,el)}function waitFor(test,fn,timeout,i){if(!waitFor.i){waitFor.i=1;waitFor.ii=[true]}i=i||waitFor.i;if(typeof waitFor.ii[i]==="undefined"){waitFor.ii[i]=true}if(test()){fn()}else{waitFor.ii[i]&&setTimeout(function(){waitFor.ii[i]&&waitFor(test,fn,timeout,i)},timeout||100)}return waitFor.i++}waitFor.stop=function(i){waitFor.ii[i]=false};function fit($el,measuresToFit){var elData=$el.data(),measures=elData.measures;if(measures&&(!elData.l||elData.l.W!==measures.width||elData.l.H!==measures.height||elData.l.r!==measures.ratio||elData.l.w!==measuresToFit.w||elData.l.h!==measuresToFit.h)){var height=minMaxLimit(measuresToFit.h,0,measures.height),width=height*measures.ratio;UTIL.setRatio($el,width,height);elData.l={W:measures.width,H:measures.height,r:measures.ratio,w:measuresToFit.w,h:measuresToFit.h}}return true}function setStyle($el,style){var el=$el[0];if(el.styleSheet){el.styleSheet.cssText=style}else{$el.html(style)}}function findShadowEdge(pos,min,max,dir){return min===max?false:dir==="vertical"?pos<=min?"top":pos>=max?"bottom":"top bottom":pos<=min?"left":pos>=max?"right":"left right"}function smartClick($el,fn,_options){_options=_options||{};$el.each(function(){var $this=$(this),thisData=$this.data(),startEvent;if(thisData.clickOn)return;thisData.clickOn=true;$.extend(touch($this,{onStart:function(e){startEvent=e;(_options.onStart||noop).call(this,e)},onMove:_options.onMove||noop,onTouchEnd:_options.onTouchEnd||noop,onEnd:function(result){if(result.moved)return;fn.call(this,startEvent)}}),{noMove:true})})}function div(classes,child){return'<div class="'+classes+'">'+(child||"")+"</div>"}function cls(className){return"."+className}function createVideoFrame(videoItem){var frame='<iframe src="'+videoItem.p+videoItem.type+".com/embed/"+videoItem.id+'" frameborder="0" allowfullscreen></iframe>';return frame}function shuffle(array){var l=array.length;while(l){var i=Math.floor(Math.random()*l--);var t=array[l];array[l]=array[i];array[i]=t}return array}function clone(array){return Object.prototype.toString.call(array)=="[object Array]"&&$.map(array,function(frame){return $.extend({},frame)})}function lockScroll($el,left,top){$el.scrollLeft(left||0).scrollTop(top||0)}function optionsToLowerCase(options){if(options){var opts={};$.each(options,function(key,value){opts[key.toLowerCase()]=value});return opts}}function getRatio(_ratio){if(!_ratio)return;var ratio=+_ratio;if(!isNaN(ratio)){return ratio}else{ratio=_ratio.split("/");return+ratio[0]/+ratio[1]||undefined}}function addEvent(el,e,fn,bool){if(!e)return;el.addEventListener?el.addEventListener(e,fn,!!bool):el.attachEvent("on"+e,fn)}function validateRestrictions(position,restriction){if(position>restriction.max){position=restriction.max}else{if(position<restriction.min){position=restriction.min}}return position}function validateSlidePos(opt,navShaftTouchTail,guessIndex,offsetNav,$guessNavFrame,$navWrap,dir){var position,size,wrapSize;if(dir==="horizontal"){size=opt.thumbwidth;wrapSize=$navWrap.width()}else{size=opt.thumbheight;wrapSize=$navWrap.height()}if((size+opt.margin)*(guessIndex+1)>=wrapSize-offsetNav){if(dir==="horizontal"){position=-$guessNavFrame.position().left}else{position=-$guessNavFrame.position().top}}else{if((size+opt.margin)*guessIndex<=Math.abs(offsetNav)){if(dir==="horizontal"){position=-$guessNavFrame.position().left+wrapSize-(size+opt.margin)}else{position=-$guessNavFrame.position().top+wrapSize-(size+opt.margin)}}else{position=offsetNav}}position=validateRestrictions(position,navShaftTouchTail);return position||0}function elIsDisabled(el){return!!el.getAttribute("disabled")}function disableAttr(FLAG,disable){if(disable){return{disabled:FLAG}}else{return{tabindex:FLAG*-1+"",disabled:FLAG}}}function addEnterUp(el,fn){addEvent(el,"keyup",function(e){elIsDisabled(el)||e.keyCode==13&&fn.call(el,e)})}function addFocus(el,fn){addEvent(el,"focus",el.onfocusin=function(e){fn.call(el,e)},true)}function stopEvent(e,stopPropagation){e.preventDefault?e.preventDefault():e.returnValue=false;stopPropagation&&e.stopPropagation&&e.stopPropagation()}function stubEvent($el,eventType){var isIOS=/ip(ad|hone|od)/i.test(window.navigator.userAgent);if(isIOS&&eventType==="touchend"){$el.on("touchend",function(e){$DOCUMENT.trigger("mouseup",e)})}$el.on(eventType,function(e){stopEvent(e,true);return false})}function getDirectionSign(forward){return forward?">":"<"}var UTIL=function(){function setRatioClass($el,wh,ht){var rateImg=wh/ht;if(rateImg<=1){$el.parent().removeClass(horizontalImageClass);$el.parent().addClass(verticalImageClass)}else{$el.parent().removeClass(verticalImageClass);$el.parent().addClass(horizontalImageClass)}}function setThumbAttr($frame,value,searchAttr){var attr=searchAttr;if(!$frame.attr(attr)&&$frame.attr(attr)!==undefined){$frame.attr(attr,value)}if($frame.find("["+attr+"]").length){$frame.find("["+attr+"]").each(function(){$(this).attr(attr,value)})}}function isExpectedCaption(frameItem,isExpected,undefined){var expected=false,frameExpected;frameItem.showCaption===undefined||frameItem.showCaption===true?frameExpected=true:frameExpected=false;if(!isExpected){return false}if(frameItem.caption&&frameExpected){expected=true}return expected}return{setRatio:setRatioClass,setThumbAttr:setThumbAttr,isExpectedCaption:isExpectedCaption}}(UTIL||{},jQuery);function slide($el,options){var elData=$el.data(),elPos=Math.round(options.pos),onEndFn=function(){if(elData&&elData.sliding){elData.sliding=false}(options.onEnd||noop)()};if(typeof options.overPos!=="undefined"&&options.overPos!==options.pos){elPos=options.overPos}var translate=$.extend(getTranslate(elPos,options.direction),options.width&&{width:options.width},options.height&&{height:options.height});if(elData&&elData.sliding){elData.sliding=true}if(CSS3){$el.css($.extend(getDuration(options.time),translate));if(options.time>10){afterTransition($el,"transform",onEndFn,options.time)}else{onEndFn()}}else{$el.stop().animate(translate,options.time,BEZIER,onEndFn)}}function fade($el1,$el2,$frames,options,fadeStack,chain){var chainedFLAG=typeof chain!=="undefined";if(!chainedFLAG){fadeStack.push(arguments);Array.prototype.push.call(arguments,fadeStack.length);if(fadeStack.length>1)return}$el1=$el1||$($el1);$el2=$el2||$($el2);var _$el1=$el1[0],_$el2=$el2[0],crossfadeFLAG=options.method==="crossfade",onEndFn=function(){if(!onEndFn.done){onEndFn.done=true;var args=(chainedFLAG||fadeStack.shift())&&fadeStack.shift();args&&fade.apply(this,args);(options.onEnd||noop)(!!args)}},time=options.time/(chain||1);$frames.removeClass(fadeRearClass+" "+fadeFrontClass);$el1.stop().addClass(fadeRearClass);$el2.stop().addClass(fadeFrontClass);crossfadeFLAG&&_$el2&&$el1.fadeTo(0,0);$el1.fadeTo(crossfadeFLAG?time:0,1,crossfadeFLAG&&onEndFn);$el2.fadeTo(time,0,onEndFn);_$el1&&crossfadeFLAG||_$el2||onEndFn()}var lastEvent,moveEventType,preventEvent,preventEventTimeout,dragDomEl;function extendEvent(e){var touch=(e.touches||[])[0]||e;e._x=touch.pageX||touch.originalEvent.pageX;e._y=touch.clientY||touch.originalEvent.clientY;e._now=$.now()}function touch($el,options){var el=$el[0],tail={},touchEnabledFLAG,startEvent,$target,controlTouch,touchFLAG,targetIsSelectFLAG,targetIsLinkFlag,tolerance,moved;function onStart(e){$target=$(e.target);tail.checked=targetIsSelectFLAG=targetIsLinkFlag=moved=false;if(touchEnabledFLAG||tail.flow||e.touches&&e.touches.length>1||e.which>1||lastEvent&&lastEvent.type!==e.type&&preventEvent||(targetIsSelectFLAG=options.select&&$target.is(options.select,el)))return targetIsSelectFLAG;touchFLAG=e.type==="touchstart";targetIsLinkFlag=$target.is("a, a *",el);controlTouch=tail.control;tolerance=tail.noMove||tail.noSwipe||controlTouch?16:!tail.snap?4:0;extendEvent(e);startEvent=lastEvent=e;moveEventType=e.type.replace(/down|start/,"move").replace(/Down/,"Move");(options.onStart||noop).call(el,e,{control:controlTouch,$target:$target});touchEnabledFLAG=tail.flow=true;if(!touchFLAG||tail.go)stopEvent(e)}function onMove(e){if(e.touches&&e.touches.length>1||MS_POINTER&&!e.isPrimary||moveEventType!==e.type||!touchEnabledFLAG){touchEnabledFLAG&&onEnd();(options.onTouchEnd||noop)();return}extendEvent(e);var xDiff=Math.abs(e._x-startEvent._x),yDiff=Math.abs(e._y-startEvent._y),xyDiff=xDiff-yDiff,xWin=(tail.go||tail.x||xyDiff>=0)&&!tail.noSwipe,yWin=xyDiff<0;if(touchFLAG&&!tail.checked){if(touchEnabledFLAG=xWin){stopEvent(e)}}else{stopEvent(e);if(movedEnough(xDiff,yDiff)){(options.onMove||noop).call(el,e,{touch:touchFLAG})}}if(!moved&&movedEnough(xDiff,yDiff)&&Math.sqrt(Math.pow(xDiff,2)+Math.pow(yDiff,2))>tolerance){moved=true}tail.checked=tail.checked||xWin||yWin}function movedEnough(xDiff,yDiff){return xDiff>yDiff&&xDiff>1.5}function onEnd(e){(options.onTouchEnd||noop)();var _touchEnabledFLAG=touchEnabledFLAG;tail.control=touchEnabledFLAG=false;if(_touchEnabledFLAG){tail.flow=false}if(!_touchEnabledFLAG||targetIsLinkFlag&&!tail.checked)return;e&&stopEvent(e);preventEvent=true;clearTimeout(preventEventTimeout);preventEventTimeout=setTimeout(function(){preventEvent=false},1e3);(options.onEnd||noop).call(el,{moved:moved,$target:$target,control:controlTouch,touch:touchFLAG,startEvent:startEvent,aborted:!e||e.type==="MSPointerCancel"})}function onOtherStart(){if(tail.flow)return;tail.flow=true}function onOtherEnd(){if(!tail.flow)return;tail.flow=false}if(MS_POINTER){addEvent(el,"MSPointerDown",onStart);addEvent(document,"MSPointerMove",onMove);addEvent(document,"MSPointerCancel",onEnd);addEvent(document,"MSPointerUp",onEnd)}else{addEvent(el,"touchstart",onStart);addEvent(el,"touchmove",onMove);addEvent(el,"touchend",onEnd);addEvent(document,"touchstart",onOtherStart);addEvent(document,"touchend",onOtherEnd);addEvent(document,"touchcancel",onOtherEnd);$WINDOW.on("scroll",onOtherEnd);$el.on("mousedown pointerdown",onStart);$DOCUMENT.on("mousemove pointermove",onMove).on("mouseup pointerup",onEnd)}if(Modernizr.touch){dragDomEl="a"}else{dragDomEl="div"}$el.on("click",dragDomEl,function(e){tail.checked&&stopEvent(e)});return tail}function moveOnTouch($el,options){var el=$el[0],elData=$el.data(),tail={},startCoo,coo,startElPos,moveElPos,edge,moveTrack,startTime,endTime,min,max,snap,dir,slowFLAG,controlFLAG,moved,tracked;function startTracking(e,noStop){tracked=true;startCoo=coo=dir==="vertical"?e._y:e._x;startTime=e._now;moveTrack=[[startTime,startCoo]];startElPos=moveElPos=tail.noMove||noStop?0:stop($el,(options.getPos||noop)());(options.onStart||noop).call(el,e)}function onStart(e,result){min=tail.min;max=tail.max;snap=tail.snap,dir=tail.direction||"horizontal",$el.navdir=dir;slowFLAG=e.altKey;tracked=moved=false;controlFLAG=result.control;if(!controlFLAG&&!elData.sliding){startTracking(e)}}function onMove(e,result){if(!tail.noSwipe){if(!tracked){startTracking(e)}coo=dir==="vertical"?e._y:e._x;moveTrack.push([e._now,coo]);moveElPos=startElPos-(startCoo-coo);edge=findShadowEdge(moveElPos,min,max,dir);if(moveElPos<=min){moveElPos=edgeResistance(moveElPos,min)}else if(moveElPos>=max){moveElPos=edgeResistance(moveElPos,max)}if(!tail.noMove){$el.css(getTranslate(moveElPos,dir));if(!moved){moved=true;result.touch||MS_POINTER||$el.addClass(grabbingClass)}(options.onMove||noop).call(el,e,{pos:moveElPos,edge:edge})}}}function onEnd(result){if(tail.noSwipe&&result.moved)return;if(!tracked){startTracking(result.startEvent,true)}result.touch||MS_POINTER||$el.removeClass(grabbingClass);endTime=$.now();var _backTimeIdeal=endTime-TOUCH_TIMEOUT,_backTime,_timeDiff,_timeDiffLast,backTime=null,backCoo,virtualPos,limitPos,newPos,overPos,time=TRANSITION_DURATION,speed,friction=options.friction;for(var _i=moveTrack.length-1;_i>=0;_i--){_backTime=moveTrack[_i][0];_timeDiff=Math.abs(_backTime-_backTimeIdeal);if(backTime===null||_timeDiff<_timeDiffLast){backTime=_backTime;backCoo=moveTrack[_i][1]}else if(backTime===_backTimeIdeal||_timeDiff>_timeDiffLast){break}_timeDiffLast=_timeDiff}newPos=minMaxLimit(moveElPos,min,max);var cooDiff=backCoo-coo,forwardFLAG=cooDiff>=0,timeDiff=endTime-backTime,longTouchFLAG=timeDiff>TOUCH_TIMEOUT,swipeFLAG=!longTouchFLAG&&moveElPos!==startElPos&&newPos===moveElPos;if(snap){newPos=minMaxLimit(Math[swipeFLAG?forwardFLAG?"floor":"ceil":"round"](moveElPos/snap)*snap,min,max);min=max=newPos}if(swipeFLAG&&(snap||newPos===moveElPos)){speed=-(cooDiff/timeDiff);time*=minMaxLimit(Math.abs(speed),options.timeLow,options.timeHigh);virtualPos=Math.round(moveElPos+speed*time/friction);if(!snap){newPos=virtualPos}if(!forwardFLAG&&virtualPos>max||forwardFLAG&&virtualPos<min){limitPos=forwardFLAG?min:max;overPos=virtualPos-limitPos;if(!snap){newPos=limitPos}overPos=minMaxLimit(newPos+overPos*.03,limitPos-50,limitPos+50);time=Math.abs((moveElPos-overPos)/(speed/friction))}}time*=slowFLAG?10:1;(options.onEnd||noop).call(el,$.extend(result,{moved:result.moved||longTouchFLAG&&snap,pos:moveElPos,newPos:newPos,overPos:overPos,time:time,dir:dir}))}tail=$.extend(touch(options.$wrap,$.extend({},options,{onStart:onStart,onMove:onMove,onEnd:onEnd})),tail);return tail}function wheel($el,options){var el=$el[0],lockFLAG,lastDirection,lastNow,tail={prevent:{}};addEvent(el,WHEEL,function(e){var yDelta=e.wheelDeltaY||-1*e.deltaY||0,xDelta=e.wheelDeltaX||-1*e.deltaX||0,xWin=Math.abs(xDelta)&&!Math.abs(yDelta),direction=getDirectionSign(xDelta<0),sameDirection=lastDirection===direction,now=$.now(),tooFast=now-lastNow<TOUCH_TIMEOUT;lastDirection=direction;lastNow=now;if(!xWin||!tail.ok||tail.prevent[direction]&&!lockFLAG){return}else{stopEvent(e,true);if(lockFLAG&&sameDirection&&tooFast){return}}if(options.shift){lockFLAG=true;clearTimeout(tail.t);tail.t=setTimeout(function(){lockFLAG=false},SCROLL_LOCK_TIMEOUT)}(options.onEnd||noop)(e,options.shift?direction:xDelta)});return tail}jQuery.Fotorama=function($fotorama,opts){$HTML=$("html");$BODY=$("body");var that=this,stamp=$.now(),stampClass=_fotoramaClass+stamp,fotorama=$fotorama[0],data,dataFrameCount=1,fotoramaData=$fotorama.data(),size,$style=$("<style></style>"),$anchor=$(div(hiddenClass)),$wrap=$fotorama.find(cls(wrapClass)),$stage=$wrap.find(cls(stageClass)),stage=$stage[0],$stageShaft=$fotorama.find(cls(stageShaftClass)),$stageFrame=$(),$arrPrev=$fotorama.find(cls(arrPrevClass)),$arrNext=$fotorama.find(cls(arrNextClass)),$arrs=$fotorama.find(cls(arrClass)),$navWrap=$fotorama.find(cls(navWrapClass)),$nav=$navWrap.find(cls(navClass)),$navShaft=$nav.find(cls(navShaftClass)),$navFrame,$navDotFrame=$(),$navThumbFrame=$(),stageShaftData=$stageShaft.data(),navShaftData=$navShaft.data(),$thumbBorder=$fotorama.find(cls(thumbBorderClass)),$thumbArrLeft=$fotorama.find(cls(thumbArrLeft)),$thumbArrRight=$fotorama.find(cls(thumbArrRight)),$fullscreenIcon=$fotorama.find(cls(fullscreenIconClass)),fullscreenIcon=$fullscreenIcon[0],$videoPlay=$(div(videoPlayClass)),$videoClose=$fotorama.find(cls(videoCloseClass)),videoClose=$videoClose[0],$spinner=$fotorama.find(cls(fotoramaSpinnerClass)),$videoPlaying,activeIndex=false,activeFrame,activeIndexes,repositionIndex,dirtyIndex,lastActiveIndex,prevIndex,nextIndex,nextAutoplayIndex,startIndex,o_loop,o_nav,o_navThumbs,o_navTop,o_allowFullScreen,o_nativeFullScreen,o_fade,o_thumbSide,o_thumbSide2,o_transitionDuration,o_transition,o_shadows,o_rtl,o_keyboard,lastOptions={},measures={},measuresSetFLAG,stageShaftTouchTail={},stageWheelTail={},navShaftTouchTail={},navWheelTail={},scrollTop,scrollLeft,showedFLAG,pausedAutoplayFLAG,stoppedAutoplayFLAG,toDeactivate={},toDetach={},measuresStash,touchedFLAG,hoverFLAG,navFrameKey,stageLeft=0,fadeStack=[];$wrap[STAGE_FRAME_KEY]=$('<div class="'+stageFrameClass+'"></div>');$wrap[NAV_THUMB_FRAME_KEY]=$($.Fotorama.jst.thumb());$wrap[NAV_DOT_FRAME_KEY]=$($.Fotorama.jst.dots());toDeactivate[STAGE_FRAME_KEY]=[];toDeactivate[NAV_THUMB_FRAME_KEY]=[];toDeactivate[NAV_DOT_FRAME_KEY]=[];toDetach[STAGE_FRAME_KEY]={};$wrap.addClass(CSS3?wrapCss3Class:wrapCss2Class);fotoramaData.fotorama=this;function checkForVideo(){$.each(data,function(i,dataFrame){if(!dataFrame.i){dataFrame.i=dataFrameCount++;var video=findVideoId(dataFrame.video,true);if(video){var thumbs={};dataFrame.video=video;if(!dataFrame.img&&!dataFrame.thumb){thumbs=getVideoThumbs(dataFrame,data,that)}else{dataFrame.thumbsReady=true}updateData(data,{img:thumbs.img,thumb:thumbs.thumb},dataFrame.i,that)}}})}function allowKey(key){return o_keyboard[key]}function setStagePosition(){if($stage!==undefined){if(opts.navdir=="vertical"){var padding=opts.thumbwidth+opts.thumbmargin;$stage.css("left",padding);$arrNext.css("right",padding);$fullscreenIcon.css("right",padding);$wrap.css("width",$wrap.css("width")+padding);$stageShaft.css("max-width",$wrap.width()-padding)}else{$stage.css("left","");$arrNext.css("right","");$fullscreenIcon.css("right","");$wrap.css("width",$wrap.css("width")+padding);$stageShaft.css("max-width","")}}}function bindGlobalEvents(FLAG){var keydownCommon="keydown."+_fotoramaClass,localStamp=_fotoramaClass+stamp,keydownLocal="keydown."+localStamp,keyupLocal="keyup."+localStamp,resizeLocal="resize."+localStamp+" "+"orientationchange."+localStamp,showParams;if(FLAG){$DOCUMENT.on(keydownLocal,function(e){var catched,index;if($videoPlaying&&e.keyCode===27){catched=true;unloadVideo($videoPlaying,true,true)}else if(that.fullScreen||opts.keyboard&&!that.index){if(e.keyCode===27){catched=true;that.cancelFullScreen()}else if(e.shiftKey&&e.keyCode===32&&allowKey("space")||!e.altKey&&!e.metaKey&&e.keyCode===37&&allowKey("left")||e.keyCode===38&&allowKey("up")&&$(":focus").attr("data-gallery-role")){that.longPress.progress();index="<"}else if(e.keyCode===32&&allowKey("space")||!e.altKey&&!e.metaKey&&e.keyCode===39&&allowKey("right")||e.keyCode===40&&allowKey("down")&&$(":focus").attr("data-gallery-role")){that.longPress.progress();index=">"}else if(e.keyCode===36&&allowKey("home")){that.longPress.progress();index="<<"}else if(e.keyCode===35&&allowKey("end")){that.longPress.progress();index=">>"}}(catched||index)&&stopEvent(e);showParams={index:index,slow:e.altKey,user:true};index&&(that.longPress.inProgress?that.showWhileLongPress(showParams):that.show(showParams))});if(FLAG){$DOCUMENT.on(keyupLocal,function(e){if(that.longPress.inProgress){that.showEndLongPress({user:true})}that.longPress.reset()})}if(!that.index){$DOCUMENT.off(keydownCommon).on(keydownCommon,"textarea, input, select",function(e){!$BODY.hasClass(_fullscreenClass)&&e.stopPropagation()})}$WINDOW.on(resizeLocal,that.resize)}else{$DOCUMENT.off(keydownLocal);$WINDOW.off(resizeLocal)}}function appendElements(FLAG){if(FLAG===appendElements.f)return;if(FLAG){$fotorama.addClass(_fotoramaClass+" "+stampClass).before($anchor).before($style);addInstance(that)}else{$anchor.detach();$style.detach();$fotorama.html(fotoramaData.urtext).removeClass(stampClass);hideInstance(that)}bindGlobalEvents(FLAG);appendElements.f=FLAG}function setData(){data=that.data=data||clone(opts.data)||getDataFromHtml($fotorama);size=that.size=data.length;ready.ok&&opts.shuffle&&shuffle(data);checkForVideo();activeIndex=limitIndex(activeIndex);size&&appendElements(true)}function stageNoMove(){var _noMove=size<2||$videoPlaying;stageShaftTouchTail.noMove=_noMove||o_fade;stageShaftTouchTail.noSwipe=_noMove||!opts.swipe;!o_transition&&$stageShaft.toggleClass(grabClass,!opts.click&&!stageShaftTouchTail.noMove&&!stageShaftTouchTail.noSwipe);MS_POINTER&&$wrap.toggleClass(wrapPanYClass,!stageShaftTouchTail.noSwipe)}function setAutoplayInterval(interval){if(interval===true)interval="";opts.autoplay=Math.max(+interval||AUTOPLAY_INTERVAL,o_transitionDuration*1.5)}function updateThumbArrow(opt){if(opt.navarrows&&opt.nav==="thumbs"){$thumbArrLeft.show();$thumbArrRight.show()}else{$thumbArrLeft.hide();$thumbArrRight.hide()}}function getThumbsInSlide($el,opts){return Math.floor($wrap.width()/(opts.thumbwidth+opts.thumbmargin))}function setOptions(){if(!opts.nav||opts.nav==="dots"){opts.navdir="horizontal"}that.options=opts=optionsToLowerCase(opts);thumbsPerSlide=getThumbsInSlide($wrap,opts);o_fade=opts.transition==="crossfade"||opts.transition==="dissolve";o_loop=opts.loop&&(size>2||o_fade&&(!o_transition||o_transition!=="slide"));o_transitionDuration=+opts.transitionduration||TRANSITION_DURATION;o_rtl=opts.direction==="rtl";o_keyboard=$.extend({},opts.keyboard&&KEYBOARD_OPTIONS,opts.keyboard);updateThumbArrow(opts);var classes={add:[],remove:[]};function addOrRemoveClass(FLAG,value){classes[FLAG?"add":"remove"].push(value)}if(size>1){o_nav=opts.nav;o_navTop=opts.navposition==="top";classes.remove.push(selectClass);$arrs.toggle(opts.arrows)}else{o_nav=false;$arrs.hide()}arrsUpdate();stageWheelUpdate();thumbArrUpdate();if(opts.autoplay)setAutoplayInterval(opts.autoplay);o_thumbSide=numberFromMeasure(opts.thumbwidth)||THUMB_SIZE;o_thumbSide2=numberFromMeasure(opts.thumbheight)||THUMB_SIZE;stageWheelTail.ok=navWheelTail.ok=opts.trackpad&&!SLOW;stageNoMove();extendMeasures(opts,[measures]);o_navThumbs=o_nav==="thumbs";if($navWrap.filter(":hidden")&&!!o_nav){$navWrap.show()}if(o_navThumbs){frameDraw(size,"navThumb");$navFrame=$navThumbFrame;navFrameKey=NAV_THUMB_FRAME_KEY;setStyle($style,$.Fotorama.jst.style({w:o_thumbSide,h:o_thumbSide2,b:opts.thumbborderwidth,m:opts.thumbmargin,s:stamp,q:!COMPAT}));$nav.addClass(navThumbsClass).removeClass(navDotsClass)}else if(o_nav==="dots"){frameDraw(size,"navDot");$navFrame=$navDotFrame;navFrameKey=NAV_DOT_FRAME_KEY;$nav.addClass(navDotsClass).removeClass(navThumbsClass)}else{$navWrap.hide();o_nav=false;$nav.removeClass(navThumbsClass+" "+navDotsClass)}if(o_nav){if(o_navTop){$navWrap.insertBefore($stage)}else{$navWrap.insertAfter($stage)}frameAppend.nav=false;frameAppend($navFrame,$navShaft,"nav")}o_allowFullScreen=opts.allowfullscreen;if(o_allowFullScreen){$fullscreenIcon.prependTo($stage);o_nativeFullScreen=FULLSCREEN&&o_allowFullScreen==="native";stubEvent($fullscreenIcon,"touchend")}else{$fullscreenIcon.detach();o_nativeFullScreen=false}addOrRemoveClass(o_fade,wrapFadeClass);addOrRemoveClass(!o_fade,wrapSlideClass);addOrRemoveClass(!opts.captions,wrapNoCaptionsClass);addOrRemoveClass(o_rtl,wrapRtlClass);addOrRemoveClass(opts.arrows,wrapToggleArrowsClass);o_shadows=opts.shadows&&!SLOW;addOrRemoveClass(!o_shadows,wrapNoShadowsClass);$wrap.addClass(classes.add.join(" ")).removeClass(classes.remove.join(" "));lastOptions=$.extend({},opts);setStagePosition()}function normalizeIndex(index){return index<0?(size+index%size)%size:index>=size?index%size:index}function limitIndex(index){return minMaxLimit(index,0,size-1)}function edgeIndex(index){return o_loop?normalizeIndex(index):limitIndex(index)}function getPrevIndex(index){return index>0||o_loop?index-1:false}function getNextIndex(index){return index<size-1||o_loop?index+1:false}function setStageShaftMinmaxAndSnap(){stageShaftTouchTail.min=o_loop?-Infinity:-getPosByIndex(size-1,measures.w,opts.margin,repositionIndex);stageShaftTouchTail.max=o_loop?Infinity:-getPosByIndex(0,measures.w,opts.margin,repositionIndex);stageShaftTouchTail.snap=measures.w+opts.margin}function setNavShaftMinMax(){var isVerticalDir=opts.navdir==="vertical";var param=isVerticalDir?$navShaft.height():$navShaft.width();var mainParam=isVerticalDir?measures.h:measures.nw;navShaftTouchTail.min=Math.min(0,mainParam-param);navShaftTouchTail.max=0;navShaftTouchTail.direction=opts.navdir;$navShaft.toggleClass(grabClass,!(navShaftTouchTail.noMove=navShaftTouchTail.min===navShaftTouchTail.max))}function eachIndex(indexes,type,fn){if(typeof indexes==="number"){indexes=new Array(indexes);var rangeFLAG=true}return $.each(indexes,function(i,index){if(rangeFLAG)index=i;if(typeof index==="number"){var dataFrame=data[normalizeIndex(index)];if(dataFrame){var key="$"+type+"Frame",$frame=dataFrame[key];fn.call(this,i,index,dataFrame,$frame,key,$frame&&$frame.data())}}})}function setMeasures(width,height,ratio,index){if(!measuresSetFLAG||measuresSetFLAG==="*"&&index===startIndex){width=measureIsValid(opts.width)||measureIsValid(width)||WIDTH;height=measureIsValid(opts.height)||measureIsValid(height)||HEIGHT;that.resize({width:width,ratio:opts.ratio||ratio||width/height},0,index!==startIndex&&"*")}}function loadImg(indexes,type,specialMeasures,again){eachIndex(indexes,type,function(i,index,dataFrame,$frame,key,frameData){if(!$frame)return;var fullFLAG=that.fullScreen&&!frameData.$full&&type==="stage";if(frameData.$img&&!again&&!fullFLAG)return;var img=new Image,$img=$(img),imgData=$img.data();frameData[fullFLAG?"$full":"$img"]=$img;var srcKey=type==="stage"?fullFLAG?"full":"img":"thumb",src=dataFrame[srcKey],dummy=fullFLAG?dataFrame["img"]:dataFrame[type==="stage"?"thumb":"img"];if(type==="navThumb")$frame=frameData.$wrap;function triggerTriggerEvent(event){var _index=normalizeIndex(index);triggerEvent(event,{index:_index,src:src,frame:data[_index]})}function error(){$img.remove();$.Fotorama.cache[src]="error";if((!dataFrame.html||type!=="stage")&&dummy&&dummy!==src){dataFrame[srcKey]=src=dummy;frameData.$full=null;loadImg([index],type,specialMeasures,true)}else{if(src&&!dataFrame.html&&!fullFLAG){$frame.trigger("f:error").removeClass(loadingClass).addClass(errorClass);triggerTriggerEvent("error")}else if(type==="stage"){$frame.trigger("f:load").removeClass(loadingClass+" "+errorClass).addClass(loadedClass);triggerTriggerEvent("load");setMeasures()}frameData.state="error";if(size>1&&data[index]===dataFrame&&!dataFrame.html&&!dataFrame.deleted&&!dataFrame.video&&!fullFLAG){dataFrame.deleted=true;that.splice(index,1)}}}function loaded(){$.Fotorama.measures[src]=imgData.measures=$.Fotorama.measures[src]||{width:img.width,height:img.height,ratio:img.width/img.height};setMeasures(imgData.measures.width,imgData.measures.height,imgData.measures.ratio,index);$img.off("load error").addClass(""+(fullFLAG?imgFullClass:imgClass)).attr("aria-hidden","false").prependTo($frame);if($frame.hasClass(stageFrameClass)&&!$frame.hasClass(videoContainerClass)){$frame.attr("href",$img.attr("src"))}fit($img,($.isFunction(specialMeasures)?specialMeasures():specialMeasures)||measures);$.Fotorama.cache[src]=frameData.state="loaded";setTimeout(function(){$frame.trigger("f:load").removeClass(loadingClass+" "+errorClass).addClass(loadedClass+" "+(fullFLAG?loadedFullClass:loadedImgClass));if(type==="stage"){triggerTriggerEvent("load")}else if(dataFrame.thumbratio===AUTO||!dataFrame.thumbratio&&opts.thumbratio===AUTO){dataFrame.thumbratio=imgData.measures.ratio;reset()}},0)}if(!src){error();return}function waitAndLoad(){var _i=10;waitFor(function(){return!touchedFLAG||!_i--&&!SLOW},function(){loaded()})}if(!$.Fotorama.cache[src]){$.Fotorama.cache[src]="*";$img.on("load",waitAndLoad).on("error",error)}else{(function justWait(){if($.Fotorama.cache[src]==="error"){error()}else if($.Fotorama.cache[src]==="loaded"){setTimeout(waitAndLoad,0)}else{setTimeout(justWait,100)}})()}frameData.state="";img.src=src;if(frameData.data.caption){img.alt=frameData.data.caption||""}if(frameData.data.full){$(img).data("original",frameData.data.full)}if(UTIL.isExpectedCaption(dataFrame,opts.showcaption)){$(img).attr("aria-labelledby",dataFrame.labelledby)}})}function updateFotoramaState(){var $frame=activeFrame[STAGE_FRAME_KEY];if($frame&&!$frame.data().state){$spinner.addClass(spinnerShowClass);$frame.on("f:load f:error",function(){$frame.off("f:load f:error");$spinner.removeClass(spinnerShowClass)})}}function addNavFrameEvents(frame){addEnterUp(frame,onNavFrameClick);addFocus(frame,function(){setTimeout(function(){lockScroll($nav)},0);slideNavShaft({time:o_transitionDuration,guessIndex:$(this).data().eq,minMax:navShaftTouchTail})})}function frameDraw(indexes,type){eachIndex(indexes,type,function(i,index,dataFrame,$frame,key,frameData){if($frame)return;$frame=dataFrame[key]=$wrap[key].clone();frameData=$frame.data();frameData.data=dataFrame;var frame=$frame[0],labelledbyValue="labelledby"+$.now();if(type==="stage"){if(dataFrame.html){$('<div class="'+htmlClass+'"></div>').append(dataFrame._html?$(dataFrame.html).removeAttr("id").html(dataFrame._html):dataFrame.html).appendTo($frame)}if(dataFrame.id){labelledbyValue=dataFrame.id||labelledbyValue}dataFrame.labelledby=labelledbyValue;if(UTIL.isExpectedCaption(dataFrame,opts.showcaption)){$($.Fotorama.jst.frameCaption({caption:dataFrame.caption,labelledby:labelledbyValue})).appendTo($frame)}dataFrame.video&&$frame.addClass(stageFrameVideoClass).append($videoPlay.clone());addFocus(frame,function(){setTimeout(function(){lockScroll($stage)},0);clickToShow({index:frameData.eq,user:true})});$stageFrame=$stageFrame.add($frame)}else if(type==="navDot"){addNavFrameEvents(frame);$navDotFrame=$navDotFrame.add($frame)}else if(type==="navThumb"){addNavFrameEvents(frame);frameData.$wrap=$frame.children(":first");$navThumbFrame=$navThumbFrame.add($frame);if(dataFrame.video){frameData.$wrap.append($videoPlay.clone())}}})}function callFit($img,measuresToFit){return $img&&$img.length&&fit($img,measuresToFit)}function stageFramePosition(indexes){eachIndex(indexes,"stage",function(i,index,dataFrame,$frame,key,frameData){if(!$frame)return;var normalizedIndex=normalizeIndex(index);frameData.eq=normalizedIndex;toDetach[STAGE_FRAME_KEY][normalizedIndex]=$frame.css($.extend({left:o_fade?0:getPosByIndex(index,measures.w,opts.margin,repositionIndex)},o_fade&&getDuration(0)));if(isDetached($frame[0])){$frame.appendTo($stageShaft);unloadVideo(dataFrame.$video)}callFit(frameData.$img,measures);callFit(frameData.$full,measures);if($frame.hasClass(stageFrameClass)&&!($frame.attr("aria-hidden")==="false"&&$frame.hasClass(activeClass))){$frame.attr("aria-hidden","true")}})}function thumbsDraw(pos,loadFLAG){var leftLimit,rightLimit,exceedLimit;if(o_nav!=="thumbs"||isNaN(pos))return;leftLimit=-pos;rightLimit=-pos+measures.nw;if(opts.navdir==="vertical"){pos=pos-opts.thumbheight;rightLimit=-pos+measures.h}$navThumbFrame.each(function(){var $this=$(this),thisData=$this.data(),eq=thisData.eq,getSpecialMeasures=function(){return{h:o_thumbSide2,w:thisData.w}},specialMeasures=getSpecialMeasures(),exceedLimit=opts.navdir==="vertical"?thisData.t>rightLimit:thisData.l>rightLimit;specialMeasures.w=thisData.w;if(thisData.l+thisData.w<leftLimit||exceedLimit||callFit(thisData.$img,specialMeasures))return;loadFLAG&&loadImg([eq],"navThumb",getSpecialMeasures)})}function frameAppend($frames,$shaft,type){if(!frameAppend[type]){var thumbsFLAG=type==="nav"&&o_navThumbs,left=0,top=0;$shaft.append($frames.filter(function(){var actual,$this=$(this),frameData=$this.data();for(var _i=0,_l=data.length;_i<_l;_i++){if(frameData.data===data[_i]){actual=true;frameData.eq=_i;break}}return actual||$this.remove()&&false}).sort(function(a,b){return $(a).data().eq-$(b).data().eq}).each(function(){var $this=$(this),frameData=$this.data();UTIL.setThumbAttr($this,frameData.data.caption,"aria-label")}).each(function(){if(!thumbsFLAG)return;var $this=$(this),frameData=$this.data(),thumbwidth=Math.round(o_thumbSide2*frameData.data.thumbratio)||o_thumbSide,thumbheight=Math.round(o_thumbSide/frameData.data.thumbratio)||o_thumbSide2;frameData.t=top;frameData.h=thumbheight;frameData.l=left;frameData.w=thumbwidth;$this.css({width:thumbwidth});top+=thumbheight+opts.thumbmargin;left+=thumbwidth+opts.thumbmargin}));frameAppend[type]=true}}function getDirection(x){return x-stageLeft>measures.w/3}function disableDirrection(i){return!o_loop&&(!(activeIndex+i)||!(activeIndex-size+i))&&!$videoPlaying}function arrsUpdate(){var disablePrev=disableDirrection(0),disableNext=disableDirrection(1);$arrPrev.toggleClass(arrDisabledClass,disablePrev).attr(disableAttr(disablePrev,false));$arrNext.toggleClass(arrDisabledClass,disableNext).attr(disableAttr(disableNext,false))}function thumbArrUpdate(){var isLeftDisable=false,isRightDisable=false;if(opts.navtype==="thumbs"&&!opts.loop){activeIndex==0?isLeftDisable=true:isLeftDisable=false;activeIndex==opts.data.length-1?isRightDisable=true:isRightDisable=false}if(opts.navtype==="slides"){var pos=readPosition($navShaft,opts.navdir);pos>=navShaftTouchTail.max?isLeftDisable=true:isLeftDisable=false;pos<=navShaftTouchTail.min?isRightDisable=true:isRightDisable=false}$thumbArrLeft.toggleClass(arrDisabledClass,isLeftDisable).attr(disableAttr(isLeftDisable,true));$thumbArrRight.toggleClass(arrDisabledClass,isRightDisable).attr(disableAttr(isRightDisable,true))}function stageWheelUpdate(){if(stageWheelTail.ok){stageWheelTail.prevent={"<":disableDirrection(0),">":disableDirrection(1)}}}function getNavFrameBounds($navFrame){var navFrameData=$navFrame.data(),left,top,width,height;if(o_navThumbs){left=navFrameData.l;top=navFrameData.t;width=navFrameData.w;height=navFrameData.h}else{left=$navFrame.position().left;width=$navFrame.width()}var horizontalBounds={c:left+width/2,min:-left+opts.thumbmargin*10,max:-left+measures.w-width-opts.thumbmargin*10};var verticalBounds={c:top+height/2,min:-top+opts.thumbmargin*10,max:-top+measures.h-height-opts.thumbmargin*10};return opts.navdir==="vertical"?verticalBounds:horizontalBounds}function slideThumbBorder(time){var navFrameData=activeFrame[navFrameKey].data();slide($thumbBorder,{time:time*1.2,pos:opts.navdir==="vertical"?navFrameData.t:navFrameData.l,width:navFrameData.w,height:navFrameData.h,direction:opts.navdir})}function slideNavShaft(options){var $guessNavFrame=data[options.guessIndex][navFrameKey],typeOfAnimation=opts.navtype;var overflowFLAG,time,minMax,boundTop,boundLeft,l,pos,x;if($guessNavFrame){if(typeOfAnimation==="thumbs"){overflowFLAG=navShaftTouchTail.min!==navShaftTouchTail.max;minMax=options.minMax||overflowFLAG&&getNavFrameBounds(activeFrame[navFrameKey]);boundTop=overflowFLAG&&(options.keep&&slideNavShaft.t?slideNavShaft.l:minMaxLimit((options.coo||measures.nw/2)-getNavFrameBounds($guessNavFrame).c,minMax.min,minMax.max));boundLeft=overflowFLAG&&(options.keep&&slideNavShaft.l?slideNavShaft.l:minMaxLimit((options.coo||measures.nw/2)-getNavFrameBounds($guessNavFrame).c,minMax.min,minMax.max));l=opts.navdir==="vertical"?boundTop:boundLeft;pos=overflowFLAG&&minMaxLimit(l,navShaftTouchTail.min,navShaftTouchTail.max)||0;time=options.time*1.1;slide($navShaft,{time:time,pos:pos,direction:opts.navdir,onEnd:function(){thumbsDraw(pos,true);thumbArrUpdate()}});setShadow($nav,findShadowEdge(pos,navShaftTouchTail.min,navShaftTouchTail.max,opts.navdir));slideNavShaft.l=l}else{x=readPosition($navShaft,opts.navdir);time=options.time*1.11;pos=validateSlidePos(opts,navShaftTouchTail,options.guessIndex,x,$guessNavFrame,$navWrap,opts.navdir);slide($navShaft,{time:time,pos:pos,direction:opts.navdir,onEnd:function(){thumbsDraw(pos,true);thumbArrUpdate()}});setShadow($nav,findShadowEdge(pos,navShaftTouchTail.min,navShaftTouchTail.max,opts.navdir))}}}function navUpdate(){deactivateFrames(navFrameKey);toDeactivate[navFrameKey].push(activeFrame[navFrameKey].addClass(activeClass).attr("data-active",true))}function deactivateFrames(key){var _toDeactivate=toDeactivate[key];while(_toDeactivate.length){_toDeactivate.shift().removeClass(activeClass).attr("data-active",false)}}function detachFrames(key){var _toDetach=toDetach[key];$.each(activeIndexes,function(i,index){delete _toDetach[normalizeIndex(index)]});$.each(_toDetach,function(index,$frame){delete _toDetach[index];$frame.detach()})}function stageShaftReposition(skipOnEnd){repositionIndex=dirtyIndex=activeIndex;var $frame=activeFrame[STAGE_FRAME_KEY];if($frame){deactivateFrames(STAGE_FRAME_KEY);toDeactivate[STAGE_FRAME_KEY].push($frame.addClass(activeClass).attr("data-active",true));if($frame.hasClass(stageFrameClass)){$frame.attr("aria-hidden","false")}skipOnEnd||that.showStage.onEnd(true);stop($stageShaft,0,true);detachFrames(STAGE_FRAME_KEY);stageFramePosition(activeIndexes);setStageShaftMinmaxAndSnap();setNavShaftMinMax();addEnterUp($stageShaft[0],function(){if(!$fotorama.hasClass(fullscreenClass)){that.requestFullScreen();$fullscreenIcon.focus()}})}}function extendMeasures(options,measuresArray){if(!options)return;$.each(measuresArray,function(i,measures){if(!measures)return;$.extend(measures,{width:options.width||measures.width,height:options.height,minwidth:options.minwidth,maxwidth:options.maxwidth,minheight:options.minheight,maxheight:options.maxheight,ratio:getRatio(options.ratio)})})}function triggerEvent(event,extra){$fotorama.trigger(_fotoramaClass+":"+event,[that,extra])}function onTouchStart(){clearTimeout(onTouchEnd.t);touchedFLAG=1;if(opts.stopautoplayontouch){that.stopAutoplay()}else{pausedAutoplayFLAG=true}}function onTouchEnd(){if(!touchedFLAG)return;if(!opts.stopautoplayontouch){releaseAutoplay();changeAutoplay()}onTouchEnd.t=setTimeout(function(){touchedFLAG=0},TRANSITION_DURATION+TOUCH_TIMEOUT)}function releaseAutoplay(){pausedAutoplayFLAG=!!($videoPlaying||stoppedAutoplayFLAG)}function changeAutoplay(){clearTimeout(changeAutoplay.t);waitFor.stop(changeAutoplay.w);if(!opts.autoplay||pausedAutoplayFLAG){if(that.autoplay){that.autoplay=false;triggerEvent("stopautoplay")}return}if(!that.autoplay){that.autoplay=true;triggerEvent("startautoplay")}var _activeIndex=activeIndex;var frameData=activeFrame[STAGE_FRAME_KEY].data();changeAutoplay.w=waitFor(function(){return frameData.state||_activeIndex!==activeIndex},function(){changeAutoplay.t=setTimeout(function(){if(pausedAutoplayFLAG||_activeIndex!==activeIndex)return;var _nextAutoplayIndex=nextAutoplayIndex,nextFrameData=data[_nextAutoplayIndex][STAGE_FRAME_KEY].data();changeAutoplay.w=waitFor(function(){return nextFrameData.state||_nextAutoplayIndex!==nextAutoplayIndex},function(){if(pausedAutoplayFLAG||_nextAutoplayIndex!==nextAutoplayIndex)return;that.show(o_loop?getDirectionSign(!o_rtl):nextAutoplayIndex)})},opts.autoplay)})}that.startAutoplay=function(interval){if(that.autoplay)return this;pausedAutoplayFLAG=stoppedAutoplayFLAG=false;setAutoplayInterval(interval||opts.autoplay);changeAutoplay();return this};that.stopAutoplay=function(){if(that.autoplay){pausedAutoplayFLAG=stoppedAutoplayFLAG=true;changeAutoplay()}return this};that.showSlide=function(slideDir){var currentPosition=readPosition($navShaft,opts.navdir),pos,time=500*1.1,size=opts.navdir==="horizontal"?opts.thumbwidth:opts.thumbheight,onEnd=function(){thumbArrUpdate()};if(slideDir==="next"){pos=currentPosition-(size+opts.margin)*thumbsPerSlide}if(slideDir==="prev"){pos=currentPosition+(size+opts.margin)*thumbsPerSlide}pos=validateRestrictions(pos,navShaftTouchTail);thumbsDraw(pos,true);slide($navShaft,{time:time,pos:pos,direction:opts.navdir,onEnd:onEnd})};that.showWhileLongPress=function(options){if(that.longPress.singlePressInProgress){return}var index=calcActiveIndex(options);calcGlobalIndexes(index);var time=calcTime(options)/50;var _activeFrame=activeFrame;that.activeFrame=activeFrame=data[activeIndex];var silent=_activeFrame===activeFrame&&!options.user;that.showNav(silent,options,time);return this};that.showEndLongPress=function(options){if(that.longPress.singlePressInProgress){return}var index=calcActiveIndex(options);calcGlobalIndexes(index);var time=calcTime(options)/50;var _activeFrame=activeFrame;that.activeFrame=activeFrame=data[activeIndex];var silent=_activeFrame===activeFrame&&!options.user;that.showStage(silent,options,time);showedFLAG=typeof lastActiveIndex!=="undefined"&&lastActiveIndex!==activeIndex;lastActiveIndex=activeIndex;return this};function calcActiveIndex(options){var index;if(typeof options!=="object"){index=options;options={}}else{index=options.index}index=index===">"?dirtyIndex+1:index==="<"?dirtyIndex-1:index==="<<"?0:index===">>"?size-1:index;index=isNaN(index)?undefined:index;index=typeof index==="undefined"?activeIndex||0:index;return index}function calcGlobalIndexes(index){that.activeIndex=activeIndex=edgeIndex(index);prevIndex=getPrevIndex(activeIndex);nextIndex=getNextIndex(activeIndex);nextAutoplayIndex=normalizeIndex(activeIndex+(o_rtl?-1:1));activeIndexes=[activeIndex,prevIndex,nextIndex];dirtyIndex=o_loop?index:activeIndex}function calcTime(options){var diffIndex=Math.abs(lastActiveIndex-dirtyIndex),time=getNumber(options.time,function(){return Math.min(o_transitionDuration*(1+(diffIndex-1)/12),o_transitionDuration*2)});if(options.slow){time*=10}return time}that.showStage=function(silent,options,time){unloadVideo($videoPlaying,activeFrame.i!==data[normalizeIndex(repositionIndex)].i);frameDraw(activeIndexes,"stage");stageFramePosition(SLOW?[dirtyIndex]:[dirtyIndex,getPrevIndex(dirtyIndex),getNextIndex(dirtyIndex)]);updateTouchTails("go",true);silent||triggerEvent("show",{user:options.user,time:time});pausedAutoplayFLAG=true;var overPos=options.overPos;var onEnd=that.showStage.onEnd=function(skipReposition){if(onEnd.ok)return;onEnd.ok=true;skipReposition||stageShaftReposition(true);if(!silent){triggerEvent("showend",{user:options.user})}if(!skipReposition&&o_transition&&o_transition!==opts.transition){that.setOptions({transition:o_transition});o_transition=false;return}updateFotoramaState();loadImg(activeIndexes,"stage");updateTouchTails("go",false);stageWheelUpdate();stageCursor();releaseAutoplay();changeAutoplay();if(that.fullScreen){activeFrame[STAGE_FRAME_KEY].find("."+imgFullClass).attr("aria-hidden",false);activeFrame[STAGE_FRAME_KEY].find("."+imgClass).attr("aria-hidden",true)}else{activeFrame[STAGE_FRAME_KEY].find("."+imgFullClass).attr("aria-hidden",true);activeFrame[STAGE_FRAME_KEY].find("."+imgClass).attr("aria-hidden",false)}};if(!o_fade){slide($stageShaft,{pos:-getPosByIndex(dirtyIndex,measures.w,opts.margin,repositionIndex),overPos:overPos,time:time,onEnd:onEnd})}else{var $activeFrame=activeFrame[STAGE_FRAME_KEY],$prevActiveFrame=data[lastActiveIndex]&&activeIndex!==lastActiveIndex?data[lastActiveIndex][STAGE_FRAME_KEY]:null;fade($activeFrame,$prevActiveFrame,$stageFrame,{time:time,method:opts.transition,onEnd:onEnd},fadeStack)}arrsUpdate()};that.showNav=function(silent,options,time){thumbArrUpdate();if(o_nav){navUpdate();var guessIndex=limitIndex(activeIndex+minMaxLimit(dirtyIndex-lastActiveIndex,-1,1));slideNavShaft({time:time,coo:guessIndex!==activeIndex&&options.coo,guessIndex:typeof options.coo!=="undefined"?guessIndex:activeIndex,keep:silent});if(o_navThumbs)slideThumbBorder(time)}};that.show=function(options){that.longPress.singlePressInProgress=true;var index=calcActiveIndex(options);calcGlobalIndexes(index);var time=calcTime(options);var _activeFrame=activeFrame;that.activeFrame=activeFrame=data[activeIndex];var silent=_activeFrame===activeFrame&&!options.user;that.showStage(silent,options,time);that.showNav(silent,options,time);showedFLAG=typeof lastActiveIndex!=="undefined"&&lastActiveIndex!==activeIndex;lastActiveIndex=activeIndex;that.longPress.singlePressInProgress=false;return this};that.requestFullScreen=function(){if(o_allowFullScreen&&!that.fullScreen){var isVideo=$((that.activeFrame||{}).$stageFrame||{}).hasClass("fotorama-video-container");if(isVideo){return}scrollTop=$WINDOW.scrollTop();scrollLeft=$WINDOW.scrollLeft();lockScroll($WINDOW);updateTouchTails("x",true);measuresStash=$.extend({},measures);$fotorama.addClass(fullscreenClass).appendTo($BODY.addClass(_fullscreenClass));$HTML.addClass(_fullscreenClass);unloadVideo($videoPlaying,true,true);that.fullScreen=true;if(o_nativeFullScreen){fullScreenApi.request(fotorama)}that.resize();loadImg(activeIndexes,"stage");updateFotoramaState();triggerEvent("fullscreenenter");if(!("ontouchstart"in window)){$fullscreenIcon.focus()}}return this};function cancelFullScreen(){if(that.fullScreen){that.fullScreen=false;if(FULLSCREEN){fullScreenApi.cancel(fotorama)}$BODY.removeClass(_fullscreenClass);$HTML.removeClass(_fullscreenClass);$fotorama.removeClass(fullscreenClass).insertAfter($anchor);measures=$.extend({},measuresStash);unloadVideo($videoPlaying,true,true);updateTouchTails("x",false);that.resize();loadImg(activeIndexes,"stage");lockScroll($WINDOW,scrollLeft,scrollTop);triggerEvent("fullscreenexit")}}that.cancelFullScreen=function(){if(o_nativeFullScreen&&fullScreenApi.is()){fullScreenApi.cancel(document)}else{cancelFullScreen()}return this};that.toggleFullScreen=function(){return that[(that.fullScreen?"cancel":"request")+"FullScreen"]()};that.resize=function(options){if(!data)return this;var time=arguments[1]||0,setFLAG=arguments[2];thumbsPerSlide=getThumbsInSlide($wrap,opts);extendMeasures(!that.fullScreen?optionsToLowerCase(options):{width:$(window).width(),maxwidth:null,minwidth:null,height:$(window).height(),maxheight:null,minheight:null},[measures,setFLAG||that.fullScreen||opts]);var width=measures.width,height=measures.height,ratio=measures.ratio,windowHeight=$WINDOW.height()-(o_nav?$nav.height():0);if(measureIsValid(width)){$wrap.css({width:""});$wrap.css({height:""});$stage.css({width:""});$stage.css({height:""});$stageShaft.css({width:""});$stageShaft.css({height:""});$nav.css({width:""});$nav.css({height:""});$wrap.css({minWidth:measures.minwidth||0,maxWidth:measures.maxwidth||MAX_WIDTH});if(o_nav==="dots"){$navWrap.hide()}width=measures.W=measures.w=$wrap.width();measures.nw=o_nav&&numberFromWhatever(opts.navwidth,width)||width;$stageShaft.css({width:measures.w,marginLeft:(measures.W-measures.w)/2});height=numberFromWhatever(height,windowHeight);height=height||ratio&&width/ratio;if(height){width=Math.round(width);height=measures.h=Math.round(minMaxLimit(height,numberFromWhatever(measures.minheight,windowHeight),numberFromWhatever(measures.maxheight,windowHeight)));$stage.css({width:width,height:height});if(opts.navdir==="vertical"&&!that.fullscreen){$nav.width(opts.thumbwidth+opts.thumbmargin*2)}if(opts.navdir==="horizontal"&&!that.fullscreen){$nav.height(opts.thumbheight+opts.thumbmargin*2)}if(o_nav==="dots"){$nav.width(width).height("auto");$navWrap.show()}if(opts.navdir==="vertical"&&that.fullScreen){$stage.css("height",$WINDOW.height())}if(opts.navdir==="horizontal"&&that.fullScreen){$stage.css("height",$WINDOW.height()-$nav.height())}if(o_nav){switch(opts.navdir){case"vertical":$navWrap.removeClass(navShafthorizontalClass);$navWrap.removeClass(navShaftListClass);$navWrap.addClass(navShaftVerticalClass);$nav.stop().animate({height:measures.h,width:opts.thumbwidth},time);break;case"list":$navWrap.removeClass(navShaftVerticalClass);$navWrap.removeClass(navShafthorizontalClass);$navWrap.addClass(navShaftListClass);break;default:$navWrap.removeClass(navShaftVerticalClass);$navWrap.removeClass(navShaftListClass);$navWrap.addClass(navShafthorizontalClass);$nav.stop().animate({width:measures.nw},time);break}stageShaftReposition();slideNavShaft({guessIndex:activeIndex,time:time,keep:true});if(o_navThumbs&&frameAppend.nav)slideThumbBorder(time)}measuresSetFLAG=setFLAG||true;ready.ok=true;ready()}}stageLeft=$stage.offset().left;setStagePosition();return this};that.setOptions=function(options){$.extend(opts,options);reset();return this};that.shuffle=function(){data&&shuffle(data)&&reset();return this};function setShadow($el,edge){if(o_shadows){$el.removeClass(shadowsLeftClass+" "+shadowsRightClass);$el.removeClass(shadowsTopClass+" "+shadowsBottomClass);edge&&!$videoPlaying&&$el.addClass(edge.replace(/^|\s/g," "+shadowsClass+"--"))}}that.longPress={threshold:1,count:0,thumbSlideTime:20,progress:function(){if(!this.inProgress){this.count++;this.inProgress=this.count>this.threshold}},end:function(){if(this.inProgress){this.isEnded=true}},reset:function(){this.count=0;this.inProgress=false;this.isEnded=false}};that.destroy=function(){that.cancelFullScreen();that.stopAutoplay();data=that.data=null;appendElements();activeIndexes=[];detachFrames(STAGE_FRAME_KEY);reset.ok=false;return this};that.playVideo=function(){var dataFrame=activeFrame,video=dataFrame.video,_activeIndex=activeIndex;if(typeof video==="object"&&dataFrame.videoReady){o_nativeFullScreen&&that.fullScreen&&that.cancelFullScreen();waitFor(function(){return!fullScreenApi.is()||_activeIndex!==activeIndex},function(){if(_activeIndex===activeIndex){dataFrame.$video=dataFrame.$video||$(div(videoClass)).append(createVideoFrame(video));dataFrame.$video.appendTo(dataFrame[STAGE_FRAME_KEY]);$wrap.addClass(wrapVideoClass);$videoPlaying=dataFrame.$video;stageNoMove();$arrs.blur();$fullscreenIcon.blur();triggerEvent("loadvideo")}})}return this};that.stopVideo=function(){unloadVideo($videoPlaying,true,true);return this};that.spliceByIndex=function(index,newImgObj){newImgObj.i=index+1;newImgObj.img&&$.ajax({url:newImgObj.img,type:"HEAD",success:function(){data.splice(index,1,newImgObj);reset()}})};function unloadVideo($video,unloadActiveFLAG,releaseAutoplayFLAG){if(unloadActiveFLAG){$wrap.removeClass(wrapVideoClass);$videoPlaying=false;stageNoMove()}if($video&&$video!==$videoPlaying){$video.remove();triggerEvent("unloadvideo")}if(releaseAutoplayFLAG){releaseAutoplay();changeAutoplay()}}function toggleControlsClass(FLAG){$wrap.toggleClass(wrapNoControlsClass,FLAG)}function stageCursor(e){if(stageShaftTouchTail.flow)return;var x=e?e.pageX:stageCursor.x,pointerFLAG=x&&!disableDirrection(getDirection(x))&&opts.click;if(stageCursor.p!==pointerFLAG&&$stage.toggleClass(pointerClass,pointerFLAG)){stageCursor.p=pointerFLAG;stageCursor.x=x}}$stage.on("mousemove",stageCursor);function clickToShow(showOptions){clearTimeout(clickToShow.t);if(opts.clicktransition&&opts.clicktransition!==opts.transition){setTimeout(function(){var _o_transition=opts.transition;that.setOptions({transition:opts.clicktransition});o_transition=_o_transition;clickToShow.t=setTimeout(function(){that.show(showOptions)},10)},0)}else{that.show(showOptions)}}function onStageTap(e,toggleControlsFLAG){var target=e.target,$target=$(target);if($target.hasClass(videoPlayClass)){that.playVideo()}else if(target===fullscreenIcon){that.toggleFullScreen()}else if($videoPlaying){target===videoClose&&unloadVideo($videoPlaying,true,true)}else if(!$fotorama.hasClass(fullscreenClass)){that.requestFullScreen()}}function updateTouchTails(key,value){stageShaftTouchTail[key]=navShaftTouchTail[key]=value}stageShaftTouchTail=moveOnTouch($stageShaft,{onStart:onTouchStart,onMove:function(e,result){setShadow($stage,result.edge)},onTouchEnd:onTouchEnd,onEnd:function(result){var toggleControlsFLAG;setShadow($stage);toggleControlsFLAG=(MS_POINTER&&!hoverFLAG||result.touch)&&opts.arrows;if((result.moved||toggleControlsFLAG&&result.pos!==result.newPos&&!result.control)&&result.$target[0]!==$fullscreenIcon[0]){var index=getIndexByPos(result.newPos,measures.w,opts.margin,repositionIndex);that.show({index:index,time:o_fade?o_transitionDuration:result.time,overPos:result.overPos,user:true})}else if(!result.aborted&&!result.control){onStageTap(result.startEvent,toggleControlsFLAG)}},timeLow:1,timeHigh:1,friction:2,select:"."+selectClass+", ."+selectClass+" *",$wrap:$stage,direction:"horizontal"});navShaftTouchTail=moveOnTouch($navShaft,{onStart:onTouchStart,onMove:function(e,result){setShadow($nav,result.edge)},onTouchEnd:onTouchEnd,onEnd:function(result){function onEnd(){slideNavShaft.l=result.newPos;releaseAutoplay();changeAutoplay();thumbsDraw(result.newPos,true);thumbArrUpdate()}if(!result.moved){var target=result.$target.closest("."+navFrameClass,$navShaft)[0];target&&onNavFrameClick.call(target,result.startEvent)}else if(result.pos!==result.newPos){pausedAutoplayFLAG=true;slide($navShaft,{time:result.time,pos:result.newPos,overPos:result.overPos,direction:opts.navdir,onEnd:onEnd});thumbsDraw(result.newPos);o_shadows&&setShadow($nav,findShadowEdge(result.newPos,navShaftTouchTail.min,navShaftTouchTail.max,result.dir))}else{onEnd()}},timeLow:.5,timeHigh:2,friction:5,$wrap:$nav,direction:opts.navdir});stageWheelTail=wheel($stage,{shift:true,onEnd:function(e,direction){onTouchStart();onTouchEnd();that.show({index:direction,slow:e.altKey})}});navWheelTail=wheel($nav,{onEnd:function(e,direction){onTouchStart();onTouchEnd();var newPos=stop($navShaft)+direction*.25;$navShaft.css(getTranslate(minMaxLimit(newPos,navShaftTouchTail.min,navShaftTouchTail.max),opts.navdir));o_shadows&&setShadow($nav,findShadowEdge(newPos,navShaftTouchTail.min,navShaftTouchTail.max,opts.navdir));navWheelTail.prevent={"<":newPos>=navShaftTouchTail.max,">":newPos<=navShaftTouchTail.min};clearTimeout(navWheelTail.t);navWheelTail.t=setTimeout(function(){slideNavShaft.l=newPos;thumbsDraw(newPos,true)},TOUCH_TIMEOUT);thumbsDraw(newPos)}});$wrap.hover(function(){setTimeout(function(){if(touchedFLAG)return;toggleControlsClass(!(hoverFLAG=true))},0)},function(){if(!hoverFLAG)return;toggleControlsClass(!(hoverFLAG=false))});function onNavFrameClick(e){var index=$(this).data().eq;if(opts.navtype==="thumbs"){clickToShow({index:index,slow:e.altKey,user:true,coo:e._x-$nav.offset().left})}else{clickToShow({index:index,slow:e.altKey,user:true})}}function onArrClick(e){clickToShow({index:$arrs.index(this)?">":"<",slow:e.altKey,user:true})}smartClick($arrs,function(e){stopEvent(e);onArrClick.call(this,e)},{onStart:function(){onTouchStart();stageShaftTouchTail.control=true},onTouchEnd:onTouchEnd});smartClick($thumbArrLeft,function(e){stopEvent(e);if(opts.navtype==="thumbs"){that.show("<")}else{that.showSlide("prev")}});smartClick($thumbArrRight,function(e){stopEvent(e);if(opts.navtype==="thumbs"){that.show(">")}else{that.showSlide("next")}});function addFocusOnControls(el){addFocus(el,function(){setTimeout(function(){lockScroll($stage)},0);toggleControlsClass(false)})}$arrs.each(function(){addEnterUp(this,function(e){onArrClick.call(this,e)});addFocusOnControls(this)});addEnterUp(fullscreenIcon,function(){if($fotorama.hasClass(fullscreenClass)){that.cancelFullScreen();$stageShaft.focus()}else{that.requestFullScreen();$fullscreenIcon.focus()}});addFocusOnControls(fullscreenIcon);function reset(){setData();setOptions();if(!reset.i){reset.i=true;var _startindex=opts.startindex;activeIndex=repositionIndex=dirtyIndex=lastActiveIndex=startIndex=edgeIndex(_startindex)||0}if(size){if(changeToRtl())return;if($videoPlaying){unloadVideo($videoPlaying,true)}activeIndexes=[];detachFrames(STAGE_FRAME_KEY);reset.ok=true;that.show({index:activeIndex,time:0});that.resize()}else{that.destroy()}}function changeToRtl(){if(!changeToRtl.f===o_rtl){changeToRtl.f=o_rtl;activeIndex=size-1-activeIndex;that.reverse();return true}}$.each("load push pop shift unshift reverse sort splice".split(" "),function(i,method){that[method]=function(){data=data||[];if(method!=="load"){Array.prototype[method].apply(data,arguments)}else if(arguments[0]&&typeof arguments[0]==="object"&&arguments[0].length){data=clone(arguments[0])}reset();return that}});function ready(){if(ready.ok){ready.ok=false;triggerEvent("ready")}}reset()};$.fn.fotorama=function(opts){return this.each(function(){var that=this,$fotorama=$(this),fotoramaData=$fotorama.data(),fotorama=fotoramaData.fotorama;if(!fotorama){waitFor(function(){return!isHidden(that)},function(){fotoramaData.urtext=$fotorama.html();new $.Fotorama($fotorama,$.extend({},OPTIONS,window.fotoramaDefaults,opts,fotoramaData))})}else{fotorama.setOptions(opts,true)}})};$.Fotorama.instances=[];function calculateIndexes(){$.each($.Fotorama.instances,function(index,instance){instance.index=index})}function addInstance(instance){$.Fotorama.instances.push(instance);calculateIndexes()}function hideInstance(instance){$.Fotorama.instances.splice(instance.index,1);calculateIndexes()}$.Fotorama.cache={};$.Fotorama.measures={};$=$||{};$.Fotorama=$.Fotorama||{};$.Fotorama.jst=$.Fotorama.jst||{};$.Fotorama.jst.dots=function(v){var __t,__p="",__e=_.escape;__p+='<div class="fotorama__nav__frame fotorama__nav__frame--dot" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__dot"></div>\r\n</div>';return __p};$.Fotorama.jst.frameCaption=function(v){var __t,__p="",__e=_.escape;__p+='<div class="fotorama__caption" aria-hidden="true">\r\n <div class="fotorama__caption__wrap" id="'+((__t=v.labelledby)==null?"":__t)+'">'+((__t=v.caption)==null?"":__t)+"</div>\r\n</div>\r\n";return __p};$.Fotorama.jst.style=function(v){var __t,__p="",__e=_.escape;__p+=".fotorama"+((__t=v.s)==null?"":__t)+" .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:"+((__t=v.m)==null?"":__t)+"px;\r\nheight:"+((__t=v.h)==null?"":__t)+"px}\r\n.fotorama"+((__t=v.s)==null?"":__t)+" .fotorama__thumb-border{\r\nheight:"+((__t=v.h)==null?"":__t)+"px;\r\nborder-width:"+((__t=v.b)==null?"":__t)+"px;\r\nmargin-top:"+((__t=v.m)==null?"":__t)+"px}";return __p};$.Fotorama.jst.thumb=function(v){var __t,__p="",__e=_.escape;__p+='<div class="fotorama__nav__frame fotorama__nav__frame--thumb" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__thumb">\r\n </div>\r\n</div>';return __p}})(window,document,location,typeof jQuery!=="undefined"&&jQuery); +fotoramaVersion="4.6.4";(function(window,document,location,$,undefined){"use strict";var _fotoramaClass="fotorama",_fullscreenClass="fotorama__fullscreen",wrapClass=_fotoramaClass+"__wrap",wrapCss2Class=wrapClass+"--css2",wrapCss3Class=wrapClass+"--css3",wrapVideoClass=wrapClass+"--video",wrapFadeClass=wrapClass+"--fade",wrapSlideClass=wrapClass+"--slide",wrapNoControlsClass=wrapClass+"--no-controls",wrapNoShadowsClass=wrapClass+"--no-shadows",wrapPanYClass=wrapClass+"--pan-y",wrapRtlClass=wrapClass+"--rtl",wrapOnlyActiveClass=wrapClass+"--only-active",wrapNoCaptionsClass=wrapClass+"--no-captions",wrapToggleArrowsClass=wrapClass+"--toggle-arrows",stageClass=_fotoramaClass+"__stage",stageFrameClass=stageClass+"__frame",stageFrameVideoClass=stageFrameClass+"--video",stageShaftClass=stageClass+"__shaft",grabClass=_fotoramaClass+"__grab",pointerClass=_fotoramaClass+"__pointer",arrClass=_fotoramaClass+"__arr",arrDisabledClass=arrClass+"--disabled",arrPrevClass=arrClass+"--prev",arrNextClass=arrClass+"--next",navClass=_fotoramaClass+"__nav",navWrapClass=navClass+"-wrap",navShaftClass=navClass+"__shaft",navShaftVerticalClass=navWrapClass+"--vertical",navShaftListClass=navWrapClass+"--list",navShafthorizontalClass=navWrapClass+"--horizontal",navDotsClass=navClass+"--dots",navThumbsClass=navClass+"--thumbs",navFrameClass=navClass+"__frame",fadeClass=_fotoramaClass+"__fade",fadeFrontClass=fadeClass+"-front",fadeRearClass=fadeClass+"-rear",shadowClass=_fotoramaClass+"__shadow",shadowsClass=shadowClass+"s",shadowsLeftClass=shadowsClass+"--left",shadowsRightClass=shadowsClass+"--right",shadowsTopClass=shadowsClass+"--top",shadowsBottomClass=shadowsClass+"--bottom",activeClass=_fotoramaClass+"__active",selectClass=_fotoramaClass+"__select",hiddenClass=_fotoramaClass+"--hidden",fullscreenClass=_fotoramaClass+"--fullscreen",fullscreenIconClass=_fotoramaClass+"__fullscreen-icon",errorClass=_fotoramaClass+"__error",loadingClass=_fotoramaClass+"__loading",loadedClass=_fotoramaClass+"__loaded",loadedFullClass=loadedClass+"--full",loadedImgClass=loadedClass+"--img",grabbingClass=_fotoramaClass+"__grabbing",imgClass=_fotoramaClass+"__img",imgFullClass=imgClass+"--full",thumbClass=_fotoramaClass+"__thumb",thumbArrLeft=thumbClass+"__arr--left",thumbArrRight=thumbClass+"__arr--right",thumbBorderClass=thumbClass+"-border",htmlClass=_fotoramaClass+"__html",videoContainerClass=_fotoramaClass+"-video-container",videoClass=_fotoramaClass+"__video",videoPlayClass=videoClass+"-play",videoCloseClass=videoClass+"-close",horizontalImageClass=_fotoramaClass+"_horizontal_ratio",verticalImageClass=_fotoramaClass+"_vertical_ratio",fotoramaSpinnerClass=_fotoramaClass+"__spinner",spinnerShowClass=fotoramaSpinnerClass+"--show";var JQUERY_VERSION=$&&$.fn.jquery.split(".");if(!JQUERY_VERSION||JQUERY_VERSION[0]<1||JQUERY_VERSION[0]==1&&JQUERY_VERSION[1]<8){throw"Fotorama requires jQuery 1.8 or later and will not run without it."}var _={};var Modernizr=function(window,document,undefined){var version="2.8.3",Modernizr={},docElement=document.documentElement,mod="modernizr",modElem=document.createElement(mod),mStyle=modElem.style,inputElem,toString={}.toString,prefixes=" -webkit- -moz- -o- -ms- ".split(" "),omPrefixes="Webkit Moz O ms",cssomPrefixes=omPrefixes.split(" "),domPrefixes=omPrefixes.toLowerCase().split(" "),tests={},inputs={},attrs={},classes=[],slice=classes.slice,featureName,injectElementWithStyles=function(rule,callback,nodes,testnames){var style,ret,node,docOverflow,div=document.createElement("div"),body=document.body,fakeBody=body||document.createElement("body");if(parseInt(nodes,10)){while(nodes--){node=document.createElement("div");node.id=testnames?testnames[nodes]:mod+(nodes+1);div.appendChild(node)}}style=["­",'<style id="s',mod,'">',rule,"</style>"].join("");div.id=mod;(body?div:fakeBody).innerHTML+=style;fakeBody.appendChild(div);if(!body){fakeBody.style.background="";fakeBody.style.overflow="hidden";docOverflow=docElement.style.overflow;docElement.style.overflow="hidden";docElement.appendChild(fakeBody)}ret=callback(div,rule);if(!body){fakeBody.parentNode.removeChild(fakeBody);docElement.style.overflow=docOverflow}else{div.parentNode.removeChild(div)}return!!ret},_hasOwnProperty={}.hasOwnProperty,hasOwnProp;if(!is(_hasOwnProperty,"undefined")&&!is(_hasOwnProperty.call,"undefined")){hasOwnProp=function(object,property){return _hasOwnProperty.call(object,property)}}else{hasOwnProp=function(object,property){return property in object&&is(object.constructor.prototype[property],"undefined")}}if(!Function.prototype.bind){Function.prototype.bind=function bind(that){var target=this;if(typeof target!="function"){throw new TypeError}var args=slice.call(arguments,1),bound=function(){if(this instanceof bound){var F=function(){};F.prototype=target.prototype;var self=new F;var result=target.apply(self,args.concat(slice.call(arguments)));if(Object(result)===result){return result}return self}else{return target.apply(that,args.concat(slice.call(arguments)))}};return bound}}function setCss(str){mStyle.cssText=str}function setCssAll(str1,str2){return setCss(prefixes.join(str1+";")+(str2||""))}function is(obj,type){return typeof obj===type}function contains(str,substr){return!!~(""+str).indexOf(substr)}function testProps(props,prefixed){for(var i in props){var prop=props[i];if(!contains(prop,"-")&&mStyle[prop]!==undefined){return prefixed=="pfx"?prop:true}}return false}function testDOMProps(props,obj,elem){for(var i in props){var item=obj[props[i]];if(item!==undefined){if(elem===false)return props[i];if(is(item,"function")){return item.bind(elem||obj)}return item}}return false}function testPropsAll(prop,prefixed,elem){var ucProp=prop.charAt(0).toUpperCase()+prop.slice(1),props=(prop+" "+cssomPrefixes.join(ucProp+" ")+ucProp).split(" ");if(is(prefixed,"string")||is(prefixed,"undefined")){return testProps(props,prefixed)}else{props=(prop+" "+domPrefixes.join(ucProp+" ")+ucProp).split(" ");return testDOMProps(props,prefixed,elem)}}tests["touch"]=function(){var bool;if("ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch){bool=true}else{injectElementWithStyles(["@media (",prefixes.join("touch-enabled),("),mod,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(node){bool=node.offsetTop===9})}return bool};tests["csstransforms3d"]=function(){var ret=!!testPropsAll("perspective");if(ret&&"webkitPerspective"in docElement.style){injectElementWithStyles("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(node,rule){ret=node.offsetLeft===9&&node.offsetHeight===3})}return ret};tests["csstransitions"]=function(){return testPropsAll("transition")};for(var feature in tests){if(hasOwnProp(tests,feature)){featureName=feature.toLowerCase();Modernizr[featureName]=tests[feature]();classes.push((Modernizr[featureName]?"":"no-")+featureName)}}Modernizr.addTest=function(feature,test){if(typeof feature=="object"){for(var key in feature){if(hasOwnProp(feature,key)){Modernizr.addTest(key,feature[key])}}}else{feature=feature.toLowerCase();if(Modernizr[feature]!==undefined){return Modernizr}test=typeof test=="function"?test():test;if(typeof enableClasses!=="undefined"&&enableClasses){docElement.className+=" "+(test?"":"no-")+feature}Modernizr[feature]=test}return Modernizr};setCss("");modElem=inputElem=null;Modernizr._version=version;Modernizr._prefixes=prefixes;Modernizr._domPrefixes=domPrefixes;Modernizr._cssomPrefixes=cssomPrefixes;Modernizr.testProp=function(prop){return testProps([prop])};Modernizr.testAllProps=testPropsAll;Modernizr.testStyles=injectElementWithStyles;Modernizr.prefixed=function(prop,obj,elem){if(!obj){return testPropsAll(prop,"pfx")}else{return testPropsAll(prop,obj,elem)}};return Modernizr}(window,document);var fullScreenApi={ok:false,is:function(){return false},request:function(){},cancel:function(){},event:"",prefix:""},browserPrefixes="webkit moz o ms khtml".split(" ");if(typeof document.cancelFullScreen!="undefined"){fullScreenApi.ok=true}else{for(var i=0,il=browserPrefixes.length;i<il;i++){fullScreenApi.prefix=browserPrefixes[i];if(typeof document[fullScreenApi.prefix+"CancelFullScreen"]!="undefined"){fullScreenApi.ok=true;break}}}if(fullScreenApi.ok){fullScreenApi.event=fullScreenApi.prefix+"fullscreenchange";fullScreenApi.is=function(){switch(this.prefix){case"":return document.fullScreen;case"webkit":return document.webkitIsFullScreen;default:return document[this.prefix+"FullScreen"]}};fullScreenApi.request=function(el){return this.prefix===""?el.requestFullScreen():el[this.prefix+"RequestFullScreen"]()};fullScreenApi.cancel=function(el){return this.prefix===""?document.cancelFullScreen():document[this.prefix+"CancelFullScreen"]()}}function bez(coOrdArray){var encodedFuncName="bez_"+$.makeArray(arguments).join("_").replace(".","p");if(typeof $["easing"][encodedFuncName]!=="function"){var polyBez=function(p1,p2){var A=[null,null],B=[null,null],C=[null,null],bezCoOrd=function(t,ax){C[ax]=3*p1[ax];B[ax]=3*(p2[ax]-p1[ax])-C[ax];A[ax]=1-C[ax]-B[ax];return t*(C[ax]+t*(B[ax]+t*A[ax]))},xDeriv=function(t){return C[0]+t*(2*B[0]+3*A[0]*t)},xForT=function(t){var x=t,i=0,z;while(++i<14){z=bezCoOrd(x,0)-t;if(Math.abs(z)<.001)break;x-=z/xDeriv(x)}return x};return function(t){return bezCoOrd(xForT(t),1)}};$["easing"][encodedFuncName]=function(x,t,b,c,d){return c*polyBez([coOrdArray[0],coOrdArray[1]],[coOrdArray[2],coOrdArray[3]])(t/d)+b}}return encodedFuncName}var $WINDOW=$(window),$DOCUMENT=$(document),$HTML,$BODY,QUIRKS_FORCE=location.hash.replace("#","")==="quirks",TRANSFORMS3D=Modernizr.csstransforms3d,CSS3=TRANSFORMS3D&&!QUIRKS_FORCE,COMPAT=TRANSFORMS3D||document.compatMode==="CSS1Compat",FULLSCREEN=fullScreenApi.ok,MOBILE=navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i),SLOW=!CSS3||MOBILE,MS_POINTER=navigator.msPointerEnabled,WHEEL="onwheel"in document.createElement("div")?"wheel":document.onmousewheel!==undefined?"mousewheel":"DOMMouseScroll",TOUCH_TIMEOUT=250,TRANSITION_DURATION=300,SCROLL_LOCK_TIMEOUT=1400,AUTOPLAY_INTERVAL=5e3,MARGIN=2,THUMB_SIZE=64,WIDTH=500,HEIGHT=333,STAGE_FRAME_KEY="$stageFrame",NAV_DOT_FRAME_KEY="$navDotFrame",NAV_THUMB_FRAME_KEY="$navThumbFrame",AUTO="auto",BEZIER=bez([.1,0,.25,1]),MAX_WIDTH=1200,thumbsPerSlide=1,OPTIONS={width:null,minwidth:null,maxwidth:"100%",height:null,minheight:null,maxheight:null,ratio:null,margin:MARGIN,nav:"dots",navposition:"bottom",navwidth:null,thumbwidth:THUMB_SIZE,thumbheight:THUMB_SIZE,thumbmargin:MARGIN,thumbborderwidth:MARGIN,allowfullscreen:false,transition:"slide",clicktransition:null,transitionduration:TRANSITION_DURATION,captions:true,startindex:0,loop:false,autoplay:false,stopautoplayontouch:true,keyboard:false,arrows:true,click:true,swipe:false,trackpad:false,shuffle:false,direction:"ltr",shadows:true,showcaption:true,navdir:"horizontal",navarrows:true,navtype:"thumbs"},KEYBOARD_OPTIONS={left:true,right:true,down:true,up:true,space:false,home:false,end:false};function noop(){}function minMaxLimit(value,min,max){return Math.max(isNaN(min)?-Infinity:min,Math.min(isNaN(max)?Infinity:max,value))}function readTransform(css,dir){return css.match(/ma/)&&css.match(/-?\d+(?!d)/g)[css.match(/3d/)?dir==="vertical"?13:12:dir==="vertical"?5:4]}function readPosition($el,dir){if(CSS3){return+readTransform($el.css("transform"),dir)}else{return+$el.css(dir==="vertical"?"top":"left").replace("px","")}}function getTranslate(pos,direction){var obj={};if(CSS3){switch(direction){case"vertical":obj.transform="translate3d(0, "+pos+"px,0)";break;case"list":break;default:obj.transform="translate3d("+pos+"px,0,0)";break}}else{direction==="vertical"?obj.top=pos:obj.left=pos}return obj}function getDuration(time){return{"transition-duration":time+"ms"}}function unlessNaN(value,alternative){return isNaN(value)?alternative:value}function numberFromMeasure(value,measure){return unlessNaN(+String(value).replace(measure||"px",""))}function numberFromPercent(value){return/%$/.test(value)?numberFromMeasure(value,"%"):undefined}function numberFromWhatever(value,whole){return unlessNaN(numberFromPercent(value)/100*whole,numberFromMeasure(value))}function measureIsValid(value){return(!isNaN(numberFromMeasure(value))||!isNaN(numberFromMeasure(value,"%")))&&value}function getPosByIndex(index,side,margin,baseIndex){return(index-(baseIndex||0))*(side+(margin||0))}function getIndexByPos(pos,side,margin,baseIndex){return-Math.round(pos/(side+(margin||0))-(baseIndex||0))}function bindTransitionEnd($el){var elData=$el.data();if(elData.tEnd)return;var el=$el[0],transitionEndEvent={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",msTransition:"MSTransitionEnd",transition:"transitionend"};addEvent(el,transitionEndEvent[Modernizr.prefixed("transition")],function(e){elData.tProp&&e.propertyName.match(elData.tProp)&&elData.onEndFn()});elData.tEnd=true}function afterTransition($el,property,fn,time){var ok,elData=$el.data();if(elData){elData.onEndFn=function(){if(ok)return;ok=true;clearTimeout(elData.tT);fn()};elData.tProp=property;clearTimeout(elData.tT);elData.tT=setTimeout(function(){elData.onEndFn()},time*1.5);bindTransitionEnd($el)}}function stop($el,pos){var dir=$el.navdir||"horizontal";if($el.length){var elData=$el.data();if(CSS3){$el.css(getDuration(0));elData.onEndFn=noop;clearTimeout(elData.tT)}else{$el.stop()}var lockedPos=getNumber(pos,function(){return readPosition($el,dir)});$el.css(getTranslate(lockedPos,dir));return lockedPos}}function getNumber(){var number;for(var _i=0,_l=arguments.length;_i<_l;_i++){number=_i?arguments[_i]():arguments[_i];if(typeof number==="number"){break}}return number}function edgeResistance(pos,edge){return Math.round(pos+(edge-pos)/1.5)}function getProtocol(){getProtocol.p=getProtocol.p||(location.protocol==="https:"?"https://":"http://");return getProtocol.p}function parseHref(href){var a=document.createElement("a");a.href=href;return a}function findVideoId(href,forceVideo){if(typeof href!=="string")return href;href=parseHref(href);var id,type;if(href.host.match(/youtube\.com/)&&href.search){id=href.search.split("v=")[1];if(id){var ampersandPosition=id.indexOf("&");if(ampersandPosition!==-1){id=id.substring(0,ampersandPosition)}type="youtube"}}else if(href.host.match(/youtube\.com|youtu\.be/)){id=href.pathname.replace(/^\/(embed\/|v\/)?/,"").replace(/\/.*/,"");type="youtube"}else if(href.host.match(/vimeo\.com/)){type="vimeo";id=href.pathname.replace(/^\/(video\/)?/,"").replace(/\/.*/,"")}if((!id||!type)&&forceVideo){id=href.href;type="custom"}return id?{id:id,type:type,s:href.search.replace(/^\?/,""),p:getProtocol()}:false}function getVideoThumbs(dataFrame,data,fotorama){var img,thumb,video=dataFrame.video;if(video.type==="youtube"){thumb=getProtocol()+"img.youtube.com/vi/"+video.id+"/default.jpg";img=thumb.replace(/\/default.jpg$/,"/hqdefault.jpg");dataFrame.thumbsReady=true}else if(video.type==="vimeo"){$.ajax({url:getProtocol()+"vimeo.com/api/v2/video/"+video.id+".json",dataType:"jsonp",success:function(json){dataFrame.thumbsReady=true;updateData(data,{img:json[0].thumbnail_large,thumb:json[0].thumbnail_small},dataFrame.i,fotorama)}})}else{dataFrame.thumbsReady=true}return{img:img,thumb:thumb}}function updateData(data,_dataFrame,i,fotorama){for(var _i=0,_l=data.length;_i<_l;_i++){var dataFrame=data[_i];if(dataFrame.i===i&&dataFrame.thumbsReady){var clear={videoReady:true};clear[STAGE_FRAME_KEY]=clear[NAV_THUMB_FRAME_KEY]=clear[NAV_DOT_FRAME_KEY]=false;fotorama.splice(_i,1,$.extend({},dataFrame,clear,_dataFrame));break}}}function getDataFromHtml($el){var data=[];function getDataFromImg($img,imgData,checkVideo){var $child=$img.children("img").eq(0),_imgHref=$img.attr("href"),_imgSrc=$img.attr("src"),_thumbSrc=$child.attr("src"),_video=imgData.video,video=checkVideo?findVideoId(_imgHref,_video===true):false;if(video){_imgHref=false}else{video=_video}getDimensions($img,$child,$.extend(imgData,{video:video,img:imgData.img||_imgHref||_imgSrc||_thumbSrc,thumb:imgData.thumb||_thumbSrc||_imgSrc||_imgHref}))}function getDimensions($img,$child,imgData){var separateThumbFLAG=imgData.thumb&&imgData.img!==imgData.thumb,width=numberFromMeasure(imgData.width||$img.attr("width")),height=numberFromMeasure(imgData.height||$img.attr("height"));$.extend(imgData,{width:width,height:height,thumbratio:getRatio(imgData.thumbratio||numberFromMeasure(imgData.thumbwidth||$child&&$child.attr("width")||separateThumbFLAG||width)/numberFromMeasure(imgData.thumbheight||$child&&$child.attr("height")||separateThumbFLAG||height))})}$el.children().each(function(){var $this=$(this),dataFrame=optionsToLowerCase($.extend($this.data(),{id:$this.attr("id")}));if($this.is("a, img")){getDataFromImg($this,dataFrame,true)}else if(!$this.is(":empty")){getDimensions($this,null,$.extend(dataFrame,{html:this,_html:$this.html()}))}else return;data.push(dataFrame)});return data}function isHidden(el){return el.offsetWidth===0&&el.offsetHeight===0}function isDetached(el){return!$.contains(document.documentElement,el)}function waitFor(test,fn,timeout,i){if(!waitFor.i){waitFor.i=1;waitFor.ii=[true]}i=i||waitFor.i;if(typeof waitFor.ii[i]==="undefined"){waitFor.ii[i]=true}if(test()){fn()}else{waitFor.ii[i]&&setTimeout(function(){waitFor.ii[i]&&waitFor(test,fn,timeout,i)},timeout||100)}return waitFor.i++}waitFor.stop=function(i){waitFor.ii[i]=false};function fit($el,measuresToFit){var elData=$el.data(),measures=elData.measures;if(measures&&(!elData.l||elData.l.W!==measures.width||elData.l.H!==measures.height||elData.l.r!==measures.ratio||elData.l.w!==measuresToFit.w||elData.l.h!==measuresToFit.h)){var height=minMaxLimit(measuresToFit.h,0,measures.height),width=height*measures.ratio;UTIL.setRatio($el,width,height);elData.l={W:measures.width,H:measures.height,r:measures.ratio,w:measuresToFit.w,h:measuresToFit.h}}return true}function setStyle($el,style){var el=$el[0];if(el.styleSheet){el.styleSheet.cssText=style}else{$el.html(style)}}function findShadowEdge(pos,min,max,dir){return min===max?false:dir==="vertical"?pos<=min?"top":pos>=max?"bottom":"top bottom":pos<=min?"left":pos>=max?"right":"left right"}function smartClick($el,fn,_options){_options=_options||{};$el.each(function(){var $this=$(this),thisData=$this.data(),startEvent;if(thisData.clickOn)return;thisData.clickOn=true;$.extend(touch($this,{onStart:function(e){startEvent=e;(_options.onStart||noop).call(this,e)},onMove:_options.onMove||noop,onTouchEnd:_options.onTouchEnd||noop,onEnd:function(result){if(result.moved)return;fn.call(this,startEvent)}}),{noMove:true})})}function div(classes,child){return'<div class="'+classes+'">'+(child||"")+"</div>"}function cls(className){return"."+className}function createVideoFrame(videoItem){var frame='<iframe src="'+videoItem.p+videoItem.type+".com/embed/"+videoItem.id+'" frameborder="0" allowfullscreen></iframe>';return frame}function shuffle(array){var l=array.length;while(l){var i=Math.floor(Math.random()*l--);var t=array[l];array[l]=array[i];array[i]=t}return array}function clone(array){return Object.prototype.toString.call(array)=="[object Array]"&&$.map(array,function(frame){return $.extend({},frame)})}function lockScroll($el,left,top){$el.scrollLeft(left||0).scrollTop(top||0)}function optionsToLowerCase(options){if(options){var opts={};$.each(options,function(key,value){opts[key.toLowerCase()]=value});return opts}}function getRatio(_ratio){if(!_ratio)return;var ratio=+_ratio;if(!isNaN(ratio)){return ratio}else{ratio=_ratio.split("/");return+ratio[0]/+ratio[1]||undefined}}function addEvent(el,e,fn,bool){if(!e)return;el.addEventListener?el.addEventListener(e,fn,!!bool):el.attachEvent("on"+e,fn)}function validateRestrictions(position,restriction){if(position>restriction.max){position=restriction.max}else{if(position<restriction.min){position=restriction.min}}return position}function validateSlidePos(opt,navShaftTouchTail,guessIndex,offsetNav,$guessNavFrame,$navWrap,dir){var position,size,wrapSize;if(dir==="horizontal"){size=opt.thumbwidth;wrapSize=$navWrap.width()}else{size=opt.thumbheight;wrapSize=$navWrap.height()}if((size+opt.margin)*(guessIndex+1)>=wrapSize-offsetNav){if(dir==="horizontal"){position=-$guessNavFrame.position().left}else{position=-$guessNavFrame.position().top}}else{if((size+opt.margin)*guessIndex<=Math.abs(offsetNav)){if(dir==="horizontal"){position=-$guessNavFrame.position().left+wrapSize-(size+opt.margin)}else{position=-$guessNavFrame.position().top+wrapSize-(size+opt.margin)}}else{position=offsetNav}}position=validateRestrictions(position,navShaftTouchTail);return position||0}function elIsDisabled(el){return!!el.getAttribute("disabled")}function disableAttr(FLAG,disable){if(disable){return{disabled:FLAG}}else{return{tabindex:FLAG*-1+"",disabled:FLAG}}}function addEnterUp(el,fn){addEvent(el,"keyup",function(e){elIsDisabled(el)||e.keyCode==13&&fn.call(el,e)})}function addFocus(el,fn){addEvent(el,"focus",el.onfocusin=function(e){fn.call(el,e)},true)}function stopEvent(e,stopPropagation){e.preventDefault?e.preventDefault():e.returnValue=false;stopPropagation&&e.stopPropagation&&e.stopPropagation()}function stubEvent($el,eventType){var isIOS=/ip(ad|hone|od)/i.test(window.navigator.userAgent);if(isIOS&&eventType==="touchend"){$el.on("touchend",function(e){$DOCUMENT.trigger("mouseup",e)})}$el.on(eventType,function(e){stopEvent(e,true);return false})}function getDirectionSign(forward){return forward?">":"<"}var UTIL=function(){function setRatioClass($el,wh,ht){var rateImg=wh/ht;if(rateImg<=1){$el.parent().removeClass(horizontalImageClass);$el.parent().addClass(verticalImageClass)}else{$el.parent().removeClass(verticalImageClass);$el.parent().addClass(horizontalImageClass)}}function setThumbAttr($frame,value,searchAttr){var attr=searchAttr;if(!$frame.attr(attr)&&$frame.attr(attr)!==undefined){$frame.attr(attr,value)}if($frame.find("["+attr+"]").length){$frame.find("["+attr+"]").each(function(){$(this).attr(attr,value)})}}function isExpectedCaption(frameItem,isExpected,undefined){var expected=false,frameExpected;frameItem.showCaption===undefined||frameItem.showCaption===true?frameExpected=true:frameExpected=false;if(!isExpected){return false}if(frameItem.caption&&frameExpected){expected=true}return expected}return{setRatio:setRatioClass,setThumbAttr:setThumbAttr,isExpectedCaption:isExpectedCaption}}(UTIL||{},jQuery);function slide($el,options){var elData=$el.data(),elPos=Math.round(options.pos),onEndFn=function(){if(elData&&elData.sliding){elData.sliding=false}(options.onEnd||noop)()};if(typeof options.overPos!=="undefined"&&options.overPos!==options.pos){elPos=options.overPos}var translate=$.extend(getTranslate(elPos,options.direction),options.width&&{width:options.width},options.height&&{height:options.height});if(elData&&elData.sliding){elData.sliding=true}if(CSS3){$el.css($.extend(getDuration(options.time),translate));if(options.time>10){afterTransition($el,"transform",onEndFn,options.time)}else{onEndFn()}}else{$el.stop().animate(translate,options.time,BEZIER,onEndFn)}}function fade($el1,$el2,$frames,options,fadeStack,chain){var chainedFLAG=typeof chain!=="undefined";if(!chainedFLAG){fadeStack.push(arguments);Array.prototype.push.call(arguments,fadeStack.length);if(fadeStack.length>1)return}$el1=$el1||$($el1);$el2=$el2||$($el2);var _$el1=$el1[0],_$el2=$el2[0],crossfadeFLAG=options.method==="crossfade",onEndFn=function(){if(!onEndFn.done){onEndFn.done=true;var args=(chainedFLAG||fadeStack.shift())&&fadeStack.shift();args&&fade.apply(this,args);(options.onEnd||noop)(!!args)}},time=options.time/(chain||1);$frames.removeClass(fadeRearClass+" "+fadeFrontClass);$el1.stop().addClass(fadeRearClass);$el2.stop().addClass(fadeFrontClass);crossfadeFLAG&&_$el2&&$el1.fadeTo(0,0);$el1.fadeTo(crossfadeFLAG?time:0,1,crossfadeFLAG&&onEndFn);$el2.fadeTo(time,0,onEndFn);_$el1&&crossfadeFLAG||_$el2||onEndFn()}var lastEvent,moveEventType,preventEvent,preventEventTimeout,dragDomEl;function extendEvent(e){var touch=(e.touches||[])[0]||e;e._x=touch.pageX||touch.originalEvent.pageX;e._y=touch.clientY||touch.originalEvent.clientY;e._now=$.now()}function touch($el,options){var el=$el[0],tail={},touchEnabledFLAG,startEvent,$target,controlTouch,touchFLAG,targetIsSelectFLAG,targetIsLinkFlag,tolerance,moved;function onStart(e){$target=$(e.target);tail.checked=targetIsSelectFLAG=targetIsLinkFlag=moved=false;if(touchEnabledFLAG||tail.flow||e.touches&&e.touches.length>1||e.which>1||lastEvent&&lastEvent.type!==e.type&&preventEvent||(targetIsSelectFLAG=options.select&&$target.is(options.select,el)))return targetIsSelectFLAG;touchFLAG=e.type==="touchstart";targetIsLinkFlag=$target.is("a, a *",el);controlTouch=tail.control;tolerance=tail.noMove||tail.noSwipe||controlTouch?16:!tail.snap?4:0;extendEvent(e);startEvent=lastEvent=e;moveEventType=e.type.replace(/down|start/,"move").replace(/Down/,"Move");(options.onStart||noop).call(el,e,{control:controlTouch,$target:$target});touchEnabledFLAG=tail.flow=true;if(!touchFLAG||tail.go)stopEvent(e)}function onMove(e){if(e.touches&&e.touches.length>1||MS_POINTER&&!e.isPrimary||moveEventType!==e.type||!touchEnabledFLAG){touchEnabledFLAG&&onEnd();(options.onTouchEnd||noop)();return}extendEvent(e);var xDiff=Math.abs(e._x-startEvent._x),yDiff=Math.abs(e._y-startEvent._y),xyDiff=xDiff-yDiff,xWin=(tail.go||tail.x||xyDiff>=0)&&!tail.noSwipe,yWin=xyDiff<0;if(touchFLAG&&!tail.checked){if(touchEnabledFLAG=xWin){stopEvent(e)}}else{stopEvent(e);if(movedEnough(xDiff,yDiff)){(options.onMove||noop).call(el,e,{touch:touchFLAG})}}if(!moved&&movedEnough(xDiff,yDiff)&&Math.sqrt(Math.pow(xDiff,2)+Math.pow(yDiff,2))>tolerance){moved=true}tail.checked=tail.checked||xWin||yWin}function movedEnough(xDiff,yDiff){return xDiff>yDiff&&xDiff>1.5}function onEnd(e){(options.onTouchEnd||noop)();var _touchEnabledFLAG=touchEnabledFLAG;tail.control=touchEnabledFLAG=false;if(_touchEnabledFLAG){tail.flow=false}if(!_touchEnabledFLAG||targetIsLinkFlag&&!tail.checked)return;e&&stopEvent(e);preventEvent=true;clearTimeout(preventEventTimeout);preventEventTimeout=setTimeout(function(){preventEvent=false},1e3);(options.onEnd||noop).call(el,{moved:moved,$target:$target,control:controlTouch,touch:touchFLAG,startEvent:startEvent,aborted:!e||e.type==="MSPointerCancel"})}function onOtherStart(){if(tail.flow)return;tail.flow=true}function onOtherEnd(){if(!tail.flow)return;tail.flow=false}if(MS_POINTER){addEvent(el,"MSPointerDown",onStart);addEvent(document,"MSPointerMove",onMove);addEvent(document,"MSPointerCancel",onEnd);addEvent(document,"MSPointerUp",onEnd)}else{addEvent(el,"touchstart",onStart);addEvent(el,"touchmove",onMove);addEvent(el,"touchend",onEnd);addEvent(document,"touchstart",onOtherStart);addEvent(document,"touchend",onOtherEnd);addEvent(document,"touchcancel",onOtherEnd);$WINDOW.on("scroll",onOtherEnd);$el.on("mousedown pointerdown",onStart);$DOCUMENT.on("mousemove pointermove",onMove).on("mouseup pointerup",onEnd)}if(Modernizr.touch){dragDomEl="a"}else{dragDomEl="div"}$el.on("click",dragDomEl,function(e){tail.checked&&stopEvent(e)});return tail}function moveOnTouch($el,options){var el=$el[0],elData=$el.data(),tail={},startCoo,coo,startElPos,moveElPos,edge,moveTrack,startTime,endTime,min,max,snap,dir,slowFLAG,controlFLAG,moved,tracked;function startTracking(e,noStop){tracked=true;startCoo=coo=dir==="vertical"?e._y:e._x;startTime=e._now;moveTrack=[[startTime,startCoo]];startElPos=moveElPos=tail.noMove||noStop?0:stop($el,(options.getPos||noop)());(options.onStart||noop).call(el,e)}function onStart(e,result){min=tail.min;max=tail.max;snap=tail.snap,dir=tail.direction||"horizontal",$el.navdir=dir;slowFLAG=e.altKey;tracked=moved=false;controlFLAG=result.control;if(!controlFLAG&&!elData.sliding){startTracking(e)}}function onMove(e,result){if(!tail.noSwipe){if(!tracked){startTracking(e)}coo=dir==="vertical"?e._y:e._x;moveTrack.push([e._now,coo]);moveElPos=startElPos-(startCoo-coo);edge=findShadowEdge(moveElPos,min,max,dir);if(moveElPos<=min){moveElPos=edgeResistance(moveElPos,min)}else if(moveElPos>=max){moveElPos=edgeResistance(moveElPos,max)}if(!tail.noMove){$el.css(getTranslate(moveElPos,dir));if(!moved){moved=true;result.touch||MS_POINTER||$el.addClass(grabbingClass)}(options.onMove||noop).call(el,e,{pos:moveElPos,edge:edge})}}}function onEnd(result){if(tail.noSwipe&&result.moved)return;if(!tracked){startTracking(result.startEvent,true)}result.touch||MS_POINTER||$el.removeClass(grabbingClass);endTime=$.now();var _backTimeIdeal=endTime-TOUCH_TIMEOUT,_backTime,_timeDiff,_timeDiffLast,backTime=null,backCoo,virtualPos,limitPos,newPos,overPos,time=TRANSITION_DURATION,speed,friction=options.friction;for(var _i=moveTrack.length-1;_i>=0;_i--){_backTime=moveTrack[_i][0];_timeDiff=Math.abs(_backTime-_backTimeIdeal);if(backTime===null||_timeDiff<_timeDiffLast){backTime=_backTime;backCoo=moveTrack[_i][1]}else if(backTime===_backTimeIdeal||_timeDiff>_timeDiffLast){break}_timeDiffLast=_timeDiff}newPos=minMaxLimit(moveElPos,min,max);var cooDiff=backCoo-coo,forwardFLAG=cooDiff>=0,timeDiff=endTime-backTime,longTouchFLAG=timeDiff>TOUCH_TIMEOUT,swipeFLAG=!longTouchFLAG&&moveElPos!==startElPos&&newPos===moveElPos;if(snap){newPos=minMaxLimit(Math[swipeFLAG?forwardFLAG?"floor":"ceil":"round"](moveElPos/snap)*snap,min,max);min=max=newPos}if(swipeFLAG&&(snap||newPos===moveElPos)){speed=-(cooDiff/timeDiff);time*=minMaxLimit(Math.abs(speed),options.timeLow,options.timeHigh);virtualPos=Math.round(moveElPos+speed*time/friction);if(!snap){newPos=virtualPos}if(!forwardFLAG&&virtualPos>max||forwardFLAG&&virtualPos<min){limitPos=forwardFLAG?min:max;overPos=virtualPos-limitPos;if(!snap){newPos=limitPos}overPos=minMaxLimit(newPos+overPos*.03,limitPos-50,limitPos+50);time=Math.abs((moveElPos-overPos)/(speed/friction))}}time*=slowFLAG?10:1;(options.onEnd||noop).call(el,$.extend(result,{moved:result.moved||longTouchFLAG&&snap,pos:moveElPos,newPos:newPos,overPos:overPos,time:time,dir:dir}))}tail=$.extend(touch(options.$wrap,$.extend({},options,{onStart:onStart,onMove:onMove,onEnd:onEnd})),tail);return tail}function wheel($el,options){var el=$el[0],lockFLAG,lastDirection,lastNow,tail={prevent:{}};addEvent(el,WHEEL,function(e){var yDelta=e.wheelDeltaY||-1*e.deltaY||0,xDelta=e.wheelDeltaX||-1*e.deltaX||0,xWin=Math.abs(xDelta)&&!Math.abs(yDelta),direction=getDirectionSign(xDelta<0),sameDirection=lastDirection===direction,now=$.now(),tooFast=now-lastNow<TOUCH_TIMEOUT;lastDirection=direction;lastNow=now;if(!xWin||!tail.ok||tail.prevent[direction]&&!lockFLAG){return}else{stopEvent(e,true);if(lockFLAG&&sameDirection&&tooFast){return}}if(options.shift){lockFLAG=true;clearTimeout(tail.t);tail.t=setTimeout(function(){lockFLAG=false},SCROLL_LOCK_TIMEOUT)}(options.onEnd||noop)(e,options.shift?direction:xDelta)});return tail}jQuery.Fotorama=function($fotorama,opts){$HTML=$("html");$BODY=$("body");var that=this,stamp=$.now(),stampClass=_fotoramaClass+stamp,fotorama=$fotorama[0],data,dataFrameCount=1,fotoramaData=$fotorama.data(),size,$style=$("<style></style>"),$anchor=$(div(hiddenClass)),$wrap=$fotorama.find(cls(wrapClass)),$stage=$wrap.find(cls(stageClass)),stage=$stage[0],$stageShaft=$fotorama.find(cls(stageShaftClass)),$stageFrame=$(),$arrPrev=$fotorama.find(cls(arrPrevClass)),$arrNext=$fotorama.find(cls(arrNextClass)),$arrs=$fotorama.find(cls(arrClass)),$navWrap=$fotorama.find(cls(navWrapClass)),$nav=$navWrap.find(cls(navClass)),$navShaft=$nav.find(cls(navShaftClass)),$navFrame,$navDotFrame=$(),$navThumbFrame=$(),stageShaftData=$stageShaft.data(),navShaftData=$navShaft.data(),$thumbBorder=$fotorama.find(cls(thumbBorderClass)),$thumbArrLeft=$fotorama.find(cls(thumbArrLeft)),$thumbArrRight=$fotorama.find(cls(thumbArrRight)),$fullscreenIcon=$fotorama.find(cls(fullscreenIconClass)),fullscreenIcon=$fullscreenIcon[0],$videoPlay=$(div(videoPlayClass)),$videoClose=$fotorama.find(cls(videoCloseClass)),videoClose=$videoClose[0],$spinner=$fotorama.find(cls(fotoramaSpinnerClass)),$videoPlaying,activeIndex=false,activeFrame,activeIndexes,repositionIndex,dirtyIndex,lastActiveIndex,prevIndex,nextIndex,nextAutoplayIndex,startIndex,o_loop,o_nav,o_navThumbs,o_navTop,o_allowFullScreen,o_nativeFullScreen,o_fade,o_thumbSide,o_thumbSide2,o_transitionDuration,o_transition,o_shadows,o_rtl,o_keyboard,lastOptions={},measures={},measuresSetFLAG,stageShaftTouchTail={},stageWheelTail={},navShaftTouchTail={},navWheelTail={},scrollTop,scrollLeft,showedFLAG,pausedAutoplayFLAG,stoppedAutoplayFLAG,toDeactivate={},toDetach={},measuresStash,touchedFLAG,hoverFLAG,navFrameKey,stageLeft=0,fadeStack=[];$wrap[STAGE_FRAME_KEY]=$('<div class="'+stageFrameClass+'"></div>');$wrap[NAV_THUMB_FRAME_KEY]=$($.Fotorama.jst.thumb());$wrap[NAV_DOT_FRAME_KEY]=$($.Fotorama.jst.dots());toDeactivate[STAGE_FRAME_KEY]=[];toDeactivate[NAV_THUMB_FRAME_KEY]=[];toDeactivate[NAV_DOT_FRAME_KEY]=[];toDetach[STAGE_FRAME_KEY]={};$wrap.addClass(CSS3?wrapCss3Class:wrapCss2Class);fotoramaData.fotorama=this;function checkForVideo(){$.each(data,function(i,dataFrame){if(!dataFrame.i){dataFrame.i=dataFrameCount++;var video=findVideoId(dataFrame.video,true);if(video){var thumbs={};dataFrame.video=video;if(!dataFrame.img&&!dataFrame.thumb){thumbs=getVideoThumbs(dataFrame,data,that)}else{dataFrame.thumbsReady=true}updateData(data,{img:thumbs.img,thumb:thumbs.thumb},dataFrame.i,that)}}})}function allowKey(key){return o_keyboard[key]}function setStagePosition(){if($stage!==undefined){if(opts.navdir=="vertical"){var padding=opts.thumbwidth+opts.thumbmargin;$stage.css("left",padding);$arrNext.css("right",padding);$fullscreenIcon.css("right",padding);$wrap.css("width",$wrap.css("width")+padding);$stageShaft.css("max-width",$wrap.width()-padding)}else{$stage.css("left","");$arrNext.css("right","");$fullscreenIcon.css("right","");$wrap.css("width",$wrap.css("width")+padding);$stageShaft.css("max-width","")}}}function bindGlobalEvents(FLAG){var keydownCommon="keydown."+_fotoramaClass,localStamp=_fotoramaClass+stamp,keydownLocal="keydown."+localStamp,keyupLocal="keyup."+localStamp,resizeLocal="resize."+localStamp+" "+"orientationchange."+localStamp,showParams;if(FLAG){$DOCUMENT.on(keydownLocal,function(e){var catched,index;if($videoPlaying&&e.keyCode===27){catched=true;unloadVideo($videoPlaying,true,true)}else if(that.fullScreen||opts.keyboard&&!that.index){if(e.keyCode===27){catched=true;that.cancelFullScreen()}else if(e.shiftKey&&e.keyCode===32&&allowKey("space")||!e.altKey&&!e.metaKey&&e.keyCode===37&&allowKey("left")||e.keyCode===38&&allowKey("up")&&$(":focus").attr("data-gallery-role")){that.longPress.progress();index="<"}else if(e.keyCode===32&&allowKey("space")||!e.altKey&&!e.metaKey&&e.keyCode===39&&allowKey("right")||e.keyCode===40&&allowKey("down")&&$(":focus").attr("data-gallery-role")){that.longPress.progress();index=">"}else if(e.keyCode===36&&allowKey("home")){that.longPress.progress();index="<<"}else if(e.keyCode===35&&allowKey("end")){that.longPress.progress();index=">>"}}(catched||index)&&stopEvent(e);showParams={index:index,slow:e.altKey,user:true};index&&(that.longPress.inProgress?that.showWhileLongPress(showParams):that.show(showParams))});if(FLAG){$DOCUMENT.on(keyupLocal,function(e){if(that.longPress.inProgress){that.showEndLongPress({user:true})}that.longPress.reset()})}if(!that.index){$DOCUMENT.off(keydownCommon).on(keydownCommon,"textarea, input, select",function(e){!$BODY.hasClass(_fullscreenClass)&&e.stopPropagation()})}$WINDOW.on(resizeLocal,that.resize)}else{$DOCUMENT.off(keydownLocal);$WINDOW.off(resizeLocal)}}function appendElements(FLAG){if(FLAG===appendElements.f)return;if(FLAG){$fotorama.addClass(_fotoramaClass+" "+stampClass).before($anchor).before($style);addInstance(that)}else{$anchor.detach();$style.detach();$fotorama.html(fotoramaData.urtext).removeClass(stampClass);hideInstance(that)}bindGlobalEvents(FLAG);appendElements.f=FLAG}function setData(){data=that.data=data||clone(opts.data)||getDataFromHtml($fotorama);size=that.size=data.length;ready.ok&&opts.shuffle&&shuffle(data);checkForVideo();activeIndex=limitIndex(activeIndex);size&&appendElements(true)}function stageNoMove(){var _noMove=size<2||$videoPlaying;stageShaftTouchTail.noMove=_noMove||o_fade;stageShaftTouchTail.noSwipe=_noMove||!opts.swipe;!o_transition&&$stageShaft.toggleClass(grabClass,!opts.click&&!stageShaftTouchTail.noMove&&!stageShaftTouchTail.noSwipe);MS_POINTER&&$wrap.toggleClass(wrapPanYClass,!stageShaftTouchTail.noSwipe)}function setAutoplayInterval(interval){if(interval===true)interval="";opts.autoplay=Math.max(+interval||AUTOPLAY_INTERVAL,o_transitionDuration*1.5)}function updateThumbArrow(opt){if(opt.navarrows&&opt.nav==="thumbs"){$thumbArrLeft.show();$thumbArrRight.show()}else{$thumbArrLeft.hide();$thumbArrRight.hide()}}function getThumbsInSlide($el,opts){return Math.floor($wrap.width()/(opts.thumbwidth+opts.thumbmargin))}function setOptions(){if(!opts.nav||opts.nav==="dots"){opts.navdir="horizontal"}that.options=opts=optionsToLowerCase(opts);thumbsPerSlide=getThumbsInSlide($wrap,opts);o_fade=opts.transition==="crossfade"||opts.transition==="dissolve";o_loop=opts.loop&&(size>2||o_fade&&(!o_transition||o_transition!=="slide"));o_transitionDuration=+opts.transitionduration||TRANSITION_DURATION;o_rtl=opts.direction==="rtl";o_keyboard=$.extend({},opts.keyboard&&KEYBOARD_OPTIONS,opts.keyboard);updateThumbArrow(opts);var classes={add:[],remove:[]};function addOrRemoveClass(FLAG,value){classes[FLAG?"add":"remove"].push(value)}if(size>1){o_nav=opts.nav;o_navTop=opts.navposition==="top";classes.remove.push(selectClass);$arrs.toggle(!!opts.arrows)}else{o_nav=false;$arrs.hide()}arrsUpdate();stageWheelUpdate();thumbArrUpdate();if(opts.autoplay)setAutoplayInterval(opts.autoplay);o_thumbSide=numberFromMeasure(opts.thumbwidth)||THUMB_SIZE;o_thumbSide2=numberFromMeasure(opts.thumbheight)||THUMB_SIZE;stageWheelTail.ok=navWheelTail.ok=opts.trackpad&&!SLOW;stageNoMove();extendMeasures(opts,[measures]);o_navThumbs=o_nav==="thumbs";if($navWrap.filter(":hidden")&&!!o_nav){$navWrap.show()}if(o_navThumbs){frameDraw(size,"navThumb");$navFrame=$navThumbFrame;navFrameKey=NAV_THUMB_FRAME_KEY;setStyle($style,$.Fotorama.jst.style({w:o_thumbSide,h:o_thumbSide2,b:opts.thumbborderwidth,m:opts.thumbmargin,s:stamp,q:!COMPAT}));$nav.addClass(navThumbsClass).removeClass(navDotsClass)}else if(o_nav==="dots"){frameDraw(size,"navDot");$navFrame=$navDotFrame;navFrameKey=NAV_DOT_FRAME_KEY;$nav.addClass(navDotsClass).removeClass(navThumbsClass)}else{$navWrap.hide();o_nav=false;$nav.removeClass(navThumbsClass+" "+navDotsClass)}if(o_nav){if(o_navTop){$navWrap.insertBefore($stage)}else{$navWrap.insertAfter($stage)}frameAppend.nav=false;frameAppend($navFrame,$navShaft,"nav")}o_allowFullScreen=opts.allowfullscreen;if(o_allowFullScreen){$fullscreenIcon.prependTo($stage);o_nativeFullScreen=FULLSCREEN&&o_allowFullScreen==="native";stubEvent($fullscreenIcon,"touchend")}else{$fullscreenIcon.detach();o_nativeFullScreen=false}addOrRemoveClass(o_fade,wrapFadeClass);addOrRemoveClass(!o_fade,wrapSlideClass);addOrRemoveClass(!opts.captions,wrapNoCaptionsClass);addOrRemoveClass(o_rtl,wrapRtlClass);addOrRemoveClass(opts.arrows,wrapToggleArrowsClass);o_shadows=opts.shadows&&!SLOW;addOrRemoveClass(!o_shadows,wrapNoShadowsClass);$wrap.addClass(classes.add.join(" ")).removeClass(classes.remove.join(" "));lastOptions=$.extend({},opts);setStagePosition()}function normalizeIndex(index){return index<0?(size+index%size)%size:index>=size?index%size:index}function limitIndex(index){return minMaxLimit(index,0,size-1)}function edgeIndex(index){return o_loop?normalizeIndex(index):limitIndex(index)}function getPrevIndex(index){return index>0||o_loop?index-1:false}function getNextIndex(index){return index<size-1||o_loop?index+1:false}function setStageShaftMinmaxAndSnap(){stageShaftTouchTail.min=o_loop?-Infinity:-getPosByIndex(size-1,measures.w,opts.margin,repositionIndex);stageShaftTouchTail.max=o_loop?Infinity:-getPosByIndex(0,measures.w,opts.margin,repositionIndex);stageShaftTouchTail.snap=measures.w+opts.margin}function setNavShaftMinMax(){var isVerticalDir=opts.navdir==="vertical";var param=isVerticalDir?$navShaft.height():$navShaft.width();var mainParam=isVerticalDir?measures.h:measures.nw;navShaftTouchTail.min=Math.min(0,mainParam-param);navShaftTouchTail.max=0;navShaftTouchTail.direction=opts.navdir;$navShaft.toggleClass(grabClass,!(navShaftTouchTail.noMove=navShaftTouchTail.min===navShaftTouchTail.max))}function eachIndex(indexes,type,fn){if(typeof indexes==="number"){indexes=new Array(indexes);var rangeFLAG=true}return $.each(indexes,function(i,index){if(rangeFLAG)index=i;if(typeof index==="number"){var dataFrame=data[normalizeIndex(index)];if(dataFrame){var key="$"+type+"Frame",$frame=dataFrame[key];fn.call(this,i,index,dataFrame,$frame,key,$frame&&$frame.data())}}})}function setMeasures(width,height,ratio,index){if(!measuresSetFLAG||measuresSetFLAG==="*"&&index===startIndex){width=measureIsValid(opts.width)||measureIsValid(width)||WIDTH;height=measureIsValid(opts.height)||measureIsValid(height)||HEIGHT;that.resize({width:width,ratio:opts.ratio||ratio||width/height},0,index!==startIndex&&"*")}}function loadImg(indexes,type,specialMeasures,again){eachIndex(indexes,type,function(i,index,dataFrame,$frame,key,frameData){if(!$frame)return;var fullFLAG=that.fullScreen&&!frameData.$full&&type==="stage";if(frameData.$img&&!again&&!fullFLAG)return;var img=new Image,$img=$(img),imgData=$img.data();frameData[fullFLAG?"$full":"$img"]=$img;var srcKey=type==="stage"?fullFLAG?"full":"img":"thumb",src=dataFrame[srcKey],dummy=fullFLAG?dataFrame["img"]:dataFrame[type==="stage"?"thumb":"img"];if(type==="navThumb")$frame=frameData.$wrap;function triggerTriggerEvent(event){var _index=normalizeIndex(index);triggerEvent(event,{index:_index,src:src,frame:data[_index]})}function error(){$img.remove();$.Fotorama.cache[src]="error";if((!dataFrame.html||type!=="stage")&&dummy&&dummy!==src){dataFrame[srcKey]=src=dummy;frameData.$full=null;loadImg([index],type,specialMeasures,true)}else{if(src&&!dataFrame.html&&!fullFLAG){$frame.trigger("f:error").removeClass(loadingClass).addClass(errorClass);triggerTriggerEvent("error")}else if(type==="stage"){$frame.trigger("f:load").removeClass(loadingClass+" "+errorClass).addClass(loadedClass);triggerTriggerEvent("load");setMeasures()}frameData.state="error";if(size>1&&data[index]===dataFrame&&!dataFrame.html&&!dataFrame.deleted&&!dataFrame.video&&!fullFLAG){dataFrame.deleted=true;that.splice(index,1)}}}function loaded(){$.Fotorama.measures[src]=imgData.measures=$.Fotorama.measures[src]||{width:img.width,height:img.height,ratio:img.width/img.height};setMeasures(imgData.measures.width,imgData.measures.height,imgData.measures.ratio,index);$img.off("load error").addClass(""+(fullFLAG?imgFullClass:imgClass)).attr("aria-hidden","false").prependTo($frame);if($frame.hasClass(stageFrameClass)&&!$frame.hasClass(videoContainerClass)){$frame.attr("href",$img.attr("src"))}fit($img,($.isFunction(specialMeasures)?specialMeasures():specialMeasures)||measures);$.Fotorama.cache[src]=frameData.state="loaded";setTimeout(function(){$frame.trigger("f:load").removeClass(loadingClass+" "+errorClass).addClass(loadedClass+" "+(fullFLAG?loadedFullClass:loadedImgClass));if(type==="stage"){triggerTriggerEvent("load")}else if(dataFrame.thumbratio===AUTO||!dataFrame.thumbratio&&opts.thumbratio===AUTO){dataFrame.thumbratio=imgData.measures.ratio;reset()}},0)}if(!src){error();return}function waitAndLoad(){var _i=10;waitFor(function(){return!touchedFLAG||!_i--&&!SLOW},function(){loaded()})}if(!$.Fotorama.cache[src]){$.Fotorama.cache[src]="*";$img.on("load",waitAndLoad).on("error",error)}else{(function justWait(){if($.Fotorama.cache[src]==="error"){error()}else if($.Fotorama.cache[src]==="loaded"){setTimeout(waitAndLoad,0)}else{setTimeout(justWait,100)}})()}frameData.state="";img.src=src;if(frameData.data.caption){img.alt=frameData.data.caption||""}if(frameData.data.full){$(img).data("original",frameData.data.full)}if(UTIL.isExpectedCaption(dataFrame,opts.showcaption)){$(img).attr("aria-labelledby",dataFrame.labelledby)}})}function updateFotoramaState(){var $frame=activeFrame[STAGE_FRAME_KEY];if($frame&&!$frame.data().state){$spinner.addClass(spinnerShowClass);$frame.on("f:load f:error",function(){$frame.off("f:load f:error");$spinner.removeClass(spinnerShowClass)})}}function addNavFrameEvents(frame){addEnterUp(frame,onNavFrameClick);addFocus(frame,function(){setTimeout(function(){lockScroll($nav)},0);slideNavShaft({time:o_transitionDuration,guessIndex:$(this).data().eq,minMax:navShaftTouchTail})})}function frameDraw(indexes,type){eachIndex(indexes,type,function(i,index,dataFrame,$frame,key,frameData){if($frame)return;$frame=dataFrame[key]=$wrap[key].clone();frameData=$frame.data();frameData.data=dataFrame;var frame=$frame[0],labelledbyValue="labelledby"+$.now();if(type==="stage"){if(dataFrame.html){$('<div class="'+htmlClass+'"></div>').append(dataFrame._html?$(dataFrame.html).removeAttr("id").html(dataFrame._html):dataFrame.html).appendTo($frame)}if(dataFrame.id){labelledbyValue=dataFrame.id||labelledbyValue}dataFrame.labelledby=labelledbyValue;if(UTIL.isExpectedCaption(dataFrame,opts.showcaption)){$($.Fotorama.jst.frameCaption({caption:dataFrame.caption,labelledby:labelledbyValue})).appendTo($frame)}dataFrame.video&&$frame.addClass(stageFrameVideoClass).append($videoPlay.clone());addFocus(frame,function(){setTimeout(function(){lockScroll($stage)},0);clickToShow({index:frameData.eq,user:true})});$stageFrame=$stageFrame.add($frame)}else if(type==="navDot"){addNavFrameEvents(frame);$navDotFrame=$navDotFrame.add($frame)}else if(type==="navThumb"){addNavFrameEvents(frame);frameData.$wrap=$frame.children(":first");$navThumbFrame=$navThumbFrame.add($frame);if(dataFrame.video){frameData.$wrap.append($videoPlay.clone())}}})}function callFit($img,measuresToFit){return $img&&$img.length&&fit($img,measuresToFit)}function stageFramePosition(indexes){eachIndex(indexes,"stage",function(i,index,dataFrame,$frame,key,frameData){if(!$frame)return;var normalizedIndex=normalizeIndex(index);frameData.eq=normalizedIndex;toDetach[STAGE_FRAME_KEY][normalizedIndex]=$frame.css($.extend({left:o_fade?0:getPosByIndex(index,measures.w,opts.margin,repositionIndex)},o_fade&&getDuration(0)));if(isDetached($frame[0])){$frame.appendTo($stageShaft);unloadVideo(dataFrame.$video)}callFit(frameData.$img,measures);callFit(frameData.$full,measures);if($frame.hasClass(stageFrameClass)&&!($frame.attr("aria-hidden")==="false"&&$frame.hasClass(activeClass))){$frame.attr("aria-hidden","true")}})}function thumbsDraw(pos,loadFLAG){var leftLimit,rightLimit,exceedLimit;if(o_nav!=="thumbs"||isNaN(pos))return;leftLimit=-pos;rightLimit=-pos+measures.nw;if(opts.navdir==="vertical"){pos=pos-opts.thumbheight;rightLimit=-pos+measures.h}$navThumbFrame.each(function(){var $this=$(this),thisData=$this.data(),eq=thisData.eq,getSpecialMeasures=function(){return{h:o_thumbSide2,w:thisData.w}},specialMeasures=getSpecialMeasures(),exceedLimit=opts.navdir==="vertical"?thisData.t>rightLimit:thisData.l>rightLimit;specialMeasures.w=thisData.w;if(thisData.l+thisData.w<leftLimit||exceedLimit||callFit(thisData.$img,specialMeasures))return;loadFLAG&&loadImg([eq],"navThumb",getSpecialMeasures)})}function frameAppend($frames,$shaft,type){if(!frameAppend[type]){var thumbsFLAG=type==="nav"&&o_navThumbs,left=0,top=0;$shaft.append($frames.filter(function(){var actual,$this=$(this),frameData=$this.data();for(var _i=0,_l=data.length;_i<_l;_i++){if(frameData.data===data[_i]){actual=true;frameData.eq=_i;break}}return actual||$this.remove()&&false}).sort(function(a,b){return $(a).data().eq-$(b).data().eq}).each(function(){var $this=$(this),frameData=$this.data();UTIL.setThumbAttr($this,frameData.data.caption,"aria-label")}).each(function(){if(!thumbsFLAG)return;var $this=$(this),frameData=$this.data(),thumbwidth=Math.round(o_thumbSide2*frameData.data.thumbratio)||o_thumbSide,thumbheight=Math.round(o_thumbSide/frameData.data.thumbratio)||o_thumbSide2;frameData.t=top;frameData.h=thumbheight;frameData.l=left;frameData.w=thumbwidth;$this.css({width:thumbwidth});top+=thumbheight+opts.thumbmargin;left+=thumbwidth+opts.thumbmargin}));frameAppend[type]=true}}function getDirection(x){return x-stageLeft>measures.w/3}function disableDirrection(i){return!o_loop&&(!(activeIndex+i)||!(activeIndex-size+i))&&!$videoPlaying}function arrsUpdate(){var disablePrev=disableDirrection(0),disableNext=disableDirrection(1);$arrPrev.toggleClass(arrDisabledClass,disablePrev).attr(disableAttr(disablePrev,false));$arrNext.toggleClass(arrDisabledClass,disableNext).attr(disableAttr(disableNext,false))}function thumbArrUpdate(){var isLeftDisable=false,isRightDisable=false;if(opts.navtype==="thumbs"&&!opts.loop){activeIndex==0?isLeftDisable=true:isLeftDisable=false;activeIndex==opts.data.length-1?isRightDisable=true:isRightDisable=false}if(opts.navtype==="slides"){var pos=readPosition($navShaft,opts.navdir);pos>=navShaftTouchTail.max?isLeftDisable=true:isLeftDisable=false;pos<=navShaftTouchTail.min?isRightDisable=true:isRightDisable=false}$thumbArrLeft.toggleClass(arrDisabledClass,isLeftDisable).attr(disableAttr(isLeftDisable,true));$thumbArrRight.toggleClass(arrDisabledClass,isRightDisable).attr(disableAttr(isRightDisable,true))}function stageWheelUpdate(){if(stageWheelTail.ok){stageWheelTail.prevent={"<":disableDirrection(0),">":disableDirrection(1)}}}function getNavFrameBounds($navFrame){var navFrameData=$navFrame.data(),left,top,width,height;if(o_navThumbs){left=navFrameData.l;top=navFrameData.t;width=navFrameData.w;height=navFrameData.h}else{left=$navFrame.position().left;width=$navFrame.width()}var horizontalBounds={c:left+width/2,min:-left+opts.thumbmargin*10,max:-left+measures.w-width-opts.thumbmargin*10};var verticalBounds={c:top+height/2,min:-top+opts.thumbmargin*10,max:-top+measures.h-height-opts.thumbmargin*10};return opts.navdir==="vertical"?verticalBounds:horizontalBounds}function slideThumbBorder(time){var navFrameData=activeFrame[navFrameKey].data();slide($thumbBorder,{time:time*1.2,pos:opts.navdir==="vertical"?navFrameData.t:navFrameData.l,width:navFrameData.w,height:navFrameData.h,direction:opts.navdir})}function slideNavShaft(options){var $guessNavFrame=data[options.guessIndex][navFrameKey],typeOfAnimation=opts.navtype;var overflowFLAG,time,minMax,boundTop,boundLeft,l,pos,x;if($guessNavFrame){if(typeOfAnimation==="thumbs"){overflowFLAG=navShaftTouchTail.min!==navShaftTouchTail.max;minMax=options.minMax||overflowFLAG&&getNavFrameBounds(activeFrame[navFrameKey]);boundTop=overflowFLAG&&(options.keep&&slideNavShaft.t?slideNavShaft.l:minMaxLimit((options.coo||measures.nw/2)-getNavFrameBounds($guessNavFrame).c,minMax.min,minMax.max));boundLeft=overflowFLAG&&(options.keep&&slideNavShaft.l?slideNavShaft.l:minMaxLimit((options.coo||measures.nw/2)-getNavFrameBounds($guessNavFrame).c,minMax.min,minMax.max));l=opts.navdir==="vertical"?boundTop:boundLeft;pos=overflowFLAG&&minMaxLimit(l,navShaftTouchTail.min,navShaftTouchTail.max)||0;time=options.time*1.1;slide($navShaft,{time:time,pos:pos,direction:opts.navdir,onEnd:function(){thumbsDraw(pos,true);thumbArrUpdate()}});setShadow($nav,findShadowEdge(pos,navShaftTouchTail.min,navShaftTouchTail.max,opts.navdir));slideNavShaft.l=l}else{x=readPosition($navShaft,opts.navdir);time=options.time*1.11;pos=validateSlidePos(opts,navShaftTouchTail,options.guessIndex,x,$guessNavFrame,$navWrap,opts.navdir);slide($navShaft,{time:time,pos:pos,direction:opts.navdir,onEnd:function(){thumbsDraw(pos,true);thumbArrUpdate()}});setShadow($nav,findShadowEdge(pos,navShaftTouchTail.min,navShaftTouchTail.max,opts.navdir))}}}function navUpdate(){deactivateFrames(navFrameKey);toDeactivate[navFrameKey].push(activeFrame[navFrameKey].addClass(activeClass).attr("data-active",true))}function deactivateFrames(key){var _toDeactivate=toDeactivate[key];while(_toDeactivate.length){_toDeactivate.shift().removeClass(activeClass).attr("data-active",false)}}function detachFrames(key){var _toDetach=toDetach[key];$.each(activeIndexes,function(i,index){delete _toDetach[normalizeIndex(index)]});$.each(_toDetach,function(index,$frame){delete _toDetach[index];$frame.detach()})}function stageShaftReposition(skipOnEnd){repositionIndex=dirtyIndex=activeIndex;var $frame=activeFrame[STAGE_FRAME_KEY];if($frame){deactivateFrames(STAGE_FRAME_KEY);toDeactivate[STAGE_FRAME_KEY].push($frame.addClass(activeClass).attr("data-active",true));if($frame.hasClass(stageFrameClass)){$frame.attr("aria-hidden","false")}skipOnEnd||that.showStage.onEnd(true);stop($stageShaft,0,true);detachFrames(STAGE_FRAME_KEY);stageFramePosition(activeIndexes);setStageShaftMinmaxAndSnap();setNavShaftMinMax();addEnterUp($stageShaft[0],function(){if(!$fotorama.hasClass(fullscreenClass)){that.requestFullScreen();$fullscreenIcon.focus()}})}}function extendMeasures(options,measuresArray){if(!options)return;$.each(measuresArray,function(i,measures){if(!measures)return;$.extend(measures,{width:options.width||measures.width,height:options.height,minwidth:options.minwidth,maxwidth:options.maxwidth,minheight:options.minheight,maxheight:options.maxheight,ratio:getRatio(options.ratio)})})}function triggerEvent(event,extra){$fotorama.trigger(_fotoramaClass+":"+event,[that,extra])}function onTouchStart(){clearTimeout(onTouchEnd.t);touchedFLAG=1;if(opts.stopautoplayontouch){that.stopAutoplay()}else{pausedAutoplayFLAG=true}}function onTouchEnd(){if(!touchedFLAG)return;if(!opts.stopautoplayontouch){releaseAutoplay();changeAutoplay()}onTouchEnd.t=setTimeout(function(){touchedFLAG=0},TRANSITION_DURATION+TOUCH_TIMEOUT)}function releaseAutoplay(){pausedAutoplayFLAG=!!($videoPlaying||stoppedAutoplayFLAG)}function changeAutoplay(){clearTimeout(changeAutoplay.t);waitFor.stop(changeAutoplay.w);if(!opts.autoplay||pausedAutoplayFLAG){if(that.autoplay){that.autoplay=false;triggerEvent("stopautoplay")}return}if(!that.autoplay){that.autoplay=true;triggerEvent("startautoplay")}var _activeIndex=activeIndex;var frameData=activeFrame[STAGE_FRAME_KEY].data();changeAutoplay.w=waitFor(function(){return frameData.state||_activeIndex!==activeIndex},function(){changeAutoplay.t=setTimeout(function(){if(pausedAutoplayFLAG||_activeIndex!==activeIndex)return;var _nextAutoplayIndex=nextAutoplayIndex,nextFrameData=data[_nextAutoplayIndex][STAGE_FRAME_KEY].data();changeAutoplay.w=waitFor(function(){return nextFrameData.state||_nextAutoplayIndex!==nextAutoplayIndex},function(){if(pausedAutoplayFLAG||_nextAutoplayIndex!==nextAutoplayIndex)return;that.show(o_loop?getDirectionSign(!o_rtl):nextAutoplayIndex)})},opts.autoplay)})}that.startAutoplay=function(interval){if(that.autoplay)return this;pausedAutoplayFLAG=stoppedAutoplayFLAG=false;setAutoplayInterval(interval||opts.autoplay);changeAutoplay();return this};that.stopAutoplay=function(){if(that.autoplay){pausedAutoplayFLAG=stoppedAutoplayFLAG=true;changeAutoplay()}return this};that.showSlide=function(slideDir){var currentPosition=readPosition($navShaft,opts.navdir),pos,time=500*1.1,size=opts.navdir==="horizontal"?opts.thumbwidth:opts.thumbheight,onEnd=function(){thumbArrUpdate()};if(slideDir==="next"){pos=currentPosition-(size+opts.margin)*thumbsPerSlide}if(slideDir==="prev"){pos=currentPosition+(size+opts.margin)*thumbsPerSlide}pos=validateRestrictions(pos,navShaftTouchTail);thumbsDraw(pos,true);slide($navShaft,{time:time,pos:pos,direction:opts.navdir,onEnd:onEnd})};that.showWhileLongPress=function(options){if(that.longPress.singlePressInProgress){return}var index=calcActiveIndex(options);calcGlobalIndexes(index);var time=calcTime(options)/50;var _activeFrame=activeFrame;that.activeFrame=activeFrame=data[activeIndex];var silent=_activeFrame===activeFrame&&!options.user;that.showNav(silent,options,time);return this};that.showEndLongPress=function(options){if(that.longPress.singlePressInProgress){return}var index=calcActiveIndex(options);calcGlobalIndexes(index);var time=calcTime(options)/50;var _activeFrame=activeFrame;that.activeFrame=activeFrame=data[activeIndex];var silent=_activeFrame===activeFrame&&!options.user;that.showStage(silent,options,time);showedFLAG=typeof lastActiveIndex!=="undefined"&&lastActiveIndex!==activeIndex;lastActiveIndex=activeIndex;return this};function calcActiveIndex(options){var index;if(typeof options!=="object"){index=options;options={}}else{index=options.index}index=index===">"?dirtyIndex+1:index==="<"?dirtyIndex-1:index==="<<"?0:index===">>"?size-1:index;index=isNaN(index)?undefined:index;index=typeof index==="undefined"?activeIndex||0:index;return index}function calcGlobalIndexes(index){that.activeIndex=activeIndex=edgeIndex(index);prevIndex=getPrevIndex(activeIndex);nextIndex=getNextIndex(activeIndex);nextAutoplayIndex=normalizeIndex(activeIndex+(o_rtl?-1:1));activeIndexes=[activeIndex,prevIndex,nextIndex];dirtyIndex=o_loop?index:activeIndex}function calcTime(options){var diffIndex=Math.abs(lastActiveIndex-dirtyIndex),time=getNumber(options.time,function(){return Math.min(o_transitionDuration*(1+(diffIndex-1)/12),o_transitionDuration*2)});if(options.slow){time*=10}return time}that.showStage=function(silent,options,time){unloadVideo($videoPlaying,activeFrame.i!==data[normalizeIndex(repositionIndex)].i);frameDraw(activeIndexes,"stage");stageFramePosition(SLOW?[dirtyIndex]:[dirtyIndex,getPrevIndex(dirtyIndex),getNextIndex(dirtyIndex)]);updateTouchTails("go",true);silent||triggerEvent("show",{user:options.user,time:time});pausedAutoplayFLAG=true;var overPos=options.overPos;var onEnd=that.showStage.onEnd=function(skipReposition){if(onEnd.ok)return;onEnd.ok=true;skipReposition||stageShaftReposition(true);if(!silent){triggerEvent("showend",{user:options.user})}if(!skipReposition&&o_transition&&o_transition!==opts.transition){that.setOptions({transition:o_transition});o_transition=false;return}updateFotoramaState();loadImg(activeIndexes,"stage");updateTouchTails("go",false);stageWheelUpdate();stageCursor();releaseAutoplay();changeAutoplay();if(that.fullScreen){activeFrame[STAGE_FRAME_KEY].find("."+imgFullClass).attr("aria-hidden",false);activeFrame[STAGE_FRAME_KEY].find("."+imgClass).attr("aria-hidden",true)}else{activeFrame[STAGE_FRAME_KEY].find("."+imgFullClass).attr("aria-hidden",true);activeFrame[STAGE_FRAME_KEY].find("."+imgClass).attr("aria-hidden",false)}};if(!o_fade){slide($stageShaft,{pos:-getPosByIndex(dirtyIndex,measures.w,opts.margin,repositionIndex),overPos:overPos,time:time,onEnd:onEnd})}else{var $activeFrame=activeFrame[STAGE_FRAME_KEY],$prevActiveFrame=data[lastActiveIndex]&&activeIndex!==lastActiveIndex?data[lastActiveIndex][STAGE_FRAME_KEY]:null;fade($activeFrame,$prevActiveFrame,$stageFrame,{time:time,method:opts.transition,onEnd:onEnd},fadeStack)}arrsUpdate()};that.showNav=function(silent,options,time){thumbArrUpdate();if(o_nav){navUpdate();var guessIndex=limitIndex(activeIndex+minMaxLimit(dirtyIndex-lastActiveIndex,-1,1));slideNavShaft({time:time,coo:guessIndex!==activeIndex&&options.coo,guessIndex:typeof options.coo!=="undefined"?guessIndex:activeIndex,keep:silent});if(o_navThumbs)slideThumbBorder(time)}};that.show=function(options){that.longPress.singlePressInProgress=true;var index=calcActiveIndex(options);calcGlobalIndexes(index);var time=calcTime(options);var _activeFrame=activeFrame;that.activeFrame=activeFrame=data[activeIndex];var silent=_activeFrame===activeFrame&&!options.user;that.showStage(silent,options,time);that.showNav(silent,options,time);showedFLAG=typeof lastActiveIndex!=="undefined"&&lastActiveIndex!==activeIndex;lastActiveIndex=activeIndex;that.longPress.singlePressInProgress=false;return this};that.requestFullScreen=function(){if(o_allowFullScreen&&!that.fullScreen){var isVideo=$((that.activeFrame||{}).$stageFrame||{}).hasClass("fotorama-video-container");if(isVideo){return}scrollTop=$WINDOW.scrollTop();scrollLeft=$WINDOW.scrollLeft();lockScroll($WINDOW);updateTouchTails("x",true);measuresStash=$.extend({},measures);$fotorama.addClass(fullscreenClass).appendTo($BODY.addClass(_fullscreenClass));$HTML.addClass(_fullscreenClass);unloadVideo($videoPlaying,true,true);that.fullScreen=true;if(o_nativeFullScreen){fullScreenApi.request(fotorama)}that.resize();loadImg(activeIndexes,"stage");updateFotoramaState();triggerEvent("fullscreenenter");if(!("ontouchstart"in window)){$fullscreenIcon.focus()}}return this};function cancelFullScreen(){if(that.fullScreen){that.fullScreen=false;if(FULLSCREEN){fullScreenApi.cancel(fotorama)}$BODY.removeClass(_fullscreenClass);$HTML.removeClass(_fullscreenClass);$fotorama.removeClass(fullscreenClass).insertAfter($anchor);measures=$.extend({},measuresStash);unloadVideo($videoPlaying,true,true);updateTouchTails("x",false);that.resize();loadImg(activeIndexes,"stage");lockScroll($WINDOW,scrollLeft,scrollTop);triggerEvent("fullscreenexit")}}that.cancelFullScreen=function(){if(o_nativeFullScreen&&fullScreenApi.is()){fullScreenApi.cancel(document)}else{cancelFullScreen()}return this};that.toggleFullScreen=function(){return that[(that.fullScreen?"cancel":"request")+"FullScreen"]()};that.resize=function(options){if(!data)return this;var time=arguments[1]||0,setFLAG=arguments[2];thumbsPerSlide=getThumbsInSlide($wrap,opts);extendMeasures(!that.fullScreen?optionsToLowerCase(options):{width:$(window).width(),maxwidth:null,minwidth:null,height:$(window).height(),maxheight:null,minheight:null},[measures,setFLAG||that.fullScreen||opts]);var width=measures.width,height=measures.height,ratio=measures.ratio,windowHeight=$WINDOW.height()-(o_nav?$nav.height():0);if(measureIsValid(width)){$wrap.css({width:""});$wrap.css({height:""});$stage.css({width:""});$stage.css({height:""});$stageShaft.css({width:""});$stageShaft.css({height:""});$nav.css({width:""});$nav.css({height:""});$wrap.css({minWidth:measures.minwidth||0,maxWidth:measures.maxwidth||MAX_WIDTH});if(o_nav==="dots"){$navWrap.hide()}width=measures.W=measures.w=$wrap.width();measures.nw=o_nav&&numberFromWhatever(opts.navwidth,width)||width;$stageShaft.css({width:measures.w,marginLeft:(measures.W-measures.w)/2});height=numberFromWhatever(height,windowHeight);height=height||ratio&&width/ratio;if(height){width=Math.round(width);height=measures.h=Math.round(minMaxLimit(height,numberFromWhatever(measures.minheight,windowHeight),numberFromWhatever(measures.maxheight,windowHeight)));$stage.css({width:width,height:height});if(opts.navdir==="vertical"&&!that.fullscreen){$nav.width(opts.thumbwidth+opts.thumbmargin*2)}if(opts.navdir==="horizontal"&&!that.fullscreen){$nav.height(opts.thumbheight+opts.thumbmargin*2)}if(o_nav==="dots"){$nav.width(width).height("auto");$navWrap.show()}if(opts.navdir==="vertical"&&that.fullScreen){$stage.css("height",$WINDOW.height())}if(opts.navdir==="horizontal"&&that.fullScreen){$stage.css("height",$WINDOW.height()-$nav.height())}if(o_nav){switch(opts.navdir){case"vertical":$navWrap.removeClass(navShafthorizontalClass);$navWrap.removeClass(navShaftListClass);$navWrap.addClass(navShaftVerticalClass);$nav.stop().animate({height:measures.h,width:opts.thumbwidth},time);break;case"list":$navWrap.removeClass(navShaftVerticalClass);$navWrap.removeClass(navShafthorizontalClass);$navWrap.addClass(navShaftListClass);break;default:$navWrap.removeClass(navShaftVerticalClass);$navWrap.removeClass(navShaftListClass);$navWrap.addClass(navShafthorizontalClass);$nav.stop().animate({width:measures.nw},time);break}stageShaftReposition();slideNavShaft({guessIndex:activeIndex,time:time,keep:true});if(o_navThumbs&&frameAppend.nav)slideThumbBorder(time)}measuresSetFLAG=setFLAG||true;ready.ok=true;ready()}}stageLeft=$stage.offset().left;setStagePosition();return this};that.setOptions=function(options){$.extend(opts,options);reset();return this};that.shuffle=function(){data&&shuffle(data)&&reset();return this};function setShadow($el,edge){if(o_shadows){$el.removeClass(shadowsLeftClass+" "+shadowsRightClass);$el.removeClass(shadowsTopClass+" "+shadowsBottomClass);edge&&!$videoPlaying&&$el.addClass(edge.replace(/^|\s/g," "+shadowsClass+"--"))}}that.longPress={threshold:1,count:0,thumbSlideTime:20,progress:function(){if(!this.inProgress){this.count++;this.inProgress=this.count>this.threshold}},end:function(){if(this.inProgress){this.isEnded=true}},reset:function(){this.count=0;this.inProgress=false;this.isEnded=false}};that.destroy=function(){that.cancelFullScreen();that.stopAutoplay();data=that.data=null;appendElements();activeIndexes=[];detachFrames(STAGE_FRAME_KEY);reset.ok=false;return this};that.playVideo=function(){var dataFrame=activeFrame,video=dataFrame.video,_activeIndex=activeIndex;if(typeof video==="object"&&dataFrame.videoReady){o_nativeFullScreen&&that.fullScreen&&that.cancelFullScreen();waitFor(function(){return!fullScreenApi.is()||_activeIndex!==activeIndex},function(){if(_activeIndex===activeIndex){dataFrame.$video=dataFrame.$video||$(div(videoClass)).append(createVideoFrame(video));dataFrame.$video.appendTo(dataFrame[STAGE_FRAME_KEY]);$wrap.addClass(wrapVideoClass);$videoPlaying=dataFrame.$video;stageNoMove();$arrs.blur();$fullscreenIcon.blur();triggerEvent("loadvideo")}})}return this};that.stopVideo=function(){unloadVideo($videoPlaying,true,true);return this};that.spliceByIndex=function(index,newImgObj){newImgObj.i=index+1;newImgObj.img&&$.ajax({url:newImgObj.img,type:"HEAD",success:function(){data.splice(index,1,newImgObj);reset()}})};function unloadVideo($video,unloadActiveFLAG,releaseAutoplayFLAG){if(unloadActiveFLAG){$wrap.removeClass(wrapVideoClass);$videoPlaying=false;stageNoMove()}if($video&&$video!==$videoPlaying){$video.remove();triggerEvent("unloadvideo")}if(releaseAutoplayFLAG){releaseAutoplay();changeAutoplay()}}function toggleControlsClass(FLAG){$wrap.toggleClass(wrapNoControlsClass,FLAG)}function stageCursor(e){if(stageShaftTouchTail.flow)return;var x=e?e.pageX:stageCursor.x,pointerFLAG=x&&!disableDirrection(getDirection(x))&&opts.click;if(stageCursor.p!==pointerFLAG&&$stage.toggleClass(pointerClass,pointerFLAG)){stageCursor.p=pointerFLAG;stageCursor.x=x}}$stage.on("mousemove",stageCursor);function clickToShow(showOptions){clearTimeout(clickToShow.t);if(opts.clicktransition&&opts.clicktransition!==opts.transition){setTimeout(function(){var _o_transition=opts.transition;that.setOptions({transition:opts.clicktransition});o_transition=_o_transition;clickToShow.t=setTimeout(function(){that.show(showOptions)},10)},0)}else{that.show(showOptions)}}function onStageTap(e,toggleControlsFLAG){var target=e.target,$target=$(target);if($target.hasClass(videoPlayClass)){that.playVideo()}else if(target===fullscreenIcon){that.toggleFullScreen()}else if($videoPlaying){target===videoClose&&unloadVideo($videoPlaying,true,true)}else if(!$fotorama.hasClass(fullscreenClass)){that.requestFullScreen()}}function updateTouchTails(key,value){stageShaftTouchTail[key]=navShaftTouchTail[key]=value}stageShaftTouchTail=moveOnTouch($stageShaft,{onStart:onTouchStart,onMove:function(e,result){setShadow($stage,result.edge)},onTouchEnd:onTouchEnd,onEnd:function(result){var toggleControlsFLAG;setShadow($stage);toggleControlsFLAG=(MS_POINTER&&!hoverFLAG||result.touch)&&opts.arrows;if((result.moved||toggleControlsFLAG&&result.pos!==result.newPos&&!result.control)&&result.$target[0]!==$fullscreenIcon[0]){var index=getIndexByPos(result.newPos,measures.w,opts.margin,repositionIndex);that.show({index:index,time:o_fade?o_transitionDuration:result.time,overPos:result.overPos,user:true})}else if(!result.aborted&&!result.control){onStageTap(result.startEvent,toggleControlsFLAG)}},timeLow:1,timeHigh:1,friction:2,select:"."+selectClass+", ."+selectClass+" *",$wrap:$stage,direction:"horizontal"});navShaftTouchTail=moveOnTouch($navShaft,{onStart:onTouchStart,onMove:function(e,result){setShadow($nav,result.edge)},onTouchEnd:onTouchEnd,onEnd:function(result){function onEnd(){slideNavShaft.l=result.newPos;releaseAutoplay();changeAutoplay();thumbsDraw(result.newPos,true);thumbArrUpdate()}if(!result.moved){var target=result.$target.closest("."+navFrameClass,$navShaft)[0];target&&onNavFrameClick.call(target,result.startEvent)}else if(result.pos!==result.newPos){pausedAutoplayFLAG=true;slide($navShaft,{time:result.time,pos:result.newPos,overPos:result.overPos,direction:opts.navdir,onEnd:onEnd});thumbsDraw(result.newPos);o_shadows&&setShadow($nav,findShadowEdge(result.newPos,navShaftTouchTail.min,navShaftTouchTail.max,result.dir))}else{onEnd()}},timeLow:.5,timeHigh:2,friction:5,$wrap:$nav,direction:opts.navdir});stageWheelTail=wheel($stage,{shift:true,onEnd:function(e,direction){onTouchStart();onTouchEnd();that.show({index:direction,slow:e.altKey})}});navWheelTail=wheel($nav,{onEnd:function(e,direction){onTouchStart();onTouchEnd();var newPos=stop($navShaft)+direction*.25;$navShaft.css(getTranslate(minMaxLimit(newPos,navShaftTouchTail.min,navShaftTouchTail.max),opts.navdir));o_shadows&&setShadow($nav,findShadowEdge(newPos,navShaftTouchTail.min,navShaftTouchTail.max,opts.navdir));navWheelTail.prevent={"<":newPos>=navShaftTouchTail.max,">":newPos<=navShaftTouchTail.min};clearTimeout(navWheelTail.t);navWheelTail.t=setTimeout(function(){slideNavShaft.l=newPos;thumbsDraw(newPos,true)},TOUCH_TIMEOUT);thumbsDraw(newPos)}});$wrap.hover(function(){setTimeout(function(){if(touchedFLAG)return;toggleControlsClass(!(hoverFLAG=true))},0)},function(){if(!hoverFLAG)return;toggleControlsClass(!(hoverFLAG=false))});function onNavFrameClick(e){var index=$(this).data().eq;if(opts.navtype==="thumbs"){clickToShow({index:index,slow:e.altKey,user:true,coo:e._x-$nav.offset().left})}else{clickToShow({index:index,slow:e.altKey,user:true})}}function onArrClick(e){clickToShow({index:$arrs.index(this)?">":"<",slow:e.altKey,user:true})}smartClick($arrs,function(e){stopEvent(e);onArrClick.call(this,e)},{onStart:function(){onTouchStart();stageShaftTouchTail.control=true},onTouchEnd:onTouchEnd});smartClick($thumbArrLeft,function(e){stopEvent(e);if(opts.navtype==="thumbs"){that.show("<")}else{that.showSlide("prev")}});smartClick($thumbArrRight,function(e){stopEvent(e);if(opts.navtype==="thumbs"){that.show(">")}else{that.showSlide("next")}});function addFocusOnControls(el){addFocus(el,function(){setTimeout(function(){lockScroll($stage)},0);toggleControlsClass(false)})}$arrs.each(function(){addEnterUp(this,function(e){onArrClick.call(this,e)});addFocusOnControls(this)});addEnterUp(fullscreenIcon,function(){if($fotorama.hasClass(fullscreenClass)){that.cancelFullScreen();$stageShaft.focus()}else{that.requestFullScreen();$fullscreenIcon.focus()}});addFocusOnControls(fullscreenIcon);function reset(){setData();setOptions();if(!reset.i){reset.i=true;var _startindex=opts.startindex;activeIndex=repositionIndex=dirtyIndex=lastActiveIndex=startIndex=edgeIndex(_startindex)||0}if(size){if(changeToRtl())return;if($videoPlaying){unloadVideo($videoPlaying,true)}activeIndexes=[];detachFrames(STAGE_FRAME_KEY);reset.ok=true;that.show({index:activeIndex,time:0});that.resize()}else{that.destroy()}}function changeToRtl(){if(!changeToRtl.f===o_rtl){changeToRtl.f=o_rtl;activeIndex=size-1-activeIndex;that.reverse();return true}}$.each("load push pop shift unshift reverse sort splice".split(" "),function(i,method){that[method]=function(){data=data||[];if(method!=="load"){Array.prototype[method].apply(data,arguments)}else if(arguments[0]&&typeof arguments[0]==="object"&&arguments[0].length){data=clone(arguments[0])}reset();return that}});function ready(){if(ready.ok){ready.ok=false;triggerEvent("ready")}}reset()};$.fn.fotorama=function(opts){return this.each(function(){var that=this,$fotorama=$(this),fotoramaData=$fotorama.data(),fotorama=fotoramaData.fotorama;if(!fotorama){waitFor(function(){return!isHidden(that)},function(){fotoramaData.urtext=$fotorama.html();new $.Fotorama($fotorama,$.extend({},OPTIONS,window.fotoramaDefaults,opts,fotoramaData))})}else{fotorama.setOptions(opts,true)}})};$.Fotorama.instances=[];function calculateIndexes(){$.each($.Fotorama.instances,function(index,instance){instance.index=index})}function addInstance(instance){$.Fotorama.instances.push(instance);calculateIndexes()}function hideInstance(instance){$.Fotorama.instances.splice(instance.index,1);calculateIndexes()}$.Fotorama.cache={};$.Fotorama.measures={};$=$||{};$.Fotorama=$.Fotorama||{};$.Fotorama.jst=$.Fotorama.jst||{};$.Fotorama.jst.dots=function(v){var __t,__p="",__e=_.escape;__p+='<div class="fotorama__nav__frame fotorama__nav__frame--dot" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__dot"></div>\r\n</div>';return __p};$.Fotorama.jst.frameCaption=function(v){var __t,__p="",__e=_.escape;__p+='<div class="fotorama__caption" aria-hidden="true">\r\n <div class="fotorama__caption__wrap" id="'+((__t=v.labelledby)==null?"":__t)+'">'+((__t=v.caption)==null?"":__t)+"</div>\r\n</div>\r\n";return __p};$.Fotorama.jst.style=function(v){var __t,__p="",__e=_.escape;__p+=".fotorama"+((__t=v.s)==null?"":__t)+" .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:"+((__t=v.m)==null?"":__t)+"px;\r\nheight:"+((__t=v.h)==null?"":__t)+"px}\r\n.fotorama"+((__t=v.s)==null?"":__t)+" .fotorama__thumb-border{\r\nheight:"+((__t=v.h)==null?"":__t)+"px;\r\nborder-width:"+((__t=v.b)==null?"":__t)+"px;\r\nmargin-top:"+((__t=v.m)==null?"":__t)+"px}";return __p};$.Fotorama.jst.thumb=function(v){var __t,__p="",__e=_.escape;__p+='<div class="fotorama__nav__frame fotorama__nav__frame--thumb" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__thumb">\r\n </div>\r\n</div>';return __p}})(window,document,location,typeof jQuery!=="undefined"&&jQuery); From 8f9a7cdec76e1225e4c46d101d5643d742bf757c Mon Sep 17 00:00:00 2001 From: Stjepan Udovicic <stjepan.udovicic@inchoo.net> Date: Sun, 14 Oct 2018 21:17:28 +0200 Subject: [PATCH 118/309] Ensure integer values are not quoted as strings --- .../ProductCategoryCondition.php | 2 +- .../Magento/Catalog/Model/ProductCategoryList.php | 6 +++++- .../Model/ResourceModel/Product/Collection.php | 15 ++++++++++----- lib/internal/Magento/Framework/Mview/View.php | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php index f70bab73d0830..d37f97c38a4f6 100644 --- a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php +++ b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php @@ -104,7 +104,7 @@ private function getCategoryIds(Filter $filter): array } } - return array_unique(array_merge($categoryIds, ...$childCategoryIds)); + return array_map('intval', array_unique(array_merge($categoryIds, ...$childCategoryIds))); } /** diff --git a/app/code/Magento/Catalog/Model/ProductCategoryList.php b/app/code/Magento/Catalog/Model/ProductCategoryList.php index 5bbae772d5c2b..c8b075ffb494e 100644 --- a/app/code/Magento/Catalog/Model/ProductCategoryList.php +++ b/app/code/Magento/Catalog/Model/ProductCategoryList.php @@ -80,7 +80,11 @@ public function getCategoryIds($productId) Select::SQL_UNION_ALL ); - $this->categoryIdList[$productId] = $this->productResource->getConnection()->fetchCol($unionSelect); + $this->categoryIdList[$productId] = array_map( + 'intval', + $this->productResource->getConnection()->fetchCol($unionSelect) + ); + } return $this->categoryIdList[$productId]; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 0d62d120f80e0..5afac59b25db6 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1809,7 +1809,8 @@ protected function _productLimitationJoinWebsite() } $conditions[] = $this->getConnection()->quoteInto( 'product_website.website_id IN(?)', - $filters['website_ids'] + $filters['website_ids'], + 'int' ); } elseif (isset( $filters['store_id'] @@ -1821,7 +1822,7 @@ protected function _productLimitationJoinWebsite() ) { $joinWebsite = true; $websiteId = $this->_storeManager->getStore($filters['store_id'])->getWebsiteId(); - $conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId); + $conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId, 'int'); } $fromPart = $this->getSelect()->getPart(\Magento\Framework\DB\Select::FROM); @@ -2017,12 +2018,16 @@ protected function _applyProductLimitations() $conditions = [ 'cat_index.product_id=e.entity_id', - $this->getConnection()->quoteInto('cat_index.store_id=?', $filters['store_id']), + $this->getConnection()->quoteInto('cat_index.store_id=?', $filters['store_id'], 'int'), ]; if (isset($filters['visibility']) && !isset($filters['store_table'])) { - $conditions[] = $this->getConnection()->quoteInto('cat_index.visibility IN(?)', $filters['visibility']); + $conditions[] = $this->getConnection()->quoteInto( + 'cat_index.visibility IN(?)', + $filters['visibility'], + 'int' + ); } - $conditions[] = $this->getConnection()->quoteInto('cat_index.category_id=?', $filters['category_id']); + $conditions[] = $this->getConnection()->quoteInto('cat_index.category_id=?', $filters['category_id'], 'int'); if (isset($filters['category_is_anchor'])) { $conditions[] = $this->getConnection()->quoteInto('cat_index.is_parent=?', $filters['category_is_anchor']); } diff --git a/lib/internal/Magento/Framework/Mview/View.php b/lib/internal/Magento/Framework/Mview/View.php index a937c62dfc23a..589034886010f 100644 --- a/lib/internal/Magento/Framework/Mview/View.php +++ b/lib/internal/Magento/Framework/Mview/View.php @@ -283,7 +283,7 @@ public function update() for ($vsFrom = $lastVersionId; $vsFrom < $currentVersionId; $vsFrom += $versionBatchSize) { // Don't go past the current version for atomicy. $versionTo = min($currentVersionId, $vsFrom + $versionBatchSize); - $ids = $this->getChangelog()->getList($vsFrom, $versionTo); + $ids = array_map('intval', $this->getChangelog()->getList($vsFrom, $versionTo)); // We run the actual indexer in batches. // Chunked AFTER loading to avoid duplicates in separate chunks. From 42ecb9c78de2ebb4c1c475b2ef2e286faded0b5f Mon Sep 17 00:00:00 2001 From: Lewis Voncken <lewis@experius.nl> Date: Mon, 15 Oct 2018 15:47:35 +0200 Subject: [PATCH 119/309] [BUGFIX] [issue-10205] Always set the entity_type_id for updating product attributes because the route contains the specification products/attributes --- .../Catalog/Model/Product/Attribute/Repository.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php index f6d3ca36c1e1e..48ca12321a250 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php @@ -106,6 +106,11 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr */ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attribute) { + $attribute->setEntityTypeId( + $this->eavConfig + ->getEntityType(\Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE) + ->getId() + ); if ($attribute->getAttributeId()) { $existingModel = $this->get($attribute->getAttributeCode()); @@ -144,11 +149,6 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib $attribute->setBackendModel( $this->productHelper->getAttributeBackendModelByInputType($attribute->getFrontendInput()) ); - $attribute->setEntityTypeId( - $this->eavConfig - ->getEntityType(\Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE) - ->getId() - ); $attribute->setIsUserDefined(1); } if (!empty($attribute->getData(AttributeInterface::OPTIONS))) { From e30a12e6bf954763662f68f5eec87d2cbc2e93dd Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh@Maheshs-MacBook-Air.local> Date: Tue, 16 Oct 2018 00:08:57 +0530 Subject: [PATCH 120/309] reference pull request of #18604 for 2.3-develop --- app/code/Magento/Catalog/Pricing/Price/BasePrice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php index 54a13be864db7..77368517a3155 100644 --- a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php @@ -30,7 +30,7 @@ public function getValue() $this->value = false; foreach ($this->priceInfo->getPrices() as $price) { if ($price instanceof BasePriceProviderInterface && $price->getValue() !== false) { - $this->value = min($price->getValue(), $this->value ?: $price->getValue()); + $this->value = min($price->getValue(), $this->value !== false ? $this->value: $price->getValue()); } } } From 954ee4fbecf96d3898bbad21d46f2430d6ebd544 Mon Sep 17 00:00:00 2001 From: Ayaz Mittaqi <ayaz.mittaqi024@webkul.com> Date: Tue, 16 Oct 2018 02:51:20 +0530 Subject: [PATCH 121/309] fixed issue regarding referer url of compare page during adding product to wishlist. --- .../Magento/Catalog/Block/Product/Compare/ListCompare.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php index 6c54aa4e171ef..76f5dbd1bea88 100644 --- a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php +++ b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php @@ -122,12 +122,7 @@ public function __construct( */ public function getAddToWishlistParams($product) { - $continueUrl = $this->urlEncoder->encode($this->getUrl('customer/account')); - $urlParamName = Action::PARAM_NAME_URL_ENCODED; - - $continueUrlParams = [$urlParamName => $continueUrl]; - - return $this->_wishlistHelper->getAddParams($product, $continueUrlParams); + return $this->_wishlistHelper->getAddParams($product); } /** From a9bc411deaecd93be411a3bc8c8da003b2e31298 Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Tue, 16 Oct 2018 10:57:14 +0530 Subject: [PATCH 122/309] Fix SKU limit in import new products for 2.3 with backward compatible --- .../CatalogImportExport/Model/Import/Product/Validator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 2aa2105991883..b184334b179ea 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -7,6 +7,7 @@ use Magento\CatalogImportExport\Model\Import\Product; use Magento\Framework\Validator\AbstractValidator; +use Magento\Catalog\Model\Product\Attribute\Backend\Sku; /** * Class Validator @@ -69,6 +70,8 @@ protected function textValidation($attrCode, $type) $val = $this->string->cleanString($this->_rowData[$attrCode]); if ($type == 'text') { $valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH; + } else if ($attrCode == Product::COL_SKU) { + $valid = $this->string->strlen($val) < SKU::SKU_MAX_LENGTH; } else { $valid = $this->string->strlen($val) < Product::DB_MAX_VARCHAR_LENGTH; } From 4d9fab4557a2c704b5f600841abff62778c74ce8 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Wed, 10 Oct 2018 16:10:25 +0300 Subject: [PATCH 123/309] MAGETWO-90021: [Catalog] ProductAttributeMediaGalleryManagementInterface removes product from index, impossible to restore - Make Gallery management to get product in editable mode. Fix test. --- .../Catalog/Model/Product/Gallery/GalleryManagement.php | 6 +++--- .../testsuite/Magento/GraphQl/Catalog/ProductViewTest.php | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php index 4d274a071d087..4be4bb09efc98 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php @@ -54,7 +54,7 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) if (!$this->contentValidator->isValid($entryContent)) { throw new InputException(__('The image content is invalid. Verify the content and try again.')); } - $product = $this->productRepository->get($sku); + $product = $this->productRepository->get($sku, true); $existingMediaGalleryEntries = $product->getMediaGalleryEntries(); $existingEntryIds = []; @@ -88,7 +88,7 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) */ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry) { - $product = $this->productRepository->get($sku); + $product = $this->productRepository->get($sku, true); $existingMediaGalleryEntries = $product->getMediaGalleryEntries(); if ($existingMediaGalleryEntries == null) { throw new NoSuchEntityException( @@ -129,7 +129,7 @@ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry) */ public function remove($sku, $entryId) { - $product = $this->productRepository->get($sku); + $product = $this->productRepository->get($sku, true); $existingMediaGalleryEntries = $product->getMediaGalleryEntries(); if ($existingMediaGalleryEntries == null) { throw new NoSuchEntityException( diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index 34cebde64d03a..b9993b420f427 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -285,7 +285,6 @@ public function testQueryAllFieldsSimpleProduct() */ public function testQueryMediaGalleryEntryFieldsSimpleProduct() { - $this->markTestSkipped("Skipped until ticket MAGETWO-90021 is resolved."); $productSku = 'simple'; $query = <<<QUERY @@ -453,7 +452,9 @@ public function testQueryMediaGalleryEntryFieldsSimpleProduct() } short_description sku - small_image + small_image { + path + } small_image_label special_from_date special_price From 1f05fd8b259b94f272d9c44ae147c5df7910e9ff Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <mikalai_shostka@epam.com> Date: Tue, 16 Oct 2018 14:46:29 +0300 Subject: [PATCH 124/309] MAGETWO-95445: Category Flat Data indexer doesnt show status as reindex required after deleting store/storeview - Delete abandoned tables in full reindex --- .../Catalog/Helper/Product/Flat/Indexer.php | 32 +++++++++++++++++++ .../Indexer/Category/Flat/Action/Full.php | 31 +++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Helper/Product/Flat/Indexer.php b/app/code/Magento/Catalog/Helper/Product/Flat/Indexer.php index 93eaa23b89f16..0366e01ee86ea 100644 --- a/app/code/Magento/Catalog/Helper/Product/Flat/Indexer.php +++ b/app/code/Magento/Catalog/Helper/Product/Flat/Indexer.php @@ -466,6 +466,17 @@ public function getFlatTableName($storeId) return sprintf('%s_%s', $this->getTable('catalog_product_flat'), $storeId); } + /** + * Retrieve Catalog Product Flat Table name + * + * @param int $storeId + * @return string + */ + private function getCategoryFlatTableName(int $storeId): string + { + return sprintf('%s_store_%s', $this->getTable('catalog_category_flat'), $storeId); + } + /** * Retrieve loaded attribute by code * @@ -515,4 +526,25 @@ public function deleteAbandonedStoreFlatTables() $connection->dropTable($table); } } + + /** + * Delete all category flat tables for not existing stores + * + * @return void + */ + public function deleteAbandonedStoreCategoryFlatTables() + { + $connection = $this->_resource->getConnection(); + $existentTables = $connection->getTables($connection->getTableName('catalog_category_flat_store_%')); + $actualStoreTables = []; + foreach ($this->_storeManager->getStores() as $store) { + $actualStoreTables[] = $this->getCategoryFlatTableName($store->getId()); + } + + $tablesToDelete = array_diff($existentTables, $actualStoreTables); + + foreach ($tablesToDelete as $table) { + $connection->dropTable($table); + } + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php index 64a8f930d83ee..c1e6decf6afef 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php @@ -5,6 +5,11 @@ */ namespace Magento\Catalog\Model\Indexer\Category\Flat\Action; +use Magento\Framework\App\ResourceConnection; + +/** + * Class for full reindex flat categories + */ class Full extends \Magento\Catalog\Model\Indexer\Category\Flat\AbstractAction { /** @@ -19,6 +24,28 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Flat\AbstractAction */ protected $allowTableChanges = true; + /** + * @var \Magento\Catalog\Helper\Product\Flat\Indexer + */ + private $indexer; + + /** + * @param ResourceConnection $resource + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper + * @param \Magento\Catalog\Helper\Product\Flat\Indexer $indexer + */ + public function __construct( + ResourceConnection $resource, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, + \Magento\Catalog\Helper\Product\Flat\Indexer $indexer = null + ) { + $this->indexer = $indexer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Catalog\Helper\Product\Flat\Indexer::class); + parent::__construct($resource, $storeManager, $resourceHelper); + } + /** * Add suffix to table name to show it is old * @@ -92,6 +119,7 @@ protected function populateFlatTables(array $stores) /** * Create table and add attributes as fields for specified store. + * * This routine assumes that DDL operations are allowed * * @param int $store @@ -109,6 +137,7 @@ protected function createTable($store) /** * Create category flat tables and add attributes as fields. + * * Tables are created only if DDL operations are allowed * * @param \Magento\Store\Model\Store[] $stores if empty, create tables for all stores of the application @@ -182,7 +211,7 @@ public function reindexAll() $stores = $this->storeManager->getStores(); $this->populateFlatTables($stores); $this->switchTables($stores); - + $this->indexer->deleteAbandonedStoreCategoryFlatTables(); $this->allowTableChanges = true; return $this; From 11290c5d74cb2a9878735fac7dd4648d1923f71c Mon Sep 17 00:00:00 2001 From: anuj <anuj.gupta701@webkul.com> Date: Tue, 16 Oct 2018 18:42:50 +0530 Subject: [PATCH 125/309] issue #18618 fixed --- app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php index bb190260e4776..5059d61eb3f7e 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php @@ -123,7 +123,8 @@ protected function _prepareColumns() 'header' => __('Order Total'), 'index' => 'grand_total', 'type' => 'currency', - 'currency' => 'order_currency_code' + 'currency' => 'order_currency_code', + 'rate' => 1 ] ); From 11b4c70bb1807e1db9703f8b0fa241de9790e512 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 16 Oct 2018 11:10:29 -0500 Subject: [PATCH 126/309] MAGETWO-95723: Product Review "Save and Next" and "Save and Previous" not working - Fixed query ordering - Added test coverage --- .../Review/Product/Collection.php | 1 - .../Review/Product/CollectionTest.php | 73 ++++++++++++++++++- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php b/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php index 99c963501a9d4..68dc8d753b5bd 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php +++ b/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php @@ -406,7 +406,6 @@ public function getResultingIds() $idsSelect->reset(Select::LIMIT_COUNT); $idsSelect->reset(Select::LIMIT_OFFSET); $idsSelect->reset(Select::COLUMNS); - $idsSelect->reset(Select::ORDER); $idsSelect->columns('rt.review_id'); return $this->getConnection()->fetchCol($idsSelect); } diff --git a/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php index a7f859b23dfa2..39129dee28c7c 100644 --- a/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php @@ -3,20 +3,85 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Review\Model\ResourceModel\Review\Product; class CollectionTest extends \PHPUnit\Framework\TestCase { /** + * @param string $status + * @param int $expectedCount + * @param string $sortAttribute + * @param string $dir + * @param callable $assertion + * @dataProvider sortOrderAssertionsDataProvider * @magentoDataFixture Magento/Review/_files/different_reviews.php */ - public function testGetResultingIds() - { + public function testGetResultingIds( + ?string $status, + int $expectedCount, + string $sortAttribute, + string $dir, + callable $assertion + ) { + /** + * @var $collection \Magento\Review\Model\ResourceModel\Review\Product\Collection + */ $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Review\Model\ResourceModel\Review\Product\Collection::class ); - $collection->addStatusFilter(\Magento\Review\Model\Review::STATUS_APPROVED); + if ($status) { + $collection->addStatusFilter($status); + } + $collection->setOrder($sortAttribute, $dir); $actual = $collection->getResultingIds(); - $this->assertCount(2, $actual); + $this->assertCount($expectedCount, $actual); + $assertion($actual); + } + + /** + * @return array + */ + public function sortOrderAssertionsDataProvider() :array + { + return [ + [ + \Magento\Review\Model\Review::STATUS_APPROVED, + 2, + 'rt.review_id', + 'DESC', + function (array $actual) :void { + $this->assertLessThan($actual[0], $actual[1]); + } + ], + [ + \Magento\Review\Model\Review::STATUS_APPROVED, + 2, + 'rt.review_id', + 'ASC', + function (array $actual) :void { + $this->assertLessThan($actual[1], $actual[0]); + } + ], + [ + \Magento\Review\Model\Review::STATUS_APPROVED, + 2, + 'rt.created_at', + 'ASC', + function (array $actual) :void { + $this->assertLessThan($actual[1], $actual[0]); + } + ], + [ + null, + 3, + 'rt.review_id', + 'ASC', + function (array $actual) :void { + $this->assertLessThan($actual[1], $actual[0]); + } + ] + ]; } } From 1c7c6fbf4ca26a5518634ecfec919aba3593e0c6 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Wed, 17 Oct 2018 17:09:36 +0300 Subject: [PATCH 127/309] MAGETWO-90021: [Catalog] ProductAttributeMediaGalleryManagementInterface removes product from index, impossible to restore - Change code back as there are no changes in 2.3.0 release. --- .../testsuite/Magento/GraphQl/Catalog/ProductViewTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index b9993b420f427..167ab91b9d01e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -452,9 +452,7 @@ public function testQueryMediaGalleryEntryFieldsSimpleProduct() } short_description sku - small_image { - path - } + small_image small_image_label special_from_date special_price @@ -485,7 +483,7 @@ public function testQueryMediaGalleryEntryFieldsSimpleProduct() QUERY; $response = $this->graphQlQuery($query); - + /** * @var ProductRepositoryInterface $productRepository */ From 75ebeaf1c7e8beb7a6ec2ccb34c1f4df6e1fc544 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 17 Oct 2018 13:07:08 -0500 Subject: [PATCH 128/309] MAGETWO-95723: Product Review "Save and Next" and "Save and Previous" not working - Added test class description --- .../Model/ResourceModel/Review/Product/CollectionTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php index 39129dee28c7c..559c62e42dec3 100644 --- a/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php @@ -7,6 +7,9 @@ namespace Magento\Review\Model\ResourceModel\Review\Product; +/** + * Tests some functionality of the Product Review collection + */ class CollectionTest extends \PHPUnit\Framework\TestCase { /** From e728763d084f12d34a06ccfedb1bc90b651658cd Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel@daniel-ruf.de> Date: Wed, 17 Oct 2018 23:24:35 +0200 Subject: [PATCH 129/309] fix: cache count() results for loops --- app/code/Magento/Paypal/Model/Report/Settlement.php | 3 ++- .../Test/TestCase/Product/AddCompareProductsTest.php | 3 ++- .../Checkout/Test/TestCase/ShoppingCartPerCustomerTest.php | 3 ++- .../Test/TestStep/FillShippingInformationStep.php | 3 ++- .../Test/Constraint/AssertProductsQtyAfterOrderCancel.php | 3 ++- .../Catalog/Model/Layer/Filter/Price/AlgorithmBaseTest.php | 3 ++- .../Magento/Sniffs/Translation/ConstantUsageSniffTest.php | 3 ++- .../testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php | 5 +++-- .../Framework/App/Test/Unit/Language/DictionaryTest.php | 3 ++- lib/internal/Magento/Framework/Archive.php | 7 ++++--- lib/internal/Magento/Framework/Cache/Backend/Memcached.php | 3 ++- lib/internal/Magento/Framework/Filter/Template.php | 3 ++- lib/internal/Magento/Framework/System/Ftp.php | 3 ++- .../src/Magento/Setup/Module/I18n/Parser/Adapter/Html.php | 6 ++++-- setup/src/Magento/Setup/Module/I18n/Parser/Adapter/Js.php | 6 ++++-- 15 files changed, 37 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Report/Settlement.php b/app/code/Magento/Paypal/Model/Report/Settlement.php index 5dc51518f0b11..b2ab1627b24d4 100644 --- a/app/code/Magento/Paypal/Model/Report/Settlement.php +++ b/app/code/Magento/Paypal/Model/Report/Settlement.php @@ -409,7 +409,8 @@ public function parseCsv($localCsv, $format = 'new') private function getBodyItems(array $line, array $sectionColumns, array $rowMap) { $bodyItem = []; - for ($i = 1, $count = count($line); $i < $count; $i++) { + $lineCount = count($line); + for ($i = 1, $count = $lineCount; $i < $count; $i++) { if (isset($rowMap[$sectionColumns[$i]])) { if (in_array($rowMap[$sectionColumns[$i]], $this->dateTimeColumns)) { $line[$i] = $this->formatDateTimeColumns($line[$i]); diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/AddCompareProductsTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/AddCompareProductsTest.php index c700dbc362cbb..da0aa49d9697b 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/AddCompareProductsTest.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/AddCompareProductsTest.php @@ -76,7 +76,8 @@ public function tearDown() { $this->cmsIndex->open(); $this->cmsIndex->getLinksBlock()->openLink("Compare Products"); - for ($i = 1; $i <= count($this->products); $i++) { + $productsCount = count($this->products); + for ($i = 1; $i <= $productsCount; $i++) { $this->catalogProductCompare->getCompareProductsBlock()->removeProduct(); } } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPerCustomerTest.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPerCustomerTest.php index bdd54ce3559f9..381c18d3ee686 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPerCustomerTest.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPerCustomerTest.php @@ -95,7 +95,8 @@ public function test( $customers = []; $cartFixtures = []; - for ($i = 0; $i < count($checkoutData); $i++) { + $checkoutDataCount = count($checkoutData); + for ($i = 0; $i < $checkoutDataCount; $i++) { $customers[$i] = $this->fixtureFactory->createByCode('customer', ['dataset' => $customerDataset]); $customers[$i]->persist(); diff --git a/dev/tests/functional/tests/app/Magento/Multishipping/Test/TestStep/FillShippingInformationStep.php b/dev/tests/functional/tests/app/Magento/Multishipping/Test/TestStep/FillShippingInformationStep.php index 67c1826f40a24..248dfc8e16dc9 100644 --- a/dev/tests/functional/tests/app/Magento/Multishipping/Test/TestStep/FillShippingInformationStep.php +++ b/dev/tests/functional/tests/app/Magento/Multishipping/Test/TestStep/FillShippingInformationStep.php @@ -58,7 +58,8 @@ public function __construct( public function run() { $shippingMethods = []; - for ($i = 0; $i < count($this->customer->getAddress()); $i++) { + $addressCount = $this->customer->getAddress(); + for ($i = 0; $i < $addressCount; $i++) { $shippingMethods[] = $this->shippingMethod; } $this->shippingInformation->getShippingBlock()->selectShippingMethod($shippingMethods); diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductsQtyAfterOrderCancel.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductsQtyAfterOrderCancel.php index 28259c8f6d93b..7d229bc358995 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductsQtyAfterOrderCancel.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductsQtyAfterOrderCancel.php @@ -52,7 +52,8 @@ public function processAssert( AssertProductForm $assertProductForm, AssertConfigurableProductForm $assertConfigurableProductForm ) { - for ($i = 0; $i < count($order->getEntityId()['products']); $i++) { + $productsCount = count($order->getEntityId()['products']); + for ($i = 0; $i < $productsCount; $i++) { $product = $order->getEntityId()['products'][$i]; $productData = $product->getData(); if ($product instanceof BundleProduct) { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/AlgorithmBaseTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/AlgorithmBaseTest.php index 4b69bcd4615db..87ba4d7c2808c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/AlgorithmBaseTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/AlgorithmBaseTest.php @@ -112,7 +112,8 @@ public function testPricesSegmentation($categoryId, array $entityIds, array $int $items = $model->calculateSeparators($interval); $this->assertEquals(array_keys($intervalItems), array_keys($items)); - for ($i = 0; $i < count($intervalItems); ++$i) { + $intervalItemsCount = count($intervalItems); + for ($i = 0; $i < $intervalItemsCount; ++$i) { $this->assertInternalType('array', $items[$i]); $this->assertEquals($intervalItems[$i]['from'], $items[$i]['from']); $this->assertEquals($intervalItems[$i]['to'], $items[$i]['to']); diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/ConstantUsageSniffTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/ConstantUsageSniffTest.php index 39ad80850dd30..06a8a0accc732 100644 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/ConstantUsageSniffTest.php +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/ConstantUsageSniffTest.php @@ -67,7 +67,8 @@ private function tokenizeString($fileContent) $lineNumber = 1; $tokens = token_get_all($fileContent); $snifferTokens = []; - for ($i = 0; $i < count($tokens); $i++) { + $tokensCount = count($tokens); + for ($i = 0; $i < $tokensCount; $i++) { $content = is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i]; $snifferTokens[$i]['line'] = $lineNumber; $snifferTokens[$i]['content'] = $content; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php index 2a6079d619d4c..0560158a82668 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php @@ -501,8 +501,9 @@ private function _checkConstantWithClasspath($constant, $class, $replacement, $c { $classPathParts = explode('\\', $class); $classPartialPath = ''; - for ($i = count($classPathParts) - 1; $i >= 0; $i--) { - if ($i === (count($classPathParts) - 1)) { + $classPathPartsCount = count($classPathParts); + for ($i = $classPathPartsCount - 1; $i >= 0; $i--) { + if ($i === ($classPathPartsCount - 1)) { $classPartialPath = $classPathParts[$i] . $classPartialPath; } else { $classPartialPath = $classPathParts[$i] . '\\' . $classPartialPath; diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Language/DictionaryTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Language/DictionaryTest.php index 472fff4f4f287..67518a6c7d142 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Language/DictionaryTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Language/DictionaryTest.php @@ -52,7 +52,8 @@ public function testDictionaryGetter() } $file = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\File\ReadInterface::class); - for ($i = 0; $i < count($data); $i++) { + $dataCount = count($data); + for ($i = 0; $i < $dataCount; $i++) { $file->expects($this->at($i))->method('readCsv')->will($this->returnValue($data[$i])); } $file->expects($this->at($i))->method('readCsv')->will($this->returnValue(false)); diff --git a/lib/internal/Magento/Framework/Archive.php b/lib/internal/Magento/Framework/Archive.php index d43c976431b51..7c4b6984b3fc4 100644 --- a/lib/internal/Magento/Framework/Archive.php +++ b/lib/internal/Magento/Framework/Archive.php @@ -96,14 +96,15 @@ public function pack($source, $destination = 'packed.tgz', $skipRoot = false) { $archivers = $this->_getArchivers($destination); $interimSource = ''; - for ($i = 0; $i < count($archivers); $i++) { - if ($i == count($archivers) - 1) { + $archiversCount = count($archivers); + for ($i = 0; $i < $archiversCount; $i++) { + if ($i == $archiversCount - 1) { $packed = $destination; } else { $packed = dirname($destination) . '/~tmp-' . microtime(true) . $archivers[$i] . '.' . $archivers[$i]; } $source = $this->_getArchiver($archivers[$i])->pack($source, $packed, $skipRoot); - if ($interimSource && $i < count($archivers)) { + if ($interimSource && $i < $archiversCount) { unlink($interimSource); } $interimSource = $source; diff --git a/lib/internal/Magento/Framework/Cache/Backend/Memcached.php b/lib/internal/Magento/Framework/Cache/Backend/Memcached.php index c5e7a7e9e7c09..c793714f69153 100644 --- a/lib/internal/Magento/Framework/Cache/Backend/Memcached.php +++ b/lib/internal/Magento/Framework/Cache/Backend/Memcached.php @@ -84,7 +84,8 @@ public function save($data, $id, $tags = [], $specificLifetime = false) if (is_string($data) && strlen($data) > $this->_options['slab_size']) { $dataChunks = str_split($data, $this->_options['slab_size']); - for ($i = 0, $cnt = count($dataChunks); $i < $cnt; $i++) { + $dataChunksCount = count($dataChunks); + for ($i = 0, $cnt = $dataChunksCount; $i < $cnt; $i++) { $chunkId = $this->_getChunkId($id, $i); if (!parent::save($dataChunks[$i], $chunkId, $tags, $specificLifetime)) { diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index 10b6a17ea57dc..27797d0867924 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -374,7 +374,8 @@ protected function getVariable($value, $default = '{no_value_defined}') $stackVars = $tokenizer->tokenize(); $result = $default; $last = 0; - for ($i = 0; $i < count($stackVars); $i++) { + $stackVarsCount = count($stackVars); + for ($i = 0; $i < $stackVarsCount; $i++) { if ($i == 0 && isset($this->templateVars[$stackVars[$i]['name']])) { // Getting of template value $stackVars[$i]['variable'] = & $this->templateVars[$stackVars[$i]['name']]; diff --git a/lib/internal/Magento/Framework/System/Ftp.php b/lib/internal/Magento/Framework/System/Ftp.php index ad0673ff5d7d3..c915d50741dd2 100644 --- a/lib/internal/Magento/Framework/System/Ftp.php +++ b/lib/internal/Magento/Framework/System/Ftp.php @@ -56,7 +56,8 @@ public function mkdirRecursive($path, $mode = 0777) $dir = explode("/", $path); $path = ""; $ret = true; - for ($i = 0; $i < count($dir); $i++) { + $dirCount = count($dir); + for ($i = 0; $i < $dirCount; $i++) { $path .= "/" . $dir[$i]; if (!@ftp_chdir($this->_conn, $path)) { @ftp_chdir($this->_conn, "/"); diff --git a/setup/src/Magento/Setup/Module/I18n/Parser/Adapter/Html.php b/setup/src/Magento/Setup/Module/I18n/Parser/Adapter/Html.php index a4e3063abece4..9112192f6eb27 100644 --- a/setup/src/Magento/Setup/Module/I18n/Parser/Adapter/Html.php +++ b/setup/src/Magento/Setup/Module/I18n/Parser/Adapter/Html.php @@ -31,7 +31,8 @@ protected function _parse() $results = []; preg_match_all(Filter::CONSTRUCTION_PATTERN, $data, $results, PREG_SET_ORDER); - for ($i = 0; $i < count($results); $i++) { + $resultsCount = count($results); + for ($i = 0; $i < $resultsCount; $i++) { if ($results[$i][1] === Filter::TRANS_DIRECTIVE_NAME) { $directive = []; if (preg_match(Filter::TRANS_DIRECTIVE_REGEX, $results[$i][2], $directive) !== 1) { @@ -43,7 +44,8 @@ protected function _parse() } preg_match_all(self::HTML_FILTER, $data, $results, PREG_SET_ORDER); - for ($i = 0; $i < count($results); $i++) { + $resultsCount = count($results); + for ($i = 0; $i < $resultsCount; $i++) { if (!empty($results[$i]['value'])) { $this->_addPhrase($results[$i]['value']); } diff --git a/setup/src/Magento/Setup/Module/I18n/Parser/Adapter/Js.php b/setup/src/Magento/Setup/Module/I18n/Parser/Adapter/Js.php index 4095b12c9a6c6..0f6a402e2609c 100644 --- a/setup/src/Magento/Setup/Module/I18n/Parser/Adapter/Js.php +++ b/setup/src/Magento/Setup/Module/I18n/Parser/Adapter/Js.php @@ -22,7 +22,8 @@ protected function _parse() $fileRow = fgets($fileHandle, 4096); $results = []; preg_match_all('/mage\.__\(\s*([\'"])(.*?[^\\\])\1.*?[),]/', $fileRow, $results, PREG_SET_ORDER); - for ($i = 0; $i < count($results); $i++) { + $resultsCount = count($results); + for ($i = 0; $i < $resultsCount; $i++) { if (isset($results[$i][2])) { $quote = $results[$i][1]; $this->_addPhrase($quote . $results[$i][2] . $quote, $lineNumber); @@ -30,7 +31,8 @@ protected function _parse() } preg_match_all('/\\$t\(\s*([\'"])(.*?[^\\\])\1.*?[),]/', $fileRow, $results, PREG_SET_ORDER); - for ($i = 0; $i < count($results); $i++) { + $resultsCount = count($results); + for ($i = 0; $i < $resultsCount; $i++) { if (isset($results[$i][2])) { $quote = $results[$i][1]; $this->_addPhrase($quote . $results[$i][2] . $quote, $lineNumber); From b65b49f1bae762988574a9d23f3892f3f65cc913 Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Thu, 18 Oct 2018 12:12:23 +0530 Subject: [PATCH 130/309] Fix SKU limit in import new products for 2.3 with backward compatible allow 64 characters for SKU --- .../Model/Import/Product/Validator.php | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index b184334b179ea..793ad935363d3 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -71,7 +71,7 @@ protected function textValidation($attrCode, $type) if ($type == 'text') { $valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH; } else if ($attrCode == Product::COL_SKU) { - $valid = $this->string->strlen($val) < SKU::SKU_MAX_LENGTH; + $valid = $this->string->strlen($val) <= SKU::SKU_MAX_LENGTH; } else { $valid = $this->string->strlen($val) < Product::DB_MAX_VARCHAR_LENGTH; } @@ -153,12 +153,7 @@ public function isRequiredAttributeValid($attrCode, array $attributeParams, arra $doCheck = true; } - if ($doCheck === true) { - return isset($rowData[$attrCode]) - && strlen(trim($rowData[$attrCode])) - && trim($rowData[$attrCode]) !== $this->context->getEmptyAttributeValueConstant(); - } - return true; + return $doCheck ? isset($rowData[$attrCode]) && strlen(trim($rowData[$attrCode])) : true; } /** @@ -196,11 +191,6 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) if (!strlen(trim($rowData[$attrCode]))) { return true; } - - if ($rowData[$attrCode] === $this->context->getEmptyAttributeValueConstant() && !$attrParams['is_required']) { - return true; - } - switch ($attrParams['type']) { case 'varchar': case 'text': @@ -222,12 +212,6 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) break; } } - - $uniqueValues = array_unique($values); - if (count($uniqueValues) != count($values)) { - $valid = false; - $this->_addMessages([RowValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES]); - } break; case 'datetime': $val = trim($rowData[$attrCode]); From 57902258f066e4802f026f64e12f48663475b0b3 Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Thu, 18 Oct 2018 12:31:58 +0530 Subject: [PATCH 131/309] Fix SKU limit in import new products for 2.3 with backward compatible allow 64 characters --- .../Model/Import/Product/Validator.php | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 793ad935363d3..21f7f87875e12 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -153,7 +153,12 @@ public function isRequiredAttributeValid($attrCode, array $attributeParams, arra $doCheck = true; } - return $doCheck ? isset($rowData[$attrCode]) && strlen(trim($rowData[$attrCode])) : true; + if ($doCheck === true) { + return isset($rowData[$attrCode]) + && strlen(trim($rowData[$attrCode])) + && trim($rowData[$attrCode]) !== $this->context->getEmptyAttributeValueConstant(); + } + return true; } /** @@ -191,6 +196,11 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) if (!strlen(trim($rowData[$attrCode]))) { return true; } + + if ($rowData[$attrCode] === $this->context->getEmptyAttributeValueConstant() && !$attrParams['is_required']) { + return true; + } + switch ($attrParams['type']) { case 'varchar': case 'text': @@ -212,6 +222,12 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) break; } } + + $uniqueValues = array_unique($values); + if (count($uniqueValues) != count($values)) { + $valid = false; + $this->_addMessages([RowValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES]); + } break; case 'datetime': $val = trim($rowData[$attrCode]); @@ -328,4 +344,4 @@ public function init($context) $validator->init($context); } } -} +} \ No newline at end of file From 2fc6596cb403a788a0226b1ae03bea00b68bf26d Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <mikalai_shostka@epam.com> Date: Thu, 18 Oct 2018 15:57:24 +0300 Subject: [PATCH 132/309] MAGETWO-95445: Category Flat Data indexer doesnt show status as reindex required after deleting store/storeview - Delete abandoned tables in full reindex --- .../Catalog/Helper/Product/Flat/Indexer.php | 32 ---------- .../Indexer/Category/Flat/Action/Full.php | 63 +++++++++++-------- 2 files changed, 38 insertions(+), 57 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Product/Flat/Indexer.php b/app/code/Magento/Catalog/Helper/Product/Flat/Indexer.php index 0366e01ee86ea..93eaa23b89f16 100644 --- a/app/code/Magento/Catalog/Helper/Product/Flat/Indexer.php +++ b/app/code/Magento/Catalog/Helper/Product/Flat/Indexer.php @@ -466,17 +466,6 @@ public function getFlatTableName($storeId) return sprintf('%s_%s', $this->getTable('catalog_product_flat'), $storeId); } - /** - * Retrieve Catalog Product Flat Table name - * - * @param int $storeId - * @return string - */ - private function getCategoryFlatTableName(int $storeId): string - { - return sprintf('%s_store_%s', $this->getTable('catalog_category_flat'), $storeId); - } - /** * Retrieve loaded attribute by code * @@ -526,25 +515,4 @@ public function deleteAbandonedStoreFlatTables() $connection->dropTable($table); } } - - /** - * Delete all category flat tables for not existing stores - * - * @return void - */ - public function deleteAbandonedStoreCategoryFlatTables() - { - $connection = $this->_resource->getConnection(); - $existentTables = $connection->getTables($connection->getTableName('catalog_category_flat_store_%')); - $actualStoreTables = []; - foreach ($this->_storeManager->getStores() as $store) { - $actualStoreTables[] = $this->getCategoryFlatTableName($store->getId()); - } - - $tablesToDelete = array_diff($existentTables, $actualStoreTables); - - foreach ($tablesToDelete as $table) { - $connection->dropTable($table); - } - } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php index c1e6decf6afef..624001b498449 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php @@ -5,8 +5,6 @@ */ namespace Magento\Catalog\Model\Indexer\Category\Flat\Action; -use Magento\Framework\App\ResourceConnection; - /** * Class for full reindex flat categories */ @@ -24,28 +22,6 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Flat\AbstractAction */ protected $allowTableChanges = true; - /** - * @var \Magento\Catalog\Helper\Product\Flat\Indexer - */ - private $indexer; - - /** - * @param ResourceConnection $resource - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper - * @param \Magento\Catalog\Helper\Product\Flat\Indexer $indexer - */ - public function __construct( - ResourceConnection $resource, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, - \Magento\Catalog\Helper\Product\Flat\Indexer $indexer = null - ) { - $this->indexer = $indexer ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Helper\Product\Flat\Indexer::class); - parent::__construct($resource, $storeManager, $resourceHelper); - } - /** * Add suffix to table name to show it is old * @@ -196,6 +172,43 @@ protected function switchTables(array $stores = []) return $this; } + /** + * Retrieve all actual Catalog Product Flat Table names + * + * @return string[] + */ + private function getActualStoreTablesForCategoryFlat(): array + { + $actualStoreTables = []; + foreach ($this->storeManager->getStores() as $store) { + $actualStoreTables[] = sprintf( + '%s_store_%s', + $this->connection->getTableName('catalog_category_flat'), $store->getId() + ); + } + + return $actualStoreTables; + } + + /** + * Delete all category flat tables for not existing stores + * + * @return void + */ + private function deleteAbandonedStoreCategoryFlatTables(): void + { + $existentTables = $this->connection->getTables( + $this->connection->getTableName('catalog_category_flat_store_%') + ); + $actualStoreTables = $this->getActualStoreTablesForCategoryFlat(); + + $tablesToDelete = array_diff($existentTables, $actualStoreTables); + + foreach ($tablesToDelete as $table) { + $this->connection->dropTable($table); + } + } + /** * Transactional rebuild flat data from eav * @@ -211,7 +224,7 @@ public function reindexAll() $stores = $this->storeManager->getStores(); $this->populateFlatTables($stores); $this->switchTables($stores); - $this->indexer->deleteAbandonedStoreCategoryFlatTables(); + $this->deleteAbandonedStoreCategoryFlatTables(); $this->allowTableChanges = true; return $this; From bb4f60197ea9af573434f838ed61c014e3fbd132 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Thu, 18 Oct 2018 17:09:34 +0300 Subject: [PATCH 133/309] GraphQL-174: Absolute image paths for Products --- .../Model/Resolver/Product/Image.php | 66 ------------------- .../Model/Resolver/Product/ProductImage.php | 57 +++++++--------- 2 files changed, 22 insertions(+), 101 deletions(-) delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Image.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Image.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Image.php deleted file mode 100644 index 6830aecb78f10..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Image.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogGraphQl\Model\Resolver\Product; - -use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\Product\ImageFactory; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; - -/** - * Returns product's image. If the image is not set, returns a placeholder - */ -class Image implements ResolverInterface -{ - /** - * Product image factory - * - * @var ImageFactory - */ - private $productImageFactory; - - /** - * @param ImageFactory $productImageFactory - */ - public function __construct( - ImageFactory $productImageFactory - ) { - $this->productImageFactory = $productImageFactory; - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ): array { - if (!isset($value['model'])) { - throw new LocalizedException(__('"model" value should be specified')); - } - /** @var Product $product */ - $product = $value['model']; - $imageType = $field->getName(); - $path = $product->getData($imageType); - - $image = $this->productImageFactory->create(); - $image->setDestinationSubdir($imageType) - ->setBaseFile($path); - $imageUrl = $image->getUrl(); - - return [ - 'url' => $imageUrl, - 'path' => $path, - ]; - } -} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php index 9dc0dd68ee457..3caa79e68c235 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php @@ -8,43 +8,34 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Catalog\Model\Product; -use Magento\Catalog\Helper\ImageFactory as CatalogImageHelperFactory; +use Magento\Catalog\Model\Product\ImageFactory; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Store\Model\StoreManagerInterface; /** - * Return product image paths by image type. + * Returns product's image data */ class ProductImage implements ResolverInterface { /** - * @var CatalogImageHelperFactory - */ - private $catalogImageHelperFactory; - - /** - * @var StoreManagerInterface + * Product image factory + * + * @var ImageFactory */ - private $storeManager; + private $productImageFactory; /** - * @param CatalogImageHelperFactory $catalogImageHelperFactory - * @param StoreManagerInterface $storeManager + * @param ImageFactory $productImageFactory */ public function __construct( - CatalogImageHelperFactory $catalogImageHelperFactory, - StoreManagerInterface $storeManager + ImageFactory $productImageFactory ) { - $this->catalogImageHelperFactory = $catalogImageHelperFactory; - $this->storeManager = $storeManager; + $this->productImageFactory = $productImageFactory; } /** - * Get product's image by type. - * * @inheritdoc */ public function resolve( @@ -53,30 +44,26 @@ public function resolve( ResolveInfo $info, array $value = null, array $args = null - ) { + ): array { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var Product $product */ $product = $value['model']; $imageType = $field->getName(); - /** @var \Magento\Catalog\Helper\Image $catalogImageHelper */ - $catalogImageHelper = $this->catalogImageHelperFactory->create(); + $imagePath = $product->getData($imageType); + $imageLabel = $product->getData($imageType . '_' . 'label') ?? $product->getName(); - /** @var \Magento\Catalog\Helper\Image $image */ - $image = $catalogImageHelper->init( - $product, - 'product_' . $imageType, - ['type' => $imageType] - ); + $image = $this->productImageFactory->create(); + $image->setDestinationSubdir($imageType) + ->setBaseFile($imagePath); + $imageUrl = $image->getUrl(); - $imageData = [ - 'url' => $image->getUrl(), - 'path' => $product->getData($imageType), - 'label' => $image->getLabel() + return [ + 'url' => $imageUrl, + 'path' => $imagePath, + 'label' => $imageLabel, ]; - - return $imageData; } } From 8433083f3c6a0c33500d7ad1ce7ead82c59a9cfe Mon Sep 17 00:00:00 2001 From: Timon de Groot <timon@marissen.net> Date: Fri, 19 Oct 2018 11:55:18 +0200 Subject: [PATCH 134/309] Restructure estimate-service.js --- .../web/js/model/cart/estimate-service.js | 101 ++++++++++-------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js index 76e3d911e7d3f..f1da0ed7916e7 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js @@ -14,55 +14,70 @@ define([ 'use strict'; var rateProcessors = [], - totalsProcessors = []; + totalsProcessors = [], - quote.shippingAddress.subscribe(function () { - var type = quote.shippingAddress().getType(); + /** + * Estimate totals for shipping address and update shipping rates. + */ + estimateTotalsAndUpdateRates = function() { + var type = quote.shippingAddress().getType(); - if ( - quote.isVirtual() || - window.checkoutConfig.activeCarriers && window.checkoutConfig.activeCarriers.length === 0 - ) { - // update totals block when estimated address was set - totalsProcessors['default'] = totalsDefaultProvider; - totalsProcessors[type] ? - totalsProcessors[type].estimateTotals(quote.shippingAddress()) : - totalsProcessors['default'].estimateTotals(quote.shippingAddress()); - } else { - // check if user data not changed -> load rates from cache - if (!cartCache.isChanged('address', quote.shippingAddress()) && - !cartCache.isChanged('cartVersion', customerData.get('cart')()['data_id']) && - cartCache.get('rates') + if ( + quote.isVirtual() || + window.checkoutConfig.activeCarriers && window.checkoutConfig.activeCarriers.length === 0 ) { - shippingService.setShippingRates(cartCache.get('rates')); + // update totals block when estimated address was set + totalsProcessors['default'] = totalsDefaultProvider; + totalsProcessors[type] ? + totalsProcessors[type].estimateTotals(quote.shippingAddress()) : + totalsProcessors['default'].estimateTotals(quote.shippingAddress()); + } else { + // check if user data not changed -> load rates from cache + if (!cartCache.isChanged('address', quote.shippingAddress()) && + !cartCache.isChanged('cartVersion', customerData.get('cart')()['data_id']) && + cartCache.get('rates') + ) { + shippingService.setShippingRates(cartCache.get('rates')); - return; + return; + } + + // update rates list when estimated address was set + rateProcessors['default'] = defaultProcessor; + rateProcessors[type] ? + rateProcessors[type].getRates(quote.shippingAddress()) : + rateProcessors['default'].getRates(quote.shippingAddress()); + + // save rates to cache after load + shippingService.getShippingRates().subscribe(function (rates) { + cartCache.set('rates', rates); + }); } + }, - // update rates list when estimated address was set - rateProcessors['default'] = defaultProcessor; - rateProcessors[type] ? - rateProcessors[type].getRates(quote.shippingAddress()) : - rateProcessors['default'].getRates(quote.shippingAddress()); + /** + * Estimate totals for shipping address. + */ + estimateTotalsShipping = function() { + totalsDefaultProvider.estimateTotals(quote.shippingAddress()); + }, - // save rates to cache after load - shippingService.getShippingRates().subscribe(function (rates) { - cartCache.set('rates', rates); - }); - } - }); - quote.shippingMethod.subscribe(function () { - totalsDefaultProvider.estimateTotals(quote.shippingAddress()); - }); - quote.billingAddress.subscribe(function () { - var type = quote.billingAddress().getType(); + /** + * Estimate totals for billing address. + */ + estimateTotalsBilling = function() { + var type = quote.billingAddress().getType(); + + if (quote.isVirtual()) { + // update totals block when estimated address was set + totalsProcessors['default'] = totalsDefaultProvider; + totalsProcessors[type] ? + totalsProcessors[type].estimateTotals(quote.billingAddress()) : + totalsProcessors['default'].estimateTotals(quote.billingAddress()); + } + }; - if (quote.isVirtual()) { - // update totals block when estimated address was set - totalsProcessors['default'] = totalsDefaultProvider; - totalsProcessors[type] ? - totalsProcessors[type].estimateTotals(quote.billingAddress()) : - totalsProcessors['default'].estimateTotals(quote.billingAddress()); - } - }); + quote.shippingAddress.subscribe(estimateTotalsAndUpdateRates); + quote.shippingMethod.subscribe(estimateTotalsShipping); + quote.billingAddress.subscribe(estimateTotalsBilling); }); From 4fd6a03fe5367032b30c0c828246756d8f1db24c Mon Sep 17 00:00:00 2001 From: Timon de Groot <timon@marissen.net> Date: Fri, 19 Oct 2018 11:55:55 +0200 Subject: [PATCH 135/309] Estimate totals when cart data changes --- .../Checkout/view/frontend/web/js/model/cart/estimate-service.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js index f1da0ed7916e7..f85a1385e0815 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js @@ -80,4 +80,5 @@ define([ quote.shippingAddress.subscribe(estimateTotalsAndUpdateRates); quote.shippingMethod.subscribe(estimateTotalsShipping); quote.billingAddress.subscribe(estimateTotalsBilling); + customerData.get('cart').subscribe(estimateTotalsShipping); }); From 9688034346f5b322b9767ab6af5c8163e67b28ac Mon Sep 17 00:00:00 2001 From: Jakub Idziak <j.idziak@macopedia.pl> Date: Thu, 18 Oct 2018 16:13:36 +0200 Subject: [PATCH 136/309] ISSUE-5021 - fixed place order for custom shipping methods with underscore in carrier code --- .../Magento/Checkout/Model/PaymentInformationManagement.php | 5 ++--- .../Test/Unit/Model/PaymentInformationManagementTest.php | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php index 164109177d4e9..947f7b896f67a 100644 --- a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php @@ -115,9 +115,8 @@ public function savePaymentInformation( $quote->setDataChanges(true); $shippingAddress = $quote->getShippingAddress(); if ($shippingAddress && $shippingAddress->getShippingMethod()) { - $shippingDataArray = explode('_', $shippingAddress->getShippingMethod()); - $shippingCarrier = array_shift($shippingDataArray); - $shippingAddress->setLimitCarrier($shippingCarrier); + $shippingRate = $shippingAddress->getShippingRateByCode($shippingAddress->getShippingMethod()); + $shippingAddress->setLimitCarrier($shippingRate->getCarrier()); } } $this->paymentMethodManagement->set($cartId, $paymentMethod); diff --git a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php index 77c15fccfa8ae..ea841e86586ba 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php @@ -172,9 +172,11 @@ private function getMockForAssignBillingAddress($cartId, $billingAddressMock) $billingAddressId = 1; $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); $quoteBillingAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); + $shippingRate = $this->createPartialMock(\Magento\Quote\Model\Quote\Address\Rate::class, []); + $shippingRate->setCarrier('flatrate'); $quoteShippingAddress = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, - ['setLimitCarrier', 'getShippingMethod'] + ['setLimitCarrier', 'getShippingMethod', 'getShippingRateByCode'] ); $this->cartRepositoryMock->expects($this->any())->method('getActive')->with($cartId)->willReturn($quoteMock); $quoteMock->expects($this->once())->method('getBillingAddress')->willReturn($quoteBillingAddress); @@ -183,6 +185,7 @@ private function getMockForAssignBillingAddress($cartId, $billingAddressMock) $quoteMock->expects($this->once())->method('removeAddress')->with($billingAddressId); $quoteMock->expects($this->once())->method('setBillingAddress')->with($billingAddressMock); $quoteMock->expects($this->once())->method('setDataChanges')->willReturnSelf(); + $quoteShippingAddress->expects($this->any())->method('getShippingRateByCode')->willReturn($shippingRate); $quoteShippingAddress->expects($this->any())->method('getShippingMethod')->willReturn('flatrate_flatrate'); $quoteShippingAddress->expects($this->once())->method('setLimitCarrier')->with('flatrate')->willReturnSelf(); } From 7584f715e27ec3fb6d7dcf6511999f27466e35ae Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 19 Oct 2018 08:01:01 -0400 Subject: [PATCH 137/309] Allow set billing information via API with existing address Not setting the customerId with an existing address caused a `NoSuchEntityException` to be thrown during address validation https://github.com/magento/magento2/blob/56af1e73ce21867b770a7458ab6e109f4a1eface/app/code/Magento/Quote/Model/QuoteAddressValidator.php#L84 Fixes #17485 --- app/code/Magento/Quote/Model/BillingAddressManagement.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Quote/Model/BillingAddressManagement.php b/app/code/Magento/Quote/Model/BillingAddressManagement.php index 70a8c94fefad0..e8af185b8c2f0 100644 --- a/app/code/Magento/Quote/Model/BillingAddressManagement.php +++ b/app/code/Magento/Quote/Model/BillingAddressManagement.php @@ -77,6 +77,7 @@ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $addres { /** @var \Magento\Quote\Model\Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); + $address->setCustomerId($quote->getCustomerId()); $quote->removeAddress($quote->getBillingAddress()->getId()); $quote->setBillingAddress($address); try { From 9a249e97c4a645f513a1f0aaaeed5957689c8e7f Mon Sep 17 00:00:00 2001 From: Timon de Groot <timon@marissen.net> Date: Fri, 19 Oct 2018 14:02:15 +0200 Subject: [PATCH 138/309] Fix js static error --- .../view/frontend/web/js/model/cart/estimate-service.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js index f85a1385e0815..54e496131972e 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js @@ -19,7 +19,7 @@ define([ /** * Estimate totals for shipping address and update shipping rates. */ - estimateTotalsAndUpdateRates = function() { + estimateTotalsAndUpdateRates = function () { var type = quote.shippingAddress().getType(); if ( @@ -58,14 +58,14 @@ define([ /** * Estimate totals for shipping address. */ - estimateTotalsShipping = function() { + estimateTotalsShipping = function () { totalsDefaultProvider.estimateTotals(quote.shippingAddress()); }, /** * Estimate totals for billing address. */ - estimateTotalsBilling = function() { + estimateTotalsBilling = function () { var type = quote.billingAddress().getType(); if (quote.isVirtual()) { From c3e4b0414e703d04934584792daf948d844305ba Mon Sep 17 00:00:00 2001 From: Timon de Groot <timon@marissen.net> Date: Fri, 19 Oct 2018 14:16:39 +0200 Subject: [PATCH 139/309] Add test for cart data mutations/estimation call --- .../Checkout/frontend/js/model/cart/estimate-service.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js index 3d8325a3ecd54..31009555bd9f9 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js @@ -150,5 +150,10 @@ define([ }); expect(mocks['Magento_Checkout/js/model/cart/totals-processor/default'].estimateTotals).toHaveBeenCalled(); }); + + it('test subscribe when cart data was changed', function () { + mocks['Magento_Customer/js/customer-data'].get('cart')({ data_id: 2 }); + expect(mocks['Magento_Checkout/js/model/cart/totals-processor/default'].estimateTotals).toHaveBeenCalled(); + }); }); }); From 3d3b45f384b2166fc86b5d4ec8eb856236336f64 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Fri, 19 Oct 2018 18:54:47 +0300 Subject: [PATCH 140/309] GraphQL-174: Absolute image paths for Products --- .../Model/Resolver/Product/ProductImage.php | 31 +------ .../Resolver/Product/ProductImage/Label.php | 85 +++++++++++++++++++ .../Resolver/Product/ProductImage/Path.php | 45 ++++++++++ .../Resolver/Product/ProductImage/Url.php | 75 ++++++++++++++++ .../CatalogGraphQl/etc/schema.graphqls | 6 +- 5 files changed, 211 insertions(+), 31 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Path.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php index 3caa79e68c235..d1566162472b0 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php @@ -8,7 +8,6 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\Product\ImageFactory; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -19,22 +18,6 @@ */ class ProductImage implements ResolverInterface { - /** - * Product image factory - * - * @var ImageFactory - */ - private $productImageFactory; - - /** - * @param ImageFactory $productImageFactory - */ - public function __construct( - ImageFactory $productImageFactory - ) { - $this->productImageFactory = $productImageFactory; - } - /** * @inheritdoc */ @@ -48,22 +31,14 @@ public function resolve( if (!isset($value['model'])) { throw new LocalizedException(__('"model" value should be specified')); } + /** @var Product $product */ $product = $value['model']; $imageType = $field->getName(); - $imagePath = $product->getData($imageType); - $imageLabel = $product->getData($imageType . '_' . 'label') ?? $product->getName(); - - $image = $this->productImageFactory->create(); - $image->setDestinationSubdir($imageType) - ->setBaseFile($imagePath); - $imageUrl = $image->getUrl(); - return [ - 'url' => $imageUrl, - 'path' => $imagePath, - 'label' => $imageLabel, + 'model' => $product, + 'image_type' => $imageType, ]; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php new file mode 100644 index 0000000000000..5b752e7184893 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductImage; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Product as ProductResourceModel; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Returns product's image label + */ +class Label implements ResolverInterface +{ + /** + * @var ProductResourceModel + */ + private $productResource; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @param ProductResourceModel $productResource + * @param StoreManagerInterface $storeManager + */ + public function __construct( + ProductResourceModel $productResource, + StoreManagerInterface $storeManager + ) { + $this->productResource = $productResource; + $this->storeManager = $storeManager; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($value['image_type'])) { + throw new LocalizedException(__('"image_type" value should be specified')); + } + + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + + /** @var Product $product */ + $product = $value['model']; + + $imageLabel = $this->getImageLabel((int)$product->getEntityId(), $value['image_type']); + return $imageLabel; + } + + /** + * @param int $productId + * @param string $imageType + * @return string + */ + private function getImageLabel(int $productId, string $imageType): string + { + $storeId = $this->storeManager->getStore()->getId(); + + $imageLabel = $this->productResource->getAttributeRawValue($productId, $imageType . '_label', $storeId); + if (empty($imageLabel)) { + $imageLabel = $this->productResource->getAttributeRawValue($productId, 'name', $storeId); + } + return $imageLabel; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Path.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Path.php new file mode 100644 index 0000000000000..249f1dc40b19a --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Path.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductImage; + +use Magento\Catalog\Model\Product; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * Returns product's image path + */ +class Path implements ResolverInterface +{ + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($value['image_type'])) { + throw new LocalizedException(__('"image_type" value should be specified')); + } + + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + + /** @var Product $product */ + $product = $value['model']; + + $imagePath = $product->getData($value['image_type']); + return $imagePath; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php new file mode 100644 index 0000000000000..d9136e742b178 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductImage; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\ImageFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * Returns product's image url + */ +class Url implements ResolverInterface +{ + /** + * @var ImageFactory + */ + private $productImageFactory; + + /** + * @param ImageFactory $productImageFactory + */ + public function __construct( + ImageFactory $productImageFactory + ) { + $this->productImageFactory = $productImageFactory; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($value['image_type'])) { + throw new LocalizedException(__('"image_type" value should be specified')); + } + + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + + /** @var Product $product */ + $product = $value['model']; + $imagePath = $product->getData($value['image_type']); + + $imageUrl = $this->getImageUrl($value['image_type'], $imagePath); + return $imageUrl; + } + + /** + * @param string $imageType + * @param string $imagePath + * @return string + */ + private function getImageUrl(string $imageType, string $imagePath): string + { + $image = $this->productImageFactory->create(); + $image->setDestinationSubdir($imageType) + ->setBaseFile($imagePath); + $imageUrl = $image->getUrl(); + return $imageUrl; + } +} diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index f87e524a26b3f..fb042c7e59c15 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -350,9 +350,9 @@ type CustomizableFileValue @doc(description: "CustomizableFileValue defines the } type ProductImage @doc(description: "Product image information. Contains image relative path, URL and label") { - url: String - path: String - label: String + url: String @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage\\Url") + path: String @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage\\Path") + label: String @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage\\Label") } interface CustomizableOptionInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\CustomizableOptionTypeResolver") @doc(description: "The CustomizableOptionInterface contains basic information about a customizable option. It can be implemented by several types of configurable options.") { From c366d477010c08e2c9ffdbe09edcd61b1082efa2 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Fri, 19 Oct 2018 19:59:59 +0300 Subject: [PATCH 141/309] GraphQL-174: Absolute image paths for Products --- .../Resolver/Product/ProductImage/Label.php | 27 ++++++++++++------- .../Resolver/Product/ProductImage/Url.php | 4 +-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php index 5b752e7184893..e9020c2df5333 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Label.php @@ -62,24 +62,33 @@ public function resolve( /** @var Product $product */ $product = $value['model']; + $imageType = $value['image_type']; + $imagePath = $product->getData($imageType); + $productId = (int)$product->getEntityId(); + + // null if image is not set + if (null === $imagePath) { + return $this->getAttributeValue($productId, 'name'); + } + + $imageLabel = $this->getAttributeValue($productId, $imageType . '_label'); + if (null === $imageLabel) { + $imageLabel = $this->getAttributeValue($productId, 'name'); + } - $imageLabel = $this->getImageLabel((int)$product->getEntityId(), $value['image_type']); return $imageLabel; } /** * @param int $productId - * @param string $imageType - * @return string + * @param string $attributeCode + * @return null|string Null if attribute value is not exists */ - private function getImageLabel(int $productId, string $imageType): string + private function getAttributeValue(int $productId, string $attributeCode): ?string { $storeId = $this->storeManager->getStore()->getId(); - $imageLabel = $this->productResource->getAttributeRawValue($productId, $imageType . '_label', $storeId); - if (empty($imageLabel)) { - $imageLabel = $this->productResource->getAttributeRawValue($productId, 'name', $storeId); - } - return $imageLabel; + $value = $this->productResource->getAttributeRawValue($productId, $attributeCode, $storeId); + return is_array($value) && empty($value) ? null : $value; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php index d9136e742b178..c6659080ee9e9 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php @@ -61,10 +61,10 @@ public function resolve( /** * @param string $imageType - * @param string $imagePath + * @param string|null $imagePath Null if image is not set * @return string */ - private function getImageUrl(string $imageType, string $imagePath): string + private function getImageUrl(string $imageType, ?string $imagePath): string { $image = $this->productImageFactory->create(); $image->setDestinationSubdir($imageType) From 0a253644a43f39d8ab40701011512534f4fc34bb Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Fri, 19 Oct 2018 23:39:54 +0300 Subject: [PATCH 142/309] Fixed issue #4468 "Unable to insert multiple catalog product list widgets (with pager) in CMS page" --- .../Block/Product/ProductsList.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php index 5b9905e0c9d81..a0cfc08406189 100644 --- a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php +++ b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php @@ -198,7 +198,7 @@ public function getProductPriceHtml( ? $arguments['display_minimal_price'] : true; - /** @var \Magento\Framework\Pricing\Render $priceRender */ + /** @var \Magento\Framework\Pricing\Render $priceRender */ $priceRender = $this->getLayout()->getBlock('product.price.render.default'); if (!$priceRender) { $priceRender = $this->getLayout()->createBlock( @@ -347,7 +347,7 @@ public function getPagerHtml() if (!$this->pager) { $this->pager = $this->getLayout()->createBlock( \Magento\Catalog\Block\Product\Widget\Html\Pager::class, - 'widget.products.list.pager' + $this->getWidgetPagerBlockName() ); $this->pager->setUseContainer(true) @@ -408,4 +408,19 @@ private function getPriceCurrency() } return $this->priceCurrency; } + + /** + * @return string + */ + private function getWidgetPagerBlockName() + { + $pageName = $this->getData('page_var_name'); + $pagerBlockName = 'widget.products.list.pager'; + + if (!$pageName) { + return $pagerBlockName; + } + + return $pagerBlockName . '.' . $pageName; + } } From 80669e3c52cd94ce60f5ee2e31eda78584bba97e Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Sat, 20 Oct 2018 18:31:29 +0300 Subject: [PATCH 143/309] graphQl-44: added ProductTextAttributeInput --- .../Resolver/Product/ProductTextAttribute.php | 6 ++-- .../ProductTextAttribute/FormatList.php | 13 +++++-- .../Product/ProductTextAttribute/Html.php | 2 +- .../CatalogGraphQl/etc/schema.graphqls | 34 ++++++++++++++----- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php index 7649464e2e1ca..2ff6faa0a74ee 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php @@ -39,7 +39,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function resolve( Field $field, @@ -55,8 +55,8 @@ public function resolve( /* @var $product Product */ $product = $value['model']; $fieldName = $field->getName(); - $formatIdentifier = $args['format'] ?? $this->defaultFormat; - $format = $this->formatList->create($formatIdentifier); + $formatIdentifier = $args['filter']['description']['format'] ?? $this->defaultFormat; + $format = $this->formatList->getFormatByIdentifier($formatIdentifier); $result = ['content' => $format->getContent($product, $fieldName)]; return $result; diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatList.php index 2e2f21d643092..39f2074ed7592 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatList.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatList.php @@ -7,8 +7,13 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\ObjectManagerInterface; +/** + * Class FormatList + * @package Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute + */ class FormatList { /** @@ -37,10 +42,12 @@ public function __construct( * @param string $formatIdentifier * @return FormatInterface */ - public function create(string $formatIdentifier) : FormatInterface + public function getFormatByIdentifier(string $formatIdentifier) : FormatInterface { - $formatClassName = 'Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute\\' . ucfirst($formatIdentifier); - $formatInstance = $this->objectManager->get($formatClassName); + if (!isset($this->formats[$formatIdentifier])) { + throw new GraphQlInputException(__('Format %1 does not exist.', [$formatIdentifier])); + } + $formatInstance = $this->objectManager->get($this->formats[$formatIdentifier]); return $formatInstance; } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php index 75c29a3f78fac..830fbf28e3373 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php @@ -44,4 +44,4 @@ public function getContent( ): string { return $this->outputHelper->productAttribute($product, $product->getData($fieldName), $fieldName); } -} \ No newline at end of file +} diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 944cae6537bb0..a7d9ab5483d46 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -248,8 +248,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ id: Int @doc(description: "The ID number assigned to the product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\EntityIdToId") name: String @doc(description: "The product name. Customers use this name to identify the product.") sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") - description: ProductTextAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") - short_description: ProductTextAttribute @doc(description: "A short description of the product. Its use depends on the theme.") + description: ProductTextAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") + short_description: ProductTextAttribute @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") special_price: Float @doc(description: "The discounted price of the product") special_from_date: String @doc(description: "The beginning date that a product has a special price") special_to_date: String @doc(description: "The end date that a product has a special price") @@ -433,8 +433,8 @@ type CategoryProducts @doc(description: "The category products object returned i input ProductFilterInput @doc(description: "ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { name: FilterTypeInput @doc(description: "The product name. Customers use this name to identify the product.") sku: FilterTypeInput @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") - description: FilterTypeInput @doc(description: "Detailed information about the product. The value can include simple HTML tags.") - short_description: FilterTypeInput @doc(description: "A short description of the product. Its use depends on the theme.") + description: ProductTextAttributeTypeInput @doc(description: "Detailed information about the product. The value can include simple HTML tags.") + short_description: ProductTextAttributeTypeInput @doc(description: "A short description of the product. Its use depends on the theme.") price: FilterTypeInput @doc(description: "The price of an item") special_price: FilterTypeInput @doc(description: "The discounted price of the product") special_from_date: FilterTypeInput @doc(description: "The beginning date that a product has a special price") @@ -557,9 +557,25 @@ type SortFields @doc(description: "SortFields contains a default value for sort options: [SortField] @doc(description: "Available sort fields") } -type ProductTextAttribute { - content ( - format: String @doc(description: "The format of content") -): String @doc(description: "The format of content") - @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") +type ProductTextAttribute @doc(description: "Product text attribute.") { + content: String +} + +input ProductTextAttributeTypeInput @doc(description: "FilterTypeInput specifies which action will be performed in a query ") { + format: String @doc(description: "Format of the content") + eq: String @doc(description: "Equals") + finset: [String] @doc(description: "Find in set. The value can contain a set of comma-separated values") + from: String @doc(description: "From. Must be used with 'to'") + gt: String @doc(description: "Greater than") + gteq: String @doc(description: "Greater than or equal to") + in: [String] @doc(description: "In. The value can contain a set of comma-separated values") + like: String @doc(description: "Like. The specified value can contain % (percent signs) to allow matching of 0 or more characters") + lt: String @doc(description: "Less than") + lteq: String @doc(description: "Less than or equal to") + moreq: String @doc(description: "More than or equal to") + neq: String @doc(description: "Not equal to") + notnull: String @doc(description: "Not null") + null: String @doc(description: "Is null") + to: String@doc(description: "To. Must be used with 'from'") + nin: [String] @doc(description: "Not in. The value can contain a set of comma-separated values") } From ed6a2fba850caa5e2402663fee133d4f56545200 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Sat, 20 Oct 2018 12:11:28 -0400 Subject: [PATCH 144/309] Prevent exception when option text converts to false The function would incorrectly through an exception when the option text was set to a string that would evaluate to false such as "0" Fixes #13083 --- .../Magento/Eav/Model/Entity/Attribute/OptionManagement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php b/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php index 4e4e146208a47..3c3bc083fdf8f 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php @@ -141,7 +141,7 @@ public function getItems($entityType, $attributeCode) */ protected function validateOption($attribute, $optionId) { - if (!$attribute->getSource()->getOptionText($optionId)) { + if ($attribute->getSource()->getOptionText($optionId) === false) { throw new NoSuchEntityException( __( 'The "%1" attribute doesn\'t include an option with "%2" ID.', From 8bea285e9a51e5b759256c25d18b4c34fe7648ce Mon Sep 17 00:00:00 2001 From: Tomash Khamlai <tomash.khamlai@gmail.com> Date: Sat, 20 Oct 2018 22:52:03 +0300 Subject: [PATCH 145/309] Set fallback values for email and _website columns to avoid 'undefined index' error in CustomerComposite.php CLA will be signed --- .../CustomerImportExport/Model/Import/CustomerComposite.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php b/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php index 956c9695623bb..2086f41d27fa6 100644 --- a/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php +++ b/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php @@ -299,8 +299,8 @@ public function validateData() $rows = []; foreach ($source as $row) { $rows[] = [ - Address::COLUMN_EMAIL => $row[Customer::COLUMN_EMAIL], - Address::COLUMN_WEBSITE => $row[Customer::COLUMN_WEBSITE], + Address::COLUMN_EMAIL => $row[Customer::COLUMN_EMAIL] ?? null, + Address::COLUMN_WEBSITE => $row[Customer::COLUMN_WEBSITE] ?? null ]; } $source->rewind(); From 1b6ce27459ece1d6ddd2f1b3ef69114f004aeb68 Mon Sep 17 00:00:00 2001 From: GwanYeong Kim <gy741.kim@gmail.com> Date: Sun, 21 Oct 2018 21:45:51 +0900 Subject: [PATCH 146/309] Fix Useless use of Cat --- dev/travis/before_script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index 1dccc310c7a20..dbd9d1cd4fec1 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -72,7 +72,7 @@ case $TEST_SUITE in --base-path="$TRAVIS_BUILD_DIR" \ --repo='https://github.com/magento/magento2.git' \ --branch="$TRAVIS_BRANCH" - cat "$changed_files_ce" | sed 's/^/ + including /' + sed 's/^/ + including /' "$changed_files_ce" cd ../../.. ;; From c34e734c061209a766ad83d686b42ffa41378b27 Mon Sep 17 00:00:00 2001 From: Manuele Menozzi <mmenozzi@webgriffe.com> Date: Sun, 21 Oct 2018 17:20:28 +0200 Subject: [PATCH 147/309] Prevent MAGE_DIRS overwrite in pub/index.php --- pub/index.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pub/index.php b/pub/index.php index 457b83c529488..90b4778265447 100644 --- a/pub/index.php +++ b/pub/index.php @@ -25,12 +25,15 @@ } $params = $_SERVER; -$params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] = [ - DirectoryList::PUB => [DirectoryList::URL_PATH => ''], - DirectoryList::MEDIA => [DirectoryList::URL_PATH => 'media'], - DirectoryList::STATIC_VIEW => [DirectoryList::URL_PATH => 'static'], - DirectoryList::UPLOAD => [DirectoryList::URL_PATH => 'media/upload'], -]; +$params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] = array_replace_recursive( + $params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS], + [ + DirectoryList::PUB => [DirectoryList::URL_PATH => ''], + DirectoryList::MEDIA => [DirectoryList::URL_PATH => 'media'], + DirectoryList::STATIC_VIEW => [DirectoryList::URL_PATH => 'static'], + DirectoryList::UPLOAD => [DirectoryList::URL_PATH => 'media/upload'], + ] +); $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $params); /** @var \Magento\Framework\App\Http $app */ $app = $bootstrap->createApplication(\Magento\Framework\App\Http::class); From 19a2d2f2a2156b96733fadc88e16d07e871aadf7 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 21 Oct 2018 20:58:29 +0300 Subject: [PATCH 148/309] Sections less mixins: fix the issue with missing rules and incorrect default variables #18729 --- lib/web/css/source/lib/_sections.less | 15 +++++++++------ lib/web/css/source/lib/variables/_sections.less | 7 +++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/web/css/source/lib/_sections.less b/lib/web/css/source/lib/_sections.less index 372008d061580..b73e317835955 100644 --- a/lib/web/css/source/lib/_sections.less +++ b/lib/web/css/source/lib/_sections.less @@ -49,16 +49,16 @@ @_tab-control-color: @tab-control__color, @_tab-control-text-decoration: @tab-control__text-decoration, - @_tab-control-color-visited: @tab-control__color, - @_tab-control-text-decoration-visited: @tab-control__text-decoration, + @_tab-control-color-visited: @tab-control__visited__color, + @_tab-control-text-decoration-visited: @tab-control__visited__text-decoration, @_tab-control-background-color-hover: @tab-control__hover__background-color, @_tab-control-color-hover: @tab-control__hover__color, - @_tab-control-text-decoration-hover: @tab-control__text-decoration, + @_tab-control-text-decoration-hover: @tab-control__hover__text-decoration, @_tab-control-background-color-active: @tab-control__active__background-color, @_tab-control-color-active: @tab-control__active__color, - @_tab-control-text-decoration-active: @tab-control__text-decoration, + @_tab-control-text-decoration-active: @tab-control__active__text-decoration, @_tab-control-height: @tab-control__height, @_tab-control-margin-right: @tab-control__margin-right, @@ -121,6 +121,7 @@ &.active > .switch:hover { .lib-css(background, @_tab-control-background-color-active); .lib-css(color, @_tab-control-color-active); + .lib-css(text-decoration, @_tab-control-text-decoration-active); } &.active > .switch, @@ -200,8 +201,8 @@ @_accordion-control-color: @accordion-control__color, @_accordion-control-text-decoration: @accordion-control__text-decoration, - @_accordion-control-color-visited: @accordion-control__color, - @_accordion-control-text-decoration-visited: @accordion-control__text-decoration, + @_accordion-control-color-visited: @accordion-control__visited__color, + @_accordion-control-text-decoration-visited: @accordion-control__visited__text-decoration, @_accordion-control-background-color-hover: @accordion-control__hover__background-color, @_accordion-control-color-hover: @accordion-control__hover__color, @@ -275,6 +276,8 @@ &.active > .switch:focus, &.active > .switch:hover { .lib-css(background, @_accordion-control-background-color-active); + .lib-css(color, @_accordion-control-color-active); + .lib-css(text-decoration, @_accordion-control-text-decoration-active); .lib-css(padding-bottom, @_accordion-control-padding-bottom); } } diff --git a/lib/web/css/source/lib/variables/_sections.less b/lib/web/css/source/lib/variables/_sections.less index 7d961077e6f59..0868ce73c49ef 100644 --- a/lib/web/css/source/lib/variables/_sections.less +++ b/lib/web/css/source/lib/variables/_sections.less @@ -40,6 +40,9 @@ @tab-control__active__color: @text__color; @tab-control__active__text-decoration: @tab-control__text-decoration; +@tab-control__visited__color: @accordion-control__color; +@tab-control__visited__text-decoration: @accordion-control__text-decoration; + @tab-content__background-color: @tab-control__active__background-color; @tab-content__border-top-status: false; @tab-content__border: @tab-control__border-width solid @tab-control__border-color; @@ -72,8 +75,8 @@ @accordion-control__padding-bottom: @tab-control__padding-bottom; @accordion-control__padding-left: @accordion-control__padding-right; -@accordion-control__visited__color: @accordion-control__color; -@accordion-control__visited__text-decoration: @accordion-control__text-decoration; +@accordion-control__visited__color: @tab-control__visited__color; +@accordion-control__visited__text-decoration: @tab-control__visited__text-decoration; @accordion-control__hover__background-color: @tab-control__hover__background-color; @accordion-control__hover__color: @tab-control__hover__color; From 310afd896e0c3cc82d81e50f92dff928c231f2bc Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 21 Oct 2018 21:36:13 +0300 Subject: [PATCH 149/309] Fix a typo in variable name #18730 --- lib/web/css/source/lib/variables/_sections.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web/css/source/lib/variables/_sections.less b/lib/web/css/source/lib/variables/_sections.less index 0868ce73c49ef..05226d8aa3a3c 100644 --- a/lib/web/css/source/lib/variables/_sections.less +++ b/lib/web/css/source/lib/variables/_sections.less @@ -40,8 +40,8 @@ @tab-control__active__color: @text__color; @tab-control__active__text-decoration: @tab-control__text-decoration; -@tab-control__visited__color: @accordion-control__color; -@tab-control__visited__text-decoration: @accordion-control__text-decoration; +@tab-control__visited__color: @tab-control__color; +@tab-control__visited__text-decoration: @tab-control__text-decoration; @tab-content__background-color: @tab-control__active__background-color; @tab-content__border-top-status: false; From e97e0e08bff18bc84f01ce0e9ad6aee7ef70860a Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Mon, 22 Oct 2018 17:46:44 +0300 Subject: [PATCH 150/309] MAGETWO-95739: Zip code is not validated during checkout when "My billing and shipping address are the same" is unchecked --- .../web/js/model/shipping-rates-validator.js | 16 +++++++++------- .../view/frontend/web/js/view/billing-address.js | 9 +++++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js index d31c0dca38116..ca11fec7cd5a0 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js @@ -35,7 +35,6 @@ define([ var checkoutConfig = window.checkoutConfig, validators = [], observedElements = [], - postcodeElement = null, postcodeElementName = 'postcode'; validators.push(defaultValidator); @@ -79,7 +78,11 @@ define([ } $.each(elements, function (index, field) { - uiRegistry.async(formPath + '.' + field)(self.doElementBinding.bind(self)); + var elementBinding = self.doElementBinding.bind(self), + fullPath = formPath + '.' + field, + func = uiRegistry.async(fullPath); + + func(elementBinding); }); }, @@ -101,7 +104,6 @@ define([ if (element.index === postcodeElementName) { this.bindHandler(element, delay); - postcodeElement = element; } }, @@ -136,7 +138,7 @@ define([ if (!formPopUpState.isVisible()) { clearTimeout(self.validateAddressTimeout); self.validateAddressTimeout = setTimeout(function () { - self.postcodeValidation(); + self.postcodeValidation(element); self.validateFields(); }, delay); } @@ -148,7 +150,7 @@ define([ /** * @return {*} */ - postcodeValidation: function () { + postcodeValidation: function (postcodeElement) { var countryId = $('select[name="country_id"]').val(), validationResult, warnMessage; @@ -178,8 +180,8 @@ define([ */ validateFields: function () { var addressFlat = addressConverter.formDataProviderToFlatData( - this.collectObservedData(), - 'shippingAddress' + this.collectObservedData(), + 'shippingAddress' ), address; diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js index 6b5d08c2641cc..6a2f329d095d6 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js @@ -17,7 +17,8 @@ define([ 'Magento_Customer/js/customer-data', 'Magento_Checkout/js/action/set-billing-address', 'Magento_Ui/js/model/messageList', - 'mage/translate' + 'mage/translate', + 'Magento_Checkout/js/model/shipping-rates-validator' ], function ( ko, @@ -33,7 +34,8 @@ function ( customerData, setBillingAddressAction, globalMessageList, - $t + $t, + shippingRatesValidator ) { 'use strict'; @@ -71,6 +73,9 @@ function ( quote.paymentMethod.subscribe(function () { checkoutDataResolver.resolveBillingAddress(); }, this); + shippingRatesValidator.initFields( + 'checkout.steps.billing-step.payment.payments-list.checkmo-form.form-fields' + ); }, /** From 27fef74756ce331b5ab5da8457d3edd55ba71a63 Mon Sep 17 00:00:00 2001 From: Patrick Maguire <p.maguire@sumoheavy.com> Date: Mon, 22 Oct 2018 13:10:11 -0400 Subject: [PATCH 151/309] language cleanup on customer actions --- app/code/Magento/Customer/i18n/en_US.csv | 4 ++-- .../Customer/view/adminhtml/ui_component/customer_listing.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/i18n/en_US.csv b/app/code/Magento/Customer/i18n/en_US.csv index bf73d6361d4c7..578267984f985 100644 --- a/app/code/Magento/Customer/i18n/en_US.csv +++ b/app/code/Magento/Customer/i18n/en_US.csv @@ -506,10 +506,10 @@ Strong,Strong "Rebuild Customer grid index","Rebuild Customer grid index" Group,Group "Add New Customer","Add New Customer" -"Are you sure to delete selected customers?","Are you sure to delete selected customers?" +"Are you sure you want to delete the selected customers?","Are you sure you want to delete the selected customers?" "Delete items","Delete items" "Subscribe to Newsletter","Subscribe to Newsletter" -"Are you sure to unsubscribe selected customers from newsletter?","Are you sure to unsubscribe selected customers from newsletter?" +"Are you sure you want to unsubscribe the selected customers from the newsletter?","Are you sure you want to unsubscribe the selected customers from the newsletter?" "Unsubscribe from Newsletter","Unsubscribe from Newsletter" "Assign a Customer Group","Assign a Customer Group" Phone,Phone diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml index f8aa078f45e4d..6b479ad1cb290 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml @@ -49,7 +49,7 @@ <action name="delete"> <settings> <confirm> - <message translate="true">Are you sure to delete selected customers?</message> + <message translate="true">Are you sure you want to delete the selected customers?</message> <title translate="true">Delete items @@ -67,7 +67,7 @@ - Are you sure to unsubscribe selected customers from newsletter? + Are you sure you want to unsubscribe the selected customers from the newsletter? Unsubscribe from Newsletter From 78accc5569841afc37788f45847f11b52c0526c7 Mon Sep 17 00:00:00 2001 From: Mahesh Singh Date: Mon, 22 Oct 2018 23:36:21 +0530 Subject: [PATCH 152/309] issue #18655 fixed --- app/code/Magento/Integration/etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Integration/etc/adminhtml/system.xml b/app/code/Magento/Integration/etc/adminhtml/system.xml index 5abec8efbfdd6..fe80fe105493a 100644 --- a/app/code/Magento/Integration/etc/adminhtml/system.xml +++ b/app/code/Magento/Integration/etc/adminhtml/system.xml @@ -54,7 +54,7 @@ Maximum Number of authentication failures to lock out account. - + Period of time in seconds after which account will be unlocked. From 3ed1819168cd16b995ddcd652d0aa2b3c07fde17 Mon Sep 17 00:00:00 2001 From: GwanYeong Kim Date: Tue, 23 Oct 2018 09:15:11 +0900 Subject: [PATCH 153/309] Remove Duplicate field --- dev/tests/js/jasmine/assets/gallery/config.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev/tests/js/jasmine/assets/gallery/config.json b/dev/tests/js/jasmine/assets/gallery/config.json index d1d8e94d7f220..72c36293bc152 100644 --- a/dev/tests/js/jasmine/assets/gallery/config.json +++ b/dev/tests/js/jasmine/assets/gallery/config.json @@ -59,8 +59,7 @@ "arrows": "false", "thumbwidth": "90", "thumbheight": "90", - "ratio": "1", - "allowfullscreen": true + "ratio": "1" }, "fullscreen": { "nav": false From 937f5bcde4130966fc440df17a2a45e14717126d Mon Sep 17 00:00:00 2001 From: vgelani Date: Tue, 23 Oct 2018 13:59:25 +0530 Subject: [PATCH 154/309] Replace intval() function by using direct type casting to (int) --- .../Quote/Api/CartTotalRepositoryTest.php | 30 +++++++++---------- .../Mtf/Util/Generate/Fixture/SchemaXml.php | 2 +- .../Constraint/AssertProductDuplicateForm.php | 2 +- .../Handler/CatalogProductSimple/Curl.php | 2 +- ...AssertConfigurableProductDuplicateForm.php | 2 +- .../Store/Test/Handler/StoreGroup/Curl.php | 2 +- .../Store/Test/Handler/Website/Curl.php | 2 +- .../Edit/Tab/View/PersonalInfoTest.php | 4 +-- pub/errors/processor.php | 2 +- .../Magento/Setup/Model/PhpReadinessCheck.php | 8 ++--- 10 files changed, 28 insertions(+), 28 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartTotalRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartTotalRepositoryTest.php index 609ae1cfe094c..a001cae645434 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartTotalRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartTotalRepositoryTest.php @@ -162,22 +162,22 @@ protected function getQuoteItemTotalsData(\Magento\Quote\Model\Quote $quote) $item = array_shift($items); return [ ItemTotals::KEY_ITEM_ID => $item->getItemId(), - ItemTotals::KEY_PRICE => intval($item->getPrice()), - ItemTotals::KEY_BASE_PRICE => intval($item->getBasePrice()), + ItemTotals::KEY_PRICE => (int)$item->getPrice(), + ItemTotals::KEY_BASE_PRICE => (int)$item->getBasePrice(), ItemTotals::KEY_QTY => $item->getQty(), - ItemTotals::KEY_ROW_TOTAL => intval($item->getRowTotal()), - ItemTotals::KEY_BASE_ROW_TOTAL => intval($item->getBaseRowTotal()), - ItemTotals::KEY_ROW_TOTAL_WITH_DISCOUNT => intval($item->getRowTotalWithDiscount()), - ItemTotals::KEY_TAX_AMOUNT => intval($item->getTaxAmount()), - ItemTotals::KEY_BASE_TAX_AMOUNT => intval($item->getBaseTaxAmount()), - ItemTotals::KEY_TAX_PERCENT => intval($item->getTaxPercent()), - ItemTotals::KEY_DISCOUNT_AMOUNT => intval($item->getDiscountAmount()), - ItemTotals::KEY_BASE_DISCOUNT_AMOUNT => intval($item->getBaseDiscountAmount()), - ItemTotals::KEY_DISCOUNT_PERCENT => intval($item->getDiscountPercent()), - ItemTotals::KEY_PRICE_INCL_TAX => intval($item->getPriceInclTax()), - ItemTotals::KEY_BASE_PRICE_INCL_TAX => intval($item->getBasePriceInclTax()), - ItemTotals::KEY_ROW_TOTAL_INCL_TAX => intval($item->getRowTotalInclTax()), - ItemTotals::KEY_BASE_ROW_TOTAL_INCL_TAX => intval($item->getBaseRowTotalInclTax()), + ItemTotals::KEY_ROW_TOTAL => (int)$item->getRowTotal(), + ItemTotals::KEY_BASE_ROW_TOTAL => (int)$item->getBaseRowTotal(), + ItemTotals::KEY_ROW_TOTAL_WITH_DISCOUNT => (int)$item->getRowTotalWithDiscount(), + ItemTotals::KEY_TAX_AMOUNT => (int)$item->getTaxAmount(), + ItemTotals::KEY_BASE_TAX_AMOUNT => (int)$item->getBaseTaxAmount(), + ItemTotals::KEY_TAX_PERCENT => (int)$item->getTaxPercent(), + ItemTotals::KEY_DISCOUNT_AMOUNT => (int)$item->getDiscountAmount(), + ItemTotals::KEY_BASE_DISCOUNT_AMOUNT => (int)$item->getBaseDiscountAmount(), + ItemTotals::KEY_DISCOUNT_PERCENT => (int)$item->getDiscountPercent(), + ItemTotals::KEY_PRICE_INCL_TAX => (int)$item->getPriceInclTax(), + ItemTotals::KEY_BASE_PRICE_INCL_TAX => (int)$item->getBasePriceInclTax(), + ItemTotals::KEY_ROW_TOTAL_INCL_TAX => (int)$item->getRowTotalInclTax(), + ItemTotals::KEY_BASE_ROW_TOTAL_INCL_TAX => (int)$item->getBaseRowTotalInclTax(), ItemTotals::KEY_WEEE_TAX_APPLIED_AMOUNT => $item->getWeeeTaxAppliedAmount(), ItemTotals::KEY_WEEE_TAX_APPLIED => $item->getWeeeTaxApplied(), ItemTotals::KEY_NAME => $item->getName(), diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Fixture/SchemaXml.php b/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Fixture/SchemaXml.php index aa85299deea44..6d1d5b6f4b349 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Fixture/SchemaXml.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Generate/Fixture/SchemaXml.php @@ -145,7 +145,7 @@ protected function generateFixtureXml(array $config) foreach ($fields as $fieldName => $fieldValue) { $field = $this->dom->createElement('field'); $field->setAttribute('name', $fieldName); - $field->setAttribute('is_required', intval($fieldValue['is_required'])); + $field->setAttribute('is_required', (int)$fieldValue['is_required']); $fixture->appendChild($field); } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductDuplicateForm.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductDuplicateForm.php index f65aa33ff93e3..d5cf5d918817d 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductDuplicateForm.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductDuplicateForm.php @@ -116,7 +116,7 @@ function (&$item, $key, $formattingOptions) { protected function prepareUrlKey($urlKey) { preg_match("~\d+$~", $urlKey, $matches); - $key = intval($matches[0]) + 1; + $key = (int)$matches[0] + 1; return str_replace($matches[0], $key, $urlKey); } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Curl.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Curl.php index c75112fee8605..2a23903a697b3 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Curl.php @@ -411,7 +411,7 @@ protected function prepareQuantityAndStockStatus() : ['is_in_stock' => 'In Stock']; if (!isset($quantityAndStockStatus['is_in_stock'])) { - $qty = isset($quantityAndStockStatus['qty']) ? intval($quantityAndStockStatus['qty']) : null; + $qty = isset($quantityAndStockStatus['qty']) ? (int)$quantityAndStockStatus['qty'] : null; $quantityAndStockStatus['is_in_stock'] = 0 === $qty ? 'Out of Stock' : 'In Stock'; } diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductDuplicateForm.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductDuplicateForm.php index 02cb304325c24..c50f0e338c20f 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductDuplicateForm.php +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductDuplicateForm.php @@ -70,7 +70,7 @@ protected function prepareFixtureData(array $data, array $sortFields = []) protected function prepareUrlKey($urlKey) { preg_match("~\d+$~", $urlKey, $matches); - $key = intval($matches[0]) + 1; + $key = (int)$matches[0] + 1; return str_replace($matches[0], $key, $urlKey); } diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/StoreGroup/Curl.php b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/StoreGroup/Curl.php index 10ebd2b6d5f66..0c1017410961f 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/StoreGroup/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/StoreGroup/Curl.php @@ -65,7 +65,7 @@ protected function getStoreGroupIdByGroupName($storeName) throw new \Exception('Cannot find store group id'); } - return intval($matches[1]); + return (int)$matches[1]; } /** diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Website/Curl.php b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Website/Curl.php index ab0b1f2096808..0b738c5e159cd 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Website/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Handler/Website/Curl.php @@ -119,7 +119,7 @@ protected function getWebSiteIdByWebsiteName($websiteName) throw new \Exception('Cannot find website id.'); } - return intval($matches[1]); + return (int)$matches[1]; } /** diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php index 8d82ad94dfab4..161734f47bfd7 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php @@ -110,9 +110,9 @@ public function testGetCustomer() \Magento\Customer\Api\Data\CustomerInterface::class ); foreach ($expectedCustomerData as $property => $value) { - $expectedValue = is_numeric($value) ? intval($value) : $value; + $expectedValue = is_numeric($value) ? (int)$value : $value; $actualValue = isset($actualCustomerData[$property]) ? $actualCustomerData[$property] : null; - $actualValue = is_numeric($actualValue) ? intval($actualValue) : $actualValue; + $actualValue = is_numeric($actualValue) ? (int)$actualValue : $actualValue; $this->assertEquals($expectedValue, $actualValue); } } diff --git a/pub/errors/processor.php b/pub/errors/processor.php index e64956dce17c2..ee8c9331097f7 100644 --- a/pub/errors/processor.php +++ b/pub/errors/processor.php @@ -466,7 +466,7 @@ protected function _setReportData($reportData) public function saveReport($reportData) { $this->reportData = $reportData; - $this->reportId = abs(intval(microtime(true) * random_int(100, 1000))); + $this->reportId = abs((int)(microtime(true) * random_int(100, 1000))); $this->_reportFile = $this->_reportDir . '/' . $this->reportId; $this->_setReportData($reportData); diff --git a/setup/src/Magento/Setup/Model/PhpReadinessCheck.php b/setup/src/Magento/Setup/Model/PhpReadinessCheck.php index 2c4967c4c2ffd..98da6cb7a5e0f 100644 --- a/setup/src/Magento/Setup/Model/PhpReadinessCheck.php +++ b/setup/src/Magento/Setup/Model/PhpReadinessCheck.php @@ -192,7 +192,7 @@ public function checkMemoryLimit() $currentMemoryLimit = ini_get('memory_limit'); - $currentMemoryInteger = intval($currentMemoryLimit); + $currentMemoryInteger = (int)$currentMemoryLimit; if ($currentMemoryInteger > 0 && $this->dataSize->convertSizeToBytes($currentMemoryLimit) @@ -244,7 +244,7 @@ private function checkXDebugNestedLevel() $currentExtensions = $this->phpInformation->getCurrent(); if (in_array('xdebug', $currentExtensions)) { - $currentXDebugNestingLevel = intval(ini_get('xdebug.max_nesting_level')); + $currentXDebugNestingLevel = (int)ini_get('xdebug.max_nesting_level'); $minimumRequiredXDebugNestedLevel = $this->phpInformation->getRequiredMinimumXDebugNestedLevel(); if ($minimumRequiredXDebugNestedLevel > $currentXDebugNestingLevel) { @@ -286,7 +286,7 @@ private function checkPopulateRawPostSetting() $data = []; $error = false; - $iniSetting = intval(ini_get('always_populate_raw_post_data')); + $iniSetting = (int)ini_get('always_populate_raw_post_data'); $checkVersionConstraint = $this->versionParser->parseConstraints('~5.6.0'); $normalizedPhpVersion = $this->getNormalizedCurrentPhpVersion(PHP_VERSION); @@ -302,7 +302,7 @@ private function checkPopulateRawPostSetting() Please open your php.ini file and set always_populate_raw_post_data to -1. If you need more help please call your hosting provider.', PHP_VERSION, - intval(ini_get('always_populate_raw_post_data')) + (int)ini_get('always_populate_raw_post_data') ); $data['always_populate_raw_post_data'] = [ From c3b3864118cccc1529e58e76bc25e5a3c42e6365 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak Date: Tue, 23 Oct 2018 16:03:16 +0300 Subject: [PATCH 155/309] MAGETWO-90021: [Catalog] ProductAttributeMediaGalleryManagementInterface removes product from index, impossible to restore - Fix statics. --- .../Model/Product/Gallery/GalleryManagement.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php index 4be4bb09efc98..4a6cc09d11920 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php @@ -1,6 +1,5 @@ Date: Tue, 23 Oct 2018 17:05:28 +0300 Subject: [PATCH 156/309] MAGETWO-90021: [Catalog] ProductAttributeMediaGalleryManagementInterface removes product from index, impossible to restore - Fix statics. --- .../Magento/Catalog/Model/Product/Gallery/GalleryManagement.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php index 4a6cc09d11920..0e08b0af92862 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php @@ -14,6 +14,7 @@ /** * Class GalleryManagement + * * Provides implementation of api interface ProductAttributeMediaGalleryManagementInterface * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) From a8c77686312a61ab151eb4c9a7a79a5f8127b3dd Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Tue, 23 Oct 2018 13:46:53 -0500 Subject: [PATCH 157/309] Fix docblocks --- .../Timezone/LocalizedDateToUtcConverterInterface.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php index 277900c46badf..edc07bee164be 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php @@ -7,11 +7,16 @@ namespace Magento\Framework\Stdlib\DateTime\Timezone; +/* + * Interface for converting localized date to UTC + */ interface LocalizedDateToUtcConverterInterface { /** + * Convert localized date to UTC + * * @param string $data * @return string */ public function convertLocalizedDateToUtc($date); -} \ No newline at end of file +} From a18a08792acf1d251c491894d0889783d3b73c02 Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Tue, 23 Oct 2018 22:48:01 +0300 Subject: [PATCH 158/309] graphQl-44: added html content resolver --- .../Resolver/Product/ProductTextAttribute.php | 64 ------------------- .../ProductTextAttribute/FormatInterface.php | 23 ------- .../ProductTextAttribute/FormatList.php | 54 ---------------- .../Product/ProductTextAttribute/Html.php | 47 -------------- .../ProductTextAttribute/HtmlContent.php | 56 ++++++++++++++++ .../CatalogGraphQl/etc/schema.graphqls | 29 ++------- 6 files changed, 61 insertions(+), 212 deletions(-) delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatInterface.php delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatList.php delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php deleted file mode 100644 index 2ff6faa0a74ee..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php +++ /dev/null @@ -1,64 +0,0 @@ -formatList = $formatList; - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) { - if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); - } - - /* @var $product Product */ - $product = $value['model']; - $fieldName = $field->getName(); - $formatIdentifier = $args['filter']['description']['format'] ?? $this->defaultFormat; - $format = $this->formatList->getFormatByIdentifier($formatIdentifier); - $result = ['content' => $format->getContent($product, $fieldName)]; - - return $result; - } -} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatInterface.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatInterface.php deleted file mode 100644 index 2cf702bf18466..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/FormatInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -objectManager = $objectManager; - $this->formats = $formats; - } - - /** - * @param string $formatIdentifier - * @return FormatInterface - */ - public function getFormatByIdentifier(string $formatIdentifier) : FormatInterface - { - if (!isset($this->formats[$formatIdentifier])) { - throw new GraphQlInputException(__('Format %1 does not exist.', [$formatIdentifier])); - } - $formatInstance = $this->objectManager->get($this->formats[$formatIdentifier]); - - return $formatInstance; - } -} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php deleted file mode 100644 index 830fbf28e3373..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/Html.php +++ /dev/null @@ -1,47 +0,0 @@ -valueFactory = $valueFactory; - $this->outputHelper = $outputHelper; - } - - /** - * @inheritdoc - */ - public function getContent( - ModelProduct $product, - string $fieldName - ): string { - return $this->outputHelper->productAttribute($product, $product->getData($fieldName), $fieldName); - } -} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php new file mode 100644 index 0000000000000..c0b67889ecd74 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php @@ -0,0 +1,56 @@ +outputHelper = $outputHelper; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ): array { + if (!isset($value['model'])) { + return []; + } + + /* @var $product Product */ + $product = $value['model']; + $fieldName = $field->getName(); + $renderedValue = $this->outputHelper->productAttribute($product, $product->getData($fieldName), $fieldName); + + return $renderedValue; + } +} diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index a7d9ab5483d46..3ffdc85a40d68 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -248,8 +248,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ id: Int @doc(description: "The ID number assigned to the product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\EntityIdToId") name: String @doc(description: "The product name. Customers use this name to identify the product.") sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") - description: ProductTextAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") - short_description: ProductTextAttribute @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") + description: ProductTextAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") + short_description: ProductTextAttribute @doc(description: "A short description of the product. Its use depends on the theme.") special_price: Float @doc(description: "The discounted price of the product") special_from_date: String @doc(description: "The beginning date that a product has a special price") special_to_date: String @doc(description: "The end date that a product has a special price") @@ -433,8 +433,8 @@ type CategoryProducts @doc(description: "The category products object returned i input ProductFilterInput @doc(description: "ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { name: FilterTypeInput @doc(description: "The product name. Customers use this name to identify the product.") sku: FilterTypeInput @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") - description: ProductTextAttributeTypeInput @doc(description: "Detailed information about the product. The value can include simple HTML tags.") - short_description: ProductTextAttributeTypeInput @doc(description: "A short description of the product. Its use depends on the theme.") + description: FilterTypeInput @doc(description: "Detailed information about the product. The value can include simple HTML tags.") + short_description: FilterTypeInput @doc(description: "A short description of the product. Its use depends on the theme.") price: FilterTypeInput @doc(description: "The price of an item") special_price: FilterTypeInput @doc(description: "The discounted price of the product") special_from_date: FilterTypeInput @doc(description: "The beginning date that a product has a special price") @@ -558,24 +558,5 @@ type SortFields @doc(description: "SortFields contains a default value for sort } type ProductTextAttribute @doc(description: "Product text attribute.") { - content: String -} - -input ProductTextAttributeTypeInput @doc(description: "FilterTypeInput specifies which action will be performed in a query ") { - format: String @doc(description: "Format of the content") - eq: String @doc(description: "Equals") - finset: [String] @doc(description: "Find in set. The value can contain a set of comma-separated values") - from: String @doc(description: "From. Must be used with 'to'") - gt: String @doc(description: "Greater than") - gteq: String @doc(description: "Greater than or equal to") - in: [String] @doc(description: "In. The value can contain a set of comma-separated values") - like: String @doc(description: "Like. The specified value can contain % (percent signs) to allow matching of 0 or more characters") - lt: String @doc(description: "Less than") - lteq: String @doc(description: "Less than or equal to") - moreq: String @doc(description: "More than or equal to") - neq: String @doc(description: "Not equal to") - notnull: String @doc(description: "Not null") - null: String @doc(description: "Is null") - to: String@doc(description: "To. Must be used with 'from'") - nin: [String] @doc(description: "Not in. The value can contain a set of comma-separated values") + html: String @doc(description: "Attribute HTML content") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute\\HtmlContent") } From 127b1b8029a52fca7ad9726380f55c99c9db4c8a Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Tue, 23 Oct 2018 15:28:11 -0500 Subject: [PATCH 159/309] Fix docblock --- app/code/Magento/Newsletter/Model/Queue.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Newsletter/Model/Queue.php b/app/code/Magento/Newsletter/Model/Queue.php index 58c5ba8a7e04a..a3279f8c83699 100644 --- a/app/code/Magento/Newsletter/Model/Queue.php +++ b/app/code/Magento/Newsletter/Model/Queue.php @@ -136,6 +136,7 @@ class Queue extends \Magento\Framework\Model\AbstractModel implements TemplateTy * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param TimezoneInterface $timezone + * @param LocalizedDateToUtcConverterInterface $utcConverter * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( From 526bfc9267c837ef8b9bf7234869cf16d4327a26 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Tue, 23 Oct 2018 15:31:08 -0500 Subject: [PATCH 160/309] Fix docblock --- lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 8534e798481cb..6514c01f92370 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -257,6 +257,8 @@ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null) } /** + * Format date according to date and time formats, locale, timezone and pattern + * * @param string|\DateTimeInterface $date * @param int $dateType * @param int $timeType @@ -300,6 +302,7 @@ public function formatDateTime( /** * Convert date from config timezone to Utc. + * * If pass \DateTime object as argument be sure that timezone is the same with config timezone * * @param string|\DateTimeInterface $date From edc962dbf324a3866f9ac78537ce23abfc1316e4 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Tue, 23 Oct 2018 15:32:37 -0500 Subject: [PATCH 161/309] Fix static tests --- .../DateTime/Timezone/LocalizedDateToUtcConverter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php index 721ed9a384dad..420fd6e543e07 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverter.php @@ -36,13 +36,13 @@ class LocalizedDateToUtcConverter implements LocalizedDateToUtcConverterInterfac /** * LocalizedDateToUtcConverter constructor. * + * @param TimezoneInterface $timezone * @param ResolverInterface $localeResolver */ public function __construct( TimezoneInterface $timezone, ResolverInterface $localeResolver - ) - { + ) { $this->timezone = $timezone; $this->localeResolver = $localeResolver; } @@ -71,4 +71,4 @@ public function convertLocalizedDateToUtc($date) return $date->format($this->defaultFormat); } -} \ No newline at end of file +} From 4fe8c7a3ccc100d23f170dcef994477227aed35e Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Tue, 23 Oct 2018 15:33:27 -0500 Subject: [PATCH 162/309] Fix static test --- .../DateTime/Timezone/LocalizedDateToUtcConverterInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php index edc07bee164be..d10bd5f2fefb2 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone/LocalizedDateToUtcConverterInterface.php @@ -15,7 +15,7 @@ interface LocalizedDateToUtcConverterInterface /** * Convert localized date to UTC * - * @param string $data + * @param string $date * @return string */ public function convertLocalizedDateToUtc($date); From e27de555edbbc24ff8a7037ee57de4deea306472 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Tue, 23 Oct 2018 15:36:43 -0500 Subject: [PATCH 163/309] Fix static test --- .../Framework/Stdlib/DateTime/Timezone.php | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 6514c01f92370..cf747bfa2b735 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -257,15 +257,7 @@ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null) } /** - * Format date according to date and time formats, locale, timezone and pattern - * - * @param string|\DateTimeInterface $date - * @param int $dateType - * @param int $timeType - * @param string|null $locale - * @param string|null $timezone - * @param string|null $pattern - * @return string + * @inheritdoc */ public function formatDateTime( $date, @@ -301,14 +293,7 @@ public function formatDateTime( } /** - * Convert date from config timezone to Utc. - * - * If pass \DateTime object as argument be sure that timezone is the same with config timezone - * - * @param string|\DateTimeInterface $date - * @param string $format - * @throws LocalizedException - * @return string + * @inheritdoc */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s') { From 3cba00c951e0df4c843085ab761b9b97c2e3471b Mon Sep 17 00:00:00 2001 From: Tomash Khamlai Date: Tue, 23 Oct 2018 23:39:19 +0300 Subject: [PATCH 164/309] Cover \Magento\CheckoutAgreements\Model\ResourceModel\Agreement\Grid\Collection class with Integration test --- .../ResourceModel/Grid/CollectionTest.php | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/CheckoutAgreements/Model/ResourceModel/Grid/CollectionTest.php diff --git a/dev/tests/integration/testsuite/Magento/CheckoutAgreements/Model/ResourceModel/Grid/CollectionTest.php b/dev/tests/integration/testsuite/Magento/CheckoutAgreements/Model/ResourceModel/Grid/CollectionTest.php new file mode 100644 index 0000000000000..2f3112e705c94 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CheckoutAgreements/Model/ResourceModel/Grid/CollectionTest.php @@ -0,0 +1,41 @@ +collection = Bootstrap::getObjectManager()->create(Collection::class); + } + + /** + * Check that collection is filterable by store + * + * @magentoDataFixture Magento/CheckoutAgreements/_files/multi_agreements_active_with_text.php + */ + public function testAddStoresToFilter(): void + { + $collectionSize = $this->collection->addStoreFilter(1)->load(false, false)->getSize(); + $this->assertEquals(2, $collectionSize); + } +} From 0d0c210a7e081086a8235f9777a219601bd7e8c4 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Tue, 23 Oct 2018 15:40:36 -0500 Subject: [PATCH 165/309] Fix static test --- .../Framework/Stdlib/DateTime/TimezoneInterface.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php index 4aff80161ef80..d1ac24c84be9a 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php @@ -6,6 +6,8 @@ namespace Magento\Framework\Stdlib\DateTime; +use Magento\Framework\Exception\LocalizedException; + /** * Timezone Interface * @api @@ -122,6 +124,8 @@ public function getConfigTimezone($scopeType = null, $scopeCode = null); public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null); /** + * Format date according to date and time formats, locale, timezone and pattern. + * * @param string|\DateTimeInterface $date * @param int $dateType * @param int $timeType @@ -140,9 +144,14 @@ public function formatDateTime( ); /** + * Convert date from config timezone to UTC. + * + * If pass \DateTime object as argument be sure that timezone is the same with config timezone + * * @param string|\DateTimeInterface $date * @param string $format * @return string + * @throws LocalizedException * @since 100.1.0 */ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s'); From 29ccdae0897db8ef212b67a8ecf92e985c8d2e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Ca=C3=A7ador?= <20863811+samuel27m@users.noreply.github.com> Date: Tue, 23 Oct 2018 23:19:31 +0100 Subject: [PATCH 166/309] Remove unnecesary "header" block redeclaration Remove unnecesary header block redeclaration on Magento Luma Theme, which made impossible to set the header template through custom modules. Example: - Create new module. - Create new default.xml and try to use referenceBlock to set the template. - Doesn't work, because Luma theme XML files are compiled after all modules and overrides the custom module modifications. Removed because this block was already created in module Magento_Theme, so this redeclaration is just unnecesary --- .../Magento/luma/Magento_Customer/layout/default.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Customer/layout/default.xml b/app/design/frontend/Magento/luma/Magento_Customer/layout/default.xml index 1f8c162ef923a..4b08bf28ece9f 100644 --- a/app/design/frontend/Magento/luma/Magento_Customer/layout/default.xml +++ b/app/design/frontend/Magento/luma/Magento_Customer/layout/default.xml @@ -15,11 +15,6 @@ - - - welcome - - From 0dc76a43811ac8598abc4530cab1fe976aa734a2 Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Wed, 24 Oct 2018 08:44:12 +0300 Subject: [PATCH 167/309] ENGCOM-17516: added Australian states --- .../Setup/Patch/Data/AddDataForAustralia.php | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 app/code/Magento/Directory/Setup/Patch/Data/AddDataForAustralia.php diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForAustralia.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForAustralia.php new file mode 100644 index 0000000000000..7574f684bb465 --- /dev/null +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForAustralia.php @@ -0,0 +1,100 @@ +moduleDataSetup = $moduleDataSetup; + $this->dataInstallerFactory = $dataInstallerFactory; + } + + /** + * {@inheritdoc} + */ + public function apply() + { + /** @var DataInstaller $dataInstaller */ + $dataInstaller = $this->dataInstallerFactory->create(); + $dataInstaller->addCountryRegions( + $this->moduleDataSetup->getConnection(), + $this->getDataForAustralia() + ); + } + + /** + * Croatian states data. + * + * @return array + */ + private function getDataForAustralia() + { + return [ + ['AU', 'ACT', 'Australian Capital Territory'], + ['AU', 'NSW', 'New South Wales'], + ['AU', 'VIC', 'Victoria'], + ['AU', 'QLD', 'Queensland'], + ['AU', 'SA', 'South Australia'], + ['AU', 'TAS', 'Tasmania'], + ['AU', 'WA', 'Western Australia'], + ['AU', 'NT', 'Northern Territory'] + ]; + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return [ + InitializeDirectoryData::class, + ]; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.3'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} From 50ff962c0d838cfd58a2c151bbb86730f23f00c8 Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Wed, 24 Oct 2018 08:51:12 +0300 Subject: [PATCH 168/309] ENGCOM-17516: Fixed phpdocs --- .../Directory/Setup/Patch/Data/AddDataForAustralia.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForAustralia.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForAustralia.php index 7574f684bb465..0abadf7153424 100644 --- a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForAustralia.php +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForAustralia.php @@ -27,8 +27,6 @@ class AddDataForAustralia implements DataPatchInterface, PatchVersionInterface private $dataInstallerFactory; /** - * AddDataForCroatia constructor. - * * @param ModuleDataSetupInterface $moduleDataSetup * @param \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory */ @@ -54,7 +52,7 @@ public function apply() } /** - * Croatian states data. + * Australian states data. * * @return array */ From e7d5aa8c8bb959b3d03c5d8f75ce3eee4727df00 Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Tue, 23 Oct 2018 17:04:32 +0300 Subject: [PATCH 169/309] MAGETWO-95848: Customer Cart Checkout error --- .../Item/ItemProductResolver.php | 56 ++++--- .../Item/ItemProductResolverTest.php | 143 ++++++++++++++++++ 2 files changed, 176 insertions(+), 23 deletions(-) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php index 6c33ecc138aea..7de78b6612a10 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -13,16 +13,17 @@ use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Catalog\Model\Product; +use Magento\Store\Model\ScopeInterface; /** - * {@inheritdoc} + * Resolves the product from a configured item. */ class ItemProductResolver implements ItemResolverInterface { /** * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. */ - const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/configurable_product_image'; + public const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/configurable_product_image'; /** * @var ScopeConfigInterface @@ -38,27 +39,21 @@ public function __construct(ScopeConfigInterface $scopeConfig) } /** - * {@inheritdoc} + * Get the final product from a configured item by product type and selection. + * + * @param ItemInterface $item + * @return ProductInterface */ - public function getFinalProduct(ItemInterface $item) : ProductInterface + public function getFinalProduct(ItemInterface $item): ProductInterface { /** * Show parent product thumbnail if it must be always shown according to the related setting in system config * or if child thumbnail is not available. */ - $parentProduct = $item->getProduct(); - $finalProduct = $parentProduct; + $finalProduct = $item->getProduct(); $childProduct = $this->getChildProduct($item); - if ($childProduct !== $parentProduct) { - $configValue = $this->scopeConfig->getValue( - self::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - $childThumb = $childProduct->getData('thumbnail'); - $finalProduct = - ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') - ? $parentProduct - : $childProduct; + if ($childProduct !== null && $this->isUseChildProduct($childProduct)) { + $finalProduct = $childProduct; } return $finalProduct; } @@ -67,15 +62,30 @@ public function getFinalProduct(ItemInterface $item) : ProductInterface * Get item configurable child product. * * @param ItemInterface $item - * @return Product + * @return Product | null */ - private function getChildProduct(ItemInterface $item) : Product + private function getChildProduct(ItemInterface $item): ?Product { + /** @var \Magento\Quote\Model\Quote\Item\Option $option */ $option = $item->getOptionByCode('simple_product'); - $product = $item->getProduct(); - if ($option) { - $product = $option->getProduct(); - } - return $product; + return $option ? $option->getProduct() : null; + } + + /** + * Is need to use child product + * + * @param Product $childProduct + * @return bool + */ + private function isUseChildProduct(Product $childProduct): bool + { + $configValue = $this->scopeConfig->getValue( + self::CONFIG_THUMBNAIL_SOURCE, + ScopeInterface::SCOPE_STORE + ); + $childThumb = $childProduct->getData('thumbnail'); + return $configValue !== Thumbnail::OPTION_USE_PARENT_IMAGE + && $childThumb !== null + && $childThumb !== 'no_selection'; } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php new file mode 100644 index 0000000000000..8dac2dee10d37 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php @@ -0,0 +1,143 @@ +scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->parentProduct = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $this->parentProduct + ->method('getSku') + ->willReturn('parent_product'); + + $this->childProduct = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $this->childProduct + ->method('getSku') + ->willReturn('child_product'); + + $this->option = $this->getMockBuilder(Option::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->option + ->method('getProduct') + ->willReturn($this->childProduct); + + $this->item = $this->getMockBuilder(ItemInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->item + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->parentProduct); + + $this->model = new ItemProductResolver($this->scopeConfig); + } + + /** + * Test for deleted child product from configurable product + */ + public function testGetFinalProductChildIsNull(): void + { + $this->scopeConfig->expects($this->never())->method('getValue'); + $this->childProduct->expects($this->never())->method('getData'); + + $this->item->expects($this->once()) + ->method('getOptionByCode') + ->willReturn(null); + + $finalProduct = $this->model->getFinalProduct($this->item); + $this->assertEquals( + $this->parentProduct->getSku(), + $finalProduct->getSku() + ); + } + + /** + * Tests child product from configurable product + * + * @dataProvider provideScopeConfig + * @param string $expectedSku + * @param string $scopeValue + * @param string | null $thumbnail + */ + public function testGetFinalProductChild($expectedSku, $scopeValue, $thumbnail): void + { + $this->item->expects($this->once()) + ->method('getOptionByCode') + ->willReturn($this->option); + + $this->childProduct + ->expects($this->once()) + ->method('getData') + ->willReturn($thumbnail); + + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->willReturn($scopeValue); + + $finalProduct = $this->model->getFinalProduct($this->item); + $this->assertEquals($expectedSku, $finalProduct->getSku()); + } + + /** + * Dataprovider for scope test + * @return array + */ + public function provideScopeConfig(): array + { + return [ + ['child_product', Thumbnail::OPTION_USE_OWN_IMAGE, 'thumbnail'], + ['parent_product', Thumbnail::OPTION_USE_PARENT_IMAGE, 'thumbnail'], + + ['parent_product', Thumbnail::OPTION_USE_OWN_IMAGE, null], + ['parent_product', Thumbnail::OPTION_USE_OWN_IMAGE, 'no_selection'], + + ['parent_product', Thumbnail::OPTION_USE_PARENT_IMAGE, null], + ['parent_product', Thumbnail::OPTION_USE_PARENT_IMAGE, 'no_selection'], + ]; + } +} From 0a4dc35bb14a28634a7b58e9fe12e68df7e86ea7 Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Wed, 24 Oct 2018 14:21:40 +0300 Subject: [PATCH 170/309] graphQl-44: added ProductTextAttribute and fixed api functional test --- .../Resolver/Product/ProductTextAttribute.php | 33 +++++++++++++++++ .../ProductTextAttribute/HtmlContent.php | 10 ++++-- .../CatalogGraphQl/etc/schema.graphqls | 4 +-- .../GraphQl/Catalog/ProductViewTest.php | 36 ++++++++++++++++--- 4 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php new file mode 100644 index 0000000000000..171e84a65aba4 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php @@ -0,0 +1,33 @@ +getName(); + + return $value; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php index c0b67889ecd74..724d0826dbb3d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php @@ -9,6 +9,7 @@ use Magento\Catalog\Model\Product; use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Helper\Output as OutputHelper; @@ -41,14 +42,17 @@ public function resolve( ResolveInfo $info, array $value = null, array $args = null - ): array { + ): ?string { if (!isset($value['model'])) { - return []; + throw new GraphQlInputException(__('"model" value should be specified')); + } + if (!isset($value['field'])) { + throw new GraphQlInputException(__('"field" value should be specified')); } /* @var $product Product */ $product = $value['model']; - $fieldName = $field->getName(); + $fieldName = $value['field']; $renderedValue = $this->outputHelper->productAttribute($product, $product->getData($fieldName), $fieldName); return $renderedValue; diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 3ffdc85a40d68..ad724c18a5c9a 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -248,8 +248,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ id: Int @doc(description: "The ID number assigned to the product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\EntityIdToId") name: String @doc(description: "The product name. Customers use this name to identify the product.") sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") - description: ProductTextAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") - short_description: ProductTextAttribute @doc(description: "A short description of the product. Its use depends on the theme.") + description: ProductTextAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") + short_description: ProductTextAttribute @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") special_price: Float @doc(description: "The discounted price of the product") special_from_date: String @doc(description: "The beginning date that a product has a special price") special_to_date: String @doc(description: "The end date that a product has a special price") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index 7c2cda3a4551b..615e50f640380 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -44,7 +44,9 @@ public function testQueryAllFieldsSimpleProduct() attribute_set_id country_of_manufacture created_at - description + description { + html + } gift_message_available id categories { @@ -203,7 +205,9 @@ public function testQueryAllFieldsSimpleProduct() position sku } - short_description + short_description { + html + } sku small_image { path @@ -262,6 +266,7 @@ public function testQueryAllFieldsSimpleProduct() $this->assertArrayHasKey(0, $response['products']['items']); $this->assertBaseFields($product, $response['products']['items'][0]); $this->assertEavAttributes($product, $response['products']['items'][0]); + $this->assertTextEavAttributes($product, $response['products']['items'][0]); $this->assertOptions($product, $response['products']['items'][0]); $this->assertTierPrices($product, $response['products']['items'][0]); $this->assertArrayHasKey('websites', $response['products']['items'][0]); @@ -917,11 +922,9 @@ private function assertEavAttributes($product, $actualResponse) { $eavAttributes = [ 'url_key', - 'description', 'meta_description', 'meta_keyword', 'meta_title', - 'short_description', 'country_of_manufacture', 'gift_message_available', 'news_from_date', @@ -943,6 +946,31 @@ private function assertEavAttributes($product, $actualResponse) $this->assertResponseFields($actualResponse, $assertionMap); } + /** + * @param ProductInterface $product + * @param array $actualResponse + */ + private function assertTextEavAttributes($product, $actualResponse) + { + $eavAttributes = [ + 'description', + 'short_description', + ]; + $assertionMap = []; + foreach ($eavAttributes as $attributeCode) { + $expectedAttribute = $product->getCustomAttribute($attributeCode); + + $assertionMap[] = [ + 'response_field' => $this->eavAttributesToGraphQlSchemaFieldTranslator($attributeCode), + 'expected_value' => $expectedAttribute ? [ + 'html' => $expectedAttribute->getValue() + ] : null + ]; + } + + $this->assertResponseFields($actualResponse, $assertionMap); + } + /** * @param string $eavAttributeCode * @return string From 6862c04ece31c29d41be7ac65005e9bdf7c24490 Mon Sep 17 00:00:00 2001 From: Sunil Patel Date: Sat, 19 May 2018 15:50:34 +0530 Subject: [PATCH 171/309] 15259 : Unable to disable without providing Industry value --- app/code/Magento/Analytics/etc/adminhtml/system.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml index 4e21648d00ce8..c7da840b7e665 100644 --- a/app/code/Magento/Analytics/etc/adminhtml/system.xml +++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml @@ -36,6 +36,9 @@ Magento\Analytics\Model\Config\Source\Vertical Magento\Analytics\Model\Config\Backend\Vertical Magento\Analytics\Block\Adminhtml\System\Config\Vertical + + 1 + From 5ef522d0b2178c0d8086df04cdb4c282200ce7fa Mon Sep 17 00:00:00 2001 From: Tom Reece Date: Wed, 24 Oct 2018 10:46:13 -0500 Subject: [PATCH 172/309] MQE-1304: MFTF test failures between 5pm PDT and midnight PDT --- .../Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml | 2 ++ .../Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml | 2 ++ .../Mftf/Test/CartPriceRuleForConfigurableProductTest.xml | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml index aaedaedb7236c..ace50aa3e6d33 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml @@ -50,6 +50,8 @@ + + diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 60df3f27fd65b..08e859b11d1bb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -45,6 +45,8 @@ + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml index 4c194c63ec378..e2687f5f16baf 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml @@ -7,7 +7,7 @@ --> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> @@ -96,6 +96,8 @@ + + From 88197c8b1624f9dc82dbea3fc0a5cbc3986f259d Mon Sep 17 00:00:00 2001 From: Pratik Oza Date: Wed, 24 Oct 2018 22:22:56 +0530 Subject: [PATCH 173/309] Remove useless semicolon statements --- .../catalog/product/attribute/set/main/tree/attribute.phtml | 2 +- .../CatalogSearch/Model/Indexer/Fulltext/Action/Full.php | 2 +- app/code/Magento/GiftMessage/Model/ItemRepository.php | 4 ++-- app/code/Magento/GiftMessage/Model/OrderItemRepository.php | 4 ++-- app/code/Magento/GiftMessage/Model/OrderRepository.php | 2 +- .../Magento/Indexer/Console/Command/IndexerReindexCommand.php | 2 +- app/code/Magento/Indexer/Model/DimensionModes.php | 2 +- app/code/Magento/Sales/Model/Order/Validation/CanInvoice.php | 2 +- .../view/adminhtml/templates/importExportHeader.phtml | 2 +- app/code/Magento/Theme/Model/Design/Config/Validator.php | 2 +- app/code/Magento/Theme/view/frontend/templates/text.phtml | 2 +- .../Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php | 4 ++-- .../Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.php | 2 +- .../app/Magento/ImportExport/Test/Fixture/Import/File.php | 2 +- .../integration/testsuite/Magento/Deploy/_files/theme.php | 2 +- .../GiftMessage/_files/quote_with_item_message_rollback.php | 2 +- .../Test/Integrity/Magento/Backend/ControllerAclTest.php | 2 +- .../Framework/Indexer/Config/DependencyInfoProvider.php | 2 +- lib/internal/Magento/Framework/Setup/OldDbValidator.php | 2 +- .../Unit/Autoloader/GeneratedClassesAutoloader.php | 2 +- 20 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml index 223b3e9888eea..75f04eae82159 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main/tree/attribute.phtml @@ -2,4 +2,4 @@ /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. - */; + */ diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/Full.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/Full.php index 2b4be8369de59..7c93595b2b9ff 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/Full.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/Full.php @@ -400,7 +400,7 @@ public function rebuildStoreIndex($storeId, $productIds = null) } $products = $this->dataProvider ->getSearchableProducts($storeId, $staticFields, $productIds, $lastProductId, $this->batchSize); - }; + } } /** diff --git a/app/code/Magento/GiftMessage/Model/ItemRepository.php b/app/code/Magento/GiftMessage/Model/ItemRepository.php index 3c62a489af4ab..dce60079e43e3 100644 --- a/app/code/Magento/GiftMessage/Model/ItemRepository.php +++ b/app/code/Magento/GiftMessage/Model/ItemRepository.php @@ -88,7 +88,7 @@ public function get($cartId, $itemId) throw new NoSuchEntityException( __('No item with the provided ID was found in the Cart. Verify the ID and try again.') ); - }; + } $messageId = $item->getGiftMessageId(); if (!$messageId) { return null; @@ -121,7 +121,7 @@ public function save($cartId, \Magento\GiftMessage\Api\Data\MessageInterface $gi $itemId ) ); - }; + } if ($item->getIsVirtual()) { throw new InvalidTransitionException(__('Gift messages can\'t be used for virtual products.')); diff --git a/app/code/Magento/GiftMessage/Model/OrderItemRepository.php b/app/code/Magento/GiftMessage/Model/OrderItemRepository.php index 943552e2b75bc..6cfcd485f7645 100644 --- a/app/code/Magento/GiftMessage/Model/OrderItemRepository.php +++ b/app/code/Magento/GiftMessage/Model/OrderItemRepository.php @@ -89,7 +89,7 @@ public function get($orderId, $orderItemId) throw new NoSuchEntityException( __('No item with the provided ID was found in the Order. Verify the ID and try again.') ); - }; + } if (!$this->helper->isMessagesAllowed('order_item', $orderItem, $this->storeManager->getStore())) { throw new NoSuchEntityException( @@ -123,7 +123,7 @@ public function save($orderId, $orderItemId, \Magento\GiftMessage\Api\Data\Messa throw new NoSuchEntityException( __('No item with the provided ID was found in the Order. Verify the ID and try again.') ); - }; + } if ($order->getIsVirtual()) { throw new InvalidTransitionException(__("Gift messages can't be used for virtual products.")); diff --git a/app/code/Magento/GiftMessage/Model/OrderRepository.php b/app/code/Magento/GiftMessage/Model/OrderRepository.php index abf38f1287b7a..88d1adf92ed0b 100644 --- a/app/code/Magento/GiftMessage/Model/OrderRepository.php +++ b/app/code/Magento/GiftMessage/Model/OrderRepository.php @@ -106,7 +106,7 @@ public function save($orderId, \Magento\GiftMessage\Api\Data\MessageInterface $g $order = $this->orderFactory->create()->load($orderId); if (!$order->getEntityId()) { throw new NoSuchEntityException(__('No order exists with this ID. Verify your information and try again.')); - }; + } if (0 == $order->getTotalItemCount()) { throw new InputException( diff --git a/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php index 6eab92f65117a..6d0716a6f1c27 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php @@ -187,7 +187,7 @@ private function getDependentIndexerIds(string $indexerId) $this->getDependentIndexerIds($id) ); } - }; + } return array_unique($dependentIndexerIds); } diff --git a/app/code/Magento/Indexer/Model/DimensionModes.php b/app/code/Magento/Indexer/Model/DimensionModes.php index bbdee0ced8662..a9507a6f4d358 100644 --- a/app/code/Magento/Indexer/Model/DimensionModes.php +++ b/app/code/Magento/Indexer/Model/DimensionModes.php @@ -26,7 +26,7 @@ public function __construct(array $dimensions) $result = []; foreach ($dimensions as $dimension) { $result[$dimension->getName()] = $dimension; - }; + } return $result; })(...$dimensions); } diff --git a/app/code/Magento/Sales/Model/Order/Validation/CanInvoice.php b/app/code/Magento/Sales/Model/Order/Validation/CanInvoice.php index b109b87e61bbf..8ce448d06b8f2 100644 --- a/app/code/Magento/Sales/Model/Order/Validation/CanInvoice.php +++ b/app/code/Magento/Sales/Model/Order/Validation/CanInvoice.php @@ -44,7 +44,7 @@ private function isStateReadyForInvoice(OrderInterface $order) $order->getState() === Order::STATE_CLOSED ) { return false; - }; + } return true; } diff --git a/app/code/Magento/TaxImportExport/view/adminhtml/templates/importExportHeader.phtml b/app/code/Magento/TaxImportExport/view/adminhtml/templates/importExportHeader.phtml index 223b3e9888eea..75f04eae82159 100644 --- a/app/code/Magento/TaxImportExport/view/adminhtml/templates/importExportHeader.phtml +++ b/app/code/Magento/TaxImportExport/view/adminhtml/templates/importExportHeader.phtml @@ -2,4 +2,4 @@ /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. - */; + */ diff --git a/app/code/Magento/Theme/Model/Design/Config/Validator.php b/app/code/Magento/Theme/Model/Design/Config/Validator.php index 994eeba317a34..1279d9d9ccd20 100644 --- a/app/code/Magento/Theme/Model/Design/Config/Validator.php +++ b/app/code/Magento/Theme/Model/Design/Config/Validator.php @@ -80,7 +80,7 @@ public function validate(DesignConfigInterface $designConfig) ["templateName" => $name] ) ); - }; + } } } } diff --git a/app/code/Magento/Theme/view/frontend/templates/text.phtml b/app/code/Magento/Theme/view/frontend/templates/text.phtml index 4c4b38132c880..7d00235c0362c 100644 --- a/app/code/Magento/Theme/view/frontend/templates/text.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/text.phtml @@ -10,6 +10,6 @@ if (!empty($attr)) { foreach ($block->getAttributes() as $attribute => $value) { $attributes .= ' ' . $attribute . '="' . $value . '"'; } -}; +} echo '<' . $block->getTag() . $attributes . '>' . $block->getText() . 'getTag() . '>'; diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php index e83811042fd8b..c335b66505b0e 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php @@ -180,7 +180,7 @@ public function optionDataProvider() $fixtureOptions[$item['type']] = [ 'optionData' => $item, ]; - }; + } return $fixtureOptions; } @@ -230,7 +230,7 @@ public function optionNegativeDataProvider() $fixtureOptions[$key] = [ 'optionData' => $item, ]; - }; + } return $fixtureOptions; } diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.php index 0b7c31a092280..c08ea7aa9e29b 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.php +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.php @@ -87,7 +87,7 @@ public function openTab($tabName) if ($tabHeader->isVisible() && strpos($tabHeader->getAttribute('class'), '_show') === false) { $tabHeader->hover(); $tabHeader->click(); - }; + } return $this; } diff --git a/dev/tests/functional/tests/app/Magento/ImportExport/Test/Fixture/Import/File.php b/dev/tests/functional/tests/app/Magento/ImportExport/Test/Fixture/Import/File.php index 17cfdc31720cc..89f51931f8dc8 100644 --- a/dev/tests/functional/tests/app/Magento/ImportExport/Test/Fixture/Import/File.php +++ b/dev/tests/functional/tests/app/Magento/ImportExport/Test/Fixture/Import/File.php @@ -316,7 +316,7 @@ private function prepareCsv($csvContent) $explodedArray = $compiledData['explodedArray']; } else { $explodedArray[$i] = str_replace('"', '', $explodedArray[$i]); - }; + } } $data = array_diff($explodedArray, ['%to_delete%']); $this->csv[] = $data; diff --git a/dev/tests/integration/testsuite/Magento/Deploy/_files/theme.php b/dev/tests/integration/testsuite/Magento/Deploy/_files/theme.php index ffeca0f16094d..bc190a8519ae9 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/_files/theme.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/_files/theme.php @@ -24,7 +24,7 @@ function rcopy($src, $destination) // If source is not a directory stop processing if (!is_dir($src)) { return false; - }; + } // If the destination directory does not exist create it // If the destination directory could not be created stop processing diff --git a/dev/tests/integration/testsuite/Magento/GiftMessage/_files/quote_with_item_message_rollback.php b/dev/tests/integration/testsuite/Magento/GiftMessage/_files/quote_with_item_message_rollback.php index 22307b4d83956..cd17183be7f78 100644 --- a/dev/tests/integration/testsuite/Magento/GiftMessage/_files/quote_with_item_message_rollback.php +++ b/dev/tests/integration/testsuite/Magento/GiftMessage/_files/quote_with_item_message_rollback.php @@ -20,7 +20,7 @@ if ($product->getId()) { $product->delete(); } -}; +} $quote->delete(); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php index f204019988b79..46bdd3dd666df 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Magento/Backend/ControllerAclTest.php @@ -97,7 +97,7 @@ public function testAcl() $inheritanceMessage = "Backend controller $className have to inherit " . AbstractAction::class; $errorMessages[] = $inheritanceMessage; continue; - }; + } $isAclRedefinedInTheChildClass = $this->isConstantOverwritten($controllerClass) || $this->isMethodOverwritten($controllerClass); diff --git a/lib/internal/Magento/Framework/Indexer/Config/DependencyInfoProvider.php b/lib/internal/Magento/Framework/Indexer/Config/DependencyInfoProvider.php index c9d7c8a35b243..661f9f9c0ff80 100644 --- a/lib/internal/Magento/Framework/Indexer/Config/DependencyInfoProvider.php +++ b/lib/internal/Magento/Framework/Indexer/Config/DependencyInfoProvider.php @@ -49,7 +49,7 @@ public function getIndexerIdsToRunAfter(string $indexerId): array if (array_search($indexerId, $indexerData['dependencies']) !== false) { $result[] = $id; } - }; + } return $result; } diff --git a/lib/internal/Magento/Framework/Setup/OldDbValidator.php b/lib/internal/Magento/Framework/Setup/OldDbValidator.php index 005f7bdd713be..34f916674ca56 100644 --- a/lib/internal/Magento/Framework/Setup/OldDbValidator.php +++ b/lib/internal/Magento/Framework/Setup/OldDbValidator.php @@ -47,7 +47,7 @@ public function getNotUpToDateMessage(): string $requiredVersion = $versionParser->parseConstraints('>' . $error[DbVersionInfo::KEY_REQUIRED]); if ($requiredVersion->matches($currentVersion)) { $codebaseUpdateNeeded = true; - }; + } $messages[] = sprintf( "%20s %10s: %11s -> %-11s", diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/GeneratedClassesAutoloader.php b/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/GeneratedClassesAutoloader.php index 03c7d85251ac4..2d8348f64a5af 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/GeneratedClassesAutoloader.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/GeneratedClassesAutoloader.php @@ -65,7 +65,7 @@ public function load($className) include $classSourceFile; return true; } - }; + } } return false; From ba7630ba98d8fd1196497685a1c5c14586864e9c Mon Sep 17 00:00:00 2001 From: Joan He Date: Wed, 24 Oct 2018 11:57:26 -0500 Subject: [PATCH 174/309] MAGETWO-95445: Category Flat Data indexer doesnt show status as reindex required after deleting store/storeview - Fix static test failures --- .../Catalog/Model/Indexer/Category/Flat/Action/Full.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php index 624001b498449..a62e3d8f83b85 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Flat/Action/Full.php @@ -183,7 +183,8 @@ private function getActualStoreTablesForCategoryFlat(): array foreach ($this->storeManager->getStores() as $store) { $actualStoreTables[] = sprintf( '%s_store_%s', - $this->connection->getTableName('catalog_category_flat'), $store->getId() + $this->connection->getTableName('catalog_category_flat'), + $store->getId() ); } From 86e035f3ba615cb5e1bb3ff0e4821b82a57fe7fe Mon Sep 17 00:00:00 2001 From: Tomash Khamlai Date: Wed, 24 Oct 2018 21:17:45 +0300 Subject: [PATCH 175/309] Cover \Magento\CheckoutAgreements\Model\ResourceModel\Agreement\Grid\Collection class with Integration test --- .../Model/ResourceModel/Grid/CollectionTest.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CheckoutAgreements/Model/ResourceModel/Grid/CollectionTest.php b/dev/tests/integration/testsuite/Magento/CheckoutAgreements/Model/ResourceModel/Grid/CollectionTest.php index 2f3112e705c94..bc098cf1bd0ec 100644 --- a/dev/tests/integration/testsuite/Magento/CheckoutAgreements/Model/ResourceModel/Grid/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/CheckoutAgreements/Model/ResourceModel/Grid/CollectionTest.php @@ -25,7 +25,8 @@ class CollectionTest extends \PHPUnit\Framework\TestCase */ public function setUp() { - $this->collection = Bootstrap::getObjectManager()->create(Collection::class); + $this->collection = Bootstrap::getObjectManager() + ->create(Collection::class); } /** @@ -35,7 +36,9 @@ public function setUp() */ public function testAddStoresToFilter(): void { - $collectionSize = $this->collection->addStoreFilter(1)->load(false, false)->getSize(); + $collectionSize = $this->collection->addStoreFilter(1) + ->load(false, false) + ->getSize(); $this->assertEquals(2, $collectionSize); } } From 2fcc1830d0a3a24dce25f986c46b97d15f3c4bae Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Wed, 24 Oct 2018 22:36:36 +0300 Subject: [PATCH 176/309] graphQl-44: added ComplexTextValue to graphQl module --- .../Resolver/Product/ProductTextAttribute.php | 30 +++++++++- .../ProductTextAttribute/HtmlContent.php | 60 ------------------- .../Magento/CatalogGraphQl/etc/graphql/di.xml | 7 --- .../CatalogGraphQl/etc/schema.graphqls | 8 +-- .../Resolver/ComplexTextValue/HtmlFormat.php | 38 ++++++++++++ app/code/Magento/GraphQl/etc/schema.graphqls | 4 ++ 6 files changed, 71 insertions(+), 76 deletions(-) delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php create mode 100644 app/code/Magento/GraphQl/Model/Resolver/ComplexTextValue/HtmlFormat.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php index 171e84a65aba4..febcd12f0e96f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute.php @@ -10,12 +10,29 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Catalog\Model\Product; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Catalog\Helper\Output as OutputHelper; /** - * Resolve rendered content for text attributes + * Resolve rendered content for attributes where HTML content is allowed */ class ProductTextAttribute implements ResolverInterface { + /** + * @var OutputHelper + */ + private $outputHelper; + + /** + * @param OutputHelper $outputHelper + */ + public function __construct( + OutputHelper $outputHelper + ) { + $this->outputHelper = $outputHelper; + } + /** * @inheritdoc */ @@ -26,8 +43,15 @@ public function resolve( array $value = null, array $args = null ): array { - $value['field'] = $field->getName(); + if (!isset($value['model'])) { + throw new GraphQlInputException(__('"model" value should be specified')); + } + + /* @var $product Product */ + $product = $value['model']; + $fieldName = $field->getName(); + $renderedValue = $this->outputHelper->productAttribute($product, $product->getData($fieldName), $fieldName); - return $value; + return ['html' => $renderedValue]; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php deleted file mode 100644 index 724d0826dbb3d..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductTextAttribute/HtmlContent.php +++ /dev/null @@ -1,60 +0,0 @@ -outputHelper = $outputHelper; - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ): ?string { - if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); - } - if (!isset($value['field'])) { - throw new GraphQlInputException(__('"field" value should be specified')); - } - - /* @var $product Product */ - $product = $value['model']; - $fieldName = $value['field']; - $renderedValue = $this->outputHelper->productAttribute($product, $product->getData($fieldName), $fieldName); - - return $renderedValue; - } -} diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml index 8d4ca97001d3d..68a292ede6b4a 100644 --- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml @@ -63,13 +63,6 @@ Magento\Catalog\Model\Api\SearchCriteria\ProductCollectionProcessor - - - - Magento\CatalogGraphQl\Model\Resolver\Product\ProductTextAttribute\Html - - - diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index ad724c18a5c9a..fb7dd4ee2c8f0 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -248,8 +248,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ id: Int @doc(description: "The ID number assigned to the product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\EntityIdToId") name: String @doc(description: "The product name. Customers use this name to identify the product.") sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") - description: ProductTextAttribute @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") - short_description: ProductTextAttribute @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") + description: ComplexTextValue @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") + short_description: ComplexTextValue @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute") special_price: Float @doc(description: "The discounted price of the product") special_from_date: String @doc(description: "The beginning date that a product has a special price") special_to_date: String @doc(description: "The end date that a product has a special price") @@ -556,7 +556,3 @@ type SortFields @doc(description: "SortFields contains a default value for sort default: String @doc(description: "Default value of sort fields") options: [SortField] @doc(description: "Available sort fields") } - -type ProductTextAttribute @doc(description: "Product text attribute.") { - html: String @doc(description: "Attribute HTML content") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductTextAttribute\\HtmlContent") -} diff --git a/app/code/Magento/GraphQl/Model/Resolver/ComplexTextValue/HtmlFormat.php b/app/code/Magento/GraphQl/Model/Resolver/ComplexTextValue/HtmlFormat.php new file mode 100644 index 0000000000000..2ec4d24017b0e --- /dev/null +++ b/app/code/Magento/GraphQl/Model/Resolver/ComplexTextValue/HtmlFormat.php @@ -0,0 +1,38 @@ + Date: Thu, 25 Oct 2018 01:07:09 +0530 Subject: [PATCH 177/309] fixed Product Advanced Pricing design issue #18775 --- app/code/Magento/Ui/view/base/web/templates/form/field.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/form/field.html b/app/code/Magento/Ui/view/base/web/templates/form/field.html index ed84e158819a2..6a095b4da14ed 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/field.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/field.html @@ -8,8 +8,8 @@ visible="visible" css="$data.additionalClasses" attr="'data-index': index"> -
    -
    - +
    @@ -31,7 +31,7 @@