diff --git a/.htaccess.sample b/.htaccess.sample index 8e6e702ced716..3c412725f2134 100644 --- a/.htaccess.sample +++ b/.htaccess.sample @@ -111,7 +111,8 @@ ############################################ ## enable rewrites - Options +FollowSymLinks + # The following line has better security but add some performance overhead - see https://httpd.apache.org/docs/2.4/en/misc/perf-tuning.html + Options -FollowSymLinks +SymLinksIfOwnerMatch RewriteEngine on ############################################ diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index e05681212e84f..0a736890e8fe8 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -5,19 +5,18 @@ */ namespace Magento\CatalogImportExport\Model\Import; +use Magento\Catalog\Model\Config as CatalogConfig; use Magento\Catalog\Model\Product\Visibility; use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Filesystem; use Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor; use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface; use Magento\Framework\Stdlib\DateTime; -use Magento\Framework\Filesystem; use Magento\ImportExport\Model\Import; use Magento\ImportExport\Model\Import\Entity\AbstractEntity; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; -use Magento\Catalog\Model\Config as CatalogConfig; /** * Import entity product model @@ -1298,20 +1297,15 @@ protected function _saveLinks() */ protected function _saveProductAttributes(array $attributesData) { + $linkField = $this->getProductEntityLinkField(); foreach ($attributesData as $tableName => $skuData) { $tableData = []; foreach ($skuData as $sku => $attributes) { - $linkId = $this->_connection->fetchOne( - $this->_connection->select() - ->from($this->getResource()->getTable('catalog_product_entity')) - ->where('sku = ?', (string)$sku) - ->columns($this->getProductEntityLinkField()) - ); - + $linkId = $this->_oldSku[strtolower($sku)][$linkField]; foreach ($attributes as $attributeId => $storeValues) { foreach ($storeValues as $storeId => $storeValue) { $tableData[] = [ - $this->getProductEntityLinkField() => $linkId, + $linkField => $linkId, 'attribute_id' => $attributeId, 'store_id' => $storeId, 'value' => $storeValue, @@ -1321,6 +1315,7 @@ protected function _saveProductAttributes(array $attributesData) } $this->_connection->insertOnDuplicate($tableName, $tableData, ['value']); } + return $this; } diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index 048a9f88f87a2..428407912725e 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -6,7 +6,6 @@ namespace Magento\CatalogImportExport\Test\Unit\Model\Import; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\Stdlib\DateTime; use Magento\ImportExport\Model\Import; /** @@ -513,25 +512,6 @@ public function testSaveProductAttributes() ] ] ]; - $entityTable = 'catalog_product_entity'; - $resource = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModel::class) - ->disableOriginalConstructor() - ->setMethods(['getTable']) - ->getMock(); - $resource->expects($this->once())->method('getTable')->with($entityTable)->willReturnArgument(0); - $this->_resourceFactory->expects($this->once())->method('create')->willReturn($resource); - $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) - ->disableOriginalConstructor() - ->getMock(); - $selectMock->expects($this->once())->method('from')->with($entityTable, '*', null)->willReturnSelf(); - $selectMock->expects($this->once())->method('where')->with('sku = ?', $testSku)->willReturnSelf(); - $selectMock->expects($this->once())->method('columns')->with('entity_id')->willReturnSelf(); - $this->_connection->expects($this->any())->method('fetchOne')->willReturn(self::ENTITY_ID); - $this->_connection->expects($this->any())->method('select')->willReturn($selectMock); - $this->_connection->expects($this->any()) - ->method('quoteInto') - ->willReturnCallback([$this, 'returnQuoteCallback']); - $tableData[] = [ 'entity_id' => self::ENTITY_ID, 'attribute_id' => $attributeId, @@ -541,6 +521,7 @@ public function testSaveProductAttributes() $this->_connection->expects($this->once()) ->method('insertOnDuplicate') ->with($testTable, $tableData, ['value']); + $this->setPropertyValue($this->importProduct, '_oldSku', [$testSku => ['entity_id' => self::ENTITY_ID]]); $object = $this->invokeMethod($this->importProduct, '_saveProductAttributes', [$attributesData]); $this->assertEquals($this->importProduct, $object); } diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 360a11cf2748f..e6345af40f37a 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -1296,7 +1296,7 @@ private function loadUsedProducts(\Magento\Catalog\Model\Product $product, $cach if ($salableOnly) { $collection = $this->salableProcessor->process($collection); } - $usedProducts = $collection->getItems(); + $usedProducts = array_values($collection->getItems()); $this->saveUsedProductsCacheData($product, $usedProducts, $cacheKey); } $product->setData($dataFieldName, $usedProducts); diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php index bee4479526037..016dca2fa526c 100644 --- a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php +++ b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php @@ -270,13 +270,29 @@ protected function _saveCustomerEntities(array $entitiesToCreate, array $entitie $this->_connection->insertOnDuplicate( $this->_entityTable, $entitiesToUpdate, - $this->customerFields + $this->getCustomerEntityFieldsToUpdate($entitiesToUpdate) ); } return $this; } + /** + * Filter the entity that are being updated so we only change fields found in the importer file + * + * @param array $entitiesToUpdate + * @return array + */ + private function getCustomerEntityFieldsToUpdate(array $entitiesToUpdate): array + { + $firstCustomer = reset($entitiesToUpdate); + $columnsToUpdate = array_keys($firstCustomer); + $customerFieldsToUpdate = array_filter($this->customerFields, function ($field) use ($columnsToUpdate) { + return in_array($field, $columnsToUpdate); + }); + return $customerFieldsToUpdate; + } + /** * Save customer attributes. * diff --git a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php index c4818c38cd9c6..845ed0429d2c3 100644 --- a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php +++ b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php @@ -28,6 +28,19 @@ public function addCustomParameter($param, $value) return false; } + /** + * Wrapper for 'newrelic_notice_error' function + * + * @param Exception $exception + * @return void + */ + public function reportError($exception) + { + if (extension_loaded('newrelic')) { + newrelic_notice_error($exception->getMessage(), $exception); + } + } + /** * Checks whether newrelic-php5 agent is installed * diff --git a/app/code/Magento/NewRelicReporting/Model/Observer/ReportApplicationHandledExceptionToNewRelic.php b/app/code/Magento/NewRelicReporting/Model/Observer/ReportApplicationHandledExceptionToNewRelic.php new file mode 100644 index 0000000000000..724a488570207 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Model/Observer/ReportApplicationHandledExceptionToNewRelic.php @@ -0,0 +1,47 @@ +config = $config; + $this->newRelicWrapper = $newRelicWrapper; + } + + public function execute(Observer $observer) + { + if ($this->config->isNewRelicEnabled()) { + $exception = $observer->getEvent()->getException(); + $this->newRelicWrapper->reportError($exception); + } + } +} diff --git a/app/code/Magento/NewRelicReporting/Plugin/HttpPlugin.php b/app/code/Magento/NewRelicReporting/Plugin/HttpPlugin.php new file mode 100644 index 0000000000000..a37d93329d43a --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Plugin/HttpPlugin.php @@ -0,0 +1,53 @@ +config = $config; + $this->newRelicWrapper = $newRelicWrapper; + } + + /** + * Report exception to New Relic + * + * @param Http $subject + * @param Bootstrap $bootstrap + * @param \Exception $exception + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeCatchException(Http $subject, Bootstrap $bootstrap, \Exception $exception) + { + if ($this->config->isNewRelicEnabled()) { + $this->newRelicWrapper->reportError($exception); + } + } +} diff --git a/app/code/Magento/NewRelicReporting/etc/di.xml b/app/code/Magento/NewRelicReporting/etc/di.xml index a0d06105dd3fe..cba92f91cd4bb 100644 --- a/app/code/Magento/NewRelicReporting/etc/di.xml +++ b/app/code/Magento/NewRelicReporting/etc/di.xml @@ -27,4 +27,7 @@ + + + diff --git a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php index 48a87bf77cf94..158455db26455 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php @@ -363,12 +363,11 @@ public function setStoreIds($storeIds) public function getCurrentCurrencyCode() { if ($this->_currentCurrencyCode === null) { - $this->_currentCurrencyCode = count( - $this->_storeIds - ) > 0 ? $this->_storeManager->getStore( - array_shift($this->_storeIds) - )->getBaseCurrencyCode() : $this->_storeManager->getStore()->getBaseCurrencyCode(); + $this->_currentCurrencyCode = count($this->_storeIds) > 0 + ? $this->_storeManager->getStore(array_shift($this->_storeIds))->getCurrentCurrencyCode() + : $this->_storeManager->getStore()->getBaseCurrencyCode(); } + return $this->_currentCurrencyCode; } diff --git a/app/code/Magento/Reports/Model/ResourceModel/Quote/Item/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Quote/Item/Collection.php index e9dbfdae7a9a5..d219aefe81d45 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Quote/Item/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Quote/Item/Collection.php @@ -192,7 +192,7 @@ protected function getProductData(array $productIds) . ' AND product_name.attribute_id = ' . $productAttrNameId . ' AND product_name.store_id = ' . \Magento\Store\Model\Store::DEFAULT_STORE_ID, ['name' => 'product_name.value'] - )->joinInner( + )->joinLeft( ['product_price' => $productAttrPrice->getBackend()->getTable()], "product_price.{$linkField} = main_table.{$linkField}" ." AND product_price.attribute_id = {$productAttrPriceId}", diff --git a/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Grid/AbstractGridTest.php b/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Grid/AbstractGridTest.php new file mode 100644 index 0000000000000..dc16928861b1c --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Grid/AbstractGridTest.php @@ -0,0 +1,91 @@ +storeManagerMock = $this->getMockForAbstractClass( + \Magento\Store\Model\StoreManagerInterface::class, + [], + '', + true, + true, + true, + ['getStore'] + ); + + $this->model = $objectManager->getObject( + \Magento\Reports\Block\Adminhtml\Grid\AbstractGrid::class, + ['_storeManager' => $this->storeManagerMock] + ); + } + + /** + * @param $storeIds + * + * @dataProvider getCurrentCurrencyCodeDataProvider + */ + public function testGetCurrentCurrencyCode($storeIds) + { + $storeMock = $this->getMockForAbstractClass( + \Magento\Store\Api\Data\StoreInterface::class, + [], + '', + true, + true, + true, + ['getBaseCurrencyCode', 'getCurrentCurrencyCode'] + ); + + $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($storeMock); + + $this->model->setStoreIds($storeIds); + + if ($storeIds) { + $storeMock->expects($this->once())->method('getCurrentCurrencyCode')->willReturn('EUR'); + $expectedCurrencyCode = 'EUR'; + } else { + $storeMock->expects($this->once())->method('getBaseCurrencyCode')->willReturn('USD'); + $expectedCurrencyCode = 'USD'; + } + + $currencyCode = $this->model->getCurrentCurrencyCode(); + $this->assertEquals($expectedCurrencyCode, $currencyCode); + } + + /** + * DataProvider for testGetCurrentCurrencyCode. + * + * @return array + */ + public function getCurrentCurrencyCodeDataProvider() + { + return [ + [[]], + [[2]], + ]; + } +} diff --git a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Report/Quote/CollectionTest.php b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Report/Quote/CollectionTest.php index 051dc3f5f5593..7bb866287d37a 100644 --- a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Report/Quote/CollectionTest.php +++ b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Report/Quote/CollectionTest.php @@ -124,7 +124,8 @@ public function testLoadWithFilter() $this->selectMock->expects($this->once())->method('reset')->willReturnSelf(); $this->selectMock->expects($this->once())->method('from')->willReturnSelf(); $this->selectMock->expects($this->once())->method('useStraightJoin')->willReturnSelf(); - $this->selectMock->expects($this->exactly(2))->method('joinInner')->willReturnSelf(); + $this->selectMock->expects($this->once())->method('joinInner')->willReturnSelf(); + $this->selectMock->expects($this->once())->method('joinLeft')->willReturnSelf(); $collection->expects($this->once())->method('getOrdersData')->willReturn([]); $productAttributeMock->expects($this->once())->method('getBackend')->willReturnSelf(); $priceAttributeMock->expects($this->once())->method('getBackend')->willReturnSelf(); diff --git a/app/code/Magento/Search/Helper/Data.php b/app/code/Magento/Search/Helper/Data.php index f3ad8a39de00e..e813d7342761f 100644 --- a/app/code/Magento/Search/Helper/Data.php +++ b/app/code/Magento/Search/Helper/Data.php @@ -124,7 +124,7 @@ public function getSuggestUrl() { return $this->_getUrl( 'search/ajax/suggest', - ['_secure' => $this->storeManager->getStore()->isCurrentlySecure()] + ['_secure' => $this->_getRequest()->isSecure()] ); } diff --git a/app/code/Magento/Search/Test/Unit/Helper/DataTest.php b/app/code/Magento/Search/Test/Unit/Helper/DataTest.php index 5cfde7e8efda9..291362734feff 100644 --- a/app/code/Magento/Search/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Search/Test/Unit/Helper/DataTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Search\Test\Unit\Helper; /** @@ -43,6 +44,11 @@ class DataTest extends \PHPUnit\Framework\TestCase */ protected $storeManagerMock; + /** + * @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlBuilderMock; + protected function setUp() { $this->stringMock = $this->createMock(\Magento\Framework\Stdlib\StringUtils::class); @@ -53,9 +59,14 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods([]) ->getMock(); + $this->urlBuilderMock = $this->getMockBuilder(\Magento\Framework\UrlInterface::class) + ->setMethods(['getUrl']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); $this->contextMock = $this->createMock(\Magento\Framework\App\Helper\Context::class); $this->contextMock->expects($this->any())->method('getScopeConfig')->willReturn($this->scopeConfigMock); $this->contextMock->expects($this->any())->method('getRequest')->willReturn($this->requestMock); + $this->contextMock->expects($this->any())->method('getUrlBuilder')->willReturn($this->urlBuilderMock); $this->model = new \Magento\Search\Helper\Data( $this->contextMock, @@ -126,4 +137,39 @@ public function queryTextDataProvider() ['testtest', 7, 'testtes'], ]; } + + /** + * Test getSuggestUrl() take into consideration type of request(secure, non-secure). + * + * @dataProvider getSuggestUrlDataProvider + * @param bool $isSecure + * @return void + */ + public function testGetSuggestUrl(bool $isSecure) + { + $this->requestMock->expects(self::once()) + ->method('isSecure') + ->willReturn($isSecure); + $this->urlBuilderMock->expects(self::once()) + ->method('getUrl') + ->with(self::identicalTo('search/ajax/suggest'), self::identicalTo(['_secure' => $isSecure])); + $this->model->getSuggestUrl(); + } + + /** + * Provide test data for testGetSuggestUrl() test. + * + * @return array + */ + public function getSuggestUrlDataProvider() + { + return [ + 'non-secure' => [ + 'isSecure' => false, + ], + 'secure' => [ + 'secure' => true, + ], + ]; + } } diff --git a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml index c4297b404717c..2ea87be13d5e3 100644 --- a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml +++ b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml @@ -9,7 +9,7 @@ helper('Magento\Search\Helper\Data'); +$helper = $this->helper(\Magento\Search\Helper\Data::class); ?>