diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php index 1e7c800ea1c38a3275760dc44216f82559820217..85fee62eb430371836608c7baae23182d57f62f3 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php @@ -10,6 +10,7 @@ namespace Magento\CatalogInventory\Model\Indexer\Stock; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; +use Magento\Framework\EntityManager\MetadataPool; /** * Abstract action reindex class @@ -70,25 +71,33 @@ abstract class AbstractAction */ private $cacheCleaner; + /** + * @var MetadataPool + */ + private $metadataPool; + /** * @param ResourceConnection $resource * @param \Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory $indexerFactory * @param \Magento\Catalog\Model\Product\Type $catalogProductType * @param \Magento\Framework\Indexer\CacheContext $cacheContext * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param MetadataPool|null $metadataPool */ public function __construct( ResourceConnection $resource, \Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory $indexerFactory, \Magento\Catalog\Model\Product\Type $catalogProductType, \Magento\Framework\Indexer\CacheContext $cacheContext, - \Magento\Framework\Event\ManagerInterface $eventManager + \Magento\Framework\Event\ManagerInterface $eventManager, + MetadataPool $metadataPool = null ) { $this->_resource = $resource; $this->_indexerFactory = $indexerFactory; $this->_catalogProductType = $catalogProductType; $this->cacheContext = $cacheContext; $this->eventManager = $eventManager; + $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class); } /** @@ -154,10 +163,15 @@ abstract class AbstractAction public function getRelationsByChild($childIds) { $connection = $this->_getConnection(); - $select = $connection->select() - ->from($this->_getTable('catalog_product_relation'), 'parent_id') - ->where('child_id IN(?)', $childIds); - + $linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + $select = $connection->select()->from( + ['cpe' => $this->_getTable('catalog_product_entity')], + 'entity_id' + )->join( + ['relation' => $this->_getTable('catalog_product_relation')], + 'relation.parent_id = cpe.' . $linkField + )->where('child_id IN(?)', $childIds); return $connection->fetchCol($select); } @@ -230,7 +244,8 @@ abstract class AbstractAction if (!is_array($productIds)) { $productIds = [$productIds]; } - + $parentIds = $this->getRelationsByChild($productIds); + $productIds = $parentIds ? array_unique(array_merge($parentIds, $productIds)) : $productIds; $this->getCacheCleaner()->clean($productIds, function () use ($productIds) { $this->doReindex($productIds); }); @@ -248,13 +263,10 @@ abstract class AbstractAction { $connection = $this->_getConnection(); - $parentIds = $this->getRelationsByChild($productIds); - $processIds = $parentIds ? array_merge($parentIds, $productIds) : $productIds; - // retrieve product types by processIds $select = $connection->select() ->from($this->_getTable('catalog_product_entity'), ['entity_id', 'type_id']) - ->where('entity_id IN(?)', $processIds); + ->where('entity_id IN(?)', $productIds); $pairs = $connection->fetchPairs($select); $byType = []; diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/ReindexUpdatedProducts.php b/app/code/Magento/CatalogInventory/Model/Plugin/ReindexUpdatedProducts.php new file mode 100644 index 0000000000000000000000000000000000000000..f10afcd4ea329212d32cbf4f4d4fd275d0d1b396 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Model/Plugin/ReindexUpdatedProducts.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogInventory\Model\Plugin; + +use Magento\Catalog\Model\Product\Action as ProductAction; + +/** + * Plugin for Magento\Catalog\Model\Product\Action + */ +class ReindexUpdatedProducts +{ + /** + * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor + */ + private $indexerProcessor; + + /** + * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $indexerProcessor + */ + public function __construct(\Magento\CatalogInventory\Model\Indexer\Stock\Processor $indexerProcessor) + { + $this->indexerProcessor = $indexerProcessor; + } + + /** + * Reindex on product attribute mass change + * + * @param ProductAction $subject + * @param ProductAction $action + * @param array $productIds + * @return ProductAction + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterUpdateAttributes( + ProductAction $subject, + ProductAction $action, + $productIds + ) { + $this->indexerProcessor->reindexList(array_unique($productIds)); + return $action; + } +} diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 04935b11ce02ba396b6c1c65c9e0a57f8f622b3c..65bc27712142910533ac99d069350f41bf673dec 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -78,6 +78,9 @@ <type name="Magento\Catalog\Model\Product"> <plugin name="catalogInventoryAfterLoad" type="Magento\CatalogInventory\Model\Plugin\AfterProductLoad"/> </type> + <type name="Magento\Catalog\Model\Product\Action"> + <plugin name="ReindexUpdatedProducts" type="Magento\CatalogInventory\Model\Plugin\ReindexUpdatedProducts"/> + </type> <type name="Magento\CatalogInventory\Setup\UpgradeData"> <arguments> <argument name="indexerProcessor" xsi:type="object">Magento\CatalogInventory\Model\Indexer\Stock\Processor</argument> diff --git a/app/code/Magento/UrlRewrite/Block/Edit.php b/app/code/Magento/UrlRewrite/Block/Edit.php index 7311efeb114c1e0ba10207885080118fca7e74af..baee8af893083ef99331549bd1ac54fadd102e73 100644 --- a/app/code/Magento/UrlRewrite/Block/Edit.php +++ b/app/code/Magento/UrlRewrite/Block/Edit.php @@ -118,7 +118,7 @@ class Edit extends \Magento\Backend\Block\Widget\Container 'reset', [ 'label' => __('Reset'), - 'onclick' => '$(\'edit_form\').reset()', + 'onclick' => 'location.reload();', 'class' => 'scalable', 'level' => -1 ] diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ActionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ActionTest.php index 90825ab573d09210f4029fc8d395aae3f7165f5a..39905aeae10f5d5410f7fa7541b15063ca8b5cfc 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ActionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ActionTest.php @@ -84,6 +84,73 @@ class ActionTest extends \PHPUnit\Framework\TestCase } } + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppArea adminhtml + * @param string $status + * @param string $productsCount + * @dataProvider updateAttributesDataProvider + */ + public function testUpdateAttributes($status, $productsCount) + { + /** @var \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry */ + $indexerRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Indexer\IndexerRegistry::class); + $indexerRegistry->get(Fulltext::INDEXER_ID)->setScheduled(false); + + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + + /** @var \Magento\Catalog\Model\Product $product */ + $product = $productRepository->get('configurable'); + $productAttributesOptions = $product->getExtensionAttributes()->getConfigurableProductLinks(); + $attrData = ['status' => $status]; + $configurableOptionsId = []; + if (isset($productAttributesOptions)) { + foreach ($productAttributesOptions as $configurableOption) { + $configurableOptionsId[] = $configurableOption; + } + } + $this->action->updateAttributes($configurableOptionsId, $attrData, $product->getStoreId()); + + $categoryFactory = $this->objectManager->create(\Magento\Catalog\Model\CategoryFactory::class); + /** @var \Magento\Catalog\Block\Product\ListProduct $listProduct */ + $listProduct = $this->objectManager->create(\Magento\Catalog\Block\Product\ListProduct::class); + $category = $categoryFactory->create()->load(2); + $layer = $listProduct->getLayer(); + $layer->setCurrentCategory($category); + $productCollection = $layer->getProductCollection(); + $productCollection->joinField( + 'qty', + 'cataloginventory_stock_status', + 'qty', + 'product_id=entity_id', + '{{table}}.stock_id=1', + 'left' + ); + + $this->assertEquals($productsCount, $productCollection->count()); + } + + /** + * DataProvider for testUpdateAttributes + * + * @return array + */ + public function updateAttributesDataProvider() + { + return [ + [ + 'status' => 2, + 'expected_count' => 0 + ], + [ + 'status' => 1, + 'expected_count' => 1 + ], + ]; + } + public static function tearDownAfterClass() { /** @var \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry */ diff --git a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php index b687232bcaac6ec4a858858f62aa614fb34c119e..9d1d8761a16a7c4ba58f7e0362dd3999b1db9d9c 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php @@ -6,38 +6,37 @@ namespace Magento\Framework; use Zend\Stdlib\Parameters; +use Magento\TestFramework\Helper\Bootstrap; class UrlTest extends \PHPUnit\Framework\TestCase { /** * @var \Magento\Framework\UrlInterface */ - protected $_model; + protected $model; protected function setUp() { - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Framework\Url::class - ); + $this->model = Bootstrap::getObjectManager()->create(\Magento\Framework\Url::class); } public function testSetGetUseSession() { - $this->assertTrue((bool)$this->_model->getUseSession()); - $this->_model->setUseSession(false); - $this->assertFalse($this->_model->getUseSession()); + $this->assertTrue((bool)$this->model->getUseSession()); + $this->model->setUseSession(false); + $this->assertFalse($this->model->getUseSession()); } public function testSetRouteFrontName() { $value = 'route'; - $this->_model->setRouteFrontName($value); - $this->assertEquals($value, $this->_model->getData('route_front_name')); + $this->model->setRouteFrontName($value); + $this->assertEquals($value, $this->model->getData('route_front_name')); } public function testGetConfigData() { - $this->assertEquals('http://localhost/', $this->_model->getConfigData('base_url')); + $this->assertEquals('http://localhost/', $this->model->getConfigData('base_url')); } /** @@ -46,7 +45,7 @@ class UrlTest extends \PHPUnit\Framework\TestCase */ public function testGetBaseUrlDefaults() { - $this->assertEquals('http://localhost/index.php/', $this->_model->getBaseUrl()); + $this->assertEquals('http://localhost/index.php/', $this->model->getBaseUrl()); } /** @@ -56,7 +55,7 @@ class UrlTest extends \PHPUnit\Framework\TestCase */ public function testGetBaseUrlSeoRewrites() { - $this->assertEquals('http://localhost/', $this->_model->getBaseUrl()); + $this->assertEquals('http://localhost/', $this->model->getBaseUrl()); } /** @@ -75,10 +74,96 @@ class UrlTest extends \PHPUnit\Framework\TestCase */ public function testGetBaseUrlConfigured($params, $expectedUrl) { - $actualUrl = $this->_model->getBaseUrl($params); + $actualUrl = $this->model->getBaseUrl($params); $this->assertEquals($expectedUrl, $actualUrl); } + /** + * Note: isolation flushes the URL memory cache + * @magentoAppIsolation enabled + * + * @magentoConfigFixture current_store web/secure/base_url http://sample.com/ + * @magentoConfigFixture current_store web/unsecure/base_link_url http://sample.com/ + * @magentoConfigFixture current_store web/secure/base_link_url https://sample.com/ + * @magentoConfigFixture current_store web/secure/use_in_frontend 1 + * + * @magentoAppArea frontend + */ + public function testGetUnsecureUrlInSecureArea() + { + /** @var \Magento\Framework\App\Request\Http $request */ + $request = Bootstrap::getObjectManager()->create(\Magento\Framework\App\Request\Http::class); + //Emulate HTTPS request + $request->getServer()->set('HTTPS', 'on'); + $request->getServer()->set('SERVER_PORT', 443); + + $model = Bootstrap::getObjectManager()->create(\Magento\Framework\Url::class, ['request' => $request]); + + $secureUrl = $model->getUrl('some/index/controller', ['_nosid' => 1]); + $this->assertEquals( + 'https://sample.com/index.php/some/index/controller/', + $secureUrl, + 'Default URL in secure area is incorrect' + ); + + $secureUrl = $model->getUrl('some/index/controller', ['_secure' => true, '_nosid' => 1]); + $this->assertEquals( + 'https://sample.com/index.php/some/index/controller/', + $secureUrl, + 'Secure URL in secure area is incorrect' + ); + + $unsecureUrl = $model->getUrl('some/index/controller', ['_secure' => false, '_nosid' => 1]); + $this->assertEquals( + 'http://sample.com/index.php/some/index/controller/', + $unsecureUrl, + 'Unsecure URL in secure area is incorrect' + ); + } + + /** + * Note: isolation flushes the URL memory cache + * @magentoAppIsolation enabled + * + * @magentoConfigFixture current_store web/secure/base_url http://sample.com/ + * @magentoConfigFixture current_store web/unsecure/base_link_url http://sample.com/ + * @magentoConfigFixture current_store web/secure/base_link_url https://sample.com/ + * @magentoConfigFixture current_store web/secure/use_in_frontend 1 + * + * @magentoAppArea frontend + */ + public function testGetSecureUrlInUnsecureArea() + { + /** @var \Magento\Framework\App\Request\Http $request */ + $request = Bootstrap::getObjectManager()->create(\Magento\Framework\App\Request\Http::class); + //Emulate HTTPS request + $request->getServer()->set('HTTPS', 'off'); + $request->getServer()->set('SERVER_PORT', 80); + + $model = Bootstrap::getObjectManager()->create(\Magento\Framework\Url::class, ['request' => $request]); + + $secureUrl = $model->getUrl('some/index/controller', ['_nosid' => 1]); + $this->assertEquals( + 'http://sample.com/index.php/some/index/controller/', + $secureUrl, + 'Default URL in unsecure area is incorrect' + ); + + $secureUrl = $model->getUrl('some/index/controller', ['_secure' => true, '_nosid' => 1]); + $this->assertEquals( + 'https://sample.com/index.php/some/index/controller/', + $secureUrl, + 'Secure URL in unsecure area is incorrect' + ); + + $unsecureUrl = $model->getUrl('some/index/controller', ['_secure' => false, '_nosid' => 1]); + $this->assertEquals( + 'http://sample.com/index.php/some/index/controller/', + $unsecureUrl, + 'Unsecure URL in unsecure area is incorrect' + ); + } + /** * Check that url type is restored to default after call getBaseUrl with type specified in params */ @@ -87,21 +172,21 @@ class UrlTest extends \PHPUnit\Framework\TestCase /** * Get base URL with default type */ - $this->assertEquals('http://localhost/index.php/', $this->_model->getBaseUrl(), 'Incorrect link url'); + $this->assertEquals('http://localhost/index.php/', $this->model->getBaseUrl(), 'Incorrect link url'); /** * Set specified type */ - $webUrl = $this->_model->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_WEB]); + $webUrl = $this->model->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_WEB]); $this->assertEquals('http://localhost/', $webUrl, 'Incorrect web url'); - $this->assertEquals('http://localhost/index.php/', $this->_model->getBaseUrl(), 'Incorrect link url'); + $this->assertEquals('http://localhost/index.php/', $this->model->getBaseUrl(), 'Incorrect link url'); /** * Get url with type specified in params */ - $mediaUrl = $this->_model->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]); + $mediaUrl = $this->model->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]); $this->assertEquals('http://localhost/pub/media/', $mediaUrl, 'Incorrect media url'); - $this->assertEquals('http://localhost/index.php/', $this->_model->getBaseUrl(), 'Incorrect link url'); + $this->assertEquals('http://localhost/index.php/', $this->model->getBaseUrl(), 'Incorrect link url'); } public function getBaseUrlConfiguredDataProvider() @@ -121,24 +206,24 @@ class UrlTest extends \PHPUnit\Framework\TestCase public function testSetGetRouteName() { - $this->_model->setRouteName('catalog'); - $this->assertEquals('catalog', $this->_model->getRouteName()); + $this->model->setRouteName('catalog'); + $this->assertEquals('catalog', $this->model->getRouteName()); $this->markTestIncomplete('setRouteName() logic is unclear.'); } public function testSetGetControllerName() { - $this->_model->setControllerName('product'); - $this->assertEquals('product', $this->_model->getControllerName()); + $this->model->setControllerName('product'); + $this->assertEquals('product', $this->model->getControllerName()); $this->markTestIncomplete('setControllerName() logic is unclear.'); } public function testSetGetActionName() { - $this->_model->setActionName('view'); - $this->assertEquals('view', $this->_model->getActionName()); + $this->model->setActionName('view'); + $this->assertEquals('view', $this->model->getActionName()); $this->markTestIncomplete('setActionName() logic is unclear.'); } @@ -149,21 +234,21 @@ class UrlTest extends \PHPUnit\Framework\TestCase */ public function testGetRouteUrl() { - $this->assertEquals('http://localhost/index.php/', $this->_model->getRouteUrl()); + $this->assertEquals('http://localhost/index.php/', $this->model->getRouteUrl()); $this->assertEquals( 'http://localhost/index.php/catalog/product/view/id/50/', - $this->_model->getRouteUrl('catalog/product/view', ['id' => 50]) + $this->model->getRouteUrl('catalog/product/view', ['id' => 50]) ); $this->assertEquals( 'http://localhost/index.php/fancy_uri', - $this->_model->getRouteUrl('core/index/index', ['_direct' => 'fancy_uri']) + $this->model->getRouteUrl('core/index/index', ['_direct' => 'fancy_uri']) ); } public function testSetGetFragment() { - $this->_model->setFragment('value'); - $this->assertEquals('value', $this->_model->getFragment()); + $this->model->setFragment('value'); + $this->assertEquals('value', $this->model->getFragment()); } /** @@ -172,7 +257,7 @@ class UrlTest extends \PHPUnit\Framework\TestCase */ public function testGetUrl() { - $result = $this->_model->getUrl( + $result = $this->model->getUrl( 'catalog/product/view', ['_fragment' => 'anchor', '_escape' => 1, '_query' => 'foo=bar', '_nosid' => 1, 'id' => 100] ); @@ -185,9 +270,9 @@ class UrlTest extends \PHPUnit\Framework\TestCase */ public function testGetUrlDoesntAddQueryParamsOnConsequentCalls() { - $result = $this->_model->getUrl('catalog/product/view', ['_query' => 'foo=bar', '_nosid' => 1]); + $result = $this->model->getUrl('catalog/product/view', ['_query' => 'foo=bar', '_nosid' => 1]); $this->assertEquals('http://localhost/index.php/catalog/product/view/?foo=bar', $result); - $result = $this->_model->getUrl('catalog/product/view', ['_nosid' => 1]); + $result = $this->model->getUrl('catalog/product/view', ['_nosid' => 1]); $this->assertEquals('http://localhost/index.php/catalog/product/view/', $result); } @@ -198,9 +283,9 @@ class UrlTest extends \PHPUnit\Framework\TestCase */ public function testGetUrlDoesntAddFragmentOnConsequentCalls() { - $result = $this->_model->getUrl('catalog/product/view', ['_nosid' => 1, '_fragment' => 'section']); + $result = $this->model->getUrl('catalog/product/view', ['_nosid' => 1, '_fragment' => 'section']); $this->assertEquals('http://localhost/index.php/catalog/product/view/#section', $result); - $result = $this->_model->getUrl('catalog/product/view', ['_nosid' => 1]); + $result = $this->model->getUrl('catalog/product/view', ['_nosid' => 1]); $this->assertEquals('http://localhost/index.php/catalog/product/view/', $result); } @@ -226,10 +311,10 @@ class UrlTest extends \PHPUnit\Framework\TestCase $firstExpectedUrl, $secondExpectedUrl ) { - $result = $this->_model->getUrl($firstCallUrl, $firstRouteParams); + $result = $this->model->getUrl($firstCallUrl, $firstRouteParams); $this->assertEquals($firstExpectedUrl, $result); - $result = $this->_model->getUrl($secondCallUrl, $secondRouteParams); + $result = $this->model->getUrl($secondCallUrl, $secondRouteParams); $this->assertEquals($secondExpectedUrl, $result); } @@ -375,7 +460,7 @@ class UrlTest extends \PHPUnit\Framework\TestCase public function testEscape() { - $this->assertEquals('%22%27%3E%3C', $this->_model->escape('"\'><')); + $this->assertEquals('%22%27%3E%3C', $this->model->escape('"\'><')); } /** @@ -384,7 +469,7 @@ class UrlTest extends \PHPUnit\Framework\TestCase */ public function testGetDirectUrl() { - $directUrl = $this->_model->getDirectUrl('fancy_uri', ['_query' => ['foo' => 'bar']]); + $directUrl = $this->model->getDirectUrl('fancy_uri', ['_query' => ['foo' => 'bar']]); $this->assertEquals('http://localhost/index.php/fancy_uri?foo=bar', $directUrl); } @@ -400,15 +485,15 @@ class UrlTest extends \PHPUnit\Framework\TestCase $sessionId = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\Framework\Session\Generic::class )->getSessionId(); - $sessionUrl = $this->_model->sessionUrlVar('<a href="http://example.com/?___SID=U">www.example.com</a>'); + $sessionUrl = $this->model->sessionUrlVar('<a href="http://example.com/?___SID=U">www.example.com</a>'); $this->assertEquals('<a href="http://example.com/?SID=' . $sessionId . '">www.example.com</a>', $sessionUrl); } public function testUseSessionIdForUrl() { $_SERVER['HTTP_HOST'] = 'localhost'; - $this->assertFalse($this->_model->useSessionIdForUrl(true)); - $this->assertFalse($this->_model->useSessionIdForUrl(false)); + $this->assertFalse($this->model->useSessionIdForUrl(true)); + $this->assertFalse($this->model->useSessionIdForUrl(false)); } /** @@ -421,9 +506,9 @@ class UrlTest extends \PHPUnit\Framework\TestCase /** @var $request \Magento\TestFramework\Request */ $request = $objectManager->get(\Magento\Framework\App\RequestInterface::class); $request->setServer(new Parameters(['HTTP_REFERER' => 'http://localhost/'])); - $this->assertTrue($this->_model->isOwnOriginUrl()); + $this->assertTrue($this->model->isOwnOriginUrl()); $request->setServer(new Parameters(['HTTP_REFERER' => 'http://example.com/'])); - $this->assertFalse($this->_model->isOwnOriginUrl()); + $this->assertFalse($this->model->isOwnOriginUrl()); } } diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple.php new file mode 100644 index 0000000000000000000000000000000000000000..fc01daf2381bceb511efb1365b490091b93a8ba8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\GroupedProduct\Model\Product\Type\Grouped; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->get(ProductRepositoryInterface::class); + +$productLinkFactory = Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory::class); +$productIds = ['11', '22']; + +foreach ($productIds as $productId) { + /** @var $product Product */ + $product = Bootstrap::getObjectManager()->create(Product::class); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setId($productId) + ->setWebsiteIds([1]) + ->setAttributeSetId(4) + ->setName('Simple ' . $productId) + ->setSku('simple_' . $productId) + ->setPrice(100) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + + $linkedProducts[] = $productRepository->save($product); +} + +/** @var $product Product */ +$product = Bootstrap::getObjectManager()->create(Product::class); + +$product->setTypeId(Grouped::TYPE_CODE) + ->setId(1) + ->setWebsiteIds([1]) + ->setAttributeSetId(4) + ->setName('Grouped Product') + ->setSku('grouped') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); + +foreach ($linkedProducts as $linkedProduct) { + /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ + $productLink = $productLinkFactory->create(); + $productLink->setSku($product->getSku()) + ->setLinkType('associated') + ->setLinkedProductSku($linkedProduct->getSku()) + ->setLinkedProductType($linkedProduct->getTypeId()) + ->getExtensionAttributes() + ->setQty(1); + $newLinks[] = $productLink; +} + +$product->setProductLinks($newLinks); + +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_rollback.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_rollback.php new file mode 100644 index 0000000000000000000000000000000000000000..bc2f04d7b19d0d9b6a574b87339fe960b55bebff --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_rollback.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +$skuList = ['simple_11', 'simple_22', 'grouped']; +foreach ($skuList as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + + $stockStatus = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Status::class); + $stockStatus->load($product->getEntityId(), 'product_id'); + $stockStatus->delete(); + + $productRepository->delete($product); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php b/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php index d61389066433a8e8a43f387e9d0522eed8e00613..7048668b813dc28feb1b99146ca12524a0a8162b 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php @@ -9,6 +9,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; use Magento\Framework\Model\CallbackPool; use Magento\Framework\Serialize\Serializer\Json; +use Psr\Log\LoggerInterface; /** * Abstract resource model @@ -23,6 +24,11 @@ abstract class AbstractResource */ protected $serializer; + /** + * @var LoggerInterface + */ + private $logger; + /** * Constructor */ @@ -92,7 +98,7 @@ abstract class AbstractResource call_user_func($callback); } } catch (\Exception $e) { - throw $e; + $this->getLogger()->critical($e); } } return $this; @@ -252,4 +258,19 @@ abstract class AbstractResource } return $this->serializer; } + + /** + * Get logger + * + * @return LoggerInterface + * @deprecated 100.2.0 + * @since 100.2.0 + */ + private function getLogger() + { + if (null === $this->logger) { + $this->logger = ObjectManager::getInstance()->get(LoggerInterface::class); + } + return $this->logger; + } } diff --git a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/AbstractResourceTest.php b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/AbstractResourceTest.php index e7701748e2d486dcc5397cb3ef6cd55832a84168..40243978fe753d4a07bf4d943c40237a1aae3d8e 100644 --- a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/AbstractResourceTest.php +++ b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/AbstractResourceTest.php @@ -27,11 +27,7 @@ class AbstractResourceTest extends \PHPUnit\Framework\TestCase $objectManager = new ObjectManager($this); $this->serializerMock = $this->createMock(Json::class); $this->abstractResource = $objectManager->getObject(AbstractResourceStub::class); - $objectManager->setBackwardCompatibleProperty( - $this->abstractResource, - 'serializer', - $this->serializerMock - ); + $objectManager->setBackwardCompatibleProperty($this->abstractResource, 'serializer', $this->serializerMock); } /** @@ -201,9 +197,6 @@ class AbstractResourceTest extends \PHPUnit\Framework\TestCase $this->abstractResource->commit(); } - /** - * @expectedException \Exception - */ public function testCommitZeroLevelCallbackException() { /** @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject $connection */ diff --git a/lib/internal/Magento/Framework/Stdlib/Cookie/PhpCookieManager.php b/lib/internal/Magento/Framework/Stdlib/Cookie/PhpCookieManager.php index 618c70b760d0971c83954c5a703a69ce5d61ddad..91465280d1e805bef915e2faa4bf7177a68e5288 100644 --- a/lib/internal/Magento/Framework/Stdlib/Cookie/PhpCookieManager.php +++ b/lib/internal/Magento/Framework/Stdlib/Cookie/PhpCookieManager.php @@ -237,7 +237,9 @@ class PhpCookieManager implements CookieManagerInterface ) { $expireTime = $metadataArray[PhpCookieManager::KEY_EXPIRE_TIME]; } else { - if (isset($metadataArray[CookieMetadata::KEY_DURATION])) { + if (isset($metadataArray[CookieMetadata::KEY_DURATION]) + && $metadataArray[CookieMetadata::KEY_DURATION] !== PhpCookieManager::EXPIRE_AT_END_OF_SESSION_TIME + ) { $expireTime = $metadataArray[CookieMetadata::KEY_DURATION] + time(); } else { $expireTime = PhpCookieManager::EXPIRE_AT_END_OF_SESSION_TIME; diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/PhpCookieManagerTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/PhpCookieManagerTest.php index b3cb5f2aeddceee4a6a237116f8419c214d5b623..43a7a7cee16dfcd7160687de30b14a09197a1710 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/PhpCookieManagerTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/PhpCookieManagerTest.php @@ -36,6 +36,7 @@ namespace Magento\Framework\Stdlib\Test\Unit\Cookie const PUBLIC_COOKIE_NAME_DEFAULT_VALUES = 'public_cookie_name_default_values'; const PUBLIC_COOKIE_NAME_SOME_FIELDS_SET = 'public_cookie_name_some_fields_set'; const MAX_COOKIE_SIZE_TEST_NAME = 'max_cookie_size_test_name'; + const PUBLIC_COOKIE_ZERO_DURATION = 'public_cookie_zero_duration'; const MAX_NUM_COOKIE_TEST_NAME = 'max_num_cookie_test_name'; const DELETE_COOKIE_NAME = 'delete_cookie_name'; const DELETE_COOKIE_NAME_NO_METADATA = 'delete_cookie_name_no_metadata'; @@ -66,6 +67,7 @@ namespace Magento\Framework\Stdlib\Test\Unit\Cookie self::PUBLIC_COOKIE_NAME_DEFAULT_VALUES => 'self::assertPublicCookieWithDefaultValues', self::PUBLIC_COOKIE_NAME_SOME_FIELDS_SET => 'self::assertPublicCookieWithSomeFieldSet', self::MAX_COOKIE_SIZE_TEST_NAME => 'self::assertCookieSize', + self::PUBLIC_COOKIE_ZERO_DURATION => 'self::assertZeroDuration', ]; /** @@ -405,6 +407,38 @@ namespace Magento\Framework\Stdlib\Test\Unit\Cookie $this->assertTrue(self::$isSetCookieInvoked); } + public function testSetPublicCookieZeroDuration() + { + /** @var PublicCookieMetadata $publicCookieMetadata */ + $publicCookieMetadata = $this->objectManager->getObject( + \Magento\Framework\Stdlib\Cookie\PublicCookieMetadata::class, + [ + 'metadata' => [ + 'domain' => null, + 'path' => null, + 'secure' => false, + 'http_only' => false, + 'duration' => 0, + ], + ] + ); + + $this->scopeMock->expects($this->once()) + ->method('getPublicCookieMetadata') + ->with($publicCookieMetadata) + ->will( + $this->returnValue($publicCookieMetadata) + ); + + $this->cookieManager->setPublicCookie( + self::PUBLIC_COOKIE_ZERO_DURATION, + 'cookie_value', + $publicCookieMetadata + ); + + $this->assertTrue(self::$isSetCookieInvoked); + } + public function testSetPublicCookieSomeFieldsSet() { self::$isSetCookieInvoked = false; @@ -839,6 +873,30 @@ namespace Magento\Framework\Stdlib\Test\Unit\Cookie self::assertEquals('', $path); } + /** + * Assert cookie set with zero duration + * + * Suppressing UnusedPrivateMethod, since PHPMD doesn't detect callback method use. + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + */ + private static function assertZeroDuration( + $name, + $value, + $expiry, + $path, + $domain, + $secure, + $httpOnly + ) { + self::assertEquals(self::PUBLIC_COOKIE_ZERO_DURATION, $name); + self::assertEquals(self::COOKIE_VALUE, $value); + self::assertEquals(self::COOKIE_EXPIRE_END_OF_SESSION, $expiry); + self::assertFalse($secure); + self::assertFalse($httpOnly); + self::assertEquals('', $domain); + self::assertEquals('', $path); + } + protected function stubGetCookie($get, $default, $return) { $this->readerMock->expects($this->atLeastOnce()) diff --git a/lib/internal/Magento/Framework/Url.php b/lib/internal/Magento/Framework/Url.php index d917391a4e95a270ceb8517b02de515f37f704e5..11f828370d441a5eeaf75d4563bd78e45efba57b 100644 --- a/lib/internal/Magento/Framework/Url.php +++ b/lib/internal/Magento/Framework/Url.php @@ -389,6 +389,9 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur protected function _isSecure() { if ($this->_request->isSecure()) { + if ($this->getRouteParamsResolver()->hasData('secure')) { + return (bool) $this->getRouteParamsResolver()->getData('secure'); + } return true; }