diff --git a/app/code/Magento/Backend/Block/Widget/Form.php b/app/code/Magento/Backend/Block/Widget/Form.php
index 59b5cc060cc05..38d5d90a22d15 100644
--- a/app/code/Magento/Backend/Block/Widget/Form.php
+++ b/app/code/Magento/Backend/Block/Widget/Form.php
@@ -59,7 +59,6 @@ protected function _construct()
parent::_construct();
$this->setDestElementId('edit_form');
- $this->setShowGlobalIcon(false);
}
/**
diff --git a/app/code/Magento/Backend/Block/Widget/Grid.php b/app/code/Magento/Backend/Block/Widget/Grid.php
index 72ab5a265d808..66298d23389fb 100644
--- a/app/code/Magento/Backend/Block/Widget/Grid.php
+++ b/app/code/Magento/Backend/Block/Widget/Grid.php
@@ -12,7 +12,7 @@
* @api
* @deprecated 100.2.0 in favour of UI component implementation
* @method string getRowClickCallback() getRowClickCallback()
- * @method \Magento\Backend\Block\Widget\Grid setRowClickCallback() setRowClickCallback(string $value)
+ * @method \Magento\Backend\Block\Widget\Grid setRowClickCallback(string $value)
* @SuppressWarnings(PHPMD.TooManyFields)
* @since 100.0.2
*/
@@ -150,7 +150,10 @@ public function __construct(
}
/**
+ * Internal constructor, that is called from real constructor
+ *
* @return void
+ *
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
protected function _construct()
@@ -709,6 +712,7 @@ public function getGridUrl()
/**
* Grid url getter
+ *
* Version of getGridUrl() but with parameters
*
* @param array $params url parameters
diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php
index b61df8d7cb125..c8cd8c15fdb8e 100644
--- a/app/code/Magento/Bundle/Model/Product/Type.php
+++ b/app/code/Magento/Bundle/Model/Product/Type.php
@@ -823,11 +823,11 @@ private function recursiveIntval(array $array)
private function multiToFlatArray(array $array)
{
$flatArray = [];
- foreach ($array as $key => $value) {
+ foreach ($array as $value) {
if (is_array($value)) {
$flatArray = array_merge($flatArray, $this->multiToFlatArray($value));
} else {
- $flatArray[$key] = $value;
+ $flatArray[] = $value;
}
}
diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php
index a08142c10be5e..2df0ff0b6cd7c 100644
--- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php
+++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php
@@ -64,17 +64,6 @@ public function __construct(
parent::__construct($context, $registry, $formFactory, $data);
}
- /**
- * Construct block
- *
- * @return void
- */
- protected function _construct()
- {
- parent::_construct();
- $this->setShowGlobalIcon(true);
- }
-
/**
* Prepares form
*
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Crosssell.php b/app/code/Magento/Catalog/Block/Product/ProductList/Crosssell.php
index 0c547f81c85d6..596cd7cc5bdce 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Crosssell.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Crosssell.php
@@ -9,6 +9,9 @@
*/
namespace Magento\Catalog\Block\Product\ProductList;
+/**
+ * Crosssell block for product
+ */
class Crosssell extends \Magento\Catalog\Block\Product\AbstractProduct
{
/**
@@ -25,7 +28,7 @@ class Crosssell extends \Magento\Catalog\Block\Product\AbstractProduct
*/
protected function _prepareData()
{
- $product = $this->_coreRegistry->registry('product');
+ $product = $this->getProduct();
/* @var $product \Magento\Catalog\Model\Product */
$this->_itemCollection = $product->getCrossSellProductCollection()->addAttributeToSelect(
@@ -43,6 +46,7 @@ protected function _prepareData()
/**
* Before rendering html process
+ *
* Prepare items collection
*
* @return \Magento\Catalog\Block\Product\ProductList\Crosssell
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php
index 219922f9e46d5..6de70bb971367 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php
@@ -77,11 +77,13 @@ public function __construct(
}
/**
+ * Prepare data
+ *
* @return $this
*/
protected function _prepareData()
{
- $product = $this->_coreRegistry->registry('product');
+ $product = $this->getProduct();
/* @var $product \Magento\Catalog\Model\Product */
$this->_itemCollection = $product->getRelatedProductCollection()->addAttributeToSelect(
@@ -103,6 +105,8 @@ protected function _prepareData()
}
/**
+ * Before to html handler
+ *
* @return $this
*/
protected function _beforeToHtml()
@@ -112,6 +116,8 @@ protected function _beforeToHtml()
}
/**
+ * Get collection items
+ *
* @return Collection
*/
public function getItems()
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php
index 0d64ecc9bff90..24822447ae915 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php
@@ -91,11 +91,13 @@ public function __construct(
}
/**
+ * Prepare data
+ *
* @return $this
*/
protected function _prepareData()
{
- $product = $this->_coreRegistry->registry('product');
+ $product = $this->getProduct();
/* @var $product \Magento\Catalog\Model\Product */
$this->_itemCollection = $product->getUpSellProductCollection()->setPositionOrder()->addStoreFilter();
if ($this->moduleManager->isEnabled('Magento_Checkout')) {
@@ -121,6 +123,8 @@ protected function _prepareData()
}
/**
+ * Before to html handler
+ *
* @return $this
*/
protected function _beforeToHtml()
@@ -130,6 +134,8 @@ protected function _beforeToHtml()
}
/**
+ * Get items collection
+ *
* @return Collection
*/
public function getItemCollection()
@@ -145,6 +151,8 @@ public function getItemCollection()
}
/**
+ * Get collection items
+ *
* @return \Magento\Framework\DataObject[]
*/
public function getItems()
@@ -156,6 +164,8 @@ public function getItems()
}
/**
+ * Get row count
+ *
* @return float
*/
public function getRowCount()
@@ -164,6 +174,8 @@ public function getRowCount()
}
/**
+ * Set column count
+ *
* @param string $columns
* @return $this
*/
@@ -176,6 +188,8 @@ public function setColumnCount($columns)
}
/**
+ * Get column count
+ *
* @return int
*/
public function getColumnCount()
@@ -184,6 +198,8 @@ public function getColumnCount()
}
/**
+ * Reset items iterator
+ *
* @return void
*/
public function resetItemsIterator()
@@ -193,6 +209,8 @@ public function resetItemsIterator()
}
/**
+ * Get iterable item
+ *
* @return mixed
*/
public function getIterableItem()
@@ -204,6 +222,7 @@ public function getIterableItem()
/**
* Set how many items we need to show in upsell block
+ *
* Notice: this parameter will be also applied
*
* @param string $type
@@ -219,6 +238,8 @@ public function setItemLimit($type, $limit)
}
/**
+ * Get item limit
+ *
* @param string $type
* @return array|int
*/
diff --git a/app/code/Magento/Catalog/Controller/Product/Compare.php b/app/code/Magento/Catalog/Controller/Product/Compare.php
index 1ee146e5aaa70..084a82f87d645 100644
--- a/app/code/Magento/Catalog/Controller/Product/Compare.php
+++ b/app/code/Magento/Catalog/Controller/Product/Compare.php
@@ -6,6 +6,7 @@
namespace Magento\Catalog\Controller\Product;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\Data\Form\FormKey\Validator;
use Magento\Framework\View\Result\PageFactory;
@@ -15,7 +16,7 @@
* @SuppressWarnings(PHPMD.LongVariable)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-abstract class Compare extends \Magento\Framework\App\Action\Action
+abstract class Compare extends \Magento\Framework\App\Action\Action implements HttpGetActionInterface
{
/**
* Customer id
@@ -139,4 +140,15 @@ public function setCustomerId($customerId)
$this->_customerId = $customerId;
return $this;
}
+
+ /**
+ * @inheritdoc
+ */
+ public function execute()
+ {
+ $resultRedirect = $this->resultRedirectFactory->create();
+ $resultRedirect->setPath('catalog/product_compare');
+
+ return $resultRedirect;
+ }
}
diff --git a/app/code/Magento/Catalog/Model/Category/Product/PositionResolver.php b/app/code/Magento/Catalog/Model/Category/Product/PositionResolver.php
index 1e07c0cdd924e..44bf153f83697 100644
--- a/app/code/Magento/Catalog/Model/Category/Product/PositionResolver.php
+++ b/app/code/Magento/Catalog/Model/Category/Product/PositionResolver.php
@@ -43,6 +43,8 @@ public function getPositions(int $categoryId): array
$categoryId
)->order(
'ccp.position ' . \Magento\Framework\DB\Select::SQL_ASC
+ )->order(
+ 'ccp.product_id ' . \Magento\Framework\DB\Select::SQL_DESC
);
return array_flip($connection->fetchCol($select));
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
index a077eced6d5d5..fd1b6add8391f 100644
--- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml
@@ -213,4 +213,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
index d12233206ce41..59228d57502f1 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml
@@ -30,5 +30,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml
index a8b2df1fcfa67..f283a040ced41 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml
@@ -59,6 +59,7 @@
+
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Product/PositionResolverTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Product/PositionResolverTest.php
index 1ff3a1bae5c28..7ad8b1a0ab3f8 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Product/PositionResolverTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Product/PositionResolverTest.php
@@ -107,7 +107,7 @@ public function testGetPositions()
$this->select->expects($this->once())
->method('where')
->willReturnSelf();
- $this->select->expects($this->once())
+ $this->select->expects($this->exactly(2))
->method('order')
->willReturnSelf();
$this->select->expects($this->once())
diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php
index 12bc9acfa4c51..009cd690d4cd4 100644
--- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Listing/Collector/ImageTest.php
@@ -15,6 +15,7 @@
use Magento\Catalog\Helper\ImageFactory;
use Magento\Catalog\Api\Data\ProductRender\ImageInterface;
use Magento\Catalog\Helper\Image as ImageHelper;
+use Magento\Framework\View\DesignLoader;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -33,6 +34,9 @@ class ImageTest extends \PHPUnit\Framework\TestCase
/** @var DesignInterface | \PHPUnit_Framework_MockObject_MockObject */
private $design;
+ /** @var DesignLoader | \PHPUnit_Framework_MockObject_MockObject*/
+ private $designLoader;
+
/** @var Image */
private $model;
@@ -60,13 +64,15 @@ public function setUp()
->getMock();
$this->storeManager = $this->createMock(StoreManagerInterface::class);
$this->design = $this->createMock(DesignInterface::class);
+ $this->designLoader = $this->createMock(DesignLoader::class);
$this->model = new Image(
$this->imageFactory,
$this->state,
$this->storeManager,
$this->design,
$this->imageInterfaceFactory,
- $this->imageCodes
+ $this->imageCodes,
+ $this->designLoader
);
}
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
index d84f496e81915..7379600011bcf 100755
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php
@@ -11,6 +11,7 @@
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Type as ProductType;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute;
use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory as EavAttributeFactory;
use Magento\Catalog\Ui\DataProvider\CatalogEavValidationRules;
@@ -419,7 +420,7 @@ public function modifyData(array $data)
foreach ($attributes as $attribute) {
if (null !== ($attributeValue = $this->setupAttributeData($attribute))) {
- if ($attribute->getFrontendInput() === 'price' && is_scalar($attributeValue)) {
+ if ($this->isPriceAttribute($attribute, $attributeValue)) {
$attributeValue = $this->formatPrice($attributeValue);
}
$data[$productId][self::DATA_SOURCE_DEFAULT][$attribute->getAttributeCode()] = $attributeValue;
@@ -430,6 +431,32 @@ public function modifyData(array $data)
return $data;
}
+ /**
+ * Obtain if given attribute is a price
+ *
+ * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute
+ * @param string|integer $attributeValue
+ * @return bool
+ */
+ private function isPriceAttribute(ProductAttributeInterface $attribute, $attributeValue)
+ {
+ return $attribute->getFrontendInput() === 'price'
+ && is_scalar($attributeValue)
+ && !$this->isBundleSpecialPrice($attribute);
+ }
+
+ /**
+ * Obtain if current product is bundle and given attribute is special_price
+ *
+ * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute
+ * @return bool
+ */
+ private function isBundleSpecialPrice(ProductAttributeInterface $attribute)
+ {
+ return $this->locator->getProduct()->getTypeId() === ProductType::TYPE_BUNDLE
+ && $attribute->getAttributeCode() === ProductAttributeInterface::CODE_SPECIAL_PRICE;
+ }
+
/**
* Resolve data persistence
*
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php
index 216bc16968fcb..524927ac1c4b4 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php
@@ -17,12 +17,14 @@
use Magento\Framework\View\DesignInterface;
use Magento\Store\Model\StoreManager;
use Magento\Store\Model\StoreManagerInterface;
+use Magento\Framework\View\DesignLoader;
/**
* Collect enough information about image rendering on front
* If you want to add new image, that should render on front you need
* to configure this class in di.xml
*
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Image implements ProductRenderCollectorInterface
{
@@ -51,6 +53,7 @@ class Image implements ProductRenderCollectorInterface
/**
* @var DesignInterface
+ * @deprecated 2.3.0 DesignLoader is used for design theme loading
*/
private $design;
@@ -59,6 +62,11 @@ class Image implements ProductRenderCollectorInterface
*/
private $imageRenderInfoFactory;
+ /**
+ * @var DesignLoader
+ */
+ private $designLoader;
+
/**
* Image constructor.
* @param ImageFactory $imageFactory
@@ -67,6 +75,7 @@ class Image implements ProductRenderCollectorInterface
* @param DesignInterface $design
* @param ImageInterfaceFactory $imageRenderInfoFactory
* @param array $imageCodes
+ * @param DesignLoader $designLoader
*/
public function __construct(
ImageFactory $imageFactory,
@@ -74,7 +83,8 @@ public function __construct(
StoreManagerInterface $storeManager,
DesignInterface $design,
ImageInterfaceFactory $imageRenderInfoFactory,
- array $imageCodes = []
+ array $imageCodes = [],
+ DesignLoader $designLoader = null
) {
$this->imageFactory = $imageFactory;
$this->imageCodes = $imageCodes;
@@ -82,6 +92,8 @@ public function __construct(
$this->storeManager = $storeManager;
$this->design = $design;
$this->imageRenderInfoFactory = $imageRenderInfoFactory;
+ $this->designLoader = $designLoader ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(DesignLoader::class);
}
/**
@@ -124,6 +136,8 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ
}
/**
+ * Callback for emulating image creation
+ *
* Callback in which we emulate initialize default design theme, depends on current store, be settings store id
* from render info
*
@@ -136,7 +150,7 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ
public function emulateImageCreating(ProductInterface $product, $imageCode, $storeId, ImageInterface $image)
{
$this->storeManager->setCurrentStore($storeId);
- $this->design->setDefaultDesignTheme();
+ $this->designLoader->load();
$imageHelper = $this->imageFactory->create();
$imageHelper->init($product, $imageCode);
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index 9080dab0a5c13..9298939791e4b 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -132,6 +132,16 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
*/
const COL_NAME = 'name';
+ /**
+ * Column new_from_date.
+ */
+ const COL_NEW_FROM_DATE = 'new_from_date';
+
+ /**
+ * Column new_to_date.
+ */
+ const COL_NEW_TO_DATE = 'new_to_date';
+
/**
* Column product website.
*/
@@ -298,6 +308,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid',
ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually',
ValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES => "Value for multiselect attribute %s contains duplicated values",
+ ValidatorInterface::ERROR_NEW_TO_DATE => 'Make sure new_to_date is later than or the same as new_from_date',
];
//@codingStandardsIgnoreEnd
@@ -319,8 +330,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
Product::COL_TYPE => 'product_type',
Product::COL_PRODUCT_WEBSITES => 'product_websites',
'status' => 'product_online',
- 'news_from_date' => 'new_from_date',
- 'news_to_date' => 'new_to_date',
+ 'news_from_date' => self::COL_NEW_FROM_DATE,
+ 'news_to_date' => self::COL_NEW_TO_DATE,
'options_container' => 'display_product_options_in',
'minimal_price' => 'map_price',
'msrp' => 'msrp_price',
@@ -1423,7 +1434,7 @@ protected function _saveProductCategories(array $categoriesData)
$delProductId[] = $productId;
foreach (array_keys($categories) as $categoryId) {
- $categoriesIn[] = ['product_id' => $productId, 'category_id' => $categoryId, 'position' => 1];
+ $categoriesIn[] = ['product_id' => $productId, 'category_id' => $categoryId, 'position' => 0];
}
}
if (Import::BEHAVIOR_APPEND != $this->getBehavior()) {
@@ -1794,6 +1805,7 @@ protected function _saveProducts()
if ($uploadedFile) {
$uploadedImages[$columnImage] = $uploadedFile;
} else {
+ unset($rowData[$column]);
$this->addRowError(
ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE,
$rowNum,
@@ -2549,6 +2561,20 @@ public function validateRow(array $rowData, $rowNum)
}
}
}
+
+ if (!empty($rowData[self::COL_NEW_FROM_DATE]) && !empty($rowData[self::COL_NEW_TO_DATE])
+ ) {
+ $newFromTimestamp = strtotime($this->dateTime->formatDate($rowData[self::COL_NEW_FROM_DATE], false));
+ $newToTimestamp = strtotime($this->dateTime->formatDate($rowData[self::COL_NEW_TO_DATE], false));
+ if ($newFromTimestamp > $newToTimestamp) {
+ $this->addRowError(
+ ValidatorInterface::ERROR_NEW_TO_DATE,
+ $rowNum,
+ $rowData[self::COL_NEW_TO_DATE]
+ );
+ }
+ }
+
return !$this->getErrorAggregator()->isRowInvalid($rowNum);
}
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php
index f41596ad185a6..cbdc5f5beaaf9 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php
@@ -87,6 +87,8 @@ interface RowValidatorInterface extends \Magento\Framework\Validator\ValidatorIn
const ERROR_DUPLICATE_MULTISELECT_VALUES = 'duplicatedMultiselectValues';
+ const ERROR_NEW_TO_DATE = 'invalidNewToDateValue';
+
/**
* Value that means all entities (e.g. websites, groups etc.)
*/
diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml
index 7ca86b8b14a4d..387a7547f4daf 100644
--- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml
+++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml
@@ -15,6 +15,7 @@
+
diff --git a/app/code/Magento/CatalogUrlRewrite/Model/CategoryUrlPathGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/CategoryUrlPathGenerator.php
index ee20b0e934b5d..cba9218ce7c72 100644
--- a/app/code/Magento/CatalogUrlRewrite/Model/CategoryUrlPathGenerator.php
+++ b/app/code/Magento/CatalogUrlRewrite/Model/CategoryUrlPathGenerator.php
@@ -8,6 +8,9 @@
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Catalog\Model\Category;
+/**
+ * Class for generation category url_path
+ */
class CategoryUrlPathGenerator
{
/**
@@ -61,9 +64,11 @@ public function __construct(
* Build category URL path
*
* @param \Magento\Catalog\Api\Data\CategoryInterface|\Magento\Framework\Model\AbstractModel $category
+ * @param null|\Magento\Catalog\Api\Data\CategoryInterface|\Magento\Framework\Model\AbstractModel $parentCategory
* @return string
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
*/
- public function getUrlPath($category)
+ public function getUrlPath($category, $parentCategory = null)
{
if (in_array($category->getParentId(), [Category::ROOT_CATEGORY_ID, Category::TREE_ROOT_ID])) {
return '';
@@ -77,15 +82,17 @@ public function getUrlPath($category)
return $category->getUrlPath();
}
if ($this->isNeedToGenerateUrlPathForParent($category)) {
- $parentPath = $this->getUrlPath(
- $this->categoryRepository->get($category->getParentId(), $category->getStoreId())
- );
+ $parentCategory = $parentCategory === null ?
+ $this->categoryRepository->get($category->getParentId(), $category->getStoreId()) : $parentCategory;
+ $parentPath = $this->getUrlPath($parentCategory);
$path = $parentPath === '' ? $path : $parentPath . '/' . $path;
}
return $path;
}
/**
+ * Define whether we should generate URL path for parent
+ *
* @param \Magento\Catalog\Model\Category $category
* @return bool
*/
diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php
index d692918aff6a6..713dd6ac0c736 100644
--- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php
+++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php
@@ -71,16 +71,33 @@ public function execute(\Magento\Framework\Event\Observer $observer)
$useDefaultAttribute = !$category->isObjectNew() && !empty($category->getData('use_default')['url_key']);
if ($category->getUrlKey() !== false && !$useDefaultAttribute) {
$resultUrlKey = $this->categoryUrlPathGenerator->getUrlKey($category);
- if (empty($resultUrlKey)) {
- throw new \Magento\Framework\Exception\LocalizedException(__('Invalid URL key'));
- }
- $category->setUrlKey($resultUrlKey)
- ->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category));
- if (!$category->isObjectNew()) {
- $category->getResource()->saveAttribute($category, 'url_path');
- if ($category->dataHasChangedFor('url_path')) {
- $this->updateUrlPathForChildren($category);
- }
+ $this->updateUrlKey($category, $resultUrlKey);
+ } else if ($useDefaultAttribute) {
+ $resultUrlKey = $category->formatUrlKey($category->getOrigData('name'));
+ $this->updateUrlKey($category, $resultUrlKey);
+ $category->setUrlKey(null)->setUrlPath(null);
+ }
+ }
+
+ /**
+ * Update Url Key
+ *
+ * @param Category $category
+ * @param string $urlKey
+ * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ private function updateUrlKey($category, $urlKey)
+ {
+ if (empty($urlKey)) {
+ throw new \Magento\Framework\Exception\LocalizedException(__('Invalid URL key'));
+ }
+ $category->setUrlKey($urlKey)
+ ->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category));
+ if (!$category->isObjectNew()) {
+ $category->getResource()->saveAttribute($category, 'url_path');
+ if ($category->dataHasChangedFor('url_path')) {
+ $this->updateUrlPathForChildren($category);
}
}
}
@@ -110,8 +127,13 @@ protected function updateUrlPathForChildren(Category $category)
} else {
$children = $this->childrenCategoriesProvider->getChildren($category, true);
foreach ($children as $child) {
+ /** @var Category $child */
$child->setStoreId($category->getStoreId());
- $this->updateUrlPathForCategory($child);
+ if ($child->getParentId() === $category->getId()) {
+ $this->updateUrlPathForCategory($child, $category);
+ } else {
+ $this->updateUrlPathForCategory($child);
+ }
}
}
}
@@ -131,12 +153,14 @@ protected function isGlobalScope($storeId)
* Update url path for category.
*
* @param Category $category
+ * @param Category|null $parentCategory
* @return void
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
*/
- protected function updateUrlPathForCategory(Category $category)
+ protected function updateUrlPathForCategory(Category $category, Category $parentCategory = null)
{
$category->unsUrlPath();
- $category->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category));
+ $category->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category, $parentCategory));
$category->getResource()->saveAttribute($category, 'url_path');
}
}
diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/Webapi/Controller/Rest/InputParamsResolver.php b/app/code/Magento/CatalogUrlRewrite/Plugin/Webapi/Controller/Rest/InputParamsResolver.php
new file mode 100644
index 0000000000000..4e8e3840693a5
--- /dev/null
+++ b/app/code/Magento/CatalogUrlRewrite/Plugin/Webapi/Controller/Rest/InputParamsResolver.php
@@ -0,0 +1,88 @@
+request = $request;
+ }
+
+ /**
+ * Add 'save_rewrites_history' param to the product data
+ *
+ * @see \Magento\CatalogUrlRewrite\Plugin\Catalog\Controller\Adminhtml\Product\Initialization\Helper
+ * @param \Magento\Webapi\Controller\Rest\InputParamsResolver $subject
+ * @param array $result
+ * @return array
+ */
+ public function afterResolve(\Magento\Webapi\Controller\Rest\InputParamsResolver $subject, array $result): array
+ {
+ $route = $subject->getRoute();
+ $serviceMethodName = $route->getServiceMethod();
+ $serviceClassName = $route->getServiceClass();
+ $requestBodyParams = $this->request->getBodyParams();
+
+ if ($this->isProductSaveCalled($serviceClassName, $serviceMethodName)
+ && $this->isCustomAttributesExists($requestBodyParams)) {
+ foreach ($requestBodyParams['product']['custom_attributes'] as $attribute) {
+ if ($attribute['attribute_code'] === 'save_rewrites_history') {
+ foreach ($result as $resultItem) {
+ if ($resultItem instanceof \Magento\Catalog\Model\Product) {
+ $resultItem->setData('save_rewrites_history', (bool)$attribute['value']);
+ break 2;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Check that product save method called
+ *
+ * @param string $serviceClassName
+ * @param string $serviceMethodName
+ * @return bool
+ */
+ private function isProductSaveCalled(string $serviceClassName, string $serviceMethodName): bool
+ {
+ return $serviceClassName === ProductRepositoryInterface::class && $serviceMethodName === 'save';
+ }
+
+ /**
+ * Check is any custom options exists in product data
+ *
+ * @param array $requestBodyParams
+ * @return bool
+ */
+ private function isCustomAttributesExists(array $requestBodyParams): bool
+ {
+ return !empty($requestBodyParams['product']['custom_attributes']);
+ }
+}
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/Webapi/Controller/Rest/InputParamsResolverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/Webapi/Controller/Rest/InputParamsResolverTest.php
new file mode 100644
index 0000000000000..8e705b2c09f6b
--- /dev/null
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Plugin/Webapi/Controller/Rest/InputParamsResolverTest.php
@@ -0,0 +1,116 @@
+saveRewritesHistory = 'save_rewrites_history';
+ $this->requestBodyParams = [
+ 'product' => [
+ 'sku' => 'test',
+ 'custom_attributes' => [
+ ['attribute_code' => $this->saveRewritesHistory, 'value' => 1]
+ ]
+ ]
+ ];
+
+ $this->route = $this->createPartialMock(Route::class, ['getServiceMethod', 'getServiceClass']);
+ $this->request = $this->createPartialMock(RestRequest::class, ['getBodyParams']);
+ $this->request->expects($this->any())->method('getBodyParams')->willReturn($this->requestBodyParams);
+ $this->subject = $this->createPartialMock(InputParamsResolver::class, ['getRoute']);
+ $this->subject->expects($this->any())->method('getRoute')->willReturn($this->route);
+ $this->product = $this->createPartialMock(Product::class, ['setData']);
+
+ $this->result = [false, $this->product, 'test'];
+
+ $this->objectManager = new ObjectManager($this);
+ $this->plugin = $this->objectManager->getObject(
+ InputParamsResolverPlugin::class,
+ [
+ 'request' => $this->request
+ ]
+ );
+ }
+
+ public function testAfterResolve()
+ {
+ $this->route->expects($this->once())
+ ->method('getServiceClass')
+ ->willReturn(ProductRepositoryInterface::class);
+ $this->route->expects($this->once())
+ ->method('getServiceMethod')
+ ->willReturn('save');
+ $this->product->expects($this->once())
+ ->method('setData')
+ ->with($this->saveRewritesHistory, true);
+
+ $this->plugin->afterResolve($this->subject, $this->result);
+ }
+}
diff --git a/app/code/Magento/CatalogUrlRewrite/composer.json b/app/code/Magento/CatalogUrlRewrite/composer.json
index e373d8c8c1756..b4ceff96b50b7 100644
--- a/app/code/Magento/CatalogUrlRewrite/composer.json
+++ b/app/code/Magento/CatalogUrlRewrite/composer.json
@@ -16,6 +16,9 @@
"magento/module-ui": "*",
"magento/module-url-rewrite": "*"
},
+ "suggest": {
+ "magento/module-webapi": "*"
+ },
"type": "magento2-module",
"license": [
"OSL-3.0",
diff --git a/app/code/Magento/CatalogUrlRewrite/etc/webapi_rest/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/webapi_rest/di.xml
index ac8beb362f0fb..34b7487725d76 100644
--- a/app/code/Magento/CatalogUrlRewrite/etc/webapi_rest/di.xml
+++ b/app/code/Magento/CatalogUrlRewrite/etc/webapi_rest/di.xml
@@ -7,4 +7,7 @@
-->
+
+
+
diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/edit.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/edit.phtml
index 1876cf2edb786..da0a83f05ef60 100644
--- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/edit.phtml
+++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/edit.phtml
@@ -12,8 +12,6 @@
-
- = /* @escapeNotVerified */ __('Edit') ?>
-
-
+ = /* @escapeNotVerified */ __('Edit') ?>
+
diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/Blocks.php b/app/code/Magento/CmsGraphQl/Model/Resolver/Blocks.php
index 962127e16f716..e55db2a3fa42a 100644
--- a/app/code/Magento/CmsGraphQl/Model/Resolver/Blocks.php
+++ b/app/code/Magento/CmsGraphQl/Model/Resolver/Blocks.php
@@ -55,6 +55,8 @@ public function resolve(
}
/**
+ * Get block identifiers
+ *
* @param array $args
* @return string[]
* @throws GraphQlInputException
@@ -69,6 +71,8 @@ private function getBlockIdentifiers(array $args): array
}
/**
+ * Get blocks data
+ *
* @param array $blockIdentifiers
* @return array
* @throws GraphQlNoSuchEntityException
@@ -76,12 +80,12 @@ private function getBlockIdentifiers(array $args): array
private function getBlocksData(array $blockIdentifiers): array
{
$blocksData = [];
- try {
- foreach ($blockIdentifiers as $blockIdentifier) {
+ foreach ($blockIdentifiers as $blockIdentifier) {
+ try {
$blocksData[$blockIdentifier] = $this->blockDataProvider->getData($blockIdentifier);
+ } catch (NoSuchEntityException $e) {
+ $blocksData[$blockIdentifier] = new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
}
- } catch (NoSuchEntityException $e) {
- throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
}
return $blocksData;
}
diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php
index 5b7e632a73cb0..47a2439c4fad0 100644
--- a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php
+++ b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php
@@ -40,6 +40,8 @@ public function __construct(
}
/**
+ * Get block data
+ *
* @param string $blockIdentifier
* @return array
* @throws NoSuchEntityException
@@ -49,7 +51,9 @@ public function getData(string $blockIdentifier): array
$block = $this->blockRepository->getById($blockIdentifier);
if (false === $block->isActive()) {
- throw new NoSuchEntityException();
+ throw new NoSuchEntityException(
+ __('The CMS block with the "%1" ID doesn\'t exist.', $blockIdentifier)
+ );
}
$renderedContent = $this->widgetFilter->filter($block->getContent());
diff --git a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php
index 3b657dd1ab2d0..77110975401ff 100644
--- a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php
@@ -70,4 +70,14 @@ public function getIdentities()
}
return $identities;
}
+
+ /**
+ * Get price for exact simple product added to cart
+ *
+ * @inheritdoc
+ */
+ public function getProductPriceHtml(\Magento\Catalog\Model\Product $product)
+ {
+ return parent::getProductPriceHtml($this->getChildProduct());
+ }
}
diff --git a/app/code/Magento/Customer/Api/AccountManagementInterface.php b/app/code/Magento/Customer/Api/AccountManagementInterface.php
index 10fc2349968ea..e84da5b9fcd57 100644
--- a/app/code/Magento/Customer/Api/AccountManagementInterface.php
+++ b/app/code/Magento/Customer/Api/AccountManagementInterface.php
@@ -31,13 +31,15 @@ interface AccountManagementInterface
* @param \Magento\Customer\Api\Data\CustomerInterface $customer
* @param string $password
* @param string $redirectUrl
+ * @param string[] $extensions
* @return \Magento\Customer\Api\Data\CustomerInterface
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function createAccount(
\Magento\Customer\Api\Data\CustomerInterface $customer,
$password = null,
- $redirectUrl = ''
+ $redirectUrl = '',
+ $extensions = []
);
/**
@@ -48,6 +50,7 @@ public function createAccount(
* @param string $hash Password hash that we can save directly
* @param string $redirectUrl URL fed to welcome email templates. Can be used by templates to, for example, direct
* the customer to a product they were looking at after pressing confirmation link.
+ * @param string[] $extensions
* @return \Magento\Customer\Api\Data\CustomerInterface
* @throws \Magento\Framework\Exception\InputException If bad input is provided
* @throws \Magento\Framework\Exception\State\InputMismatchException If the provided email is already used
@@ -56,7 +59,8 @@ public function createAccount(
public function createAccountWithPasswordHash(
\Magento\Customer\Api\Data\CustomerInterface $customer,
$hash,
- $redirectUrl = ''
+ $redirectUrl = '',
+ $extensions = []
);
/**
diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php
index 8beecffd1c865..0e2503c837d94 100644
--- a/app/code/Magento/Customer/Model/AccountManagement.php
+++ b/app/code/Magento/Customer/Model/AccountManagement.php
@@ -794,7 +794,7 @@ public function getConfirmationStatus($customerId)
/**
* @inheritdoc
*/
- public function createAccount(CustomerInterface $customer, $password = null, $redirectUrl = '')
+ public function createAccount(CustomerInterface $customer, $password = null, $redirectUrl = '', $extensions = [])
{
if ($password !== null) {
$this->checkPasswordStrength($password);
@@ -810,7 +810,7 @@ public function createAccount(CustomerInterface $customer, $password = null, $re
} else {
$hash = null;
}
- return $this->createAccountWithPasswordHash($customer, $hash, $redirectUrl);
+ return $this->createAccountWithPasswordHash($customer, $hash, $redirectUrl, $extensions);
}
/**
@@ -818,8 +818,12 @@ public function createAccount(CustomerInterface $customer, $password = null, $re
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
- public function createAccountWithPasswordHash(CustomerInterface $customer, $hash, $redirectUrl = '')
- {
+ public function createAccountWithPasswordHash(
+ CustomerInterface $customer,
+ $hash,
+ $redirectUrl = '',
+ $extensions = []
+ ) {
// This logic allows an existing customer to be added to a different store. No new account is created.
// The plan is to move this logic into a new method called something like 'registerAccountWithStore'
if ($customer->getId()) {
@@ -892,7 +896,7 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash
$customer = $this->customerRepository->getById($customer->getId());
$newLinkToken = $this->mathRandom->getUniqueHash();
$this->changeResetPasswordLinkToken($customer, $newLinkToken);
- $this->sendEmailConfirmation($customer, $redirectUrl);
+ $this->sendEmailConfirmation($customer, $redirectUrl, $extensions);
return $customer;
}
@@ -920,9 +924,10 @@ public function getDefaultShippingAddress($customerId)
*
* @param CustomerInterface $customer
* @param string $redirectUrl
+ * @param array $extensions
* @return void
*/
- protected function sendEmailConfirmation(CustomerInterface $customer, $redirectUrl)
+ protected function sendEmailConfirmation(CustomerInterface $customer, $redirectUrl, $extensions = [])
{
try {
$hash = $this->customerRegistry->retrieveSecureData($customer->getId())->getPasswordHash();
@@ -932,7 +937,14 @@ protected function sendEmailConfirmation(CustomerInterface $customer, $redirectU
} elseif ($hash == '') {
$templateType = self::NEW_ACCOUNT_EMAIL_REGISTERED_NO_PASSWORD;
}
- $this->getEmailNotification()->newAccount($customer, $templateType, $redirectUrl, $customer->getStoreId());
+ $this->getEmailNotification()->newAccount(
+ $customer,
+ $templateType,
+ $redirectUrl,
+ $customer->getStoreId(),
+ null,
+ $extensions
+ );
} catch (MailException $e) {
// If we are not able to send a new account email, this should be ignored
$this->logger->critical($e);
@@ -1384,6 +1396,7 @@ public function changeResetPasswordLinkToken($customer, $passwordLinkToken)
$customerSecure->setRpTokenCreatedAt(
$this->dateTimeFactory->create()->format(DateTime::DATETIME_PHP_FORMAT)
);
+ $this->setIgnoreValidationFlag($customer);
$this->customerRepository->save($customer);
}
return true;
@@ -1537,4 +1550,15 @@ private function destroyCustomerSessions($customerId)
$this->saveHandler->destroy($sessionId);
}
}
+
+ /**
+ * Set ignore_validation_flag for reset password flow to skip unnecessary address and customer validation
+ *
+ * @param Customer $customer
+ * @return void
+ */
+ private function setIgnoreValidationFlag($customer)
+ {
+ $customer->setData('ignore_validation_flag', true);
+ }
}
diff --git a/app/code/Magento/Customer/Model/EmailNotification.php b/app/code/Magento/Customer/Model/EmailNotification.php
index 4b65dcca0973f..30a9dbedde8d0 100644
--- a/app/code/Magento/Customer/Model/EmailNotification.php
+++ b/app/code/Magento/Customer/Model/EmailNotification.php
@@ -17,6 +17,8 @@
use Magento\Framework\Exception\LocalizedException;
/**
+ * Class for notification customer.
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class EmailNotification implements EmailNotificationInterface
@@ -63,6 +65,8 @@ class EmailNotification implements EmailNotificationInterface
self::NEW_ACCOUNT_EMAIL_CONFIRMATION => self::XML_PATH_CONFIRM_EMAIL_TEMPLATE,
];
+ const CUSTOMER_CONFIRM_URL = 'customer/account/confirm/';
+
/**#@-*/
/**#@-*/
@@ -362,6 +366,7 @@ public function passwordResetConfirmation(CustomerInterface $customer)
* @param string $backUrl
* @param string $storeId
* @param string $sendemailStoreId
+ * @param array $extensions
* @return void
* @throws LocalizedException
*/
@@ -370,7 +375,8 @@ public function newAccount(
$type = self::NEW_ACCOUNT_EMAIL_REGISTERED,
$backUrl = '',
$storeId = 0,
- $sendemailStoreId = null
+ $sendemailStoreId = null,
+ $extensions = []
) {
$types = self::TEMPLATE_TYPES;
@@ -388,11 +394,26 @@ public function newAccount(
$customerEmailData = $this->getFullCustomerObject($customer);
+ $templateVars = [
+ 'customer' => $customerEmailData,
+ 'back_url' => $backUrl,
+ 'store' => $store
+ ];
+ if ($type == self::NEW_ACCOUNT_EMAIL_CONFIRMATION) {
+ if (empty($extensions)) {
+ $templateVars['url'] = self::CUSTOMER_CONFIRM_URL;
+ $templateVars['extensions'] = $extensions;
+ } else {
+ $templateVars['url'] = $extensions['url'];
+ $templateVars['extensions'] = $extensions['extension_info'];
+ }
+ }
+
$this->sendEmailTemplate(
$customer,
$types[$type],
self::XML_PATH_REGISTER_EMAIL_IDENTITY,
- ['customer' => $customerEmailData, 'back_url' => $backUrl, 'store' => $store],
+ $templateVars,
$storeId
);
}
diff --git a/app/code/Magento/Customer/Model/ResourceModel/Customer.php b/app/code/Magento/Customer/Model/ResourceModel/Customer.php
index f510201559687..2eb1ef897e70e 100644
--- a/app/code/Magento/Customer/Model/ResourceModel/Customer.php
+++ b/app/code/Magento/Customer/Model/ResourceModel/Customer.php
@@ -151,7 +151,9 @@ protected function _beforeSave(\Magento\Framework\DataObject $customer)
$customer->setConfirmation(null);
}
- $this->_validate($customer);
+ if (!$customer->getData('ignore_validation_flag')) {
+ $this->_validate($customer);
+ }
return $this;
}
diff --git a/app/code/Magento/Customer/Model/ResourceModel/Customer/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Customer/Relation.php
index e55c5d443c9d1..96f47154e874e 100644
--- a/app/code/Magento/Customer/Model/ResourceModel/Customer/Relation.php
+++ b/app/code/Magento/Customer/Model/ResourceModel/Customer/Relation.php
@@ -23,41 +23,43 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $customer
$defaultBillingId = $customer->getData('default_billing');
$defaultShippingId = $customer->getData('default_shipping');
- /** @var \Magento\Customer\Model\Address $address */
- foreach ($customer->getAddresses() as $address) {
- if ($address->getData('_deleted')) {
- if ($address->getId() == $defaultBillingId) {
- $customer->setData('default_billing', null);
- }
+ if (!$customer->getData('ignore_validation_flag')) {
+ /** @var \Magento\Customer\Model\Address $address */
+ foreach ($customer->getAddresses() as $address) {
+ if ($address->getData('_deleted')) {
+ if ($address->getId() == $defaultBillingId) {
+ $customer->setData('default_billing', null);
+ }
- if ($address->getId() == $defaultShippingId) {
- $customer->setData('default_shipping', null);
- }
+ if ($address->getId() == $defaultShippingId) {
+ $customer->setData('default_shipping', null);
+ }
- $removedAddressId = $address->getId();
- $address->delete();
+ $removedAddressId = $address->getId();
+ $address->delete();
- // Remove deleted address from customer address collection
- $customer->getAddressesCollection()->removeItemByKey($removedAddressId);
- } else {
- $address->setParentId(
- $customer->getId()
- )->setStoreId(
- $customer->getStoreId()
- )->setIsCustomerSaveTransaction(
- true
- )->save();
+ // Remove deleted address from customer address collection
+ $customer->getAddressesCollection()->removeItemByKey($removedAddressId);
+ } else {
+ $address->setParentId(
+ $customer->getId()
+ )->setStoreId(
+ $customer->getStoreId()
+ )->setIsCustomerSaveTransaction(
+ true
+ )->save();
- if (($address->getIsPrimaryBilling() ||
- $address->getIsDefaultBilling()) && $address->getId() != $defaultBillingId
- ) {
- $customer->setData('default_billing', $address->getId());
- }
+ if (($address->getIsPrimaryBilling() ||
+ $address->getIsDefaultBilling()) && $address->getId() != $defaultBillingId
+ ) {
+ $customer->setData('default_billing', $address->getId());
+ }
- if (($address->getIsPrimaryShipping() ||
- $address->getIsDefaultShipping()) && $address->getId() != $defaultShippingId
- ) {
- $customer->setData('default_shipping', $address->getId());
+ if (($address->getIsPrimaryShipping() ||
+ $address->getIsDefaultShipping()) && $address->getId() != $defaultShippingId
+ ) {
+ $customer->setData('default_shipping', $address->getId());
+ }
}
}
}
diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php
index a053eee5cd09b..77049dac5aa0c 100644
--- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php
+++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php
@@ -222,6 +222,7 @@ public function save(CustomerInterface $customer, $passwordHash = null)
) {
$customerModel->setDefaultShipping($prevCustomerDataArr['default_shipping']);
}
+ $this->setValidationFlag($customerArr, $customerModel);
$customerModel->save();
$this->customerRegistry->push($customerModel);
$customerId = $customerModel->getId();
@@ -231,7 +232,7 @@ public function save(CustomerInterface $customer, $passwordHash = null)
) {
$customer->setAddresses($delegatedNewOperation->getCustomer()->getAddresses());
}
- if ($customer->getAddresses() !== null) {
+ if ($customer->getAddresses() !== null && !$customerModel->getData('ignore_validation_flag')) {
if ($customer->getId()) {
$existingAddresses = $this->getById($customer->getId())->getAddresses();
$getIdFunc = function ($address) {
@@ -396,4 +397,18 @@ protected function addFilterGroupToCollection(FilterGroup $filterGroup, Collecti
$collection->addFieldToFilter($fields);
}
}
+
+ /**
+ * Set ignore_validation_flag to skip model validation
+ *
+ * @param array $customerArray
+ * @param Customer $customerModel
+ * @return void
+ */
+ private function setValidationFlag($customerArray, $customerModel)
+ {
+ if (isset($customerArray['ignore_validation_flag'])) {
+ $customerModel->setData('ignore_validation_flag', true);
+ }
+ }
}
diff --git a/app/code/Magento/Customer/view/frontend/email/account_new_confirmation.html b/app/code/Magento/Customer/view/frontend/email/account_new_confirmation.html
index 010087ace2d42..9b183d63471f3 100644
--- a/app/code/Magento/Customer/view/frontend/email/account_new_confirmation.html
+++ b/app/code/Magento/Customer/view/frontend/email/account_new_confirmation.html
@@ -9,7 +9,9 @@
"var this.getUrl($store, 'customer/account/confirm/', [_query:[id:$customer.id, key:$customer.confirmation, back_url:$back_url]])":"Account Confirmation URL",
"var this.getUrl($store, 'customer/account/')":"Customer Account URL",
"var customer.email":"Customer Email",
-"var customer.name":"Customer Name"
+"var customer.name":"Customer Name",
+"var extensions":"Extensions",
+"var url":"Url"
} @-->
{{template config_path="design/email/header_template"}}
@@ -23,7 +25,7 @@
diff --git a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml
index b9fddc46f78ec..dd42b408ff75e 100644
--- a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml
+++ b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml
@@ -92,15 +92,15 @@
- 1
elasticsearch5
+ 1
- 1
elasticsearch5
+ 1
diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/History/Download.php b/app/code/Magento/ImportExport/Controller/Adminhtml/History/Download.php
index e490ee4018376..ba37cc8aacff9 100644
--- a/app/code/Magento/ImportExport/Controller/Adminhtml/History/Download.php
+++ b/app/code/Magento/ImportExport/Controller/Adminhtml/History/Download.php
@@ -1,20 +1,28 @@
subscriberFactory = $subscriberFactory;
$this->extensionFactory = $extensionFactory;
$this->subscriberResource = $subscriberResource;
+ $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class);
}
/**
@@ -151,7 +164,8 @@ public function afterDelete(CustomerRepository $subject, $result, CustomerInterf
public function afterGetById(CustomerRepository $subject, CustomerInterface $customer)
{
$extensionAttributes = $customer->getExtensionAttributes();
-
+ $storeId = $this->storeManager->getStore()->getId();
+ $customer->setStoreId($storeId);
if ($extensionAttributes === null) {
/** @var CustomerExtensionInterface $extensionAttributes */
$extensionAttributes = $this->extensionFactory->create(CustomerInterface::class);
diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php
index e809b7e37a432..3be28cacc93e0 100644
--- a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php
+++ b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php
@@ -10,6 +10,8 @@
use Magento\Customer\Api\Data\CustomerExtensionInterface;
use Magento\Framework\Api\ExtensionAttributesFactory;
use Magento\Newsletter\Model\ResourceModel\Subscriber;
+use Magento\Store\Model\Store;
+use Magento\Store\Model\StoreManagerInterface;
class CustomerPluginTest extends \PHPUnit\Framework\TestCase
{
@@ -53,6 +55,11 @@ class CustomerPluginTest extends \PHPUnit\Framework\TestCase
*/
private $customerMock;
+ /**
+ * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $storeManagerMock;
+
protected function setUp()
{
$this->subscriberFactory = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class)
@@ -87,6 +94,8 @@ protected function setUp()
->setMethods(['getExtensionAttributes'])
->disableOriginalConstructor()
->getMockForAbstractClass();
+ $this->storeManagerMock = $this->createMock(StoreManagerInterface::class);
+
$this->subscriberFactory->expects($this->any())->method('create')->willReturn($this->subscriber);
$this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
@@ -96,6 +105,7 @@ protected function setUp()
'subscriberFactory' => $this->subscriberFactory,
'extensionFactory' => $this->extensionFactoryMock,
'subscriberResource' => $this->subscriberResourceMock,
+ 'storeManager' => $this->storeManagerMock,
]
);
}
@@ -206,6 +216,7 @@ public function testAfterGetByIdCreatesExtensionAttributesIfItIsNotSet(
) {
$subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class);
$subscriber = [$subscriberStatusKey => $subscriberStatusValue];
+ $this->prepareStoreData();
$this->extensionFactoryMock->expects($this->any())
->method('create')
@@ -233,6 +244,7 @@ public function testAfterGetByIdSetsIsSubscribedFlagIfItIsNotSet()
{
$subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class);
$subscriber = ['subscriber_id' => 1, 'subscriber_status' => 1];
+ $this->prepareStoreData();
$this->customerMock->expects($this->any())
->method('getExtensionAttributes')
@@ -267,4 +279,17 @@ public function afterGetByIdDataProvider()
[null, null, false],
];
}
+
+ /**
+ * Prepare store information
+ *
+ * @return void
+ */
+ private function prepareStoreData()
+ {
+ $storeId = 1;
+ $storeMock = $this->createMock(Store::class);
+ $storeMock->expects($this->any())->method('getId')->willReturn($storeId);
+ $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock);
+ }
}
diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js
index fccc8510ffc70..2e8a4769be10b 100644
--- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js
+++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js
@@ -6,9 +6,10 @@
define([
'jquery',
'domReady',
+ 'consoleLogger',
'jquery/ui',
'mage/cookies'
-], function ($, domReady) {
+], function ($, domReady, consoleLogger) {
'use strict';
/**
@@ -35,7 +36,9 @@ define([
* @returns {Array}
*/
$.fn.comments = function () {
- var elements = [];
+ var elements = [],
+ contents,
+ elementContents;
/**
* @param {jQuery} element - Comment holder
@@ -46,14 +49,35 @@ define([
// prevent cross origin iframe content reading
if ($(element).prop('tagName') === 'IFRAME') {
iframeHostName = $('').prop('href', $(element).prop('src'))
- .prop('hostname');
+ .prop('hostname');
if (window.location.hostname !== iframeHostName) {
return [];
}
}
- $(element).contents().each(function (index, el) {
+ /**
+ * Rewrite jQuery contents().
+ *
+ * @param {jQuery} elem
+ */
+ contents = function (elem) {
+ return $.map(elem, function (el) {
+ try {
+ return $.nodeName(el, 'iframe') ?
+ el.contentDocument || (el.contentWindow ? el.contentWindow.document : []) :
+ $.merge([], el.childNodes);
+ } catch (e) {
+ consoleLogger.error(e);
+
+ return [];
+ }
+ });
+ };
+
+ elementContents = contents($(element));
+
+ $.each(elementContents, function (index, el) {
switch (el.nodeType) {
case 1: // ELEMENT_NODE
lookup(el);
diff --git a/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml b/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml
index c127371cb5f47..afa71fe591495 100644
--- a/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml
+++ b/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml
@@ -40,9 +40,7 @@ $params = $block->getParams();
$(parent).trigger('clearTimeout');
fullScreenLoader.stopLoader();
globalMessageList.addErrorMessage({
- message: $t(
- 'A server error stopped your order from being placed. Please try to place your order again.'
- )
+ message: $t(= /* @escapeNotVerified */ json_encode($params['error_msg'])?>)
});
}
);
diff --git a/app/code/Magento/Paypal/Controller/Express/AbstractExpress.php b/app/code/Magento/Paypal/Controller/Express/AbstractExpress.php
index 571d73d07b68e..fa131f9591fa9 100644
--- a/app/code/Magento/Paypal/Controller/Express/AbstractExpress.php
+++ b/app/code/Magento/Paypal/Controller/Express/AbstractExpress.php
@@ -7,12 +7,17 @@
use Magento\Checkout\Controller\Express\RedirectLoginInterface;
use Magento\Framework\App\Action\Action as AppAction;
+use Magento\Framework\App\Action\HttpGetActionInterface;
+use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Abstract Express Checkout Controller
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-abstract class AbstractExpress extends AppAction implements RedirectLoginInterface
+abstract class AbstractExpress extends AppAction implements
+ RedirectLoginInterface,
+ HttpGetActionInterface,
+ HttpPostActionInterface
{
/**
* @var \Magento\Paypal\Model\Express\Checkout
@@ -137,6 +142,14 @@ protected function _initCheckout()
$this->getResponse()->setStatusHeader(403, '1.1', 'Forbidden');
throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t initialize Express Checkout.'));
}
+ if (!(float)$quote->getGrandTotal()) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __(
+ 'PayPal can\'t process orders with a zero balance due. '
+ . 'To finish your purchase, please go through the standard checkout process.'
+ )
+ );
+ }
if (!isset($this->_checkoutTypes[$this->_checkoutType])) {
$parameters = [
'params' => [
@@ -151,6 +164,8 @@ protected function _initCheckout()
}
/**
+ * Get Proper Checkout Token
+ *
* Search for proper checkout token in request or session or (un)set specified one
* Combined getter/setter
*
@@ -221,8 +236,7 @@ protected function _getQuote()
}
/**
- * Returns before_auth_url redirect parameter for customer session
- * @return null
+ * @inheritdoc
*/
public function getCustomerBeforeAuthUrl()
{
@@ -230,8 +244,7 @@ public function getCustomerBeforeAuthUrl()
}
/**
- * Returns a list of action flags [flag_key] => boolean
- * @return array
+ * @inheritdoc
*/
public function getActionFlagList()
{
@@ -240,6 +253,7 @@ public function getActionFlagList()
/**
* Returns login url parameter for redirect
+ *
* @return string
*/
public function getLoginUrl()
@@ -249,6 +263,7 @@ public function getLoginUrl()
/**
* Returns action name which requires redirect
+ *
* @return string
*/
public function getRedirectActionName()
@@ -269,4 +284,9 @@ public function redirectLogin()
$this->_urlHelper->addRequestParam($this->_customerUrl->getLoginUrl(), ['context' => 'checkout'])
);
}
+
+ /**
+ * @inheritdoc
+ */
+ abstract public function execute();
}
diff --git a/app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml b/app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml
deleted file mode 100644
index 1961b1002df70..0000000000000
--- a/app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.xml
deleted file mode 100644
index 0ca252aa73545..0000000000000
--- a/app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
diff --git a/app/code/Magento/Review/Model/ResourceModel/Rating.php b/app/code/Magento/Review/Model/ResourceModel/Rating.php
index 3f54c17f6ff7c..78020ce3f8184 100644
--- a/app/code/Magento/Review/Model/ResourceModel/Rating.php
+++ b/app/code/Magento/Review/Model/ResourceModel/Rating.php
@@ -178,6 +178,8 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object)
}
/**
+ * Process rating codes
+ *
* @param \Magento\Framework\Model\AbstractModel $object
* @return $this
*/
@@ -201,6 +203,8 @@ protected function processRatingCodes(\Magento\Framework\Model\AbstractModel $ob
}
/**
+ * Process rating stores
+ *
* @param \Magento\Framework\Model\AbstractModel $object
* @return $this
*/
@@ -224,6 +228,8 @@ protected function processRatingStores(\Magento\Framework\Model\AbstractModel $o
}
/**
+ * Delete rating data
+ *
* @param int $ratingId
* @param string $table
* @param array $storeIds
@@ -247,6 +253,8 @@ protected function deleteRatingData($ratingId, $table, array $storeIds)
}
/**
+ * Insert rating data
+ *
* @param string $table
* @param array $data
* @return void
@@ -269,6 +277,7 @@ protected function insertRatingData($table, array $data)
/**
* Perform actions after object delete
+ *
* Prepare rating data for reaggregate all data for reviews
*
* @param \Magento\Framework\Model\AbstractModel $object
@@ -425,9 +434,11 @@ public function getReviewSummary($object, $onlyForCurrentStore = true)
$data = $connection->fetchAll($select, [':review_id' => $object->getReviewId()]);
+ $currentStore = $this->_storeManager->isSingleStoreMode() ? $this->_storeManager->getStore()->getId() : null;
+
if ($onlyForCurrentStore) {
foreach ($data as $row) {
- if ($row['store_id'] == $this->_storeManager->getStore()->getId()) {
+ if ($row['store_id'] !== $currentStore) {
$object->addData($row);
}
}
diff --git a/app/code/Magento/Review/Model/Review.php b/app/code/Magento/Review/Model/Review.php
index c00af3fc61407..e689d4ed460ac 100644
--- a/app/code/Magento/Review/Model/Review.php
+++ b/app/code/Magento/Review/Model/Review.php
@@ -5,6 +5,7 @@
*/
namespace Magento\Review\Model;
+use Magento\Framework\DataObject;
use Magento\Catalog\Model\Product;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Review\Model\ResourceModel\Review\Product\Collection as ProductCollection;
@@ -327,6 +328,9 @@ public function appendSummary($collection)
$item->setRatingSummary($summary);
}
}
+ if (!$item->getRatingSummary()) {
+ $item->setRatingSummary(new DataObject());
+ }
}
return $this;
diff --git a/app/code/Magento/Sales/Block/Order/Items.php b/app/code/Magento/Sales/Block/Order/Items.php
index 028544cd56219..d7255a24aead5 100644
--- a/app/code/Magento/Sales/Block/Order/Items.php
+++ b/app/code/Magento/Sales/Block/Order/Items.php
@@ -5,13 +5,13 @@
*/
/**
- * Sales order view items block
- *
* @author Magento Core Team
*/
namespace Magento\Sales\Block\Order;
/**
+ * Sales order view items block.
+ *
* @api
* @since 100.0.2
*/
@@ -71,7 +71,6 @@ protected function _prepareLayout()
$this->itemCollection = $this->itemCollectionFactory->create();
$this->itemCollection->setOrderFilter($this->getOrder());
- $this->itemCollection->filterByParent(null);
/** @var \Magento\Theme\Block\Html\Pager $pagerBlock */
$pagerBlock = $this->getChildBlock('sales_order_item_pager');
@@ -87,8 +86,9 @@ protected function _prepareLayout()
}
/**
- * Determine if the pager should be displayed for order items list
- * To be called from templates(after _prepareLayout())
+ * Determine if the pager should be displayed for order items list.
+ *
+ * To be called from templates(after _prepareLayout()).
*
* @return bool
* @since 100.1.7
@@ -101,7 +101,8 @@ public function isPagerDisplayed()
/**
* Get visible items for current page.
- * To be called from templates(after _prepareLayout())
+ *
+ * To be called from templates(after _prepareLayout()).
*
* @return \Magento\Framework\DataObject[]
* @since 100.1.7
@@ -112,8 +113,9 @@ public function getItems()
}
/**
- * Get pager HTML according to our requirements
- * To be called from templates(after _prepareLayout())
+ * Get pager HTML according to our requirements.
+ *
+ * To be called from templates(after _prepareLayout()).
*
* @return string HTML output
* @since 100.1.7
diff --git a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php
index 85e34f560bb7b..8cdc90972bbb0 100644
--- a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php
+++ b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php
@@ -363,6 +363,38 @@ protected function _calcAddressHeight($address)
return $y;
}
+ /**
+ * Detect an input string is Arabic
+ *
+ * @param string $subject
+ * @return bool
+ */
+ private function isArabic(string $subject): bool
+ {
+ return (preg_match('/\p{Arabic}/u', $subject) > 0);
+ }
+
+ /**
+ * Reverse text with Arabic characters
+ *
+ * @param string $string
+ * @return string
+ */
+ private function reverseArabicText($string)
+ {
+ $splitText = explode(' ', $string);
+ for ($i = 0; $i < count($splitText); $i++) {
+ if ($this->isArabic($splitText[$i])) {
+ for ($j = $i + 1; $j < count($splitText); $j++) {
+ $tmp = $this->string->strrev($splitText[$j]);
+ $splitText[$j] = $this->string->strrev($splitText[$i]);
+ $splitText[$i] = $tmp;
+ }
+ }
+ }
+ return implode(' ', $splitText);
+ }
+
/**
* Insert order to pdf page
*
@@ -474,7 +506,7 @@ protected function insertOrder(&$page, $obj, $putOrderId = true)
if ($value !== '') {
$text = [];
foreach ($this->string->split($value, 45, true, true) as $_value) {
- $text[] = $_value;
+ $text[] = ($this->isArabic($_value)) ? $this->reverseArabicText($_value) : $_value;
}
foreach ($text as $part) {
$page->drawText(strip_tags(ltrim($part)), 35, $this->y, 'UTF-8');
@@ -491,7 +523,7 @@ protected function insertOrder(&$page, $obj, $putOrderId = true)
if ($value !== '') {
$text = [];
foreach ($this->string->split($value, 45, true, true) as $_value) {
- $text[] = $_value;
+ $text[] = ($this->isArabic($_value)) ? $this->reverseArabicText($_value) : $_value;
}
foreach ($text as $part) {
$page->drawText(strip_tags(ltrim($part)), 285, $this->y, 'UTF-8');
diff --git a/app/code/Magento/Sales/view/frontend/templates/order/items.phtml b/app/code/Magento/Sales/view/frontend/templates/order/items.phtml
index e43d32760febb..dc179b6ee4ac1 100644
--- a/app/code/Magento/Sales/view/frontend/templates/order/items.phtml
+++ b/app/code/Magento/Sales/view/frontend/templates/order/items.phtml
@@ -29,9 +29,10 @@
getItems(); ?>
+
getParentItem()) continue; ?>
-
+
= $block->getItemHtml($item) ?>
helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $item) && $item->getGiftMessageId()): ?>
helper('Magento\GiftMessage\Helper\Message')->getGiftMessageForEntity($item); ?>
@@ -62,8 +63,8 @@
-
+
isPagerDisplayed()): ?>
diff --git a/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml b/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml
index 5ecf1ebe893bc..9b3633fde60b4 100644
--- a/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml
+++ b/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml
@@ -26,14 +26,18 @@