diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php index 783c78b0f8784cac5dd38dded247708bec394baa..4a312adfb6366089f024dd06a6e33c6ed65e2f14 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassDelete.php @@ -6,29 +6,69 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product; +use Magento\Catalog\Model\Resource\Product\Collection; +use Magento\Framework\Controller\ResultFactory; + class MassDelete extends \Magento\Catalog\Controller\Adminhtml\Product { + /** + * Field id + */ + const ID_FIELD = 'entity_id'; + + /** + * Redirect url + */ + const REDIRECT_URL = 'catalog/*/index'; + + /** + * Resource collection + * + * @var string + */ + protected $collection = 'Magento\Catalog\Model\Resource\Product\Collection'; + /** * @return \Magento\Backend\Model\View\Result\Redirect */ public function execute() { - $productIds = $this->getRequest()->getParam('selected'); - if (!is_array($productIds) || empty($productIds)) { - $this->messageManager->addError(__('Please select product(s).')); - } else { - try { - foreach ($productIds as $productId) { - $product = $this->_objectManager->get('Magento\Catalog\Model\Product')->load($productId); - $product->delete(); - } - $this->messageManager->addSuccess( - __('A total of %1 record(s) have been deleted.', count($productIds)) - ); - } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $selected = $this->getRequest()->getParam('selected'); + $excluded = $this->getRequest()->getParam('excluded'); + + $collection = $this->_objectManager->create($this->collection); + try { + if (!empty($excluded)) { + $collection->addFieldToFilter(static::ID_FIELD, ['nin' => $excluded]); + $this->massAction($collection); + } elseif (!empty($selected)) { + $collection->addFieldToFilter(static::ID_FIELD, ['in' => $selected]); + $this->massAction($collection); + } else { + $this->messageManager->addError(__('Please select product(s).')); } + } catch (\Exception $e) { + $this->messageManager->addError($e->getMessage()); + } + + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + return $resultRedirect->setPath(static::REDIRECT_URL); + } + + /** + * Cancel selected orders + * + * @param Collection $collection + * @return void + */ + protected function massAction($collection) + { + $count = 0; + foreach ($collection->getItems() as $product) { + $product->delete(); + ++$count; } - return $this->resultRedirectFactory->create()->setPath('catalog/*/index'); + $this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $count)); } } diff --git a/app/code/Magento/Catalog/Plugin/Model/Product/Action/UpdateAttributesFlushCache.php b/app/code/Magento/Catalog/Plugin/Model/Product/Action/UpdateAttributesFlushCache.php new file mode 100644 index 0000000000000000000000000000000000000000..22133ccc921a18fe7e62c4ef658390f2c75a2a91 --- /dev/null +++ b/app/code/Magento/Catalog/Plugin/Model/Product/Action/UpdateAttributesFlushCache.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Plugin\Model\Product\Action; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Action; +use Magento\Indexer\Model\CacheContext; +use Magento\Framework\Event\ManagerInterface as EventManager; + +class UpdateAttributesFlushCache +{ + /** + * @var CacheContext + */ + protected $cacheContext; + + /** + * @var EventManager + */ + protected $eventManager; + + /** + * @param CacheContext $cacheContext + * @param EventManager $eventManager + */ + public function __construct( + CacheContext $cacheContext, + EventManager $eventManager + ) { + $this->cacheContext = $cacheContext; + $this->eventManager = $eventManager; + } + + /** + * @param Action $subject + * @param \Closure $proceed + * @param array $productIds + * @param array $attrData + * @param int $storeId + * @return Action + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function aroundUpdateAttributes( + Action $subject, + \Closure $proceed, + $productIds, + $attrData, + $storeId + ) { + $returnValue = $proceed($productIds, $attrData, $storeId); + + $this->cacheContext->registerEntities(Product::CACHE_TAG, $productIds); + $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + + return $returnValue; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Plugin/Model/Product/Action/UpdateAttributesFlushCacheTest.php b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/Product/Action/UpdateAttributesFlushCacheTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3d70aac87798ac25f787812fa5ce1b0460ada648 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Plugin/Model/Product/Action/UpdateAttributesFlushCacheTest.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Plugin\Model\Product\Action; + +use Magento\Catalog\Model\Product; + +class UpdateAttributesFlushCacheTest extends \PHPUnit_Framework_TestCase +{ + public function testAroundUpdateAttributes() + { + $productIds = [1, 2, 3]; + $attrData = []; + $storeId = 1; + + $productActionMock = $this->getMock('Magento\Catalog\Model\Product\Action', [], [], '', false); + + $cacheContextMock = $this->getMock('Magento\Indexer\Model\CacheContext', [], [], '', false); + $cacheContextMock->expects($this->once()) + ->method('registerEntities') + ->with(Product::CACHE_TAG, $productIds); + + + $eventManagerMock = $this->getMock('Magento\Framework\Event\ManagerInterface'); + $eventManagerMock->expects($this->once()) + ->method('dispatch') + ->with('clean_cache_by_tags', ['object' => $cacheContextMock]); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $model = $objectManager->getObject( + 'Magento\Catalog\Plugin\Model\Product\Action\UpdateAttributesFlushCache', + [ + 'cacheContext' => $cacheContextMock, + 'eventManager' => $eventManagerMock, + ] + ); + + $closureMock = function () use ($productActionMock) { + return $productActionMock; + }; + + $model->aroundUpdateAttributes($productActionMock, $closureMock, $productIds, $attrData, $storeId); + } +} diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index dd16554ac015f2044bb7984990b9e8ca4f0301f5..5cf13196cbd26779ea6c7619a34636e9aef6163b 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -76,4 +76,7 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product\Action"> + <plugin name="invalidate_pagecache_after_update_product_attributes" type="Magento\Catalog\Plugin\Model\Product\Action\UpdateAttributesFlushCache"/> + </type> </config> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js b/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js index cb65d9dcee1c12cdb1eb76ba6cc20c6da3b5695e..b7a16ce3db853a02f454b58af0d72e6e6d32dc31 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js @@ -132,6 +132,11 @@ define([ at: 'center top', of: 'body' }, + create: function (event, ui) { + $(document).on('click', '#productGrid_massaction-form button', function () { + $('#import-custom-options-apply-button').trigger('click', 'massActionTrigger'); + }); + }, open: function () { $(this).closest('.ui-dialog').addClass('ui-dialog-active'); @@ -195,19 +200,14 @@ define([ }] }); importContainer.load( - this.options.productGridUrl, { - form_key: this.options.formKey - }, + this.options.productGridUrl, + {form_key: this.options.formKey}, function () { importContainer.dialog('open'); } ); }, - 'click #productGrid_massaction-form button': function () { - $('#import-custom-options-apply-button').trigger('click', 'massActionTrigger'); - }, - /** * Change custom option type */ @@ -470,4 +470,4 @@ define([ } }); -}); \ No newline at end of file +}); diff --git a/app/code/Magento/Catalog/view/adminhtml/web/product/product.css b/app/code/Magento/Catalog/view/adminhtml/web/product/product.css index e383de8ce411d5c38a5f1e8b9e59c7cc11ee2599..fce4af6f57f8f08d8ed05262f303fb170c6e870a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/product/product.css +++ b/app/code/Magento/Catalog/view/adminhtml/web/product/product.css @@ -280,13 +280,6 @@ position: static; } -/* Change Attribute Set */ -.admin__scope-old #product_info_tabs li.removed, -.admin__scope-old div.removed, -.admin__scope-old .field.removed { - display: none !important; -} - /* Custom Options -------------------------------------- */ diff --git a/app/code/Magento/Catalog/view/base/web/js/price-box.js b/app/code/Magento/Catalog/view/base/web/js/price-box.js index c2f0e23951cfbb1be1884418627bc1afaf69bcd4..4a5e3a2f8f0ab7cfdad559f8d366f528a4e94c14 100644 --- a/app/code/Magento/Catalog/view/base/web/js/price-box.js +++ b/app/code/Magento/Catalog/view/base/web/js/price-box.js @@ -97,11 +97,11 @@ define([ 'amount': 0, 'adjustments': {} }; - additionalPrice[priceCode].amount = parseInt(additionalPrice[priceCode].amount || 0, 10) + additionalPrice[priceCode].amount = 0 + (additionalPrice[priceCode].amount || 0) + priceValue.amount; _.each(priceValue.adjustments, function (adValue, adCode) { - additionalPrice[priceCode].adjustments[adCode] = - parseInt(additionalPrice[priceCode].adjustments[adCode] || 0, 10) + adValue; + additionalPrice[priceCode].adjustments[adCode] = 0 + + (additionalPrice[priceCode].adjustments[adCode] || 0) + adValue; }); }); }); @@ -117,9 +117,9 @@ define([ origin.adjustments = origin.adjustments || {}; final.adjustments = final.adjustments || {}; - final.amount = parseInt(origin.amount, 10) + option.amount; + final.amount = 0 + origin.amount + option.amount; _.each(option.adjustments, function (pa, paCode) { - final.adjustments[paCode] = parseInt(origin.adjustments[paCode] || 0, 10) + pa; + final.adjustments[paCode] = 0 + (origin.adjustments[paCode] || 0) + pa; }); }, this); } diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index b774dd8090d1474c4ebf777b83a1c9da7cca66f1..02898a8e87fff11dbfb6607fd39224bcdf9d3395 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -74,8 +74,8 @@ define([ $('body').trigger(self.options.processStop); } - if (res.redirect) { - window.location = res.redirect; + if (res.backUrl) { + window.location = res.backUrl; return; } if (res.messages) { diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php index 573997db6c7a9467fb9b258776e3b23000b495f4..b75aabfcb45a857de4da641b4a8a9d9f944f700b 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php @@ -8,6 +8,8 @@ namespace Magento\CatalogInventory\Model\Indexer\Stock; +use Magento\Catalog\Model\Category; + /** * Abstract action reindex class * @@ -52,19 +54,36 @@ abstract class AbstractAction */ protected $_isNeedUseIdxTable = false; + /** + * @var \Magento\Indexer\Model\CacheContext + */ + private $cacheContext; + + /** + * @var \Magento\Framework\Event\ManagerInterface + */ + private $eventManager; + + /** * @param \Magento\Framework\App\Resource $resource * @param \Magento\CatalogInventory\Model\Resource\Indexer\StockFactory $indexerFactory * @param \Magento\Catalog\Model\Product\Type $catalogProductType + * @param \Magento\Indexer\Model\CacheContext $cacheContext + * @param \Magento\Framework\Event\ManagerInterface $eventManager */ public function __construct( \Magento\Framework\App\Resource $resource, \Magento\CatalogInventory\Model\Resource\Indexer\StockFactory $indexerFactory, - \Magento\Catalog\Model\Product\Type $catalogProductType + \Magento\Catalog\Model\Product\Type $catalogProductType, + \Magento\Indexer\Model\CacheContext $cacheContext, + \Magento\Framework\Event\ManagerInterface $eventManager ) { $this->_resource = $resource; $this->_indexerFactory = $indexerFactory; $this->_catalogProductType = $catalogProductType; + $this->cacheContext = $cacheContext; + $this->eventManager = $eventManager; } /** @@ -228,6 +247,16 @@ abstract class AbstractAction } } + $select = $adapter->select() + ->distinct(true) + ->from($this->_getTable('catalog_category_product'), ['category_id']) + ->where('product_id IN(?)', $processIds); + + $affectedCategories = $adapter->fetchCol($select); + $this->cacheContext->registerEntities(Category::CACHE_TAG, $affectedCategories); + + $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + return $this; } diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Indexer/Stock/Action/FullTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Indexer/Stock/Action/FullTest.php index a096f71925e83c08696139a9d64d5abed10ec239..af4fb3bba4dabb88fe5099709a28ce1cf38baa4e 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Indexer/Stock/Action/FullTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Indexer/Stock/Action/FullTest.php @@ -25,20 +25,23 @@ class FullTest extends \PHPUnit_Framework_TestCase $adapterMock = $this->getMock('Magento\Framework\DB\Adapter\AdapterInterface'); $exceptionMessage = 'exception message'; - $exception = new \Exception($exceptionMessage); $adapterMock->expects($this->once()) ->method('delete') - ->will($this->throwException($exception)); + ->will($this->throwException(new \Exception($exceptionMessage))); $resourceMock->expects($this->any()) ->method('getConnection') ->will($this->returnValue($adapterMock)); - $model = new \Magento\CatalogInventory\Model\Indexer\Stock\Action\Full( - $resourceMock, - $indexerFactoryMock, - $productTypeMock + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $model = $objectManager->getObject( + 'Magento\CatalogInventory\Model\Indexer\Stock\Action\Full', + [ + 'resource' => $resourceMock, + 'indexerFactory' => $indexerFactoryMock, + 'catalogProductType' => $productTypeMock, + ] ); $this->setExpectedException('\Magento\Framework\Exception\LocalizedException', $exceptionMessage); diff --git a/app/code/Magento/Checkout/Controller/Cart/Add.php b/app/code/Magento/Checkout/Controller/Cart/Add.php index 97c2a346efc987f653bfec9d76659ebc9b080824..05bafe1daa1ae6d614fadff393de49607e1194ba 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Add.php +++ b/app/code/Magento/Checkout/Controller/Cart/Add.php @@ -169,7 +169,7 @@ class Add extends \Magento\Checkout\Controller\Cart $result = []; if ($backUrl || $backUrl = $this->getBackUrl()) { - $result['redirect'] = $backUrl; + $result['backUrl'] = $backUrl; } else { if ($product && !$product->getIsSalable()) { $result['product'] = [ diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js index 3fc839a52be45cbd2cf3aa9e67fa1be27c052875..2ab04102c3dc2e348619af7f728e6f1eff5c6181 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js +++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js @@ -134,7 +134,7 @@ define([ var sections = sectionConfig.getAffectedSections(settings.url); if (sections) { customerData.invalidate(sections); - var redirects = ['redirect']; + var redirects = ['redirect', 'backUrl']; if (_.isObject(xhr.responseJSON) && !_.isEmpty(_.pick(xhr.responseJSON, redirects))) { return ; } diff --git a/app/code/Magento/Ui/etc/di.xml b/app/code/Magento/Ui/etc/di.xml index d81fb2c0bd00a7d2681ffdcb66148924494922bd..df69d1edebb07d1c3c082f2b6fd0f8920fea7c9d 100644 --- a/app/code/Magento/Ui/etc/di.xml +++ b/app/code/Magento/Ui/etc/di.xml @@ -180,7 +180,7 @@ <type name="Magento\Framework\Data\Argument\Interpreter\Composite"> <arguments> <argument name="interpreters" xsi:type="array"> - <item name="object" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Object</item> + <item name="object" xsi:type="object">configurableObjectArgumentInterpreterProxy</item> <item name="configurableObject" xsi:type="object">configurableObjectArgumentInterpreterProxy</item> <item name="array" xsi:type="object">arrayArgumentInterpreterProxy</item> <item name="boolean" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Boolean</item> diff --git a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less index bfdae677b798e4b9b90e54c00a62ee0d84bc1e2b..8fdf8ecbf2e8750384aa9f3b0c322ef90e17ea7b 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less @@ -10,3 +10,10 @@ } } } + +/* Change Attribute Set */ +#product_info_tabs li.removed, +div.removed, +.field.removed { + display: none !important; +} diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php index f2d2779be11166b93310f688199c584c3b63c44f..78e4e7a15ff84cd5a89547d2ffa6ede3d2a48cce 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php @@ -40,17 +40,22 @@ class ConfigurableObject implements InterpreterInterface */ public function evaluate(array $data) { - if (!isset($data['argument'])) { - throw new \InvalidArgumentException('Node "argument" required for this type.'); + if (isset($data['value'])) { + $className = $data['value']; + $arguments = []; + } else { + if (!isset($data['argument'])) { + throw new \InvalidArgumentException('Node "argument" required for this type.'); + } + foreach ($data['argument'] as $name => $argument) { + $arguments[$name] = $this->argumentInterpreter->evaluate($argument); + } + if (!isset($arguments['class'])) { + throw new \InvalidArgumentException('Node "argument" with name "class" is required for this type.'); + } + $className = $arguments['class']; + unset($arguments['class']); } - foreach ($data['argument'] as $name => $argument) { - $arguments[$name] = $this->argumentInterpreter->evaluate($argument); - } - if (!isset($arguments['class'])) { - throw new \InvalidArgumentException('Node "argument" with name "class" is required for this type.'); - } - $className = $arguments['class']; - unset($arguments['class']); return $this->objectManager->create($className, $arguments); }