diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php index 8f8f21e207bf5..071ba902f4fe0 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php @@ -31,7 +31,7 @@ abstract class Category extends \Magento\Backend\App\Action */ protected function _initCategory($getRootInstead = false) { - $categoryId = (int)$this->getRequest()->getParam('id', false); + $categoryId = $this->resolveCategoryId(); $storeId = (int)$this->getRequest()->getParam('store'); $category = $this->_objectManager->create(\Magento\Catalog\Model\Category::class); $category->setStoreId($storeId); @@ -62,6 +62,18 @@ protected function _initCategory($getRootInstead = false) return $category; } + /** + * Resolve Category Id (from get or from post) + * + * @return int + */ + private function resolveCategoryId() + { + $categoryId = (int)$this->getRequest()->getParam('id', false); + + return $categoryId ?: (int)$this->getRequest()->getParam('entity_id', false); + } + /** * Build response for ajax request * diff --git a/app/code/Magento/Catalog/Helper/Product/Compare.php b/app/code/Magento/Catalog/Helper/Product/Compare.php index 64b3411591455..3a7ce70cbff52 100644 --- a/app/code/Magento/Catalog/Helper/Product/Compare.php +++ b/app/code/Magento/Catalog/Helper/Product/Compare.php @@ -228,10 +228,9 @@ public function getRemoveUrl() */ public function getPostDataRemove($product) { - $listCleanUrl = $this->getEncodedUrl($this->_getUrl('catalog/product_compare')); $data = [ - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $listCleanUrl, - 'product' => $product->getId() + \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => '', + 'product' => $product->getId(), ]; return $this->postHelper->getPostData($this->getRemoveUrl(), $data); } @@ -253,9 +252,8 @@ public function getClearListUrl() */ public function getPostDataClearList() { - $refererUrl = $this->_getRequest()->getServer('HTTP_REFERER'); $params = [ - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $this->urlEncoder->encode($refererUrl) + \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => '', ]; return $this->postHelper->getPostData($this->getClearListUrl(), $params); } diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php index ba093c0129855..3c33c44cb0dc4 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php @@ -7,6 +7,13 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractAction { + /** + * Whether to use main or temporary index table + * + * @var bool + */ + protected $useTempTable = false; + /** * Refresh entities index * diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 9ceaa8bc9d049..4385c8add37ae 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -313,15 +313,22 @@ protected function initializeProductData(array $productData, $createNew) */ private function assignProductToWebsites(\Magento\Catalog\Model\Product $product) { + $websiteIds = $product->getWebsiteIds(); + if (!$this->storeManager->hasSingleStore()) { - if ($this->storeManager->getStore()->getCode() == \Magento\Store\Model\Store::ADMIN_CODE) { - $websiteIds = array_keys($this->storeManager->getWebsites()); - } else { - $websiteIds = [$this->storeManager->getStore()->getWebsiteId()]; - } + $websiteIds = array_unique( + array_merge( + $websiteIds, + [$this->storeManager->getStore()->getWebsiteId()] + ) + ); + } - $product->setWebsiteIds(array_unique(array_merge($product->getWebsiteIds(), $websiteIds))); + if ($this->storeManager->getStore(true)->getCode() == \Magento\Store\Model\Store::ADMIN_CODE) { + $websiteIds = array_keys($this->storeManager->getWebsites()); } + + $product->setWebsiteIds($websiteIds); } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php index 3b3468656935d..407c2027923de 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php @@ -33,6 +33,11 @@ class Category extends AbstractResource */ protected $_categoryProductTable; + /** + * @var array[] + */ + private $entitiesWhereAttributesIs; + /** * Id of 'is_active' category attribute * @@ -573,22 +578,29 @@ public function getIsActiveAttributeId() */ public function findWhereAttributeIs($entityIdsFilter, $attribute, $expectedValue) { - $linkField = $this->getLinkField(); - $bind = ['attribute_id' => $attribute->getId(), 'value' => $expectedValue]; - $selectEntities = $this->getConnection()->select()->from( - ['ce' => $this->getTable('catalog_category_entity')], - ['entity_id'] - )->joinLeft( - ['ci' => $attribute->getBackend()->getTable()], - "ci.{$linkField} = ce.{$linkField} AND attribute_id = :attribute_id", - ['value'] - )->where( - 'ci.value = :value' - )->where( - 'ce.entity_id IN (?)', - $entityIdsFilter - ); - return $this->getConnection()->fetchCol($selectEntities, $bind); + $entityIdsFilterHash = md5(serialize($entityIdsFilter)); + + if (!isset($this->entitiesWhereAttributesIs[$entityIdsFilterHash][$attribute->getId()][$expectedValue])) { + $linkField = $this->getLinkField(); + $bind = ['attribute_id' => $attribute->getId(), 'value' => $expectedValue]; + $selectEntities = $this->getConnection()->select()->from( + ['ce' => $this->getTable('catalog_category_entity')], + ['entity_id'] + )->joinLeft( + ['ci' => $attribute->getBackend()->getTable()], + "ci.{$linkField} = ce.{$linkField} AND attribute_id = :attribute_id", + ['value'] + )->where( + 'ci.value = :value' + )->where( + 'ce.entity_id IN (?)', + $entityIdsFilter + ); + $this->entitiesWhereAttributesIs[$entityIdsFilterHash][$attribute->getId()][$expectedValue] = + $this->getConnection()->fetchCol($selectEntities, $bind); + } + + return $this->entitiesWhereAttributesIs[$entityIdsFilterHash][$attribute->getId()][$expectedValue]; } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/MaxHeapTableSizeProcessor.php b/app/code/Magento/Catalog/Model/ResourceModel/MaxHeapTableSizeProcessor.php index 92946c7f2d9e1..2e599c2af3d87 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/MaxHeapTableSizeProcessor.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/MaxHeapTableSizeProcessor.php @@ -7,6 +7,9 @@ use Magento\Framework\App\ResourceConnection; +/** + * @deprecated + */ class MaxHeapTableSizeProcessor { /** diff --git a/app/code/Magento/Catalog/Plugin/Model/Indexer/Category/Product/MaxHeapTableSizeProcessorOnFullReindex.php b/app/code/Magento/Catalog/Plugin/Model/Indexer/Category/Product/MaxHeapTableSizeProcessorOnFullReindex.php index a4cd503c6b08b..f14156c7f09e7 100644 --- a/app/code/Magento/Catalog/Plugin/Model/Indexer/Category/Product/MaxHeapTableSizeProcessorOnFullReindex.php +++ b/app/code/Magento/Catalog/Plugin/Model/Indexer/Category/Product/MaxHeapTableSizeProcessorOnFullReindex.php @@ -10,6 +10,9 @@ use Magento\Catalog\Model\ResourceModel\MaxHeapTableSizeProcessor; use Psr\Log\LoggerInterface; +/** + * @deprecated + */ class MaxHeapTableSizeProcessorOnFullReindex { /** diff --git a/app/code/Magento/Catalog/Test/Unit/Helper/Product/CompareTest.php b/app/code/Magento/Catalog/Test/Unit/Helper/Product/CompareTest.php index 29786ecdce969..9f4d41aefd6bb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Helper/Product/CompareTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Helper/Product/CompareTest.php @@ -113,18 +113,13 @@ public function testGetPostDataRemove() //Data $productId = 1; $removeUrl = 'catalog/product_compare/remove'; - $compareListUrl = 'catalog/product_compare'; $postParams = [ - Action::PARAM_NAME_URL_ENCODED => strtr(base64_encode($compareListUrl), '+/=', '-_,'), + Action::PARAM_NAME_URL_ENCODED => '', 'product' => $productId ]; //Verification - $this->urlBuilder->expects($this->at(0)) - ->method('getUrl') - ->with($compareListUrl) - ->will($this->returnValue($compareListUrl)); - $this->urlBuilder->expects($this->at(1)) + $this->urlBuilder->expects($this->once()) ->method('getUrl') ->with($removeUrl) ->will($this->returnValue($removeUrl)); @@ -159,18 +154,12 @@ public function testGetClearListUrl() public function testGetPostDataClearList() { //Data - $refererUrl = 'home/'; $clearUrl = 'catalog/product_compare/clear'; $postParams = [ - Action::PARAM_NAME_URL_ENCODED => strtr(base64_encode($refererUrl), '+/=', '-_,') + Action::PARAM_NAME_URL_ENCODED => '' ]; //Verification - $this->request->expects($this->once()) - ->method('getServer') - ->with('HTTP_REFERER') - ->will($this->returnValue($refererUrl)); - $this->urlBuilder->expects($this->once()) ->method('getUrl') ->with($clearUrl) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index 5042ac1b745cf..e91bb469eb112 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -14,6 +14,7 @@ use Magento\Framework\DB\Adapter\ConnectionException; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; +use Magento\Store\Api\Data\StoreInterface; /** * Class ProductRepositoryTest @@ -284,7 +285,6 @@ protected function setUp() $storeMock->expects($this->any())->method('getWebsiteId')->willReturn('1'); $storeMock->expects($this->any())->method('getCode')->willReturn(\Magento\Store\Model\Store::ADMIN_CODE); $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); - $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->mediaGalleryProcessor = $this->getMock( \Magento\Catalog\Model\Product\Gallery\Processor::class, @@ -495,6 +495,7 @@ public function testGetBySkuFromCacheInitializedInGetById() public function testSaveExisting() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); $this->productFactoryMock->expects($this->any()) ->method('create') @@ -514,6 +515,7 @@ public function testSaveExisting() public function testSaveNew() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->at(0))->method('getIdBySku')->will($this->returnValue(null)); $this->resourceModelMock->expects($this->at(3))->method('getIdBySku')->will($this->returnValue(100)); $this->productFactoryMock->expects($this->any()) @@ -538,6 +540,7 @@ public function testSaveNew() */ public function testSaveUnableToSaveException() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->exactly(1))->method('getIdBySku')->will($this->returnValue(null)); $this->productFactoryMock->expects($this->exactly(2)) ->method('create') @@ -562,6 +565,7 @@ public function testSaveUnableToSaveException() */ public function testSaveException() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->exactly(1))->method('getIdBySku')->will($this->returnValue(null)); $this->productFactoryMock->expects($this->exactly(2)) ->method('create') @@ -587,6 +591,7 @@ public function testSaveException() */ public function testSaveInvalidProductException() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->exactly(1))->method('getIdBySku')->will($this->returnValue(null)); $this->productFactoryMock->expects($this->exactly(2)) ->method('create') @@ -610,6 +615,7 @@ public function testSaveInvalidProductException() */ public function testSaveThrowsTemporaryStateExceptionIfDatabaseConnectionErrorOccurred() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->productFactoryMock->expects($this->any()) ->method('create') ->will($this->returnValue($this->productMock)); @@ -796,6 +802,7 @@ public function cacheKeyDataProvider() */ public function testSaveExistingWithOptions(array $newOptions, array $existingOptions, array $expectedData) { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); $this->productFactoryMock->expects($this->any()) ->method('create') @@ -964,6 +971,7 @@ public function saveExistingWithOptionsDataProvider() */ public function testSaveWithLinks(array $newLinks, array $existingLinks, array $expectedData) { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $this->resourceModelMock->expects($this->any())->method('getIdBySku')->will($this->returnValue(100)); $this->productFactoryMock->expects($this->any()) ->method('create') @@ -1143,6 +1151,7 @@ protected function setupProductMocksForSave() public function testSaveExistingWithNewMediaGalleryEntries() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); $newEntriesData = [ [ "label" => "label_text", @@ -1222,8 +1231,48 @@ public function testSaveExistingWithNewMediaGalleryEntries() $this->model->save($this->productMock); } + public function websitesProvider() + { + return [ + [[1,2,3]] + ]; + } + + public function testSaveWithDifferentWebsites() + { + $storeMock = $this->getMock(StoreInterface::class); + $this->resourceModelMock->expects($this->at(0))->method('getIdBySku')->will($this->returnValue(null)); + $this->resourceModelMock->expects($this->at(3))->method('getIdBySku')->will($this->returnValue(100)); + $this->productFactoryMock->expects($this->any()) + ->method('create') + ->will($this->returnValue($this->productMock)); + $this->initializationHelperMock->expects($this->never())->method('initialize'); + $this->resourceModelMock->expects($this->once())->method('validate')->with($this->productMock) + ->willReturn(true); + $this->resourceModelMock->expects($this->once())->method('save')->with($this->productMock)->willReturn(true); + $this->extensibleDataObjectConverterMock + ->expects($this->once()) + ->method('toNestedArray') + ->will($this->returnValue($this->productData)); + $this->storeManagerMock->expects($this->any()) + ->method('getStore') + ->willReturn($storeMock); + $this->storeManagerMock->expects($this->once()) + ->method('getWebsites') + ->willReturn([ + 1 => ['first'], + 2 => ['second'], + 3 => ['third'] + ]); + $this->productMock->expects($this->once())->method('getWebsiteIds')->willReturn([1,2,3]); + $this->productMock->expects($this->once())->method('setWebsiteIds')->willReturn([2,3]); + + $this->assertEquals($this->productMock, $this->model->save($this->productMock)); + } + public function testSaveExistingWithMediaGalleryEntries() { + $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); //update one entry, delete one entry $newEntries = [ [ diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index 9993d4657533a..584a2e51514db 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -73,7 +73,6 @@ - diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandlerFactory.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandlerFactory.php index bb2df11c9083d..58be7fcb1bcb9 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandlerFactory.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandlerFactory.php @@ -75,12 +75,14 @@ public function create(array $data = []) $indexer = $this->_objectManager->create($this->handlers[$currentHandler], $data); if (!$indexer instanceof IndexerInterface) { - throw new \InvalidArgumentException($indexer . ' doesn\'t implement \Magento\Framework\IndexerInterface'); + throw new \InvalidArgumentException( + $currentHandler . ' indexer handler doesn\'t implement ' . IndexerInterface::class + ); } if ($indexer && !$indexer->isAvailable()) { throw new \LogicException( - 'Indexer handler is not available: ' . $indexer + 'Indexer handler is not available: ' . $currentHandler ); } return $indexer; diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/IndexerHandlerFactoryTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/IndexerHandlerFactoryTest.php new file mode 100644 index 0000000000000..417eb63d08ab4 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/IndexerHandlerFactoryTest.php @@ -0,0 +1,173 @@ +objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) + ->getMockForAbstractClass(); + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->getMockForAbstractClass(); + } + + public function testCreate() + { + $configPath = 'config_path'; + $currentHandler = 'current_handler'; + $currentHandlerClass = 'current_handler_class'; + $handlers = [ + $currentHandler => $currentHandlerClass, + ]; + $data = ['data']; + + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with($configPath, ScopeInterface::SCOPE_STORE) + ->willReturn($currentHandler); + + $indexerMock = $this->getMockBuilder(IndexerInterface::class) + ->getMockForAbstractClass(); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with($currentHandlerClass, $data) + ->willReturn($indexerMock); + + $indexerMock->expects($this->once()) + ->method('isAvailable') + ->willReturn(true); + + $this->model = new IndexerHandlerFactory( + $this->objectManagerMock, + $this->scopeConfigMock, + $configPath, + $handlers + ); + + $this->assertEquals($indexerMock, $this->model->create($data)); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage There is no such indexer handler: current_handler + */ + public function testCreateWithoutHandlers() + { + $configPath = 'config_path'; + $currentHandler = 'current_handler'; + $handlers = []; + $data = ['data']; + + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with($configPath, ScopeInterface::SCOPE_STORE) + ->willReturn($currentHandler); + + $this->model = new IndexerHandlerFactory( + $this->objectManagerMock, + $this->scopeConfigMock, + $configPath, + $handlers + ); + + $this->model->create($data); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage current_handler indexer handler doesn't implement + */ + public function testCreateWithWrongHandler() + { + $configPath = 'config_path'; + $currentHandler = 'current_handler'; + $currentHandlerClass = 'current_handler_class'; + $handlers = [ + $currentHandler => $currentHandlerClass, + ]; + $data = ['data']; + + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with($configPath, ScopeInterface::SCOPE_STORE) + ->willReturn($currentHandler); + + $indexerMock = $this->getMockBuilder(\stdClass::class) + ->getMockForAbstractClass(); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with($currentHandlerClass, $data) + ->willReturn($indexerMock); + + $this->model = new IndexerHandlerFactory( + $this->objectManagerMock, + $this->scopeConfigMock, + $configPath, + $handlers + ); + + $this->model->create($data); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Indexer handler is not available: current_handler + */ + public function testCreateWithoutAvailableHandler() + { + $configPath = 'config_path'; + $currentHandler = 'current_handler'; + $currentHandlerClass = 'current_handler_class'; + $handlers = [ + $currentHandler => $currentHandlerClass, + ]; + $data = ['data']; + + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with($configPath, ScopeInterface::SCOPE_STORE) + ->willReturn($currentHandler); + + $indexerMock = $this->getMockBuilder(IndexerInterface::class) + ->getMockForAbstractClass(); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with($currentHandlerClass, $data) + ->willReturn($indexerMock); + + $indexerMock->expects($this->once()) + ->method('isAvailable') + ->willReturn(false); + + $this->model = new IndexerHandlerFactory( + $this->objectManagerMock, + $this->scopeConfigMock, + $configPath, + $handlers + ); + + $this->model->create($data); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Model/CategoryBasedProductRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/CategoryBasedProductRewriteGenerator.php new file mode 100644 index 0000000000000..58025ad03f0b4 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Model/CategoryBasedProductRewriteGenerator.php @@ -0,0 +1,61 @@ +productScopeRewriteGenerator = $productScopeRewriteGenerator; + } + + /** + * Generate product url rewrites based on category + * + * @param \Magento\Catalog\Model\Product $product + * @param \Magento\Catalog\Model\Category $category + * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] + */ + public function generate(Product $product, Category $category) + { + if ($product->getVisibility() == Visibility::VISIBILITY_NOT_VISIBLE) { + return []; + } + + $storeId = $product->getStoreId(); + + $urls = $this->productScopeRewriteGenerator->isGlobalScope($storeId) + ? $this->productScopeRewriteGenerator->generateForGlobalScope([$category], $product) + : $this->productScopeRewriteGenerator->generateForSpecificStoreView($storeId, [$category], $product); + + $this->product = null; + return $urls; + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php new file mode 100644 index 0000000000000..94464ec05d824 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php @@ -0,0 +1,171 @@ +storeViewService = $storeViewService; + $this->storeManager = $storeManager; + $this->objectRegistryFactory = $objectRegistryFactory; + $this->canonicalUrlRewriteGenerator = $canonicalUrlRewriteGenerator; + $this->categoriesUrlRewriteGenerator = $categoriesUrlRewriteGenerator; + $this->currentUrlRewritesRegenerator = $currentUrlRewritesRegenerator; + $this->anchorUrlRewriteGenerator = $anchorUrlRewriteGenerator; + } + + /** + * Check is global scope + * + * @param int|null $storeId + * @return bool + */ + public function isGlobalScope($storeId) + { + return null === $storeId || $storeId == Store::DEFAULT_STORE_ID; + } + + /** + * Generate url rewrites for global scope + * + * @param Product $product + * @param \Magento\Framework\Data\Collection $productCategories + * @return array + */ + public function generateForGlobalScope($productCategories, Product $product) + { + $urls = []; + $productId = $product->getEntityId(); + + foreach ($product->getStoreIds() as $id) { + if (!$this->isGlobalScope($id) + && !$this->storeViewService->doesEntityHaveOverriddenUrlKeyForStore($id, $productId, Product::ENTITY) + ) { + $urls = array_merge($urls, $this->generateForSpecificStoreView($id, $productCategories, $product)); + } + } + + return $urls; + } + + /** + * Generate list of urls for specific store view + * + * @param int $storeId + * @param \Magento\Framework\Data\Collection $productCategories + * @param \Magento\Catalog\Model\Product $product + * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] + */ + public function generateForSpecificStoreView($storeId, $productCategories, Product $product) + { + $categories = []; + foreach ($productCategories as $category) { + if ($this->isCategoryProperForGenerating($category, $storeId)) { + $categories[] = $category; + } + } + $productCategories = $this->objectRegistryFactory->create(['entities' => $categories]); + /** + * @var $urls \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] + */ + $urls = array_merge( + $this->canonicalUrlRewriteGenerator->generate($storeId, $product), + $this->categoriesUrlRewriteGenerator->generate($storeId, $product, $productCategories), + $this->currentUrlRewritesRegenerator->generate($storeId, $product, $productCategories), + $this->anchorUrlRewriteGenerator->generate($storeId, $product, $productCategories) + ); + + /* Reduce duplicates. Last wins */ + $result = []; + foreach ($urls as $url) { + $result[$url->getTargetPath() . '-' . $url->getStoreId()] = $url; + } + $this->productCategories = null; + return $result; + } + + /** + * Check possibility for url rewrite generation + * + * @param \Magento\Catalog\Model\Category $category + * @param int $storeId + * @return bool + */ + public function isCategoryProperForGenerating(Category $category, $storeId) + { + if ($category->getParentId() != \Magento\Catalog\Model\Category::TREE_ROOT_ID) { + list(, $rootCategoryId) = $category->getParentIds(); + return $rootCategoryId == $this->storeManager->getStore($storeId)->getRootCategoryId(); + } + return false; + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteGenerator.php index db39a201d9c05..f8f8991f95a04 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteGenerator.php @@ -9,9 +9,8 @@ use Magento\CatalogUrlRewrite\Model\Product\CanonicalUrlRewriteGenerator; use Magento\CatalogUrlRewrite\Model\Product\CategoriesUrlRewriteGenerator; use Magento\CatalogUrlRewrite\Model\Product\CurrentUrlRewritesRegenerator; -use Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator; use Magento\CatalogUrlRewrite\Service\V1\StoreViewService; -use Magento\Store\Model\Store; +use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\Product\Visibility; /** @@ -26,32 +25,55 @@ class ProductUrlRewriteGenerator */ const ENTITY_TYPE = 'product'; - /** @var \Magento\CatalogUrlRewrite\Service\V1\StoreViewService */ + /** + * @deprecated + * @var \Magento\CatalogUrlRewrite\Service\V1\StoreViewService + */ protected $storeViewService; /** @var \Magento\Catalog\Model\Product */ protected $product; - /** @var \Magento\CatalogUrlRewrite\Model\Product\CurrentUrlRewritesRegenerator */ + /** + * @deprecated + * @var \Magento\CatalogUrlRewrite\Model\Product\CurrentUrlRewritesRegenerator + */ protected $currentUrlRewritesRegenerator; - /** @var \Magento\CatalogUrlRewrite\Model\Product\CategoriesUrlRewriteGenerator */ + /** + * @deprecated + * @var \Magento\CatalogUrlRewrite\Model\Product\CategoriesUrlRewriteGenerator + */ protected $categoriesUrlRewriteGenerator; - /** @var \Magento\CatalogUrlRewrite\Model\Product\CanonicalUrlRewriteGenerator */ + /** + * @deprecated + * @var \Magento\CatalogUrlRewrite\Model\Product\CanonicalUrlRewriteGenerator + */ protected $canonicalUrlRewriteGenerator; - /** @var \Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory */ + /** + * @deprecated + * @var \Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory + */ protected $objectRegistryFactory; - /** @var \Magento\CatalogUrlRewrite\Model\ObjectRegistry */ + /** + * @deprecated + * @var \Magento\CatalogUrlRewrite\Model\ObjectRegistry + */ protected $productCategories; - /** @var \Magento\Store\Model\StoreManagerInterface */ + /** + * @deprecated + * @var \Magento\Store\Model\StoreManagerInterface + */ protected $storeManager; - /** @var AnchorUrlRewriteGenerator */ - private $anchorUrlRewriteGenerator; + /** + * @var ProductScopeRewriteGenerator + */ + private $productScopeRewriteGenerator; /** * @param \Magento\CatalogUrlRewrite\Model\Product\CanonicalUrlRewriteGenerator $canonicalUrlRewriteGenerator @@ -78,17 +100,19 @@ public function __construct( } /** - * @return AnchorUrlRewriteGenerator + * Retrieve Delegator for generation rewrites in different scopes * * @deprecated + * @return ProductScopeRewriteGenerator|mixed */ - private function getAnchorUrlRewriteGenerator() + private function getProductScopeRewriteGenerator() { - if ($this->anchorUrlRewriteGenerator === null) { - $this->anchorUrlRewriteGenerator = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator::class); + if (!$this->productScopeRewriteGenerator) { + $this->productScopeRewriteGenerator = ObjectManager::getInstance() + ->get(ProductScopeRewriteGenerator::class); } - return $this->anchorUrlRewriteGenerator; + + return $this->productScopeRewriteGenerator; } /** @@ -121,80 +145,49 @@ public function generate(Product $product) /** * Check is global scope * + * @deprecated * @param int|null $storeId * @return bool */ protected function isGlobalScope($storeId) { - return null === $storeId || $storeId == Store::DEFAULT_STORE_ID; + return $this->getProductScopeRewriteGenerator()->isGlobalScope($storeId); } /** * Generate list of urls for global scope * + * @deprecated * @param \Magento\Framework\Data\Collection $productCategories * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] */ protected function generateForGlobalScope($productCategories) { - $urls = []; - $productId = $this->product->getEntityId(); - foreach ($this->product->getStoreIds() as $id) { - if (!$this->isGlobalScope($id) - && !$this->storeViewService->doesEntityHaveOverriddenUrlKeyForStore($id, $productId, Product::ENTITY) - ) { - $urls = array_merge($urls, $this->generateForSpecificStoreView($id, $productCategories)); - } - } - return $urls; + return $this->getProductScopeRewriteGenerator()->generateForGlobalScope($productCategories, $this->product); } /** * Generate list of urls for specific store view * + * @deprecated * @param int $storeId * @param \Magento\Framework\Data\Collection $productCategories * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] */ protected function generateForSpecificStoreView($storeId, $productCategories) { - $categories = []; - foreach ($productCategories as $category) { - if ($this->isCategoryProperForGenerating($category, $storeId)) { - $categories[] = $category; - } - } - $this->productCategories = $this->objectRegistryFactory->create(['entities' => $categories]); - /** - * @var $urls \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] - */ - $urls = array_merge( - $this->canonicalUrlRewriteGenerator->generate($storeId, $this->product), - $this->categoriesUrlRewriteGenerator->generate($storeId, $this->product, $this->productCategories), - $this->currentUrlRewritesRegenerator->generate($storeId, $this->product, $this->productCategories), - $this->getAnchorUrlRewriteGenerator()->generate($storeId, $this->product, $this->productCategories) - ); - - /* Reduce duplicates. Last wins */ - $result = []; - foreach ($urls as $url) { - $result[$url->getTargetPath() . '-' . $url->getStoreId()] = $url; - } - $this->productCategories = null; - return $result; + return $this->getProductScopeRewriteGenerator() + ->generateForSpecificStoreView($storeId, $productCategories, $this->product); } /** + * @deprecated * @param \Magento\Catalog\Model\Category $category * @param int $storeId * @return bool */ protected function isCategoryProperForGenerating($category, $storeId) { - if ($category->getParentId() != \Magento\Catalog\Model\Category::TREE_ROOT_ID) { - list(, $rootCategoryId) = $category->getParentIds(); - return $rootCategoryId == $this->storeManager->getStore($storeId)->getRootCategoryId(); - } - return false; + return $this->getProductScopeRewriteGenerator()->isCategoryProperForGenerating($category, $storeId); } } diff --git a/app/code/Magento/CatalogUrlRewrite/Model/UrlRewriteBunchReplacer.php b/app/code/Magento/CatalogUrlRewrite/Model/UrlRewriteBunchReplacer.php new file mode 100644 index 0000000000000..eb799dfe5991a --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Model/UrlRewriteBunchReplacer.php @@ -0,0 +1,38 @@ +urlPersist = $urlPersist; + } + + /** + * Do Bunch Replace, with default bunch value = 10000 + * + * @param array $urls + * @param int $bunchSize + * @return void + */ + public function doBunchReplace(array $urls, $bunchSize = 10000) + { + foreach (array_chunk($urls, $bunchSize) as $urlsBunch) { + $this->urlPersist->replace($urlsBunch); + } + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteMovingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteMovingObserver.php index c891ab403f81b..24126015e3b08 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteMovingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteMovingObserver.php @@ -8,7 +8,9 @@ use Magento\Catalog\Model\Category; use Magento\CatalogUrlRewrite\Block\UrlKeyRenderer; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\UrlRewriteBunchReplacer; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Store\Model\ScopeInterface; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Framework\Event\ObserverInterface; @@ -27,6 +29,11 @@ class CategoryProcessUrlRewriteMovingObserver implements ObserverInterface /** @var UrlRewriteHandler */ protected $urlRewriteHandler; + /** + * @var UrlRewriteBunchReplacer + */ + private $urlRewriteBunchReplacer; + /** * @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator * @param UrlPersistInterface $urlPersist @@ -45,6 +52,21 @@ public function __construct( $this->urlRewriteHandler = $urlRewriteHandler; } + /** + * Retrieve Url Rewrite Replacer based on bunches + * + * @deprecated + * @return UrlRewriteBunchReplacer + */ + private function getUrlRewriteBunchReplacer() + { + if (!$this->urlRewriteBunchReplacer) { + $this->urlRewriteBunchReplacer = ObjectManager::getInstance()->get(UrlRewriteBunchReplacer::class); + } + + return $this->urlRewriteBunchReplacer; + } + /** * @param \Magento\Framework\Event\Observer $observer * @return void @@ -65,7 +87,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) $this->urlRewriteHandler->generateProductUrlRewrites($category) ); $this->urlRewriteHandler->deleteCategoryRewritesForChildren($category); - $this->urlPersist->replace($urlRewrites); + $this->getUrlRewriteBunchReplacer()->doBunchReplace($urlRewrites); } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php index 806cfb8edb729..fe86bc363b49c 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php @@ -7,6 +7,8 @@ use Magento\Catalog\Model\Category; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\UrlRewriteBunchReplacer; +use Magento\Framework\App\ObjectManager; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Framework\Event\ObserverInterface; @@ -18,6 +20,11 @@ class CategoryProcessUrlRewriteSavingObserver implements ObserverInterface /** @var UrlPersistInterface */ protected $urlPersist; + /** + * @var UrlRewriteBunchReplacer + */ + private $urlRewriteBunchReplacer; + /** @var UrlRewriteHandler */ protected $urlRewriteHandler; @@ -36,6 +43,21 @@ public function __construct( $this->urlRewriteHandler = $urlRewriteHandler; } + /** + * Retrieve Url Rewrite Replacer based on bunches + * + * @deprecated + * @return UrlRewriteBunchReplacer + */ + private function getUrlRewriteBunchReplacer() + { + if (!$this->urlRewriteBunchReplacer) { + $this->urlRewriteBunchReplacer = ObjectManager::getInstance()->get(UrlRewriteBunchReplacer::class); + } + + return $this->urlRewriteBunchReplacer; + } + /** * Generate urls for UrlRewrite and save it in storage * @@ -57,7 +79,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) $this->categoryUrlRewriteGenerator->generate($category), $this->urlRewriteHandler->generateProductUrlRewrites($category) ); - $this->urlPersist->replace($urlRewrites); + + $this->getUrlRewriteBunchReplacer()->doBunchReplace($urlRewrites); } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php index b02ef76d95146..ff914dc1adb20 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php @@ -6,8 +6,10 @@ namespace Magento\CatalogUrlRewrite\Observer; use Magento\Catalog\Model\Category; +use Magento\CatalogUrlRewrite\Model\CategoryBasedProductRewriteGenerator; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Event\Observer as EventObserver; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; @@ -32,6 +34,11 @@ class UrlRewriteHandler /** @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory */ protected $productCollectionFactory; + /** + * @var CategoryBasedProductRewriteGenerator + */ + private $categoryBasedProductRewriteGenerator; + /** * @param \Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider $childrenCategoriesProvider * @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator @@ -116,11 +123,30 @@ public function getCategoryProductsUrlRewrites(Category $category, $storeId, $sa $this->isSkippedProduct[] = $product->getId(); $product->setStoreId($storeId); $product->setData('save_rewrites_history', $saveRewriteHistory); - $productUrls = array_merge($productUrls, $this->productUrlRewriteGenerator->generate($product)); + $productUrls = array_merge( + $productUrls, + $this->getCategoryBasedProductRewriteGenerator()->generate($product, $category) + ); } return $productUrls; } + /** + * Retrieve generator, which use single category for different products + * + * @deprecated + * @return CategoryBasedProductRewriteGenerator|mixed + */ + private function getCategoryBasedProductRewriteGenerator() + { + if (!$this->categoryBasedProductRewriteGenerator) { + $this->categoryBasedProductRewriteGenerator = ObjectManager::getInstance() + ->get(CategoryBasedProductRewriteGenerator::class); + } + + return $this->categoryBasedProductRewriteGenerator; + } + /** * @param Category $category * @return void diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/CategoryBasedProductRewriteGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/CategoryBasedProductRewriteGeneratorTest.php new file mode 100644 index 0000000000000..c62475f3cc7cf --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/CategoryBasedProductRewriteGeneratorTest.php @@ -0,0 +1,97 @@ +productScopeRewriteGeneratorMock = $this->getMockBuilder(ProductScopeRewriteGenerator::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->generator = new CategoryBasedProductRewriteGenerator( + $this->productScopeRewriteGeneratorMock + ); + } + + public function testGenerationWithGlobalScope() + { + $categoryMock = $this->getMockBuilder(Category::class) + ->disableOriginalConstructor() + ->getMock(); + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $storeId = 1; + $urls = ['dummy-url.html']; + + $productMock->expects($this->once()) + ->method('getVisibility') + ->willReturn(2); + $productMock->expects($this->once()) + ->method('getStoreId') + ->willReturn($storeId); + $this->productScopeRewriteGeneratorMock->expects($this->once()) + ->method('isGlobalScope') + ->with($storeId) + ->willReturn(true); + $this->productScopeRewriteGeneratorMock->expects($this->once()) + ->method('generateForGlobalScope') + ->with([$categoryMock], $productMock) + ->willReturn($urls); + + $this->assertEquals($urls, $this->generator->generate($productMock, $categoryMock)); + } + + public function testGenerationWithSpecificStore() + { + $categoryMock = $this->getMockBuilder(Category::class) + ->disableOriginalConstructor() + ->getMock(); + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $storeId = 1; + $urls = ['dummy-url.html']; + + $productMock->expects($this->once()) + ->method('getVisibility') + ->willReturn(2); + $productMock->expects($this->once()) + ->method('getStoreId') + ->willReturn($storeId); + $this->productScopeRewriteGeneratorMock->expects($this->once()) + ->method('isGlobalScope') + ->with($storeId) + ->willReturn(false); + $this->productScopeRewriteGeneratorMock->expects($this->once()) + ->method('generateForSpecificStoreView') + ->with($storeId, [$categoryMock], $productMock) + ->willReturn($urls); + + $this->assertEquals($urls, $this->generator->generate($productMock, $categoryMock)); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php new file mode 100644 index 0000000000000..99bb858e31b8f --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php @@ -0,0 +1,182 @@ +currentUrlRewritesRegenerator = $this->getMockBuilder( + \Magento\CatalogUrlRewrite\Model\Product\CurrentUrlRewritesRegenerator::class + )->disableOriginalConstructor()->getMock(); + $this->canonicalUrlRewriteGenerator = $this->getMockBuilder( + \Magento\CatalogUrlRewrite\Model\Product\CanonicalUrlRewriteGenerator::class + )->disableOriginalConstructor()->getMock(); + $this->categoriesUrlRewriteGenerator = $this->getMockBuilder( + \Magento\CatalogUrlRewrite\Model\Product\CategoriesUrlRewriteGenerator::class + )->disableOriginalConstructor()->getMock(); + $this->anchorUrlRewriteGenerator = $this->getMockBuilder( + \Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator::class + )->disableOriginalConstructor()->getMock(); + $this->objectRegistryFactory = $this->getMockBuilder( + \Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory::class + )->disableOriginalConstructor()->setMethods(['create'])->getMock(); + $this->storeViewService = $this->getMockBuilder(\Magento\CatalogUrlRewrite\Service\V1\StoreViewService::class) + ->disableOriginalConstructor()->getMock(); + $this->storeManager = $this->getMock(StoreManagerInterface::class); + + $this->productScopeGenerator = (new ObjectManager($this))->getObject( + \Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator::class, + [ + 'canonicalUrlRewriteGenerator' => $this->canonicalUrlRewriteGenerator, + 'categoriesUrlRewriteGenerator' => $this->categoriesUrlRewriteGenerator, + 'currentUrlRewritesRegenerator' => $this->currentUrlRewritesRegenerator, + 'anchorUrlRewriteGenerator' => $this->anchorUrlRewriteGenerator, + 'objectRegistryFactory' => $this->objectRegistryFactory, + 'storeViewService' => $this->storeViewService, + 'storeManager' => $this->storeManager, + ] + ); + } + + public function testGenerationForGlobalScope() + { + $product = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); + $product->expects($this->any())->method('getStoreId')->will($this->returnValue(null)); + $product->expects($this->any())->method('getStoreIds')->will($this->returnValue([1])); + $this->storeViewService->expects($this->once())->method('doesEntityHaveOverriddenUrlKeyForStore') + ->will($this->returnValue(false)); + $categoryMock = $this->getMockBuilder(Category::class) + ->disableOriginalConstructor() + ->getMock(); + $categoryMock->expects($this->once()) + ->method('getParentId') + ->willReturn(1); + $this->initObjectRegistryFactory([]); + $canonical = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); + $canonical->setTargetPath('category-1') + ->setStoreId(1); + $this->canonicalUrlRewriteGenerator->expects($this->any())->method('generate') + ->will($this->returnValue([$canonical])); + $categories = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); + $categories->setTargetPath('category-2') + ->setStoreId(2); + $this->categoriesUrlRewriteGenerator->expects($this->any())->method('generate') + ->will($this->returnValue([$categories])); + $current = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); + $current->setTargetPath('category-3') + ->setStoreId(3); + $this->currentUrlRewritesRegenerator->expects($this->any())->method('generate') + ->will($this->returnValue([$current])); + $anchorCategories = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); + $anchorCategories->setTargetPath('category-4') + ->setStoreId(4); + $this->anchorUrlRewriteGenerator->expects($this->any())->method('generate') + ->will($this->returnValue([$anchorCategories])); + + $this->assertEquals( + [ + 'category-1-1' => $canonical, + 'category-2-2' => $categories, + 'category-3-3' => $current, + 'category-4-4' => $anchorCategories + ], + $this->productScopeGenerator->generateForGlobalScope([$categoryMock], $product) + ); + } + + public function testGenerationForSpecificStore() + { + $product = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); + $product->expects($this->any())->method('getStoreId')->will($this->returnValue(1)); + $product->expects($this->never())->method('getStoreIds'); + $storeRootCategoryId = 'root-for-store-id'; + $category = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); + $category->expects($this->any())->method('getParentIds') + ->will($this->returnValue(['root-id', $storeRootCategoryId])); + $category->expects($this->any())->method('getParentId')->will($this->returnValue('parent_id')); + $category->expects($this->any())->method('getId')->will($this->returnValue('category_id')); + $store = $this->getMockBuilder(\Magento\Store\Model\Store::class)->disableOriginalConstructor()->getMock(); + $store->expects($this->any())->method('getRootCategoryId')->will($this->returnValue($storeRootCategoryId)); + $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($store)); + $this->initObjectRegistryFactory([$category]); + $canonical = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); + $canonical->setTargetPath('category-1') + ->setStoreId(1); + $this->canonicalUrlRewriteGenerator->expects($this->any())->method('generate') + ->will($this->returnValue([$canonical])); + $this->categoriesUrlRewriteGenerator->expects($this->any())->method('generate') + ->will($this->returnValue([])); + $this->currentUrlRewritesRegenerator->expects($this->any())->method('generate') + ->will($this->returnValue([])); + $this->anchorUrlRewriteGenerator->expects($this->any())->method('generate') + ->will($this->returnValue([])); + + $this->assertEquals( + ['category-1-1' => $canonical], + $this->productScopeGenerator->generateForSpecificStoreView(1, [$category], $product) + ); + } + + /** + * Test method + */ + public function testSkipGenerationForGlobalScope() + { + $product = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); + $product->expects($this->any())->method('getStoreIds')->will($this->returnValue([1, 2])); + $this->storeViewService->expects($this->exactly(2))->method('doesEntityHaveOverriddenUrlKeyForStore') + ->will($this->returnValue(true)); + + $this->assertEquals([], $this->productScopeGenerator->generateForGlobalScope([], $product)); + } + + /** + * @param array $entities + */ + protected function initObjectRegistryFactory($entities) + { + $objectRegistry = $this->getMockBuilder(\Magento\CatalogUrlRewrite\Model\ObjectRegistry::class) + ->disableOriginalConstructor()->getMock(); + $this->objectRegistryFactory->expects($this->any())->method('create') + ->with(['entities' => $entities]) + ->will($this->returnValue($objectRegistry)); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlRewriteGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlRewriteGeneratorTest.php index 4086fe256790b..d88171b90bbee 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlRewriteGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlRewriteGeneratorTest.php @@ -9,6 +9,8 @@ namespace Magento\CatalogUrlRewrite\Test\Unit\Model; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** @@ -46,6 +48,9 @@ class ProductUrlRewriteGeneratorTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection|\PHPUnit_Framework_MockObject_MockObject */ protected $categoriesCollection; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $productScopeRewriteGenerator; + /** * Test method */ @@ -59,8 +64,6 @@ protected function setUp() ->will($this->returnValue($this->categoriesCollection)); $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) ->disableOriginalConstructor()->getMock(); - $this->categoriesCollection->expects($this->exactly(2))->method('addAttributeToSelect') - ->will($this->returnSelf()); $this->currentUrlRewritesRegenerator = $this->getMockBuilder( \Magento\CatalogUrlRewrite\Model\Product\CurrentUrlRewritesRegenerator::class )->disableOriginalConstructor()->getMock(); @@ -78,7 +81,9 @@ protected function setUp() )->disableOriginalConstructor()->setMethods(['create'])->getMock(); $this->storeViewService = $this->getMockBuilder(\Magento\CatalogUrlRewrite\Service\V1\StoreViewService::class) ->disableOriginalConstructor()->getMock(); - + $this->productScopeRewriteGenerator = $this->getMockBuilder( + ProductScopeRewriteGenerator::class + )->disableOriginalConstructor()->getMock(); $this->productUrlRewriteGenerator = (new ObjectManager($this))->getObject( \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::class, [ @@ -92,172 +97,38 @@ protected function setUp() ); $reflection = new \ReflectionClass(get_class($this->productUrlRewriteGenerator)); - $reflectionProperty = $reflection->getProperty('anchorUrlRewriteGenerator'); + $reflectionProperty = $reflection->getProperty('productScopeRewriteGenerator'); $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($this->productUrlRewriteGenerator, $this->anchorUrlRewriteGenerator); - } - - /** - * Test method - */ - public function testGenerationForGlobalScope() - { - $this->product->expects($this->any())->method('getStoreId')->will($this->returnValue(null)); - $this->product->expects($this->any())->method('getStoreIds')->will($this->returnValue([1])); - $this->storeViewService->expects($this->once())->method('doesEntityHaveOverriddenUrlKeyForStore') - ->will($this->returnValue(false)); - $this->categoriesCollection->expects($this->any())->method('getIterator') - ->willReturn(new \ArrayIterator([])); - $this->initObjectRegistryFactory([]); - $canonical = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); - $canonical->setTargetPath('category-1') - ->setStoreId(1); - $this->canonicalUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([$canonical])); - $categories = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); - $categories->setTargetPath('category-2') - ->setStoreId(2); - $this->categoriesUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([$categories])); - $current = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); - $current->setTargetPath('category-3') - ->setStoreId(3); - $this->currentUrlRewritesRegenerator->expects($this->any())->method('generate') - ->will($this->returnValue([$current])); - $anchorCategories = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); - $anchorCategories->setTargetPath('category-4') - ->setStoreId(4); - $this->anchorUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([$anchorCategories])); - - $this->assertEquals( - [ - 'category-1-1' => $canonical, - 'category-2-2' => $categories, - 'category-3-3' => $current, - 'category-4-4' => $anchorCategories - ], - $this->productUrlRewriteGenerator->generate($this->product) - ); + $reflectionProperty->setValue($this->productUrlRewriteGenerator, $this->productScopeRewriteGenerator); } - /** - * Test method - */ - public function testGenerationForSpecificStore() - { - $this->product->expects($this->any())->method('getStoreId')->will($this->returnValue(1)); - $this->product->expects($this->never())->method('getStoreIds'); - $storeRootCategoryId = 'root-for-store-id'; - $category = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); - $category->expects($this->any())->method('getParentIds') - ->will($this->returnValue(['root-id', $storeRootCategoryId])); - $category->expects($this->any())->method('getParentId')->will($this->returnValue('parent_id')); - $category->expects($this->any())->method('getId')->will($this->returnValue('category_id')); - $store = $this->getMockBuilder(\Magento\Store\Model\Store::class)->disableOriginalConstructor()->getMock(); - $store->expects($this->any())->method('getRootCategoryId')->will($this->returnValue($storeRootCategoryId)); - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $this->categoriesCollection->expects($this->any())->method('getIterator') - ->willReturn(new \ArrayIterator([$category])); - $this->initObjectRegistryFactory([$category]); - $canonical = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); - $canonical->setTargetPath('category-1') - ->setStoreId(1); - $this->canonicalUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([$canonical])); - $this->categoriesUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([])); - $this->currentUrlRewritesRegenerator->expects($this->any())->method('generate') - ->will($this->returnValue([])); - $this->anchorUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([])); - - $this->assertEquals(['category-1-1' => $canonical], $this->productUrlRewriteGenerator->generate($this->product)); - } - - /** - * Test method - */ - public function testSkipRootCategoryForCategoriesGenerator() + public function testGenerate() { - $this->product->expects($this->any())->method('getStoreId')->will($this->returnValue(1)); - $this->product->expects($this->never())->method('getStoreIds'); - $store = $this->getMockBuilder(\Magento\Store\Model\Store::class)->disableOriginalConstructor()->getMock(); - $store->expects($this->any())->method('getRootCategoryId')->will($this->returnValue('root-for-store-id')); - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $rootCategory = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); - $rootCategory->expects($this->any())->method('getParentIds')->will($this->returnValue([1, 2])); - $rootCategory->expects($this->any())->method('getParentId')->will($this->returnValue(Category::TREE_ROOT_ID)); - $this->categoriesCollection->expects($this->any())->method('getIterator') - ->willReturn(new \ArrayIterator([$rootCategory])); - $this->initObjectRegistryFactory([]); - $canonical = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); - $canonical->setTargetPath('category-1') - ->setStoreId(1); - $this->canonicalUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([$canonical])); - $this->categoriesUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([])); - $this->currentUrlRewritesRegenerator->expects($this->any())->method('generate') - ->will($this->returnValue([])); - $this->anchorUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([])); - - $this->assertEquals(['category-1-1' => $canonical], $this->productUrlRewriteGenerator->generate($this->product)); - } - - /** - * Test method - */ - public function testSkipGenerationForNotStoreRootCategory() - { - $this->product->expects($this->any())->method('getStoreId')->will($this->returnValue(1)); - $this->product->expects($this->never())->method('getStoreIds'); - $category = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); - $category->expects($this->any())->method('getParentIds') - ->will($this->returnValue(['root-id', 'root-for-store-id'])); - $store = $this->getMockBuilder(\Magento\Store\Model\Store::class)->disableOriginalConstructor()->getMock(); - $store->expects($this->any())->method('getRootCategoryId')->will($this->returnValue('not-root-id')); - $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $this->categoriesCollection->expects($this->any())->method('getIterator') - ->willReturn(new \ArrayIterator([$category])); - $this->initObjectRegistryFactory([]); - $canonical = new \Magento\UrlRewrite\Service\V1\Data\UrlRewrite(); - $canonical->setTargetPath('category-1') - ->setStoreId(1); - $this->canonicalUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([$canonical])); - $this->categoriesUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([])); - $this->currentUrlRewritesRegenerator->expects($this->any())->method('generate') - ->will($this->returnValue([])); - $this->anchorUrlRewriteGenerator->expects($this->any())->method('generate') - ->will($this->returnValue([])); - - $this->assertEquals(['category-1-1' => $canonical], $this->productUrlRewriteGenerator->generate($this->product)); - } - - /** - * Test method - */ - public function testSkipGenerationForGlobalScope() - { - $this->product->expects($this->any())->method('getStoreIds')->will($this->returnValue([1, 2])); - $this->storeViewService->expects($this->exactly(2))->method('doesEntityHaveOverriddenUrlKeyForStore') - ->will($this->returnValue(true)); - - $this->assertEquals([], $this->productUrlRewriteGenerator->generate($this->product)); - } - - /** - * @param array $entities - */ - protected function initObjectRegistryFactory($entities) - { - $objectRegistry = $this->getMockBuilder(\Magento\CatalogUrlRewrite\Model\ObjectRegistry::class) - ->disableOriginalConstructor()->getMock(); - $this->objectRegistryFactory->expects($this->any())->method('create') - ->with(['entities' => $entities]) - ->will($this->returnValue($objectRegistry)); + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $storeId = 1; + $urls = ['dummy-url.html']; + + $productMock->expects($this->once()) + ->method('getVisibility') + ->willReturn(2); + $productMock->expects($this->once()) + ->method('getStoreId') + ->willReturn($storeId); + $productCategoriesMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Category\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $productCategoriesMock->expects($this->exactly(2)) + ->method('addAttributeToSelect') + ->withConsecutive(['url_key'], ['url_path']) + ->willReturnSelf(); + $productMock->expects($this->once()) + ->method('getCategoryCollection') + ->willReturn($productCategoriesMock); + $this->productScopeRewriteGenerator->expects($this->once()) + ->method('generateForSpecificStoreView') + ->willReturn($urls); + $this->assertEquals($urls, $this->productUrlRewriteGenerator->generate($productMock)); } } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/UrlRewriteBunchReplacerTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/UrlRewriteBunchReplacerTest.php new file mode 100644 index 0000000000000..7a44cc5d6f972 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/UrlRewriteBunchReplacerTest.php @@ -0,0 +1,39 @@ +urlPersistMock = $this->getMock(UrlPersistInterface::class); + $this->urlRewriteBunchReplacer = new UrlRewriteBunchReplacer( + $this->urlPersistMock + ); + } + + public function testDoBunchReplace() + { + $urls = [[1], [2]]; + $this->urlPersistMock->expects($this->exactly(2)) + ->method('replace') + ->withConsecutive([[[1]]], [[[2]]]); + $this->urlRewriteBunchReplacer->doBunchReplace($urls, 1); + } +} diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php index c2e26220aee46..d317e55127a89 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php @@ -17,6 +17,11 @@ */ class InlineEdit extends \Magento\Backend\App\Action { + /** + * Authorization level of a basic admin session + */ + const ADMIN_RESOURCE = 'Magento_Cms::save'; + /** @var PostDataProcessor */ protected $dataProcessor; diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php new file mode 100644 index 0000000000000..70fef18f3a77d --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php @@ -0,0 +1,135 @@ +authorizationMock = $this->getMockBuilder(Authorization::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->reportingMock = $this->getMockBuilder(Reporting::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->searchCriteriaBuilderMock = $this->getMockBuilder(SearchCriteriaBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->requestInterfaceMock = $this->getMockBuilder(RequestInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->filterBuilderMock = $this->getMockBuilder(FilterBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject $objectManagerMock */ + $objectManagerMock = $this->getMock(ObjectManagerInterface::class); + $objectManagerMock->expects($this->once()) + ->method('get') + ->willReturn($this->authorizationMock); + ObjectManager::setInstance($objectManagerMock); + + $this->dataProvider = new DataProvider( + $this->name, + $this->primaryFieldName, + $this->requestFieldName, + $this->reportingMock, + $this->searchCriteriaBuilderMock, + $this->requestInterfaceMock, + $this->filterBuilderMock + ); + } + + /** + * @covers \Magento\Cms\Ui\Component\DataProvider::prepareMetadata + */ + public function testPrepareMetadata() + { + $this->authorizationMock->expects($this->once()) + ->method('isAllowed') + ->with('Magento_Cms::save') + ->willReturn(false); + + $metadata = [ + 'cms_page_columns' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'editorConfig' => [ + 'enabled' => false + ] + ] + ] + ] + ] + ]; + + $this->assertEquals( + $metadata, + $this->dataProvider->prepareMetadata() + ); + + } +} diff --git a/app/code/Magento/Cms/Ui/Component/DataProvider.php b/app/code/Magento/Cms/Ui/Component/DataProvider.php index 53ba80e6ccb73..85b00d341fad8 100644 --- a/app/code/Magento/Cms/Ui/Component/DataProvider.php +++ b/app/code/Magento/Cms/Ui/Component/DataProvider.php @@ -7,11 +7,18 @@ use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\Search\SearchCriteriaBuilder; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\RequestInterface; +use Magento\Framework\AuthorizationInterface; use Magento\Framework\View\Element\UiComponent\DataProvider\Reporting; class DataProvider extends \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider { + /** + * @var AuthorizationInterface + */ + private $authorization; + /** * @param string $name * @param string $primaryFieldName @@ -45,5 +52,47 @@ public function __construct( $meta, $data ); + + $this->meta = array_replace_recursive($meta, $this->prepareMetadata()); + } + + /** + * @deprecated + * @return AuthorizationInterface|mixed + */ + private function getAuthorizationInstance() + { + if ($this->authorization === null) { + $this->authorization = ObjectManager::getInstance()->get(AuthorizationInterface::class); + } + return $this->authorization; + } + + /** + * Prepares Meta + * + * @return array + */ + public function prepareMetadata() + { + $metadata = []; + + if (!$this->getAuthorizationInstance()->isAllowed('Magento_Cms::save')) { + $metadata = [ + 'cms_page_columns' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'editorConfig' => [ + 'enabled' => false + ] + ] + ] + ] + ] + ]; + } + + return $metadata; } } diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php index c074fcc056fc3..de363262275e5 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php @@ -32,8 +32,6 @@ class Date extends AbstractDataType protected $wrappedComponent; /** - * Constructor - * * @param ContextInterface $context * @param TimezoneInterface $localeDate * @param ResolverInterface $localeResolver @@ -70,6 +68,11 @@ public function prepare() ))->getOffset(); } + // Set date format pattern by current locale + $localeDateFormat = $this->localeDate->getDateFormat(); + $config['options']['dateFormat'] = $localeDateFormat; + $config['outputDateFormat'] = $localeDateFormat; + $this->setData('config', $config); parent::prepare(); diff --git a/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php new file mode 100644 index 0000000000000..ef0743aa32947 --- /dev/null +++ b/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php @@ -0,0 +1,126 @@ +getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->context = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContextInterface::class) + ->getMockForAbstractClass(); + $this->context->expects($this->any()) + ->method('getProcessor') + ->willReturn($processorMock); + + $this->localeDate = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) + ->getMockForAbstractClass(); + + $this->localeResolver = $this->getMockBuilder(\Magento\Framework\Locale\ResolverInterface::class) + ->getMockForAbstractClass(); + } + + public function testPrepareWithTimeOffset() + { + $this->model = new Date( + $this->context, + $this->localeDate, + $this->localeResolver, + [], + [ + 'config' => [ + 'timeOffset' => 1, + ], + ] + ); + + $localeDateFormat = 'dd/MM/y'; + + $this->localeDate->expects($this->once()) + ->method('getDateFormat') + ->willReturn($localeDateFormat); + + $this->model->prepare(); + + $config = $this->model->getConfig(); + $this->assertTrue(is_array($config)); + + $this->assertArrayHasKey('options', $config); + $this->assertArrayHasKey('dateFormat', $config['options']); + $this->assertEquals($localeDateFormat, $config['options']['dateFormat']); + + $this->assertArrayHasKey('outputDateFormat', $config); + $this->assertEquals($localeDateFormat, $config['outputDateFormat']); + } + + public function testPrepareWithoutTimeOffset() + { + $defaultDateFormat = 'MM/dd/y'; + + $this->model = new Date( + $this->context, + $this->localeDate, + $this->localeResolver, + [], + [ + 'config' => [ + 'options' => [ + 'dateFormat' => $defaultDateFormat, + ], + 'outputDateFormat' => $defaultDateFormat, + ], + ] + ); + + $localeDateFormat = 'dd/MM/y'; + + $this->localeDate->expects($this->once()) + ->method('getDateFormat') + ->willReturn($localeDateFormat); + $this->localeDate->expects($this->once()) + ->method('getConfigTimezone') + ->willReturn('America/Los_Angeles'); + + $this->model->prepare(); + + $config = $this->model->getConfig(); + $this->assertTrue(is_array($config)); + + $this->assertArrayHasKey('timeOffset', $config); + + $this->assertArrayHasKey('options', $config); + $this->assertArrayHasKey('dateFormat', $config['options']); + $this->assertEquals($localeDateFormat, $config['options']['dateFormat']); + + $this->assertArrayHasKey('outputDateFormat', $config); + $this->assertEquals($localeDateFormat, $config['outputDateFormat']); + } +} diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js index 8f0fb7b007caf..16c102b8367f4 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/datepicker.js @@ -8,8 +8,10 @@ define([ 'underscore', 'jquery', 'mage/translate', - 'mage/calendar' -], function (ko, _, $, $t) { + 'mage/calendar', + 'moment', + 'mageUtils' +], function (ko, _, $, $t, calendar, moment, utils) { 'use strict'; var defaults = { @@ -46,7 +48,12 @@ define([ } $(el).calendar(options); - observable() && $(el).datepicker('setDate', observable()); + + observable() && $(el).datepicker( + 'setDate', + moment(observable(), utils.normalizeDate(config.options.dateFormat)).toDate() + ); + $(el).blur(); ko.utils.registerEventHandler(el, 'change', function () { 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 71f2834190436..aa30cf24805f0 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 @@ -9,8 +9,9 @@ define([ 'moment', 'jquery/validate', 'jquery/ui', - 'mage/translate' -], function ($, _, utils, moment) { + 'mage/translate', + 'mageUtils' +], function ($, _, utils, moment, validate, ui, $t, mageUtils) { 'use strict'; /** @@ -682,9 +683,9 @@ define([ $.mage.__('Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.') ], "validate-date": [ - function(value) { - var test = new Date(value); - return utils.isEmptyNoTrim(value) || !isNaN(test); + function(value, params, additionalParams) { + var test = moment(value, additionalParams.dateFormat); + return utils.isEmptyNoTrim(value) || test.isValid(); },$.mage.__('Please enter a valid date.') ], diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index f30c1bffa0075..4191ec1faa959 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -275,6 +275,35 @@ public function testCreateAllStoreCode($fixtureProduct) $this->deleteProduct($fixtureProduct[ProductInterface::SKU]); } + /** + * Test creating product with all store code on single store + * + * @param array $fixtureProduct + * @dataProvider productCreationProvider + */ + public function testCreateAllStoreCodeForSingleWebsite($fixtureProduct) + { + $response = $this->saveProduct($fixtureProduct, 'all'); + $this->assertArrayHasKey(ProductInterface::SKU, $response); + + /** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ + $storeManager = \Magento\TestFramework\ObjectManager::getInstance()->get( + \Magento\Store\Model\StoreManagerInterface::class + ); + + foreach ($storeManager->getStores(true) as $store) { + $code = $store->getCode(); + if ($code === Store::ADMIN_CODE) { + continue; + } + $this->assertArrayHasKey( + ProductInterface::SKU, + $this->getProduct($fixtureProduct[ProductInterface::SKU], $code) + ); + } + $this->deleteProduct($fixtureProduct[ProductInterface::SKU]); + } + public function testCreateInvalidPriceFormat() { $this->_markTestAsRestOnly("In case of SOAP type casting is handled by PHP SoapServer, no need to test it");