diff --git a/app/code/Magento/Authorizenet/Model/Directpost.php b/app/code/Magento/Authorizenet/Model/Directpost.php index d755cc71cce2b932f471e5148639b6e2b355db58..c4261e9581b82c92fec3505aae4180a1fe30011f 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost.php +++ b/app/code/Magento/Authorizenet/Model/Directpost.php @@ -119,6 +119,11 @@ class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements Tra */ protected $transactionRepository; + /** + * @var \Psr\Log\LoggerInterface + */ + private $psrLogger; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -761,7 +766,7 @@ class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements Tra { try { $transactionId = $this->getResponse()->getXTransId(); - $data = $payment->getMethodInstance()->getTransactionDetails($transactionId); + $data = $this->transactionService->getTransactionDetails($this, $transactionId); $transactionStatus = (string)$data->transaction->transactionStatus; $fdsFilterAction = (string)$data->transaction->FDSFilterAction; @@ -779,6 +784,7 @@ class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements Tra $payment->getOrder()->addStatusHistoryComment($message); } } catch (\Exception $e) { + $this->getPsrLogger()->critical($e); //this request is optional } return $this; @@ -805,7 +811,7 @@ class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements Tra $order->registerCancellation($message)->save(); } catch (\Exception $e) { //quiet decline - $this->logger->critical($e); + $this->getPsrLogger()->critical($e); } } @@ -973,4 +979,18 @@ class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements Tra return $response; } + + /** + * @return \Psr\Log\LoggerInterface + * + * @deprecated + */ + private function getPsrLogger() + { + if (null === $this->psrLogger) { + $this->psrLogger = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Psr\Log\LoggerInterface::class); + } + return $this->psrLogger; + } } diff --git a/app/code/Magento/Backend/App/BackendAppList.php b/app/code/Magento/Backend/App/BackendAppList.php index 224ce9893100c6b416da0362ca9a0e3af569bf1e..afb812b823488b29898a2027afa87b6980d9974c 100644 --- a/app/code/Magento/Backend/App/BackendAppList.php +++ b/app/code/Magento/Backend/App/BackendAppList.php @@ -44,6 +44,7 @@ class BackendAppList if ($appName && isset($this->backendApps[$appName])) { return $this->backendApps[$appName]; } + return null; } /** diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php index 0013ba1f17f1bb404640c5da4b3f8ddb2d77d632..86ed5c6a10766bb8a3f475e9b2bf70e0d4c9e279 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php @@ -59,33 +59,27 @@ class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\ "bo.parent_id = product.$linkField", [] ); - $this->_addWebsiteJoinToSelect($select, false); $status = new \Zend_Db_Expr( 'MAX(' . $connection->getCheckSql('e.required_options = 0', 'i.stock_status', '0') . ')' ); - $select->columns( - 'website_id', - 'cw' - )->join( + $select->join( ['cis' => $this->getTable('cataloginventory_stock')], '', - ['stock_id'] + ['website_id', 'stock_id'] )->joinLeft( ['bs' => $this->getTable('catalog_product_bundle_selection')], 'bs.option_id = bo.option_id', [] )->joinLeft( ['i' => $idxTable], - 'i.product_id = bs.product_id AND i.website_id = cw.website_id AND i.stock_id = cis.stock_id', + 'i.product_id = bs.product_id AND i.website_id = cis.website_id AND i.stock_id = cis.stock_id', [] )->joinLeft( ['e' => $this->getTable('catalog_product_entity')], 'e.entity_id = bs.product_id', [] - )->where( - 'cw.website_id != 0' )->group( - ['product.entity_id', 'cw.website_id', 'cis.stock_id', 'bo.option_id'] + ['product.entity_id', 'cis.website_id', 'cis.stock_id', 'bo.option_id'] )->columns( ['option_id' => 'bo.option_id', 'status' => $status] ); @@ -118,45 +112,19 @@ class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = false) { $this->_prepareBundleOptionStockData($entityIds, $usePrimaryTable); - $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); $connection = $this->getConnection(); - $select = $connection->select()->from( - ['e' => $this->getTable('catalog_product_entity')], - ['entity_id'] - ); - $this->_addWebsiteJoinToSelect($select, true); - $this->_addProductWebsiteJoinToSelect($select, 'cw.website_id', 'e.entity_id'); - $select->columns( - 'cw.website_id' - )->join( - ['cis' => $this->getTable('cataloginventory_stock')], - '', - ['stock_id'] - )->joinLeft( - ['cisi' => $this->getTable('cataloginventory_stock_item')], - 'cisi.stock_id = cis.stock_id AND cisi.product_id = e.entity_id', - [] + $select = parent::_getStockStatusSelect($entityIds, $usePrimaryTable); + $select->reset( + \Magento\Framework\DB\Select::COLUMNS + )->columns( + ['e.entity_id', 'cis.website_id', 'cis.stock_id'] )->joinLeft( ['o' => $this->_getBundleOptionTable()], - 'o.entity_id = e.entity_id AND o.website_id = cw.website_id AND o.stock_id = cis.stock_id', + 'o.entity_id = e.entity_id AND o.website_id = cis.website_id AND o.stock_id = cis.stock_id', [] )->columns( ['qty' => new \Zend_Db_Expr('0')] - )->where( - 'cw.website_id != 0' - )->where( - 'e.type_id = ?', - $this->getTypeId() - )->group( - ['e.entity_id', 'cw.website_id', 'cis.stock_id'] - ); - - // add limitation of status - $condition = $connection->quoteInto( - '=?', - \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED ); - $this->_addAttributeToSelect($select, 'status', "e.$linkField", 'cs.store_id', $condition); $statusExpr = $this->getStatusExpression($connection); $select->columns( diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php index d8ac9fdf9131d8dbab66cde7181dfa23b28ec63c..0ec9ddf3298f9daec7ab9dfec22765ad95521919 100644 --- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php +++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php @@ -458,7 +458,8 @@ class BundlePanel extends AbstractModifier 'component' => 'Magento_Bundle/js/components/bundle-input-type', 'parentContainer' => 'product_bundle_container', 'selections' => 'bundle_selections', - 'targetIndex' => 'is_default', + 'isDefaultIndex' => 'is_default', + 'userDefinedIndex' => 'selection_can_change_qty', 'dataScope' => 'type', 'label' => __('Input Type'), 'sortOrder' => 20, diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-input-type.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-input-type.js index e52856e53891c64981c9e617afb01c77b453363a..14dd426ed02aa7f144c109a0ac17016be2d56329 100644 --- a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-input-type.js +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-input-type.js @@ -26,36 +26,42 @@ define([ if (type !== this.previousType) { this.previousType = type; - - if (type === 'radio') { - this.clearValues(); - } + this.processSelections(type === 'radio'); } this._super(); }, /** - * Clears values in components like this. + * Toggle 'User Defined' column and clears values + * @param {Boolean} isRadio */ - clearValues: function () { + processSelections: function (isRadio) { var records = registry.get(this.retrieveParentName(this.parentContainer) + '.' + this.selections), checkedFound = false; records.elems.each(function (record) { record.elems.filter(function (comp) { - return comp.index === this.targetIndex; + return comp.index === this.userDefinedIndex; }, this).each(function (comp) { - if (comp.checked()) { - if (checkedFound) { - comp.clearing = true; - comp.clear(); - comp.clearing = false; - } - - checkedFound = true; - } + comp.visible(isRadio); }); + + if (isRadio) { + record.elems.filter(function (comp) { + return comp.index === this.isDefaultIndex; + }, this).each(function (comp) { + if (comp.checked()) { + if (checkedFound) { + comp.clearing = true; + comp.clear(); + comp.clearing = false; + } + + checkedFound = true; + } + }); + } }, this); }, diff --git a/app/code/Magento/CacheInvalidate/etc/events.xml b/app/code/Magento/CacheInvalidate/etc/events.xml index 47d27f98f1e796419333e74dc467a4a7628468b6..58ddeb64c9257d70788d9c63dbfbbc77099dea4f 100644 --- a/app/code/Magento/CacheInvalidate/etc/events.xml +++ b/app/code/Magento/CacheInvalidate/etc/events.xml @@ -48,4 +48,7 @@ <event name="controller_action_postdispatch_adminhtml_system_currencysymbol_save"> <observer name="flush_varnish_pagecache" instance="Magento\CacheInvalidate\Observer\InvalidateVarnishObserver"/> </event> + <event name="clean_cache_after_reindex"> + <observer name="flush_varnish_pagecache" instance="Magento\CacheInvalidate\Observer\InvalidateVarnishObserver"/> + </event> </config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/AffectCache.php b/app/code/Magento/Catalog/Model/Indexer/Category/AffectCache.php deleted file mode 100644 index 098e636b8655cd07053490f3b454c1507ef73f17..0000000000000000000000000000000000000000 --- a/app/code/Magento/Catalog/Model/Indexer/Category/AffectCache.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Catalog\Model\Indexer\Category; - -/** - * Class AffectCache - */ -class AffectCache -{ - /** - * @var \Magento\Framework\Indexer\CacheContext - */ - protected $context; - - /** - * @param \Magento\Framework\Indexer\CacheContext $context - */ - public function __construct( - \Magento\Framework\Indexer\CacheContext $context - ) { - $this->context = $context; - } - - /** - * @param \Magento\Framework\Indexer\ActionInterface $subject - * @param array $ids - * @return array - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function beforeExecute(\Magento\Framework\Indexer\ActionInterface $subject, $ids) - { - $this->context->registerEntities(\Magento\Catalog\Model\Category::CACHE_TAG, $ids); - return [$ids]; - } -} diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Flat.php b/app/code/Magento/Catalog/Model/Indexer/Category/Flat.php index 2338a72c1aa7891d76daf25eaa507c8c7074dbb8..280f53fe70186970b6fb98e9cda4b18eb5f588d0 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Flat.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Flat.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Model\Indexer\Category; +use Magento\Framework\Indexer\CacheContext; + class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface { /** @@ -17,9 +19,16 @@ class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Frame */ protected $rowsActionFactory; - /** @var \Magento\Framework\Indexer\IndexerRegistry */ + /** + * @var \Magento\Framework\Indexer\IndexerRegistry + */ protected $indexerRegistry; + /** + * @var \Magento\Framework\Indexer\CacheContext + */ + private $cacheContext; + /** * @param Flat\Action\FullFactory $fullActionFactory * @param Flat\Action\RowsFactory $rowsActionFactory @@ -54,6 +63,7 @@ class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Frame $action->reindex($ids, true); } $action->reindex($ids); + $this->getCacheContext()->registerEntities(\Magento\Catalog\Model\Category::CACHE_TAG, $ids); } /** @@ -64,6 +74,7 @@ class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Frame public function executeFull() { $this->fullActionFactory->create()->reindexAll(); + $this->getCacheContext()->registerTags([\Magento\Catalog\Model\Category::CACHE_TAG]); } /** @@ -87,4 +98,19 @@ class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Frame { $this->execute([$id]); } + + /** + * Get cache context + * + * @return \Magento\Framework\Indexer\CacheContext + * @deprecated + */ + protected function getCacheContext() + { + if (!($this->cacheContext instanceof CacheContext)) { + return \Magento\Framework\App\ObjectManager::getInstance()->get(CacheContext::class); + } else { + return $this->cacheContext; + } + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product.php index 44a24f4623d7fa008247246577548e182513da64..ffcca0d3dfb7a7b1fdb80cca772cacc8c3aba895 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Model\Indexer\Category; +use Magento\Framework\Indexer\CacheContext; + class Product implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface { /** @@ -22,9 +24,16 @@ class Product implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fr */ protected $rowsActionFactory; - /** @var \Magento\Framework\Indexer\IndexerRegistry */ + /** + * @var \Magento\Framework\Indexer\IndexerRegistry + */ protected $indexerRegistry; + /** + * @var \Magento\Framework\Indexer\CacheContext + */ + protected $cacheContext; + /** * @param Product\Action\FullFactory $fullActionFactory * @param Product\Action\RowsFactory $rowsActionFactory @@ -49,6 +58,18 @@ class Product implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fr public function execute($ids) { $this->executeAction($ids); + $this->registerEntities($ids); + } + + /** + * Add entities to cache context + * + * @param int[] $ids + * @return void + */ + protected function registerEntities($ids) + { + $this->getCacheContext()->registerEntities(\Magento\Catalog\Model\Category::CACHE_TAG, $ids); } /** @@ -59,6 +80,17 @@ class Product implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fr public function executeFull() { $this->fullActionFactory->create()->execute(); + $this->registerTags(); + } + + /** + * Add tags to cache context + * + * @return void + */ + protected function registerTags() + { + $this->getCacheContext()->registerTags([\Magento\Catalog\Model\Category::CACHE_TAG]); } /** @@ -103,4 +135,19 @@ class Product implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fr return $this; } + + /** + * Get cache context + * + * @return \Magento\Framework\Indexer\CacheContext + * @deprecated + */ + protected function getCacheContext() + { + if (!($this->cacheContext instanceof CacheContext)) { + return \Magento\Framework\App\ObjectManager::getInstance()->get(CacheContext::class); + } else { + return $this->cacheContext; + } + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/AffectCache.php b/app/code/Magento/Catalog/Model/Indexer/Product/AffectCache.php deleted file mode 100644 index 44a00e41e75459ff91be930671164083cedc1048..0000000000000000000000000000000000000000 --- a/app/code/Magento/Catalog/Model/Indexer/Product/AffectCache.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Catalog\Model\Indexer\Product; - -/** - * Class AffectCache - */ -class AffectCache -{ - /** - * @var \Magento\Framework\Indexer\CacheContext $context - */ - protected $context; - - /** - * @param \Magento\Framework\Indexer\CacheContext $context - */ - public function __construct( - \Magento\Framework\Indexer\CacheContext $context - ) { - $this->context = $context; - } - - /** - * @param \Magento\Framework\Indexer\ActionInterface $subject - * @param array $ids - * @return array - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function beforeExecute(\Magento\Framework\Indexer\ActionInterface $subject, $ids) - { - $this->context->registerEntities(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); - return [$ids]; - } -} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Category.php b/app/code/Magento/Catalog/Model/Indexer/Product/Category.php index 282591ba26b2ac17600cfa2a5dc5f222f66c3dd1..b272870b9f770e5de82cbb04d21d6c2320b7a8eb 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Category.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Category.php @@ -24,4 +24,30 @@ class Category extends \Magento\Catalog\Model\Indexer\Category\Product ) { parent::__construct($fullActionFactory, $rowsActionFactory, $indexerRegistry); } + + /** + * Add tags to cache context + * + * @return void + */ + protected function registerTags() + { + $this->getCacheContext()->registerTags( + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Catalog\Model\Product::CACHE_TAG + ] + ); + } + + /** + * Add entities to cache context + * + * @param int[] $ids + * @return void + */ + protected function registerEntities($ids) + { + $this->getCacheContext()->registerEntities(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav.php index a66637e8b3a88dd3ce05378cf7e689f9b932ee1d..0b3ccf1cc4c886bf40cd23c0013e2bb9dea77289 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Model\Indexer\Product; +use Magento\Framework\Indexer\CacheContext; + class Eav implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface { /** @@ -22,6 +24,11 @@ class Eav implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framew */ protected $_productEavIndexerFull; + /** + * @var \Magento\Framework\Indexer\CacheContext + */ + private $cacheContext; + /** * @param Eav\Action\Row $productEavIndexerRow * @param Eav\Action\Rows $productEavIndexerRows @@ -46,6 +53,7 @@ class Eav implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framew public function execute($ids) { $this->_productEavIndexerRows->execute($ids); + $this->getCacheContext()->registerEntities(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); } /** @@ -56,6 +64,12 @@ class Eav implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framew public function executeFull() { $this->_productEavIndexerFull->execute(); + $this->getCacheContext()->registerTags( + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Catalog\Model\Product::CACHE_TAG + ] + ); } /** @@ -79,4 +93,19 @@ class Eav implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framew { $this->_productEavIndexerRow->execute($id); } + + /** + * Get cache context + * + * @return \Magento\Framework\Indexer\CacheContext + * @deprecated + */ + protected function getCacheContext() + { + if (!($this->cacheContext instanceof CacheContext)) { + return \Magento\Framework\App\ObjectManager::getInstance()->get(CacheContext::class); + } else { + return $this->cacheContext; + } + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat.php index 47d525ede8c1f142c192fe270fb6a87057133639..e423d9553bcb39e3adb6bd8328311428297817e8 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Model\Indexer\Product; +use Magento\Framework\Indexer\CacheContext; + class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface { /** @@ -22,6 +24,11 @@ class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Frame */ protected $_productFlatIndexerFull; + /** + * @var \Magento\Framework\Indexer\CacheContext + */ + private $cacheContext; + /** * @param Flat\Action\Row $productFlatIndexerRow * @param Flat\Action\Rows $productFlatIndexerRows @@ -46,6 +53,7 @@ class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Frame public function execute($ids) { $this->_productFlatIndexerRows->execute($ids); + $this->getCacheContext()->registerEntities(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); } /** @@ -56,6 +64,12 @@ class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Frame public function executeFull() { $this->_productFlatIndexerFull->execute(); + $this->getCacheContext()->registerTags( + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Catalog\Model\Product::CACHE_TAG + ] + ); } /** @@ -79,4 +93,19 @@ class Flat implements \Magento\Framework\Indexer\ActionInterface, \Magento\Frame { $this->_productFlatIndexerRow->execute($id); } + + /** + * Get cache context + * + * @return \Magento\Framework\Indexer\CacheContext + * @deprecated + */ + protected function getCacheContext() + { + if (!($this->cacheContext instanceof CacheContext)) { + return \Magento\Framework\App\ObjectManager::getInstance()->get(CacheContext::class); + } else { + return $this->cacheContext; + } + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price.php index ea763354a70d7a24cc5c77403f6028563dc82159..f8ed6cb6d2fe4ce43b646e48fcee628c41e4f3ec 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Model\Indexer\Product; +use Magento\Framework\Indexer\CacheContext; + class Price implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface { /** @@ -22,6 +24,11 @@ class Price implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fram */ protected $_productPriceIndexerFull; + /** + * @var \Magento\Framework\Indexer\CacheContext + */ + private $cacheContext; + /** * @param Price\Action\Row $productPriceIndexerRow * @param Price\Action\Rows $productPriceIndexerRows @@ -46,6 +53,7 @@ class Price implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fram public function execute($ids) { $this->_productPriceIndexerRows->execute($ids); + $this->getCacheContext()->registerEntities(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); } /** @@ -56,6 +64,12 @@ class Price implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fram public function executeFull() { $this->_productPriceIndexerFull->execute(); + $this->getCacheContext()->registerTags( + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Catalog\Model\Product::CACHE_TAG + ] + ); } /** @@ -79,4 +93,19 @@ class Price implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fram { $this->_productPriceIndexerRow->execute($id); } + + /** + * Get cache context + * + * @return \Magento\Framework\Indexer\CacheContext + * @deprecated + */ + protected function getCacheContext() + { + if (!($this->cacheContext instanceof CacheContext)) { + return \Magento\Framework\App\ObjectManager::getInstance()->get(CacheContext::class); + } else { + return $this->cacheContext; + } + } } diff --git a/app/code/Magento/Catalog/Observer/AddCatalogToTopmenuItemsObserver.php b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php similarity index 74% rename from app/code/Magento/Catalog/Observer/AddCatalogToTopmenuItemsObserver.php rename to app/code/Magento/Catalog/Plugin/Block/Topmenu.php index 608c7ee74b7cdda6409432f968734f2d1171a088..098b3282a1b107e56703ee4889f9ce005f25d223 100644 --- a/app/code/Magento/Catalog/Observer/AddCatalogToTopmenuItemsObserver.php +++ b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php @@ -3,17 +3,16 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Catalog\Observer; +namespace Magento\Catalog\Plugin\Block; use Magento\Catalog\Model\Category; use Magento\Framework\Data\Collection; use Magento\Framework\Data\Tree\Node; -use Magento\Framework\Event\ObserverInterface; /** - * Observer that add Categories Tree to Topmenu + * Plugin for top menu block */ -class AddCatalogToTopmenuItemsObserver implements ObserverInterface +class Topmenu { /** * Catalog category @@ -38,11 +37,11 @@ class AddCatalogToTopmenuItemsObserver implements ObserverInterface private $layerResolver; /** + * Initialize dependencies. + * * @param \Magento\Catalog\Helper\Category $catalogCategory - * @param \Magento\Catalog\Model\Indexer\Category\Flat\State $categoryFlatState * @param \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Catalog\Helper\Category $catalogCategory * @param \Magento\Catalog\Model\Layer\Resolver $layerResolver */ public function __construct( @@ -58,26 +57,27 @@ class AddCatalogToTopmenuItemsObserver implements ObserverInterface } /** - * Checking whether the using static urls in WYSIWYG allowed event + * Build category tree for menu block. * - * @param \Magento\Framework\Event\Observer $observer + * @param \Magento\Theme\Block\Html\Topmenu $subject + * @param string $outermostClass + * @param string $childrenWrapClass + * @param int $limit * @return void + * @SuppressWarnings("PMD.UnusedFormalParameter") */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - $block = $observer->getEvent()->getBlock(); - $menuRootNode = $observer->getEvent()->getMenu(); - $block->addIdentity(Category::CACHE_TAG); - + public function beforeGetHtml( + \Magento\Theme\Block\Html\Topmenu $subject, + $outermostClass = '', + $childrenWrapClass = '', + $limit = 0 + ) { $rootId = $this->storeManager->getStore()->getRootCategoryId(); $storeId = $this->storeManager->getStore()->getId(); - /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */ $collection = $this->getCategoryTree($storeId, $rootId); - $currentCategory = $this->getCurrentCategory(); - - $mapping = [$rootId => $menuRootNode]; // use nodes stack to avoid recursion + $mapping = [$rootId => $subject->getMenu()]; // use nodes stack to avoid recursion foreach ($collection as $category) { if (!isset($mapping[$category->getParentId()])) { continue; @@ -94,8 +94,28 @@ class AddCatalogToTopmenuItemsObserver implements ObserverInterface $parentCategoryNode->addChild($categoryNode); $mapping[$category->getId()] = $categoryNode; //add node in stack + } + } - $block->addIdentity(Category::CACHE_TAG . '_' . $category->getId()); + /** + * Add list of associated identities to the top menu block for caching purposes. + * + * @param \Magento\Theme\Block\Html\Topmenu $subject + * @return void + */ + public function beforeGetIdentities(\Magento\Theme\Block\Html\Topmenu $subject) + { + $subject->addIdentity(Category::CACHE_TAG); + $rootId = $this->storeManager->getStore()->getRootCategoryId(); + $storeId = $this->storeManager->getStore()->getId(); + /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */ + $collection = $this->getCategoryTree($storeId, $rootId); + $mapping = [$rootId => $subject->getMenu()]; // use nodes stack to avoid recursion + foreach ($collection as $category) { + if (!isset($mapping[$category->getParentId()])) { + continue; + } + $subject->addIdentity(Category::CACHE_TAG . '_' . $category->getId()); } } diff --git a/app/code/Magento/Catalog/Setup/UpgradeData.php b/app/code/Magento/Catalog/Setup/UpgradeData.php index 2e05d5b0ac770bdeaacd056a52819a0c582ca1e5..542fea6770fab226e2bb7d22643d0dc74f66218e 100644 --- a/app/code/Magento/Catalog/Setup/UpgradeData.php +++ b/app/code/Magento/Catalog/Setup/UpgradeData.php @@ -257,6 +257,12 @@ class UpgradeData implements UpgradeDataInterface 'frontend_label', 'Small' ); + $categorySetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'image', + 'frontend_input_renderer', + null + ); //Design tab $categorySetup->updateAttribute( diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/AffectCacheTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/AffectCacheTest.php deleted file mode 100644 index d0d3cb3d2fa18f2d329437202eb99f2e88db97db..0000000000000000000000000000000000000000 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/AffectCacheTest.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -// @codingStandardsIgnoreFile - -namespace Magento\Catalog\Test\Unit\Model\Indexer\Category; - -class AffectCacheTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \Magento\Catalog\Model\Indexer\Category\AffectCache - */ - protected $plugin; - - /** - * @var \Magento\Framework\Indexer\CacheContext|\PHPUnit_Framework_MockObject_MockObject - */ - protected $contextMock; - - /** - * @var \Magento\Framework\Indexer\ActionInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $subjectMock; - - /** - * Set up - */ - protected function setUp() - { - $this->subjectMock = $this->getMockForAbstractClass('Magento\Framework\Indexer\ActionInterface', - [], '', false, true, true, []); - $this->contextMock = $this->getMock('Magento\Framework\Indexer\CacheContext', - [], [], '', false); - $this->plugin = new \Magento\Catalog\Model\Indexer\Category\AffectCache($this->contextMock); - } - - /** - * test beforeExecute - */ - public function testBeforeExecute() - { - $expectedIds = [5, 6, 7]; - $this->contextMock->expects($this->once()) - ->method('registerEntities') - ->with($this->equalTo(\Magento\Catalog\Model\Category::ENTITY), - $this->equalTo($expectedIds)) - ->will($this->returnValue($this->contextMock)); - $actualIds = $this->plugin->beforeExecute($this->subjectMock, $expectedIds); - $this->assertEquals([$expectedIds], $actualIds); - } -} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/FlatTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/FlatTest.php index 1f7edb65e9432ef02500ece8a2f4b57fb6da2718..0be219fdecd91f9b51436a06ca0bc1c990b8fb63 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/FlatTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/FlatTest.php @@ -32,6 +32,11 @@ class FlatTest extends \PHPUnit_Framework_TestCase */ protected $indexerRegistryMock; + /** + * @var \Magento\Framework\Indexer\CacheContext|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheContextMock; + protected function setUp() { $this->fullMock = $this->getMock( @@ -73,6 +78,15 @@ class FlatTest extends \PHPUnit_Framework_TestCase $this->rowsMock, $this->indexerRegistryMock ); + + $this->cacheContextMock = $this->getMock(\Magento\Framework\Indexer\CacheContext::class, [], [], '', false); + + $cacheContextProperty = new \ReflectionProperty( + \Magento\Catalog\Model\Indexer\Category\Flat::class, + 'cacheContext' + ); + $cacheContextProperty->setAccessible(true); + $cacheContextProperty->setValue($this->model, $this->cacheContextMock); } public function testExecuteWithIndexerInvalid() @@ -105,6 +119,10 @@ class FlatTest extends \PHPUnit_Framework_TestCase $this->rowsMock->expects($this->once())->method('create')->will($this->returnValue($rowMock)); + $this->cacheContextMock->expects($this->once()) + ->method('registerEntities') + ->with(\Magento\Catalog\Model\Category::CACHE_TAG, $ids); + $this->model->execute($ids); } @@ -127,6 +145,10 @@ class FlatTest extends \PHPUnit_Framework_TestCase $this->rowsMock->expects($this->once())->method('create')->will($this->returnValue($rowMock)); + $this->cacheContextMock->expects($this->once()) + ->method('registerEntities') + ->with(\Magento\Catalog\Model\Category::CACHE_TAG, $ids); + $this->model->execute($ids); } @@ -137,4 +159,25 @@ class FlatTest extends \PHPUnit_Framework_TestCase ->with(\Magento\Catalog\Model\Indexer\Category\Flat\State::INDEXER_ID) ->will($this->returnValue($this->indexerMock)); } + + public function testExecuteFull() + { + /** @var \Magento\Catalog\Model\Indexer\Category\Flat\Action\Full $categoryIndexerFlatFull */ + $categoryIndexerFlatFull = $this->getMock( + \Magento\Catalog\Model\Indexer\Category\Flat\Action\Full::class, + [], + [], + '', + false + ); + $this->fullMock->expects($this->once()) + ->method('create') + ->willReturn($categoryIndexerFlatFull); + $categoryIndexerFlatFull->expects($this->once()) + ->method('reindexAll'); + $this->cacheContextMock->expects($this->once()) + ->method('registerTags') + ->with([\Magento\Catalog\Model\Category::CACHE_TAG]); + $this->model->executeFull(); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/ProductTest.php index 72f53360b9a17279b83b2cac8c39112d46713511..2dd1d30500cd5c302b45f57432bf9ddf25dcd713 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/ProductTest.php @@ -32,6 +32,11 @@ class ProductTest extends \PHPUnit_Framework_TestCase */ protected $indexerRegistryMock; + /** + * @var \Magento\Framework\Indexer\CacheContext|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheContextMock; + protected function setUp() { $this->fullMock = $this->getMock( @@ -73,6 +78,15 @@ class ProductTest extends \PHPUnit_Framework_TestCase $this->rowsMock, $this->indexerRegistryMock ); + + $this->cacheContextMock = $this->getMock(\Magento\Framework\Indexer\CacheContext::class, [], [], '', false); + + $cacheContextProperty = new \ReflectionProperty( + \Magento\Catalog\Model\Indexer\Category\Product::class, + 'cacheContext' + ); + $cacheContextProperty->setAccessible(true); + $cacheContextProperty->setValue($this->model, $this->cacheContextMock); } public function testExecuteWithIndexerWorking() @@ -115,6 +129,10 @@ class ProductTest extends \PHPUnit_Framework_TestCase $this->rowsMock->expects($this->once())->method('create')->will($this->returnValue($rowMock)); + $this->cacheContextMock->expects($this->once()) + ->method('registerEntities') + ->with(\Magento\Catalog\Model\Category::CACHE_TAG, $ids); + $this->model->execute($ids); } @@ -125,4 +143,25 @@ class ProductTest extends \PHPUnit_Framework_TestCase ->with(\Magento\Catalog\Model\Indexer\Category\Product::INDEXER_ID) ->will($this->returnValue($this->indexerMock)); } + + public function testExecuteFull() + { + /** @var \Magento\Catalog\Model\Indexer\Category\Product\Action\Full $productIndexerFlatFull */ + $productIndexerFlatFull = $this->getMock( + \Magento\Catalog\Model\Indexer\Category\Product\Action\Full::class, + [], + [], + '', + false + ); + $this->fullMock->expects($this->once()) + ->method('create') + ->willReturn($productIndexerFlatFull); + $productIndexerFlatFull->expects($this->once()) + ->method('execute'); + $this->cacheContextMock->expects($this->once()) + ->method('registerTags') + ->with([\Magento\Catalog\Model\Category::CACHE_TAG]); + $this->model->executeFull(); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/AffectCacheTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/AffectCacheTest.php deleted file mode 100644 index a69eb70638a41b8e4c4fa1eeeaa081c3e7d01ac6..0000000000000000000000000000000000000000 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/AffectCacheTest.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -// @codingStandardsIgnoreFile - -namespace Magento\Catalog\Test\Unit\Model\Indexer\Product; - -class AffectCacheTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \Magento\PageCache\Model\Indexer\Product\RefreshPlugin - */ - protected $plugin; - - /** - * @var \Magento\Framework\Indexer\CacheContext|\PHPUnit_Framework_MockObject_MockObject - */ - protected $contextMock; - - /** - * @var \Magento\Framework\Indexer\ActionInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $subjectMock; - - /** - * Set up - */ - protected function setUp() - { - $this->subjectMock = $this->getMockForAbstractClass('Magento\Framework\Indexer\ActionInterface', - [], '', false, true, true, []); - $this->contextMock = $this->getMock('Magento\Framework\Indexer\CacheContext', - [], [], '', false); - $this->plugin = new \Magento\Catalog\Model\Indexer\Product\AffectCache($this->contextMock); - } - - /** - * test beforeExecute - */ - public function testBeforeExecute() - { - $expectedIds = [1, 2, 3]; - $this->contextMock->expects($this->once()) - ->method('registerEntities') - ->with($this->equalTo(\Magento\Catalog\Model\Product::ENTITY), - $this->equalTo($expectedIds)) - ->will($this->returnValue($this->contextMock)); - $actualIds = $this->plugin->beforeExecute($this->subjectMock, $expectedIds); - $this->assertEquals([$expectedIds], $actualIds); - } -} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/CategoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/CategoryTest.php index 0b80262ef9223acaee11274839f3f9fdd2eb2c0c..9910ca7f06ba635c560436139461a2c259c6b8a8 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/CategoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/CategoryTest.php @@ -32,6 +32,11 @@ class CategoryTest extends \PHPUnit_Framework_TestCase */ protected $indexerRegistryMock; + /** + * @var \Magento\Framework\Indexer\CacheContext|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheContextMock; + protected function setUp() { $this->fullMock = $this->getMock( @@ -73,6 +78,15 @@ class CategoryTest extends \PHPUnit_Framework_TestCase $this->rowsMock, $this->indexerRegistryMock ); + + $this->cacheContextMock = $this->getMock(\Magento\Framework\Indexer\CacheContext::class, [], [], '', false); + + $cacheContextProperty = new \ReflectionProperty( + \Magento\Catalog\Model\Indexer\Category\Product::class, + 'cacheContext' + ); + $cacheContextProperty->setAccessible(true); + $cacheContextProperty->setValue($this->model, $this->cacheContextMock); } public function testExecuteWithIndexerWorking() @@ -115,6 +129,10 @@ class CategoryTest extends \PHPUnit_Framework_TestCase $this->rowsMock->expects($this->once())->method('create')->will($this->returnValue($rowMock)); + $this->cacheContextMock->expects($this->once()) + ->method('registerEntities') + ->with(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); + $this->model->execute($ids); } @@ -125,4 +143,30 @@ class CategoryTest extends \PHPUnit_Framework_TestCase ->with(\Magento\Catalog\Model\Indexer\Product\Category::INDEXER_ID) ->will($this->returnValue($this->indexerMock)); } + + public function testExecuteFull() + { + /** @var \Magento\Catalog\Model\Indexer\Category\Product\Action\Full $productIndexerFlatFull */ + $productIndexerFlatFull = $this->getMock( + \Magento\Catalog\Model\Indexer\Category\Product\Action\Full::class, + [], + [], + '', + false + ); + $this->fullMock->expects($this->once()) + ->method('create') + ->willReturn($productIndexerFlatFull); + $productIndexerFlatFull->expects($this->once()) + ->method('execute'); + $this->cacheContextMock->expects($this->once()) + ->method('registerTags') + ->with( + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Catalog\Model\Product::CACHE_TAG + ] + ); + $this->model->executeFull(); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/EavTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/EavTest.php index 34801d07e557b0fad7d4e2409e923f05fb489925..446fde618b8c96fec75393950d112d7f7c39acf6 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/EavTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/EavTest.php @@ -27,6 +27,11 @@ class EavTest extends \PHPUnit_Framework_TestCase */ protected $_productEavIndexerFull; + /** + * @var \Magento\Framework\Indexer\CacheContext|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheContextMock; + protected function setUp() { $this->_productEavIndexerRow = $this->getMockBuilder('Magento\Catalog\Model\Indexer\Product\Eav\Action\Row') @@ -41,22 +46,44 @@ class EavTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $this->_model = new \Magento\Catalog\Model\Indexer\Product\Eav( + $this->model = new \Magento\Catalog\Model\Indexer\Product\Eav( $this->_productEavIndexerRow, $this->_productEavIndexerRows, $this->_productEavIndexerFull ); + + $this->cacheContextMock = $this->getMock(\Magento\Framework\Indexer\CacheContext::class, [], [], '', false); + + $cacheContextProperty = new \ReflectionProperty( + \Magento\Catalog\Model\Indexer\Product\Eav::class, + 'cacheContext' + ); + $cacheContextProperty->setAccessible(true); + $cacheContextProperty->setValue($this->model, $this->cacheContextMock); + } + + public function testExecute() + { + $ids = [1, 2, 3]; + $this->_productEavIndexerRow->expects($this->any()) + ->method('execute') + ->with($ids); + + $this->cacheContextMock->expects($this->once()) + ->method('registerEntities') + ->with(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); + + $this->model->execute($ids); } - public function testExecuteAndExecuteList() + public function testExecuteList() { $ids = [1, 2, 3]; $this->_productEavIndexerRow->expects($this->any()) ->method('execute') ->with($ids); - $this->_model->execute($ids); - $this->_model->executeList($ids); + $this->model->executeList($ids); } public function testExecuteFull() @@ -64,7 +91,16 @@ class EavTest extends \PHPUnit_Framework_TestCase $this->_productEavIndexerFull->expects($this->once()) ->method('execute'); - $this->_model->executeFull(); + $this->cacheContextMock->expects($this->once()) + ->method('registerTags') + ->with( + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Catalog\Model\Product::CACHE_TAG + ] + ); + + $this->model->executeFull(); } public function testExecuteRow() @@ -74,6 +110,6 @@ class EavTest extends \PHPUnit_Framework_TestCase ->method('execute') ->with($id); - $this->_model->executeRow($id); + $this->model->executeRow($id); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/FlatTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/FlatTest.php index cb4d485cfd1ac0d417ab6d828673cda0858c69d9..f9eb1b1d29099c4a902c7babec37cd5cc12b0436 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/FlatTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/FlatTest.php @@ -29,6 +29,11 @@ class FlatTest extends \PHPUnit_Framework_TestCase */ private $productFlatIndexerFull; + /** + * @var \Magento\Framework\Indexer\CacheContext|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheContextMock; + protected function setUp() { $this->productFlatIndexerRow = $this->getMockBuilder('Magento\Catalog\Model\Indexer\Product\Flat\Action\Row') @@ -52,21 +57,50 @@ class FlatTest extends \PHPUnit_Framework_TestCase 'productFlatIndexerFull' => $this->productFlatIndexerFull ] ); + + $this->cacheContextMock = $this->getMock(\Magento\Framework\Indexer\CacheContext::class, [], [], '', false); + + $cacheContextProperty = new \ReflectionProperty( + \Magento\Catalog\Model\Indexer\Product\Flat::class, + 'cacheContext' + ); + $cacheContextProperty->setAccessible(true); + $cacheContextProperty->setValue($this->model, $this->cacheContextMock); } - public function testExecuteAndExecuteList() + public function testExecute() { - $except = ['test']; - $this->productFlatIndexerRows->expects($this->any())->method('execute')->with($this->equalTo($except)); + $ids = [1, 2, 3]; + $this->productFlatIndexerRows->expects($this->any())->method('execute')->with($this->equalTo($ids)); - $this->model->execute($except); - $this->model->executeList($except); + $this->cacheContextMock->expects($this->once()) + ->method('registerEntities') + ->with(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); + + $this->model->execute($ids); + } + + public function testExecuteList() + { + $ids = [1, 2, 3]; + $this->productFlatIndexerRows->expects($this->any())->method('execute')->with($this->equalTo($ids)); + + $this->model->executeList($ids); } public function testExecuteFull() { $this->productFlatIndexerFull->expects($this->any())->method('execute'); + $this->cacheContextMock->expects($this->once()) + ->method('registerTags') + ->with( + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Catalog\Model\Product::CACHE_TAG + ] + ); + $this->model->executeFull(); } diff --git a/app/code/Magento/Catalog/Test/Unit/Observer/AddCatalogToTopmenuItemsObserverTest.php b/app/code/Magento/Catalog/Test/Unit/Plugin/Block/TopmenuTest.php similarity index 81% rename from app/code/Magento/Catalog/Test/Unit/Observer/AddCatalogToTopmenuItemsObserverTest.php rename to app/code/Magento/Catalog/Test/Unit/Plugin/Block/TopmenuTest.php index 393cf8223d2a2d40ed54802515cdc91fa5fc804a..e0fe2deb232f68b989abb72463268d531e5e81cd 100644 --- a/app/code/Magento/Catalog/Test/Unit/Observer/AddCatalogToTopmenuItemsObserverTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Plugin/Block/TopmenuTest.php @@ -6,16 +6,16 @@ // @codingStandardsIgnoreFile -namespace Magento\Catalog\Test\Unit\Observer; +namespace Magento\Catalog\Test\Unit\Plugin\Block; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -class AddCatalogToTopmenuItemsObserverTest extends \PHPUnit_Framework_TestCase +class TopmenuTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\Catalog\Observer\AddCatalogToTopmenuItemsObserver + * @var \Magento\Catalog\Plugin\Block\Topmenu */ - protected $_observer; + protected $block; /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Catalog\Helper\Category @@ -100,8 +100,8 @@ class AddCatalogToTopmenuItemsObserverTest extends \PHPUnit_Framework_TestCase $collection->expects($this->once())->method('getIterator') ->willReturn(new \ArrayIterator([])); - $this->_observer = (new ObjectManager($this))->getObject( - 'Magento\Catalog\Observer\AddCatalogToTopmenuItemsObserver', + $this->block = (new ObjectManager($this))->getObject( + \Magento\Catalog\Plugin\Block\Topmenu::class, [ 'catalogCategory' => $this->_catalogCategory, 'menuCategoryData' => $this->menuCategoryData, @@ -142,23 +142,11 @@ class AddCatalogToTopmenuItemsObserverTest extends \PHPUnit_Framework_TestCase ); $blockMock = $this->_getCleanMock('\Magento\Theme\Block\Html\Topmenu'); - - $eventMock = $this->getMock('\Magento\Framework\Event', ['getBlock'], [], '', false); - $eventMock->expects($this->once()) - ->method('getBlock') - ->will($this->returnValue($blockMock)); - - $observerMock = $this->getMock('\Magento\Framework\Event\Observer', ['getEvent', 'getMenu'], [], '', false); - $observerMock->expects($this->any()) - ->method('getEvent') - ->will($this->returnValue($eventMock)); - - return $observerMock; + return $blockMock; } public function testAddCatalogToTopMenuItems() { - $observer = $this->_preparationData(); - $this->_observer->execute($observer); + $this->block->beforeGetHtml($this->_preparationData()); } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php index 473d1d423067478ea94d0c8b5afcc88c68de40c2..61141072f7725e165cb29938e7878b6b240ef6a6 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php @@ -319,8 +319,7 @@ class CustomOptions extends AbstractModifier 'sortOrder' => 20, 'actions' => [ [ - 'targetName' => static::FORM_NAME . '.' . static::FORM_NAME . '.' - . static::GROUP_CUSTOM_OPTIONS_NAME . '.' . static::GRID_OPTIONS_NAME, + 'targetName' => 'ns = ${ $.ns }, index = ' . static::GRID_OPTIONS_NAME, 'actionName' => 'addChild', ] ] diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 65e14be97edd1e665caa49615319724f8b62fecf..78e46f0c39205ee7172c890739fa60a23946affa 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -46,6 +46,9 @@ <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> + <type name="Magento\Theme\Block\Html\Topmenu"> + <plugin name="catalogTopmenu" type="Magento\Catalog\Plugin\Block\Topmenu" /> + </type> <type name="Magento\Framework\Mview\View\StateInterface"> <plugin name="setStatusForMview" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\MviewState" /> </type> @@ -194,10 +197,6 @@ <plugin name="indexerCategoryFlatConfigGet" type="Magento\Catalog\Model\Indexer\Category\Flat\Plugin\IndexerConfigData" /> <plugin name="indexerProductFlatConfigGet" type="Magento\Catalog\Model\Indexer\Product\Flat\Plugin\IndexerConfigData" /> </type> - <type name="Magento\Catalog\Model\Indexer\Category\Flat"> - <plugin name="page-cache-indexer-reindex-category-flat" - type="Magento\Catalog\Model\Indexer\Category\AffectCache" sortOrder="10"/> - </type> <type name="Magento\Catalog\Model\Indexer\Product\Price\AbstractAction"> <arguments> <argument name="defaultPriceIndexer" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice</argument> @@ -213,14 +212,6 @@ </argument> </arguments> </type> - <type name="Magento\Catalog\Model\Indexer\Category\Product"> - <plugin name="page-cache-indexer-reindex-category-product" - type="Magento\Catalog\Model\Indexer\Product\AffectCache" sortOrder="10"/> - </type> - <type name="Magento\Catalog\Model\Indexer\Product\Category"> - <plugin name="page-cache-indexer-reindex-product-category" - type="Magento\Catalog\Model\Indexer\Category\AffectCache" sortOrder="10"/> - </type> <type name="Magento\Catalog\Model\Product\LinkTypeProvider"> <arguments> <argument name="linkTypes" xsi:type="array"> @@ -285,10 +276,6 @@ <argument name="indexer" xsi:type="object" shared="false">Magento\Framework\Indexer\IndexerInterface</argument> </arguments> </type> - <type name="Magento\Catalog\Model\Indexer\Product\Flat"> - <plugin name="page-cache-indexer-reindex-product-flat" - type="Magento\Catalog\Model\Indexer\Product\AffectCache" sortOrder="10"/> - </type> <type name="Magento\Framework\Model\ActionValidator\RemoveAction"> <arguments> <argument name="protectedModels" xsi:type="array"> diff --git a/app/code/Magento/Catalog/etc/frontend/events.xml b/app/code/Magento/Catalog/etc/frontend/events.xml index afb3c70a14ade23fc8caab37ab4f3b564ae1f917..5ef8c724683141c38d735fcd0e321615d562ce6a 100644 --- a/app/code/Magento/Catalog/etc/frontend/events.xml +++ b/app/code/Magento/Catalog/etc/frontend/events.xml @@ -12,7 +12,4 @@ <event name="customer_logout"> <observer name="catalog" instance="Magento\Catalog\Observer\Compare\BindCustomerLogoutObserver" shared="false" /> </event> - <event name="page_block_html_topmenu_gethtml_before"> - <observer name="catalog_add_topmenu_items" instance="Magento\Catalog\Observer\AddCatalogToTopmenuItemsObserver" /> - </event> </config> 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 ff06f34db85c69a978fccd1f472702f13b749f50..b095d24cf481067fd6edce586f8a9131dbcde3be 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 @@ -184,7 +184,7 @@ define([ var type = $(element).data('priceType'), amount = parseFloat($(element).data('priceAmount')); - if (type && amount) { + if (type && !_.isNaN(amount)) { prices[type] = { amount: amount }; diff --git a/app/code/Magento/CatalogInventory/Helper/Stock.php b/app/code/Magento/CatalogInventory/Helper/Stock.php index 599b2d44e2ae38e45e7477a5c45edb426e0a164b..13e311d1fe5aeb4af1282edcf29215fe67570199 100644 --- a/app/code/Magento/CatalogInventory/Helper/Stock.php +++ b/app/code/Magento/CatalogInventory/Helper/Stock.php @@ -12,9 +12,11 @@ use Magento\CatalogInventory\Model\ResourceModel\Stock\StatusFactory; use Magento\CatalogInventory\Model\ResourceModel\Stock\Status; use Magento\Catalog\Model\ResourceModel\Collection\AbstractCollection; use Magento\Catalog\Model\Product; +use Magento\CatalogInventory\Api\StockConfigurationInterface; /** * Class Stock + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Stock { @@ -47,6 +49,11 @@ class Stock */ private $stockRegistryProvider; + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + /** * @param StoreManagerInterface $storeManager * @param ScopeConfigInterface $scopeConfig @@ -75,7 +82,7 @@ class Stock public function assignStatusToProduct(Product $product, $status = null) { if ($status === null) { - $websiteId = $product->getStore()->getWebsiteId(); + $websiteId = $this->getStockConfiguration()->getDefaultScopeId(); $stockStatus = $this->stockRegistryProvider->getStockStatus($product->getId(), $websiteId); $status = $stockStatus->getStockStatus(); } @@ -90,7 +97,7 @@ class Stock */ public function addStockStatusToProducts(AbstractCollection $productCollection) { - $websiteId = $this->storeManager->getStore($productCollection->getStoreId())->getWebsiteId(); + $websiteId = $this->getStockConfiguration()->getDefaultScopeId(); foreach ($productCollection as $product) { $productId = $product->getId(); $stockStatus = $this->stockRegistryProvider->getStockStatus($productId, $websiteId); @@ -161,4 +168,18 @@ class Stock } return $this->stockStatusResource; } + + /** + * @return StockConfigurationInterface + * + * @deprecated + */ + private function getStockConfiguration() + { + if ($this->stockConfiguration === null) { + $this->stockConfiguration = \Magento\Framework\App\ObjectManager::getInstance() + ->get('Magento\CatalogInventory\Api\StockConfigurationInterface'); + } + return $this->stockConfiguration; + } } diff --git a/app/code/Magento/CatalogInventory/Model/Config/Backend/Backorders.php b/app/code/Magento/CatalogInventory/Model/Config/Backend/Backorders.php index db78c8e17322bd12129cc488248cd0703bb83781..1915a576939ac0aca869e5072abb3e828820dea6 100644 --- a/app/code/Magento/CatalogInventory/Model/Config/Backend/Backorders.php +++ b/app/code/Magento/CatalogInventory/Model/Config/Backend/Backorders.php @@ -25,7 +25,6 @@ class Backorders extends AbstractValue || $this->getValue() == \Magento\CatalogInventory\Model\Stock::BACKORDERS_NO ) ) { - $this->stockIndex->rebuild(); $this->_stockIndexerProcessor->markIndexerAsInvalid(); } return parent::afterSave(); diff --git a/app/code/Magento/CatalogInventory/Model/Config/Backend/Managestock.php b/app/code/Magento/CatalogInventory/Model/Config/Backend/Managestock.php index 559e6235951dbae06698ce2def0eccbbd5dfd915..6edab6f3d4eb629e2947676c21d07fe827d52cdd 100644 --- a/app/code/Magento/CatalogInventory/Model/Config/Backend/Managestock.php +++ b/app/code/Magento/CatalogInventory/Model/Config/Backend/Managestock.php @@ -20,7 +20,6 @@ class Managestock extends AbstractValue public function afterSave() { if ($this->isValueChanged()) { - $this->stockIndex->rebuild(); $this->_stockIndexerProcessor->markIndexerAsInvalid(); } return parent::afterSave(); diff --git a/app/code/Magento/CatalogInventory/Model/Configuration.php b/app/code/Magento/CatalogInventory/Model/Configuration.php index 445057c4c08d8174768a9237359bdb4f830066cd..d1509eec3d4adce87cfd4b8ecf75f63485bbeff5 100644 --- a/app/code/Magento/CatalogInventory/Model/Configuration.php +++ b/app/code/Magento/CatalogInventory/Model/Configuration.php @@ -148,7 +148,9 @@ class Configuration implements StockConfigurationInterface */ public function getDefaultScopeId() { - return (int) $this->storeManager->getWebsite()->getId(); + // TODO: should be fixed in MAGETWO-46043 + // "0" is id of admin website, which is used in backend during save entity + return 0; } /** diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/Stock.php b/app/code/Magento/CatalogInventory/Model/Indexer/Stock.php index 2fc215cb2e9766953431e42ebf5ffc14d7cac262..0e86e0ac793ac55f9c7771c3b8d37a3ba2da0f05 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/Stock.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/Stock.php @@ -1,13 +1,12 @@ <?php /** - * @category Magento - * @package Magento_CatalogInventory * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\CatalogInventory\Model\Indexer; +use Magento\Framework\Indexer\CacheContext; + class Stock implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface { /** @@ -25,6 +24,11 @@ class Stock implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fram */ protected $_productStockIndexerFull; + /** + * @var \Magento\Framework\Indexer\CacheContext + */ + private $cacheContext; + /** * @param Stock\Action\Row $productStockIndexerRow * @param Stock\Action\Rows $productStockIndexerRows @@ -50,6 +54,7 @@ class Stock implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fram public function execute($ids) { $this->_productStockIndexerRows->execute($ids); + $this->getCacheContext()->registerEntities(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); } /** @@ -60,6 +65,12 @@ class Stock implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fram public function executeFull() { $this->_productStockIndexerFull->execute(); + $this->getCacheContext()->registerTags( + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Catalog\Model\Product::CACHE_TAG + ] + ); } /** @@ -85,4 +96,19 @@ class Stock implements \Magento\Framework\Indexer\ActionInterface, \Magento\Fram { $this->_productStockIndexerRow->execute($id); } + + /** + * Get cache context + * + * @return \Magento\Framework\Indexer\CacheContext + * @deprecated + */ + protected function getCacheContext() + { + if (!($this->cacheContext instanceof CacheContext)) { + return \Magento\Framework\App\ObjectManager::getInstance()->get(CacheContext::class); + } else { + return $this->cacheContext; + } + } } diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php index 82e9e1b1c94c607103230917753f0aa1c5afaede..df910254082185145212c325568a971a75a9bd85 100644 --- a/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php +++ b/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php @@ -24,6 +24,7 @@ class AroundProductRepositorySave /** * @var StoreManagerInterface + * @deprecated */ protected $storeManager; @@ -80,7 +81,7 @@ class AroundProductRepositorySave // set fields that the customer should not care about $stockItem->setProductId($product->getId()); - $stockItem->setWebsiteId($this->storeManager->getStore($product->getStoreId())->getWebsiteId()); + $stockItem->setWebsiteId($this->stockConfiguration->getDefaultScopeId()); $this->stockRegistry->updateStockItemBySku($product->getSku(), $stockItem); diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php index 3bb9ef4c4d5c6f847d17365df179f54b0b678752..40245db2f3372234ad18a988140195e88a11b971 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php @@ -9,6 +9,7 @@ namespace Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock; use Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer; use Magento\CatalogInventory\Model\Stock; use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\CatalogInventory\Api\StockConfigurationInterface; /** * CatalogInventory Default Stock Status Indexer Resource Model @@ -42,6 +43,11 @@ class DefaultStock extends AbstractIndexer implements StockInterface */ private $queryProcessorComposite; + /** + * @var StockConfigurationInterface + */ + protected $stockConfiguration; + /** * Class constructor * @@ -176,43 +182,29 @@ class DefaultStock extends AbstractIndexer implements StockInterface */ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = false) { - $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); $connection = $this->getConnection(); $qtyExpr = $connection->getCheckSql('cisi.qty > 0', 'cisi.qty', 0); $select = $connection->select()->from( ['e' => $this->getTable('catalog_product_entity')], ['entity_id'] ); - $this->_addWebsiteJoinToSelect($select, true); - $this->_addProductWebsiteJoinToSelect($select, 'cw.website_id', 'e.entity_id'); - $select->columns('cw.website_id')->join( + $select->join( ['cis' => $this->getTable('cataloginventory_stock')], '', - ['stock_id'] - )->joinLeft( + ['website_id', 'stock_id'] + )->joinInner( ['cisi' => $this->getTable('cataloginventory_stock_item')], 'cisi.stock_id = cis.stock_id AND cisi.product_id = e.entity_id', [] - )->columns(['qty' => new \Zend_Db_Expr('SUM(' . $qtyExpr . ')')]) - ->where('cw.website_id != 0') - ->where('e.type_id = ?', $this->getTypeId()) - ->group(['e.entity_id', 'cw.website_id']); - - // add limitation of status - $condition = $connection->quoteInto( - '=?', - \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED - ); - $this->_addAttributeToSelect( - $select, - 'status', - 'e.' . $metadata->getLinkField(), - 'cs.store_id', - $condition - ); + )->columns( + ['qty' => $qtyExpr] + )->where( + 'cis.website_id = ?', + $this->getStockConfiguration()->getDefaultScopeId() + )->where('e.type_id = ?', $this->getTypeId()) + ->group(['e.entity_id', 'cis.website_id', 'cis.stock_id']); $select->columns(['status' => $this->getStatusExpression($connection, true)]); - if ($entityIds !== null) { $select->where('e.entity_id IN(?)', $entityIds); } @@ -325,6 +317,20 @@ class DefaultStock extends AbstractIndexer implements StockInterface return $statusExpr; } + /** + * @return StockConfigurationInterface + * + * @deprecated + */ + protected function getStockConfiguration() + { + if ($this->stockConfiguration === null) { + $this->stockConfiguration = \Magento\Framework\App\ObjectManager::getInstance() + ->get('Magento\CatalogInventory\Api\StockConfigurationInterface'); + } + return $this->stockConfiguration; + } + /** * @return QueryProcessorComposite */ diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php index f4877e6a3de68fd72a6550d1b6bf6c5b611593bb..17a3e0c9a99494de5efdb4f6825c33fae9e41e54 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php @@ -75,6 +75,7 @@ class Stock extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb impleme /** * @var StoreManagerInterface + * @deprecated */ protected $storeManager; @@ -189,11 +190,12 @@ class Stock extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb impleme * Set items out of stock basing on their quantities and config settings * * @param string|int $website + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void */ public function updateSetOutOfStock($website = null) { - $websiteId = $this->storeManager->getWebsite($website)->getId(); + $websiteId = $this->stockConfiguration->getDefaultScopeId(); $this->_initConfig(); $connection = $this->getConnection(); $values = ['is_in_stock' => 0, 'stock_status_changed_auto' => 1]; @@ -223,11 +225,12 @@ class Stock extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb impleme * Set items in stock basing on their quantities and config settings * * @param int|string $website + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void */ public function updateSetInStock($website) { - $websiteId = $this->storeManager->getWebsite($website)->getId(); + $websiteId = $this->stockConfiguration->getDefaultScopeId(); $this->_initConfig(); $connection = $this->getConnection(); $values = ['is_in_stock' => 1]; @@ -255,11 +258,12 @@ class Stock extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb impleme * Update items low stock date basing on their quantities and config settings * * @param int|string $website + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return void */ public function updateLowStockDate($website) { - $websiteId = $this->storeManager->getWebsite($website)->getId(); + $websiteId = $this->stockConfiguration->getDefaultScopeId(); $this->_initConfig(); $connection = $this->getConnection(); diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php index d08bd31df9eeb517f3577ce3aff34de85c42479f..32d21f388f03e30510dfb1179fdf335654b4c0a4 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php @@ -57,13 +57,13 @@ class Item extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb * * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $item * @param int $productId - * @param int $websiteId + * @param int $stockId * @return $this */ - public function loadByProductId(\Magento\CatalogInventory\Api\Data\StockItemInterface $item, $productId, $websiteId) + public function loadByProductId(\Magento\CatalogInventory\Api\Data\StockItemInterface $item, $productId, $stockId) { - $select = $this->_getLoadSelect('product_id', $productId, $item)->where('website_id = :website_id'); - $data = $this->getConnection()->fetchRow($select, [':website_id' => $websiteId]); + $select = $this->_getLoadSelect('product_id', $productId, $item)->where('stock_id = :stock_id'); + $data = $this->getConnection()->fetchRow($select, [':stock_id' => $stockId]); if ($data) { $item->setData($data); } else { diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item/StockItemCriteriaMapper.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item/StockItemCriteriaMapper.php index 551f5e0c3b8fffd04eaac0e5c18bd1a1977990d6..d1e038071ecc52a7adf9f5452076e95cd06358e5 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item/StockItemCriteriaMapper.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item/StockItemCriteriaMapper.php @@ -13,6 +13,7 @@ use Magento\Framework\Data\ObjectFactory; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface as Logger; use Magento\Framework\Data\Collection\Db\FetchStrategyInterface; +use Magento\CatalogInventory\Api\StockConfigurationInterface; /** * Interface StockItemCriteriaMapper @@ -20,6 +21,17 @@ use Magento\Framework\Data\Collection\Db\FetchStrategyInterface; */ class StockItemCriteriaMapper extends GenericMapper { + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var StoreManagerInterface + * @deprecated + */ + private $storeManager; + /** * @param Logger $logger * @param FetchStrategyInterface $fetchStrategy @@ -107,10 +119,11 @@ class StockItemCriteriaMapper extends GenericMapper /** * @inheritdoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function mapStockStatus($storeId = null) { - $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); + $websiteId = $this->getStockConfiguration()->getDefaultScopeId(); $this->getSelect()->joinLeft( ['status_table' => $this->getTable('cataloginventory_stock_status')], 'main_table.product_id=status_table.product_id' . @@ -149,4 +162,18 @@ class StockItemCriteriaMapper extends GenericMapper } $this->addFieldToFilter('main_table.qty', [$methods[$comparisonMethod] => $qty]); } + + /** + * @return StockConfigurationInterface + * + * @deprecated + */ + private function getStockConfiguration() + { + if ($this->stockConfiguration === null) { + $this->stockConfiguration = \Magento\Framework\App\ObjectManager::getInstance() + ->get('Magento\CatalogInventory\Api\StockConfigurationInterface'); + } + return $this->stockConfiguration; + } } diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php index 75b325458ccaa2c9c4bfb3c0c16ffc381c7cab90..f7410de156fb42e4549b3d1910d5c5d9d5fb0d27 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Status.php @@ -6,9 +6,11 @@ namespace Magento\CatalogInventory\Model\ResourceModel\Stock; use Magento\CatalogInventory\Model\Stock; +use Magento\CatalogInventory\Api\StockConfigurationInterface; /** * CatalogInventory Stock Status per website Resource Model + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Status extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { @@ -16,6 +18,7 @@ class Status extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb * Store model manager * * @var \Magento\Store\Model\StoreManagerInterface + * @deprecated */ protected $_storeManager; @@ -31,6 +34,11 @@ class Status extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ protected $eavConfig; + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + /** * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -190,11 +198,12 @@ class Status extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb * * @param \Magento\Framework\DB\Select $select * @param \Magento\Store\Model\Website $website + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return Status */ public function addStockStatusToSelect(\Magento\Framework\DB\Select $select, \Magento\Store\Model\Website $website) { - $websiteId = $website->getId(); + $websiteId = $this->getStockConfiguration()->getDefaultScopeId(); $select->joinLeft( ['stock_status' => $this->getMainTable()], 'e.entity_id = stock_status.product_id AND stock_status.website_id=' . $websiteId, @@ -211,7 +220,7 @@ class Status extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ public function addStockDataToCollection($collection, $isFilterInStock) { - $websiteId = $this->_storeManager->getStore($collection->getStoreId())->getWebsiteId(); + $websiteId = $this->getStockConfiguration()->getDefaultScopeId(); $joinCondition = $this->getConnection()->quoteInto( 'e.entity_id = stock_status_index.product_id' . ' AND stock_status_index.website_id = ?', $websiteId @@ -245,7 +254,7 @@ class Status extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ public function addIsInStockFilterToCollection($collection) { - $websiteId = $this->_storeManager->getStore($collection->getStoreId())->getWebsiteId(); + $websiteId = $this->getStockConfiguration()->getDefaultScopeId(); $joinCondition = $this->getConnection()->quoteInto( 'e.entity_id = stock_status_index.product_id' . ' AND stock_status_index.website_id = ?', $websiteId @@ -325,4 +334,18 @@ class Status extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb } return $statuses; } + + /** + * @return StockConfigurationInterface + * + * @deprecated + */ + private function getStockConfiguration() + { + if ($this->stockConfiguration === null) { + $this->stockConfiguration = \Magento\Framework\App\ObjectManager::getInstance() + ->get('Magento\CatalogInventory\Api\StockConfigurationInterface'); + } + return $this->stockConfiguration; + } } diff --git a/app/code/Magento/CatalogInventory/Model/StockIndex.php b/app/code/Magento/CatalogInventory/Model/StockIndex.php index a30bc203c638f251e167fa5f88eb6d9eaca6ef21..226293c8205e67acd32953463485713d64729128 100644 --- a/app/code/Magento/CatalogInventory/Model/StockIndex.php +++ b/app/code/Magento/CatalogInventory/Model/StockIndex.php @@ -78,6 +78,7 @@ class StockIndex implements StockIndexInterface * * @param int $productId * @param int $scopeId + * @deprecated * @return true * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ @@ -108,6 +109,7 @@ class StockIndex implements StockIndexInterface * * @param int $productId * @param int $websiteId + * @deprecated * @return void */ public function updateProductStockStatus($productId, $websiteId) diff --git a/app/code/Magento/CatalogInventory/Model/StockState.php b/app/code/Magento/CatalogInventory/Model/StockState.php index a5f60dce93ca7b45d3d4c4aa8783df95e72a7d80..0c8b84d473e45a47ab2ceaf69cf5089f5818a9b3 100644 --- a/app/code/Magento/CatalogInventory/Model/StockState.php +++ b/app/code/Magento/CatalogInventory/Model/StockState.php @@ -52,9 +52,9 @@ class StockState implements StockStateInterface */ public function verifyStock($productId, $scopeId = null) { - if ($scopeId === null) { + // if ($scopeId === null) { $scopeId = $this->stockConfiguration->getDefaultScopeId(); - } + // } $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); return $this->stockStateProvider->verifyStock($stockItem); } @@ -66,9 +66,9 @@ class StockState implements StockStateInterface */ public function verifyNotification($productId, $scopeId = null) { - if ($scopeId === null) { + // if ($scopeId === null) { $scopeId = $this->stockConfiguration->getDefaultScopeId(); - } + // } $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); return $this->stockStateProvider->verifyNotification($stockItem); } @@ -84,9 +84,9 @@ class StockState implements StockStateInterface */ public function checkQty($productId, $qty, $scopeId = null) { - if ($scopeId === null) { + // if ($scopeId === null) { $scopeId = $this->stockConfiguration->getDefaultScopeId(); - } + // } $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); return $this->stockStateProvider->checkQty($stockItem, $qty); } @@ -102,9 +102,9 @@ class StockState implements StockStateInterface */ public function suggestQty($productId, $qty, $scopeId = null) { - if ($scopeId === null) { + // if ($scopeId === null) { $scopeId = $this->stockConfiguration->getDefaultScopeId(); - } + // } $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); return $this->stockStateProvider->suggestQty($stockItem, $qty); } @@ -118,9 +118,9 @@ class StockState implements StockStateInterface */ public function getStockQty($productId, $scopeId = null) { - if ($scopeId === null) { + // if ($scopeId === null) { $scopeId = $this->stockConfiguration->getDefaultScopeId(); - } + // } $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); return $this->stockStateProvider->getStockQty($stockItem); } @@ -133,9 +133,9 @@ class StockState implements StockStateInterface */ public function checkQtyIncrements($productId, $qty, $websiteId = null) { - if ($websiteId === null) { + // if ($websiteId === null) { $websiteId = $this->stockConfiguration->getDefaultScopeId(); - } + // } $stockItem = $this->stockRegistryProvider->getStockItem($productId, $websiteId); return $this->stockStateProvider->checkQtyIncrements($stockItem, $qty); } @@ -150,9 +150,9 @@ class StockState implements StockStateInterface */ public function checkQuoteItemQty($productId, $itemQty, $qtyToCheck, $origQty, $scopeId = null) { - if ($scopeId === null) { + // if ($scopeId === null) { $scopeId = $this->stockConfiguration->getDefaultScopeId(); - } + // } $stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId); return $this->stockStateProvider->checkQuoteItemQty($stockItem, $itemQty, $qtyToCheck, $origQty); } diff --git a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php index d700b296871aa2faf5fcc62a5ef3b6822c2cbe24..72c9b35394717deb7b22b24fa89236d5e559e1bd 100644 --- a/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/SaveInventoryDataObserver.php @@ -17,6 +17,7 @@ class SaveInventoryDataObserver implements ObserverInterface { /** * @var StockIndexInterface + * @deprecated */ protected $stockIndex; @@ -96,14 +97,7 @@ class SaveInventoryDataObserver implements ObserverInterface public function execute(EventObserver $observer) { $product = $observer->getEvent()->getProduct(); - if ($product->getStockData() === null) { - if ($product->getIsChangedWebsites() || $product->dataHasChangedFor('status')) { - $this->stockIndex->rebuild( - $product->getId(), - $product->getStore()->getWebsiteId() - ); - } return $this; } diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php index 1f1c7a98f0907a6cea0b863477a6f57af1200f7d..df5ea40e933075bb07dd398ab3514e2cf18960d6 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Helper/StockTest.php @@ -37,6 +37,11 @@ class StockTest extends \PHPUnit_Framework_TestCase */ protected $statusFactoryMock; + /** + * @var \Magento\CatalogInventory\Api\StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $stockConfiguration; + protected function setUp() { $this->stockRegistryProviderMock = $this->getMockBuilder( @@ -55,12 +60,20 @@ class StockTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); + $this->stockConfiguration = $this->getMockBuilder('Magento\CatalogInventory\Api\StockConfigurationInterface') + ->getMock(); $this->stock = new Stock( $this->storeManagerMock, $this->scopeConfigMock, $this->statusFactoryMock, $this->stockRegistryProviderMock ); + + // Todo: \Magento\Framework\TestFramework\Unit\Helper\ObjectManager to do this automatically (MAGETWO-49793) + $reflection = new \ReflectionClass(get_class($this->stock)); + $reflectionProperty = $reflection->getProperty('stockConfiguration'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->stock, $this->stockConfiguration); } public function testAssignStatusToProduct() @@ -77,19 +90,12 @@ class StockTest extends \PHPUnit_Framework_TestCase $this->stockRegistryProviderMock->expects($this->any()) ->method('getStockStatus') ->willReturn($stockStatusMock); - $storeMock = $this->getMockBuilder('Magento\Store\Model\Store') - ->disableOriginalConstructor() - ->getMock(); - $storeMock->expects($this->once()) - ->method('getWebsiteId') - ->willReturn($websiteId); + $this->stockConfiguration->expects($this->once())->method('getDefaultScopeId')->willReturn($websiteId); + $productMock = $this->getMockBuilder('Magento\Catalog\Model\Product') ->disableOriginalConstructor() - ->setMethods(['setIsSalable', 'getStore', 'getId']) + ->setMethods(['setIsSalable', 'getId']) ->getMock(); - $productMock->expects($this->any()) - ->method('getStore') - ->willReturn($storeMock); $productMock->expects($this->once()) ->method('setIsSalable') ->with($status); @@ -134,12 +140,6 @@ class StockTest extends \PHPUnit_Framework_TestCase $productCollectionMock->expects($this->any()) ->method('getIterator') ->willReturn($iteratorMock); - $storeMock = $this->getMockBuilder('Magento\Store\Model\Store') - ->disableOriginalConstructor() - ->getMock(); - $this->storeManagerMock->expects($this->once()) - ->method('getStore') - ->willReturn($storeMock); $this->stockRegistryProviderMock->expects($this->once()) ->method('getStockStatus') ->withAnyParameters() diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Config/Backend/ManagestockTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Config/Backend/ManagestockTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b180ba0e035173514ebb444bec4e2c4487567577 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Config/Backend/ManagestockTest.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogInventory\Test\Unit\Model\Config\Backend; + +class ManagestockTest extends \PHPUnit_Framework_TestCase +{ + /** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor|\PHPUnit_Framework_MockObject_MockObject */ + protected $stockIndexerProcessor; + + /** @var \Magento\CatalogInventory\Model\Config\Backend\Managestock */ + protected $model; + + protected function setUp() + { + $this->stockIndexerProcessor = $this->getMockBuilder('Magento\CatalogInventory\Model\Indexer\Stock\Processor') + ->disableOriginalConstructor() + ->getMock(); + $this->model = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( + 'Magento\CatalogInventory\Model\Config\Backend\Managestock', + [ + 'stockIndexerProcessor' => $this->stockIndexerProcessor, + ] + ); + } + + /** + * Data provider for testSaveAndRebuildIndex + * @return array + */ + public function saveAndRebuildIndexDataProvider() + { + return [ + [1, 1], + [0, 0], + ]; + } + + /** + * @dataProvider saveAndRebuildIndexDataProvider + * + * @param int $newStockValue new value for stock status + * @param int $callCount count matcher + */ + public function testSaveAndRebuildIndex($newStockValue, $callCount) + { + $this->model->setValue($newStockValue); + $this->stockIndexerProcessor->expects($this->exactly($callCount))->method('markIndexerAsInvalid'); + $this->model->afterSave(); + } +} diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/ConfigurationTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/ConfigurationTest.php index 90b05b2d10649ce527debbea0f368d5e385f55bc..a0b2cc7225c9efe60c158b0e71946a22d5cb5512 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/ConfigurationTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/ConfigurationTest.php @@ -61,17 +61,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase public function testGetDefaultWebsiteId() { - $id = 1; - $websiteMock = $this->getMockBuilder('Magento\Store\Model\Website') - ->disableOriginalConstructor() - ->getMock(); - $websiteMock->expects($this->once()) - ->method('getId') - ->willReturn($id); - $this->storeManagerMock->expects($this->once()) - ->method('getWebsite') - ->willReturn($websiteMock); - $this->assertEquals($id, $this->model->getDefaultScopeId()); + $this->assertEquals(0, $this->model->getDefaultScopeId()); } public function testGetIsQtyTypeIds() diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php index 534960b4397833d268c56ab62c9a45d46cc997f9..cb26fda9be79e928dac5585694e71778661bf0f6 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php @@ -154,11 +154,7 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase ->method('getStockItem') ->willReturn(null); - $storeMock = $this->getMockBuilder('\Magento\Store\Model\Store') - ->disableOriginalConstructor()->getMock(); - $storeMock->expects($this->once())->method('getWebsiteId')->willReturn(1); - $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock); - + $this->stockConfiguration->expects($this->once())->method('getDefaultScopeId')->willReturn(1); $this->stockRegistry->expects($this->once())->method('getStockItem')->willReturn($this->stockItem); $this->stockRegistry->expects($this->once())->method('updateStockItemBySku'); @@ -180,11 +176,10 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase public function testAroundSave() { $productId = 5494; - $websiteId = 1; $storeId = 2; $sku = 'my product that needs saving'; $defaultScopeId = 100; - $this->stockConfiguration->expects($this->once()) + $this->stockConfiguration->expects($this->exactly(2)) ->method('getDefaultScopeId') ->willReturn($defaultScopeId); $this->stockRegistry->expects($this->once()) @@ -209,17 +204,12 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase ->method('getStockItem') ->willReturn($storedStockItem); - $storeMock = $this->getMockBuilder('\Magento\Store\Model\Store') - ->disableOriginalConstructor()->getMock(); - $storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId); - $this->storeManager->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock); - $this->product->expects(($this->exactly(2)))->method('getId')->willReturn($productId); $this->product->expects(($this->atLeastOnce()))->method('getStoreId')->willReturn($storeId); $this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($sku); $this->stockItem->expects($this->once())->method('setProductId')->with($productId); - $this->stockItem->expects($this->once())->method('setWebsiteId')->with($websiteId); + $this->stockItem->expects($this->once())->method('setWebsiteId')->with($defaultScopeId); $this->stockRegistry->expects($this->once()) ->method('updateStockItemBySku') diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/SaveInventoryDataObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/SaveInventoryDataObserverTest.php deleted file mode 100644 index 1b8347539485d11ff30b22ea3803201cf08004f2..0000000000000000000000000000000000000000 --- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/SaveInventoryDataObserverTest.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\CatalogInventory\Test\Unit\Observer; - -use Magento\CatalogInventory\Observer\SaveInventoryDataObserver; - -class SaveInventoryDataObserverTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var SaveInventoryDataObserver - */ - protected $observer; - - /** - * @var \Magento\CatalogInventory\Api\StockIndexInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $stockIndex; - - /** - * @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject - */ - protected $event; - - /** - * @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject - */ - protected $eventObserver; - - protected function setUp() - { - $this->stockIndex = $this->getMockForAbstractClass( - 'Magento\CatalogInventory\Api\StockIndexInterface', - ['rebuild'], - '', - false - ); - - $this->event = $this->getMockBuilder('Magento\Framework\Event') - ->disableOriginalConstructor() - ->setMethods(['getProduct']) - ->getMock(); - - $this->eventObserver = $this->getMockBuilder('Magento\Framework\Event\Observer') - ->disableOriginalConstructor() - ->setMethods(['getEvent']) - ->getMock(); - - $this->eventObserver->expects($this->atLeastOnce()) - ->method('getEvent') - ->will($this->returnValue($this->event)); - - $this->observer = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( - 'Magento\CatalogInventory\Observer\SaveInventoryDataObserver', - [ - 'stockIndex' => $this->stockIndex, - ] - ); - } - - public function testSaveInventoryData() - { - $productId = 4; - $websiteId = 5; - $stockData = null; - $websitesChanged = true; - $statusChanged = true; - - $store = $this->getMock('Magento\Store\Model\Store', ['getWebsiteId'], [], '', false); - $store->expects($this->once())->method('getWebsiteId')->will($this->returnValue($websiteId)); - - $product = $this->getMock( - 'Magento\Catalog\Model\Product', - ['getStockData', 'getIsChangedWebsites', 'dataHasChangedFor', 'getId', 'getStore'], - [], - '', - false - ); - $product->expects($this->once())->method('getStockData')->will($this->returnValue($stockData)); - $product->expects($this->any())->method('getIsChangedWebsites')->will($this->returnValue($websitesChanged)); - $product->expects($this->any())->method('dataHasChangedFor')->will($this->returnValue($statusChanged)); - $product->expects($this->once())->method('getId')->will($this->returnValue($productId)); - $product->expects($this->once())->method('getStore')->will($this->returnValue($store)); - - $this->stockIndex->expects($this->once())->method('rebuild')->will($this->returnValue(true)); - - $this->event->expects($this->once()) - ->method('getProduct') - ->will($this->returnValue($product)); - - $this->observer->execute($this->eventObserver); - } -} diff --git a/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php b/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php index 7571b3e9a9facba4bd8e45feee10dbe73df21676..73d1f23582ce537a3aaa62f25a467393ab76b464 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php @@ -7,7 +7,8 @@ namespace Magento\CatalogRule\Model\Indexer; use Magento\Framework\Mview\ActionInterface as MviewActionInterface; use Magento\Framework\Indexer\ActionInterface as IndexerActionInterface; -use Magento\Framework\DataObject\IdentityInterface as IdentityInterface; +use Magento\Framework\DataObject\IdentityInterface; +use Magento\Framework\Indexer\CacheContext; abstract class AbstractIndexer implements IndexerActionInterface, MviewActionInterface, IdentityInterface { @@ -28,6 +29,11 @@ abstract class AbstractIndexer implements IndexerActionInterface, MviewActionInt */ private $cacheManager; + /** + * @var \Magento\Framework\Indexer\CacheContext + */ + protected $cacheContext; + /** * @param IndexBuilder $indexBuilder * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -60,7 +66,7 @@ abstract class AbstractIndexer implements IndexerActionInterface, MviewActionInt { $this->indexBuilder->reindexFull(); $this->_eventManager->dispatch('clean_cache_by_tags', ['object' => $this]); - //TODO: remove after fix fpc. MAGETWO-49121 + //TODO: remove after fix fpc. MAGETWO-50668 $this->getCacheManager()->clean($this->getIdentities()); } @@ -144,4 +150,19 @@ abstract class AbstractIndexer implements IndexerActionInterface, MviewActionInt } return $this->cacheManager; } + + /** + * Get cache context + * + * @return \Magento\Framework\Indexer\CacheContext + * @deprecated + */ + protected function getCacheContext() + { + if (!($this->cacheContext instanceof CacheContext)) { + return \Magento\Framework\App\ObjectManager::getInstance()->get(CacheContext::class); + } else { + return $this->cacheContext; + } + } } diff --git a/app/code/Magento/CatalogRule/Model/Indexer/Product/ProductRuleIndexer.php b/app/code/Magento/CatalogRule/Model/Indexer/Product/ProductRuleIndexer.php index 08b35927226503fbc1b404500cc733a0a7d090b5..696f3c6ec803d0a69d68a9c840395abf2f2a3a34 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/Product/ProductRuleIndexer.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/Product/ProductRuleIndexer.php @@ -15,6 +15,7 @@ class ProductRuleIndexer extends AbstractIndexer protected function doExecuteList($ids) { $this->indexBuilder->reindexByIds(array_unique($ids)); + $this->getCacheContext()->registerEntities(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); } /** diff --git a/app/code/Magento/CatalogRule/Model/Indexer/Rule/RuleProductIndexer.php b/app/code/Magento/CatalogRule/Model/Indexer/Rule/RuleProductIndexer.php index 8d627bc80d313d1adfe27ca10f535b5a7c664e02..b5125501571e0817af278803845e37022efa633c 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/Rule/RuleProductIndexer.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/Rule/RuleProductIndexer.php @@ -17,6 +17,7 @@ class RuleProductIndexer extends AbstractIndexer protected function doExecuteList($ids) { $this->indexBuilder->reindexFull(); + $this->getCacheContext()->registerTags($this->getIdentities()); } /** diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/Product/ProductRuleIndexerTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/Product/ProductRuleIndexerTest.php index cd503d22c66f05e0dbb8b637ba26332051700379..c0d76d027f034b903cc4b26d982e5b48b21c9d1e 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/Product/ProductRuleIndexerTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/Product/ProductRuleIndexerTest.php @@ -3,7 +3,6 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\CatalogRule\Test\Unit\Model\Indexer\Product; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -20,6 +19,11 @@ class ProductRuleIndexerTest extends \PHPUnit_Framework_TestCase */ protected $indexer; + /** + * @var \Magento\Framework\Indexer\CacheContext|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheContextMock; + protected function setUp() { $this->indexBuilder = $this->getMock('Magento\CatalogRule\Model\Indexer\IndexBuilder', [], [], '', false); @@ -30,7 +34,17 @@ class ProductRuleIndexerTest extends \PHPUnit_Framework_TestCase 'indexBuilder' => $this->indexBuilder, ] ); + + $this->cacheContextMock = $this->getMock(\Magento\Framework\Indexer\CacheContext::class, [], [], '', false); + + $cacheContextProperty = new \ReflectionProperty( + \Magento\CatalogRule\Model\Indexer\Product\ProductRuleIndexer::class, + 'cacheContext' + ); + $cacheContextProperty->setAccessible(true); + $cacheContextProperty->setValue($this->indexer, $this->cacheContextMock); } + /** * @param array $ids * @param array $idsForIndexer @@ -38,8 +52,12 @@ class ProductRuleIndexerTest extends \PHPUnit_Framework_TestCase */ public function testDoExecuteList($ids, $idsForIndexer) { - $this->indexBuilder->expects($this->once())->method('reindexByIds')->with($idsForIndexer); - + $this->indexBuilder->expects($this->once()) + ->method('reindexByIds') + ->with($idsForIndexer); + $this->cacheContextMock->expects($this->once()) + ->method('registerEntities') + ->with(\Magento\Catalog\Model\Product::CACHE_TAG, $ids); $this->indexer->executeList($ids); } diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/Rule/RuleProductIndexerTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/Rule/RuleProductIndexerTest.php index 3af9eaff625d3f72813f4e330c52bac5aeb00da0..7522e34924284cd24d18c98e5850a4ff7844442c 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/Rule/RuleProductIndexerTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/Rule/RuleProductIndexerTest.php @@ -20,6 +20,11 @@ class RuleProductIndexerTest extends \PHPUnit_Framework_TestCase */ protected $indexer; + /** + * @var \Magento\Framework\Indexer\CacheContext|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheContextMock; + protected function setUp() { $this->indexBuilder = $this->getMock('Magento\CatalogRule\Model\Indexer\IndexBuilder', [], [], '', false); @@ -30,13 +35,31 @@ class RuleProductIndexerTest extends \PHPUnit_Framework_TestCase 'indexBuilder' => $this->indexBuilder, ] ); + + $this->cacheContextMock = $this->getMock(\Magento\Framework\Indexer\CacheContext::class, [], [], '', false); + + $cacheContextProperty = new \ReflectionProperty( + \Magento\CatalogRule\Model\Indexer\Rule\RuleProductIndexer::class, + 'cacheContext' + ); + $cacheContextProperty->setAccessible(true); + $cacheContextProperty->setValue($this->indexer, $this->cacheContextMock); } public function testDoExecuteList() { + $ids = [1, 2, 5]; $this->indexBuilder->expects($this->once())->method('reindexFull'); - - $this->indexer->executeList([1, 2, 5]); + $this->cacheContextMock->expects($this->once()) + ->method('registerTags') + ->with( + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Catalog\Model\Product::CACHE_TAG, + \Magento\Framework\App\Cache\Type\Block::CACHE_TAG + ] + ); + $this->indexer->executeList($ids); } public function testDoExecuteRow() diff --git a/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php b/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php index 72134fb16912939f5a80e42e8803fd7120e6f3a5..31ce6d550b116455052cf674e99155db3e3f7af2 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php +++ b/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php @@ -6,20 +6,18 @@ namespace Magento\CatalogSearch\Model\Search; -use Magento\CatalogSearch\Model\Search\TableMapper; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Select; use Magento\Framework\Search\Adapter\Mysql\ConditionManager; use Magento\Framework\Search\Adapter\Mysql\IndexBuilderInterface; use Magento\Framework\Search\Request\Dimension; -use Magento\Framework\Search\Request\Filter\BoolExpression; -use Magento\Framework\Search\Request\FilterInterface; -use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface; use Magento\Framework\Search\RequestInterface; use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Model\Stock; use Magento\Framework\App\ScopeResolverInterface; /** @@ -40,6 +38,7 @@ class IndexBuilder implements IndexBuilderInterface /** * @var StoreManagerInterface + * @deprecated */ private $storeManager; @@ -63,6 +62,11 @@ class IndexBuilder implements IndexBuilderInterface */ private $dimensionScopeResolver; + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + /** * @param \Magento\Framework\App\ResourceConnection $resource * @param ScopeConfigInterface $config @@ -124,16 +128,30 @@ class IndexBuilder implements IndexBuilderInterface 'search_index.entity_id = stock_index.product_id' . $this->resource->getConnection()->quoteInto( ' AND stock_index.website_id = ?', - $this->storeManager->getWebsite()->getId() + $this->getStockConfiguration()->getDefaultScopeId() ), [] ); - $select->where('stock_index.stock_status = ?', 1); + $select->where('stock_index.stock_status = ?', Stock::DEFAULT_STOCK_ID); } return $select; } + /** + * @return StockConfigurationInterface + * + * @deprecated + */ + private function getStockConfiguration() + { + if ($this->stockConfiguration === null) { + $this->stockConfiguration = \Magento\Framework\App\ObjectManager::getInstance() + ->get('Magento\CatalogInventory\Api\StockConfigurationInterface'); + } + return $this->stockConfiguration; + } + /** * Add filtering by dimensions * diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/IndexBuilderTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/IndexBuilderTest.php index 73dfd3867b0dcaf870794e6195d508309f440600..32176594936b2d996b1e14c758cf0671c76a73f5 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/IndexBuilderTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/IndexBuilderTest.php @@ -40,6 +40,9 @@ class IndexBuilderTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Search\Model\IndexScopeResolver|\PHPUnit_Framework_MockObject_MockObject */ private $resource; + /** @var \Magento\CatalogInventory\Api\StockConfigurationInterface|MockObject */ + private $stockConfiguration; + /** * @var \Magento\CatalogSearch\Model\Search\IndexBuilder */ @@ -55,6 +58,10 @@ class IndexBuilderTest extends \PHPUnit_Framework_TestCase */ private $scopeInterface; + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return void + */ protected function setUp() { $this->select = $this->getMockBuilder('\Magento\Framework\DB\Select') @@ -139,6 +146,9 @@ class IndexBuilderTest extends \PHPUnit_Framework_TestCase '', false ); + $this->stockConfiguration = $this + ->getMockBuilder('\Magento\CatalogInventory\Api\StockConfigurationInterface') + ->getMock(); $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->target = $objectManagerHelper->getObject( @@ -153,6 +163,12 @@ class IndexBuilderTest extends \PHPUnit_Framework_TestCase 'dimensionScopeResolver' => $this->dimensionScopeResolver ] ); + + // Todo: \Magento\Framework\TestFramework\Unit\Helper\ObjectManager to do this automatically (MAGETWO-49793) + $reflection = new \ReflectionClass(get_class($this->target)); + $reflectionProperty = $reflection->getProperty('stockConfiguration'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->target, $this->stockConfiguration); } public function testBuildWithOutOfStock() @@ -198,15 +214,13 @@ class IndexBuilderTest extends \PHPUnit_Framework_TestCase $this->mockBuild($index, $tableSuffix, false); + $this->stockConfiguration->expects($this->once())->method('getDefaultScopeId')->willReturn(1); $this->config->expects($this->once()) ->method('isSetFlag') ->with('cataloginventory/options/show_out_of_stock') ->will($this->returnValue(false)); $this->connection->expects($this->once())->method('quoteInto') ->with(' AND stock_index.website_id = ?', 1)->willReturn(' AND stock_index.website_id = 1'); - $website = $this->getMockBuilder('Magento\Store\Model\Website')->disableOriginalConstructor()->getMock(); - $website->expects($this->once())->method('getId')->willReturn(1); - $this->storeManager->expects($this->once())->method('getWebsite')->willReturn($website); $this->select->expects($this->at(2)) ->method('where') ->with('(someName=someValue)') diff --git a/app/code/Magento/CatalogSearch/composer.json b/app/code/Magento/CatalogSearch/composer.json index 709009300b724a41dbca6c80773684af9c15e91e..860d5080b9bdffd0282c5b989c8373b886d498af 100644 --- a/app/code/Magento/CatalogSearch/composer.json +++ b/app/code/Magento/CatalogSearch/composer.json @@ -11,6 +11,7 @@ "magento/module-eav": "100.0.*", "magento/module-backend": "100.0.*", "magento/module-theme": "100.0.*", + "magento/module-catalog-inventory": "100.0.*", "magento/framework": "100.0.*" }, "type": "magento2-module", diff --git a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Combine.php b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Combine.php index 386e77580fa4abab025f3119b056f08b2be88084..6f2cd40e97f7ad19fb5cd217881403a11a594268 100644 --- a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Combine.php +++ b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Combine.php @@ -71,7 +71,9 @@ class Combine extends \Magento\Rule\Model\Condition\Combine */ public function collectValidatedAttributes($productCollection) { + $alias = array_keys($productCollection->getSelect()->getPart('from'))[0]; foreach ($this->getConditions() as $condition) { + $condition->setData('attribute', $alias . '.' . $condition->getData('attribute')); $condition->addToCollection($productCollection); } return $this; diff --git a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/CombineTest.php b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/CombineTest.php index 34c14626d8bf7423d435c62e952c663a8b52f9ae..b0a3fdab8eb32c6c97ef798a94390f7a942f04a2 100644 --- a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/CombineTest.php +++ b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/CombineTest.php @@ -73,8 +73,13 @@ class CombineTest extends \PHPUnit_Framework_TestCase public function testCollectValidatedAttributes() { $collection = $this->getMockBuilder('\Magento\Catalog\Model\ResourceModel\Product\Collection') - ->disableOriginalConstructor() + ->disableOriginalConstructor()->setMethods(['getSelect']) + ->getMock(); + $select = $this->getMockBuilder('\Magento\Framework\DB\Select') + ->disableOriginalConstructor()->setMethods(['getPart']) ->getMock(); + $select->expects($this->any())->method('getPart')->with('from')->willReturn(['alias_table' => 'table_name']); + $collection->expects($this->any())->method('getSelect')->willReturn($select); $condition = $this->getMockBuilder('Magento\CatalogWidget\Model\Rule\Condition\Combine') ->disableOriginalConstructor()->setMethods(['addToCollection']) ->getMock(); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/place-order.js b/app/code/Magento/Checkout/view/frontend/web/js/action/place-order.js index f88b11bf87a43e7b2feb4ec95fb3eafb6f69d056..2d142417ac099b023d2e715cbac026cfdada14e9 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/place-order.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/place-order.js @@ -6,48 +6,31 @@ define( [ 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/url-builder', - 'mage/storage', - 'Magento_Checkout/js/model/error-processor', 'Magento_Customer/js/model/customer', - 'Magento_Checkout/js/model/full-screen-loader' + 'Magento_Checkout/js/model/place-order' ], - function (quote, urlBuilder, storage, errorProcessor, customer, fullScreenLoader) { + function (quote, urlBuilder, customer, placeOrderService) { 'use strict'; return function (paymentData, messageContainer) { - var serviceUrl, - payload; + var serviceUrl, payload; - /** Checkout for guest and registered customer. */ - if (!customer.isLoggedIn()) { + payload = { + cartId: quote.getQuoteId(), + billingAddress: quote.billingAddress(), + paymentMethod: paymentData + }; + + if (customer.isLoggedIn()) { + serviceUrl = urlBuilder.createUrl('/carts/mine/payment-information', {}); + } else { serviceUrl = urlBuilder.createUrl('/guest-carts/:quoteId/payment-information', { quoteId: quote.getQuoteId() }); - payload = { - cartId: quote.getQuoteId(), - email: quote.guestEmail, - paymentMethod: paymentData, - billingAddress: quote.billingAddress() - }; - } else { - serviceUrl = urlBuilder.createUrl('/carts/mine/payment-information', {}); - payload = { - cartId: quote.getQuoteId(), - paymentMethod: paymentData, - billingAddress: quote.billingAddress() - }; + payload.email = quote.guestEmail; } - fullScreenLoader.startLoader(); - - return storage.post( - serviceUrl, JSON.stringify(payload) - ).fail( - function (response) { - errorProcessor.process(response, messageContainer); - fullScreenLoader.stopLoader(); - } - ); + return placeOrderService(serviceUrl, payload, messageContainer); }; } ); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js index 8b079d1fd5d15fecb0a8a63ec7e1c7d9e27d29a7..ab46c78c6f6e74f62a8c798c4c2b039b2eaf4876 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js @@ -4,18 +4,19 @@ */ /*jshint browser:true jquery:true*/ /*global alert*/ -define([], function() { +define([], function () { /** - * @param addressData + * @param {Object} addressData * Returns new address object */ return function (addressData) { var identifier = Date.now(); + return { email: addressData.email, countryId: (addressData.country_id) ? addressData.country_id : window.checkoutConfig.defaultCountryId, - regionId: (addressData.region && addressData.region.region_id) - ? addressData.region.region_id + regionId: (addressData.region && addressData.region.region_id) ? + addressData.region.region_id : window.checkoutConfig.defaultRegionId, regionCode: (addressData.region) ? addressData.region.region_code : null, region: (addressData.region) ? addressData.region.region : null, @@ -33,25 +34,26 @@ define([], function() { suffix: addressData.suffix, vatId: addressData.vat_id, saveInAddressBook: addressData.save_in_address_book, - isDefaultShipping: function() { + customAttributes: addressData.custom_attributes, + isDefaultShipping: function () { return addressData.default_shipping; }, - isDefaultBilling: function() { + isDefaultBilling: function () { return addressData.default_billing; }, - getType: function() { + getType: function () { return 'new-customer-address'; }, - getKey: function() { + getKey: function () { return this.getType(); }, - getCacheKey: function() { + getCacheKey: function () { return this.getType() + identifier; }, - isEditable: function() { + isEditable: function () { return true; }, - canUseForBilling: function() { + canUseForBilling: function () { return true; } } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js b/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js new file mode 100644 index 0000000000000000000000000000000000000000..68548cb1b28d3e2a4abe66c8be13c9dd48b73ec2 --- /dev/null +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js @@ -0,0 +1,27 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +define( + [ + 'mage/storage', + 'Magento_Checkout/js/model/error-processor', + 'Magento_Checkout/js/model/full-screen-loader' + ], + function (storage, errorProcessor, fullScreenLoader) { + 'use strict'; + + return function (serviceUrl, payload, messageContainer) { + fullScreenLoader.startLoader(); + + return storage.post( + serviceUrl, JSON.stringify(payload) + ).fail( + function (response) { + errorProcessor.process(response, messageContainer); + fullScreenLoader.stopLoader(); + } + ); + }; + } +); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js b/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js index eb950d1e6f9a3d7d484c2379026a1cfd3a03d809..6f86ecedd1af3f5f85909c338ccd00e6cf390dc2 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js @@ -44,7 +44,7 @@ define( return totals; }, setTotals: function(totalsData) { - if (_.isObject(totalsData.extension_attributes)) { + if (_.isObject(totalsData) && _.isObject(totalsData.extension_attributes)) { _.each(totalsData.extension_attributes, function(element, index) { totalsData[index] = element; }); diff --git a/app/code/Magento/Cms/view/adminhtml/layout/cms_page_edit.xml b/app/code/Magento/Cms/view/adminhtml/layout/cms_page_edit.xml index dbc98c4b59d52a82d3af8c01851f7ac9625e6f22..2b57747de4fcaee6268c3e77d800f45df7a95549 100644 --- a/app/code/Magento/Cms/view/adminhtml/layout/cms_page_edit.xml +++ b/app/code/Magento/Cms/view/adminhtml/layout/cms_page_edit.xml @@ -6,7 +6,6 @@ */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - <update handle="styles"/> <body> <referenceContainer name="content"> <uiComponent name="cms_page_form"/> diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 2fc849be10f10988356b977298168173a18a4f7b..fc496b83febb52d1ce0eaaa400e0d1faf65c8d8a 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -509,8 +509,8 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType ->addAttributeToSelect('name') ->addAttributeToSelect('price') ->addAttributeToSelect('weight') -// ->addAttributeToSelect('msrp') -// ->addAttributeToSelect('media_gallery') + ->addAttributeToSelect('image') + ->addAttributeToSelect('status') ->addFilterByRequiredOptions() ->setStoreId($product->getStoreId()); diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php index 551df288ebcb03e2941a618ec28e667265b0af99..6f2b989e5e9bfaa9fa91643b1958be6a99c0fdc1 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php @@ -28,19 +28,11 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); $connection = $this->getConnection(); $idxTable = $usePrimaryTable ? $this->getMainTable() : $this->getIdxTable(); - $select = $connection->select()->from(['e' => $this->getTable('catalog_product_entity')], ['entity_id']); - $this->_addWebsiteJoinToSelect($select, true); - $this->_addProductWebsiteJoinToSelect($select, 'cw.website_id', 'e.entity_id'); - $select->columns( - 'cw.website_id' - )->join( - ['cis' => $this->getTable('cataloginventory_stock')], - '', - ['stock_id'] - )->joinLeft( - ['cisi' => $this->getTable('cataloginventory_stock_item')], - 'cisi.stock_id = cis.stock_id AND cisi.product_id = e.entity_id', - [] + $select = parent::_getStockStatusSelect($entityIds, $usePrimaryTable); + $select->reset( + \Magento\Framework\DB\Select::COLUMNS + )->columns( + ['e.entity_id', 'cis.website_id', 'cis.stock_id'] )->joinLeft( ['l' => $this->getTable('catalog_product_super_link')], 'l.parent_id = e.' . $metadata->getLinkField(), @@ -51,24 +43,14 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer [] )->joinLeft( ['i' => $idxTable], - 'i.product_id = l.product_id AND cw.website_id = i.website_id AND cis.stock_id = i.stock_id', + 'i.product_id = l.product_id AND cis.website_id = i.website_id AND cis.stock_id = i.stock_id', [] )->columns( ['qty' => new \Zend_Db_Expr('0')] - )->where( - 'cw.website_id != 0' - )->where( - 'e.type_id = ?', - $this->getTypeId() - )->group( - ['e.entity_id', 'cw.website_id', 'cis.stock_id'] ); - $psExpr = $this->_addAttributeToSelect($select, 'status', 'e.' . $metadata->getLinkField(), 'cs.store_id'); - $psCond = $connection->quoteInto($psExpr . '=?', ProductStatus::STATUS_ENABLED); - $statusExpr = $this->getStatusExpression($connection); - $optExpr = $connection->getCheckSql("{$psCond} AND le.required_options = 0", 'i.stock_status', 0); + $optExpr = $connection->getCheckSql("le.required_options = 0", 'i.stock_status', 0); $stockStatusExpr = $connection->getLeastSql(["MAX({$optExpr})", "MIN({$statusExpr})"]); $select->columns(['status' => $stockStatusExpr]); diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php b/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php index ee8fa65e2a3dd91bcd749ebc7837ada0a2ea4efd..17139915dd8fed0ed9c657f5210e8c5749d76a24 100644 --- a/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php +++ b/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php @@ -48,9 +48,9 @@ class ConfigurablePriceResolver implements PriceResolverInterface $productPrice = $this->priceResolver->resolvePrice($subProduct); $price = $price ? min($price, $productPrice) : $productPrice; } - if (!$price) { + if ($price === null) { throw new \Magento\Framework\Exception\LocalizedException( - __('Configurable product "%1" do not have sub-products', $product->getName()) + __('Configurable product "%1" does not have sub-products', $product->getName()) ); } return (float)$price; diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php index 5ada8354ef0aa20987b733b7d49bbc8c9162ee65..d37335e7a0842f8418308ca8d915376f0ae50ffd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/CartItemProcessorTest.php @@ -65,10 +65,15 @@ class CartItemProcessorTest extends \PHPUnit_Framework_TestCase false ); - $this->productOptionExtensionAttributes = $this->getMockBuilder(ProductOptionExtensionAttributes::class) - ->disableOriginalConstructor() - ->setMethods(['setConfigurableItemOptions']) - ->getMock(); + $this->productOptionExtensionAttributes = $this->getMockForAbstractClass( + ProductOptionExtensionAttributes::class, + [], + '', + false, + true, + true, + ['setConfigurableItemOptions'] + ); $this->model = new \Magento\ConfigurableProduct\Model\Quote\Item\CartItemProcessor( $this->objectFactoryMock, diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cb13fcf140c49dc687dcf6568e9a64fb369c71c2 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Test\Unit\Pricing\Price; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class ConfigurablePriceResolverTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\ConfigurableProduct\Pricing\Price\ConfigurablePriceResolver + */ + protected $resolver; + + /** + * @var PHPUnit_Framework_MockObject_MockObject | \Magento\ConfigurableProduct\Model\Product\Type\Configurable + */ + protected $configurable; + + /** + * @var PHPUnit_Framework_MockObject_MockObject | \Magento\ConfigurableProduct\Pricing\Price\PriceResolverInterface + */ + protected $priceResolver; + + protected function setUp() + { + $className = 'Magento\ConfigurableProduct\Model\Product\Type\Configurable'; + $this->configurable = $this->getMock($className, ['getUsedProducts'], [], '', false); + + $className = 'Magento\ConfigurableProduct\Pricing\Price\PriceResolverInterface'; + $this->priceResolver = $this->getMockForAbstractClass($className, [], '', false, true, true, ['resolvePrice']); + + $objectManager = new ObjectManager($this); + $this->resolver = $objectManager->getObject( + 'Magento\ConfigurableProduct\Pricing\Price\ConfigurablePriceResolver', + [ + 'priceResolver' => $this->priceResolver, + 'configurable' => $this->configurable, + ] + ); + } + + /** + * situation: There are no used products, thus there are no prices + * + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testResolvePriceWithNoPrices() + { + $product = $this->getMockForAbstractClass( + 'Magento\Framework\Pricing\SaleableInterface', + [], + '', + false, + true, + true, + ['getName'] + ); + $product->expects($this->once())->method('getName')->willReturn('Kiwi'); + + $this->configurable->expects($this->once())->method('getUsedProducts')->willReturn([]); + + $this->resolver->resolvePrice($product); + } + + /** + * situation: one product is supplying the price, which could be a price of zero (0) + * + * @dataProvider testResolvePriceDataProvider + */ + public function testResolvePrice($expectedValue) + { + $price = $expectedValue; + + $product = $this->getMockForAbstractClass( + 'Magento\Framework\Pricing\SaleableInterface', + [], + '', + false, + true, + true, + ['getName'] + ); + $product->expects($this->never())->method('getName'); + + $this->configurable->expects($this->once())->method('getUsedProducts')->willReturn([$product]); + $this->priceResolver->expects($this->atLeastOnce())->method('resolvePrice')->willReturn($price); + + $this->assertEquals($expectedValue, $this->resolver->resolvePrice($product)); + } + + /** + * @return array + */ + public function testResolvePriceDataProvider() + { + return [ + 'price of zero' => [0.00], + 'price of five' => [5], + ]; + } +} diff --git a/app/code/Magento/ConfigurableProduct/i18n/en_US.csv b/app/code/Magento/ConfigurableProduct/i18n/en_US.csv index ec07c74c8868b7ff8906f3cdf68327e8e26eab7b..59ae9a201670772f1fe3220bbb27adb99e1d3876 100644 --- a/app/code/Magento/ConfigurableProduct/i18n/en_US.csv +++ b/app/code/Magento/ConfigurableProduct/i18n/en_US.csv @@ -30,7 +30,7 @@ Summary,Summary "You need to choose options for your item.","You need to choose options for your item." "Some product variations fields are not valid.","Some product variations fields are not valid." "Configuration must have specified attributes","Configuration must have specified attributes" -"Configurable product ""%1"" do not have sub-products","Configurable product ""%1"" do not have sub-products" +"Configurable product ""%1"" does not have sub-products","Configurable product ""%1"" does not have sub-products" "This group contains attributes used in configurable products. Please move these attributes to another group and try again.","This group contains attributes used in configurable products. Please move these attributes to another group and try again." "This attribute is used in configurable products. You cannot remove it from the attribute set.","This attribute is used in configurable products. You cannot remove it from the attribute set." "Associated Products","Associated Products" diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index 5fafc70d1a74118863273a27dd154a733d33991c..d38e2760f1dc7fc6d31772481ab9cc64b757413e 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -23,7 +23,7 @@ define([ state: {}, priceFormat: {}, optionTemplate: '<%- data.label %>' + - '<% if (data.finalPrice.value) { %>' + + "<% if (typeof data.finalPrice.value !== 'undefined') { %>" + ' <%- data.finalPrice.formatted %>' + '<% } %>', mediaGallerySelector: '[data-gallery-role=gallery-placeholder]', diff --git a/app/code/Magento/Customer/Model/Address.php b/app/code/Magento/Customer/Model/Address.php index 54eeaf09f1ff76ef87522cccb5f2cc3d77107281..0d7638a37196a9062f05cc34e7ef9b2d983176c6 100644 --- a/app/code/Magento/Customer/Model/Address.php +++ b/app/code/Magento/Customer/Model/Address.php @@ -47,6 +47,11 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress */ protected $indexerRegistry; + /** + * @var \Magento\Customer\Model\Address\CustomAttributeListInterface + */ + private $attributeList; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -347,4 +352,27 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress $indexer = $this->indexerRegistry->get(Customer::CUSTOMER_GRID_INDEXER_ID); $indexer->reindexRow($this->getCustomerId()); } + + /** + * {@inheritdoc} + */ + protected function getCustomAttributesCodes() + { + return array_keys($this->getAttributeList()->getAttributes()); + } + + /** + * Get new AttributeList dependency for application code. + * @return \Magento\Customer\Model\Address\CustomAttributeListInterface + * @deprecated + */ + private function getAttributeList() + { + if (!$this->attributeList) { + $this->attributeList = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Customer\Model\Address\CustomAttributeListInterface::class + ); + } + return $this->attributeList; + } } diff --git a/app/code/Magento/Customer/Model/Address/AbstractAddress.php b/app/code/Magento/Customer/Model/Address/AbstractAddress.php index 6855eb242e6e5f1e9b476a175dadc841d2b9fbf6..1851f858b992f2acfdd8285aeaca897682b24792 100644 --- a/app/code/Magento/Customer/Model/Address/AbstractAddress.php +++ b/app/code/Magento/Customer/Model/Address/AbstractAddress.php @@ -263,7 +263,7 @@ class AbstractAddress extends AbstractExtensibleModel implements AddressModelInt { if (is_array($key)) { $key = $this->_implodeArrayField($key); - } elseif (is_array($value) && $this->isAddressMultilineAttribute($key)) { + } elseif (is_array($value) && !empty($value) && $this->isAddressMultilineAttribute($key)) { $value = $this->_implodeArrayValues($value); } return parent::setData($key, $value); diff --git a/app/code/Magento/Customer/Model/Address/CustomAttributeList.php b/app/code/Magento/Customer/Model/Address/CustomAttributeList.php new file mode 100644 index 0000000000000000000000000000000000000000..f83b37c2a814debcc5d6b0c1ccd6a429cb3a755a --- /dev/null +++ b/app/code/Magento/Customer/Model/Address/CustomAttributeList.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model\Address; + +class CustomAttributeList implements CustomAttributeListInterface +{ + /** + * {@inheritdoc} + */ + public function getAttributes() + { + return []; + } +} diff --git a/app/code/Magento/Customer/Model/Address/CustomAttributeListInterface.php b/app/code/Magento/Customer/Model/Address/CustomAttributeListInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..c04cfe16f49183e0e27ab8362068b75a83951d3c --- /dev/null +++ b/app/code/Magento/Customer/Model/Address/CustomAttributeListInterface.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model\Address; + +interface CustomAttributeListInterface +{ + /** + * Retrieve list of customer addresses custom attributes + * + * @return array + */ + public function getAttributes(); +} diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index 647ec87efca25a1e583df18d3d469f586996b2c2..1e5f0ce0677ecf699b12b540bb9805d8129d053e 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -49,6 +49,8 @@ type="Magento\Customer\Model\EmailNotification" /> <preference for="Magento\Customer\Api\CustomerNameGenerationInterface" type="Magento\Customer\Helper\View" /> + <preference for="Magento\Customer\Model\Address\CustomAttributeListInterface" + type="Magento\Customer\Model\Address\CustomAttributeList" /> <type name="Magento\Customer\Model\Session"> <arguments> <argument name="configShare" xsi:type="object">Magento\Customer\Model\Config\Share\Proxy</argument> diff --git a/app/code/Magento/Customer/view/frontend/web/js/model/customer/address.js b/app/code/Magento/Customer/view/frontend/web/js/model/customer/address.js index e81016145c705e9d99d86265fb6f0dfd284f9d19..30fbef98fd39ab15db1c7971e3e86ad82e367253 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/model/customer/address.js +++ b/app/code/Magento/Customer/view/frontend/web/js/model/customer/address.js @@ -32,6 +32,7 @@ define([], function() { vatId: addressData.vat_id, sameAsBilling: addressData.same_as_billing, saveInAddressBook: addressData.save_in_address_book, + customAttributes: addressData.custom_attributes, isDefaultShipping: function() { return addressData.default_shipping; }, diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index b9725a98209599aac6fe187ded298d03a8070e97..8a626dd2d42b8cdd5af810d3dd64ef58d7055834 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -1123,9 +1123,8 @@ class Carrier extends \Magento\Dhl\Model\AbstractDhl implements \Magento\Shippin throw new \Magento\Framework\Exception\LocalizedException($responseError); } $this->debugErrors($this->_errors); - - return false; } + $result->append($this->getErrorMessage()); } return $result; diff --git a/app/code/Magento/Downloadable/view/adminhtml/web/js/components/is-downloadable-handler.js b/app/code/Magento/Downloadable/view/adminhtml/web/js/components/is-downloadable-handler.js index 5e619b76c7ecd8ba22fd1e8ff3f25786ac93d33e..8fc6508cd877d752eb94640abeebbc895ec1a716 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/web/js/components/is-downloadable-handler.js +++ b/app/code/Magento/Downloadable/view/adminhtml/web/js/components/is-downloadable-handler.js @@ -22,7 +22,7 @@ define([ * Change visibility for samplesFieldset & linksFieldset based on current statuses of checkbox. */ changeVisibility: function () { - if (this.samplesFieldset && this.linksFieldset) { + if (this.samplesFieldset() && this.linksFieldset()) { if (this.checked() && !this.disabled()) { this.samplesFieldset().visible(true); this.linksFieldset().visible(true); diff --git a/app/code/Magento/Fedex/Model/Carrier.php b/app/code/Magento/Fedex/Model/Carrier.php index c03d89e480c355c2e6abe3f1d9d1585e66a793a4..4cd95cb5b4ba9d3c433d311440b509ee93c6dd3e 100644 --- a/app/code/Magento/Fedex/Model/Carrier.php +++ b/app/code/Magento/Fedex/Model/Carrier.php @@ -501,7 +501,10 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C // make general request for all methods $response = $this->_doRatesRequest(self::RATE_REQUEST_GENERAL); $preparedGeneral = $this->_prepareRateResponse($response); - if (!$preparedGeneral->getError() || $this->_result->getError() && $preparedGeneral->getError()) { + if (!$preparedGeneral->getError() + || $this->_result->getError() && $preparedGeneral->getError() + || empty($this->_result->getAllRates()) + ) { $this->_result->append($preparedGeneral); } diff --git a/app/code/Magento/GroupedProduct/Model/ResourceModel/Indexer/Stock/Grouped.php b/app/code/Magento/GroupedProduct/Model/ResourceModel/Indexer/Stock/Grouped.php index 9587a195b5e7c67d06f8a76b89d6957b5d62c403..8a3f0f1d0e900a5f4d3283076bb6ed8831c9f47a 100644 --- a/app/code/Magento/GroupedProduct/Model/ResourceModel/Indexer/Stock/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/ResourceModel/Indexer/Stock/Grouped.php @@ -26,23 +26,12 @@ class Grouped extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stoc { $connection = $this->getConnection(); $idxTable = $usePrimaryTable ? $this->getMainTable() : $this->getIdxTable(); - $select = $connection->select()->from( - ['e' => $this->getTable('catalog_product_entity')], - ['entity_id'] - ); - $this->_addWebsiteJoinToSelect($select, true); - $this->_addProductWebsiteJoinToSelect($select, 'cw.website_id', 'e.entity_id'); $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); - $select->columns( - 'cw.website_id' - )->join( - ['cis' => $this->getTable('cataloginventory_stock')], - '', - ['stock_id'] - )->joinLeft( - ['cisi' => $this->getTable('cataloginventory_stock_item')], - 'cisi.stock_id = cis.stock_id AND cisi.product_id = e.entity_id', - [] + $select = parent::_getStockStatusSelect($entityIds, $usePrimaryTable); + $select->reset( + \Magento\Framework\DB\Select::COLUMNS + )->columns( + ['e.entity_id', 'cis.website_id', 'cis.stock_id'] )->joinLeft( ['l' => $this->getTable('catalog_product_link')], 'e.' . $metadata->getLinkField() . ' = l.product_id AND l.link_type_id=' . @@ -54,31 +43,15 @@ class Grouped extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stoc [] )->joinLeft( ['i' => $idxTable], - 'i.product_id = l.linked_product_id AND cw.website_id = i.website_id AND cis.stock_id = i.stock_id', + 'i.product_id = l.linked_product_id AND cis.website_id = i.website_id AND cis.stock_id = i.stock_id', [] )->columns( ['qty' => new \Zend_Db_Expr('0')] - )->where( - 'cw.website_id != 0' - )->where( - 'e.type_id = ?', - $this->getTypeId() - )->group( - ['e.entity_id', 'cw.website_id', 'cis.stock_id'] - ); - - // add limitation of status - $productStatusExpr = $this->_addAttributeToSelect( - $select, - 'status', - 'e.' . $metadata->getLinkField(), - 'cs.store_id' ); - $productStatusCond = $connection->quoteInto($productStatusExpr . '=?', ProductStatus::STATUS_ENABLED); $statusExpression = $this->getStatusExpression($connection); - $optExpr = $connection->getCheckSql("{$productStatusCond} AND le.required_options = 0", 'i.stock_status', 0); + $optExpr = $connection->getCheckSql("le.required_options = 0", 'i.stock_status', 0); $stockStatusExpr = $connection->getLeastSql(["MAX({$optExpr})", "MIN({$statusExpression})"]); $select->columns(['status' => $stockStatusExpr]); diff --git a/app/code/Magento/Indexer/Model/Processor/InvalidateCache.php b/app/code/Magento/Indexer/Model/Processor/CleanCache.php similarity index 64% rename from app/code/Magento/Indexer/Model/Processor/InvalidateCache.php rename to app/code/Magento/Indexer/Model/Processor/CleanCache.php index d4cc663612845011019d7832736b5ca0041dd53b..b5dec17899819a074ecf5f6ae1dba30f12c44ebc 100644 --- a/app/code/Magento/Indexer/Model/Processor/InvalidateCache.php +++ b/app/code/Magento/Indexer/Model/Processor/CleanCache.php @@ -3,13 +3,9 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Indexer\Model\Processor; -/** - * Class InvalidateCache - */ -class InvalidateCache +class CleanCache { /** * @var \Magento\Framework\Indexer\CacheContext @@ -21,24 +17,16 @@ class InvalidateCache */ protected $eventManager; - /** - * @var \Magento\Framework\Module\Manager - */ - protected $moduleManager; - /** * @param \Magento\Framework\Indexer\CacheContext $context * @param \Magento\Framework\Event\Manager $eventManager - * @param \Magento\Framework\Module\Manager $moduleManager */ public function __construct( \Magento\Framework\Indexer\CacheContext $context, - \Magento\Framework\Event\Manager $eventManager, - \Magento\Framework\Module\Manager $moduleManager + \Magento\Framework\Event\Manager $eventManager ) { $this->context = $context; $this->eventManager = $eventManager; - $this->moduleManager = $moduleManager; } /** @@ -50,8 +38,18 @@ class InvalidateCache */ public function afterUpdateMview(\Magento\Indexer\Model\Processor $subject) { - if ($this->moduleManager->isEnabled('Magento_PageCache')) { - $this->eventManager->dispatch('clean_cache_after_reindex', ['object' => $this->context]); - } + $this->eventManager->dispatch('clean_cache_after_reindex', ['object' => $this->context]); + } + + /** + * Clear cache after reindex all + * + * @param \Magento\Indexer\Model\Processor $subject + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterReindexAllInvalid(\Magento\Indexer\Model\Processor $subject) + { + $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->context]); } } diff --git a/app/code/Magento/Indexer/Test/Unit/Model/Processor/InvalidateCacheTest.php b/app/code/Magento/Indexer/Test/Unit/Model/Processor/CleanCacheTest.php similarity index 54% rename from app/code/Magento/Indexer/Test/Unit/Model/Processor/InvalidateCacheTest.php rename to app/code/Magento/Indexer/Test/Unit/Model/Processor/CleanCacheTest.php index 0a9ad31ba273a50773a8c6189ebd6a441aa2a610..511eb798e55289b8d94e576ffe326a929ddf63b3 100644 --- a/app/code/Magento/Indexer/Test/Unit/Model/Processor/InvalidateCacheTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Model/Processor/CleanCacheTest.php @@ -3,10 +3,9 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Indexer\Test\Unit\Model\Processor; -class InvalidateCacheTest extends \PHPUnit_Framework_TestCase +class CleanCacheTest extends \PHPUnit_Framework_TestCase { /** * Tested plugin @@ -36,13 +35,6 @@ class InvalidateCacheTest extends \PHPUnit_Framework_TestCase */ protected $eventManagerMock; - /** - * Module manager mock - * - * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject - */ - protected $moduleManager; - /** * Set up */ @@ -51,25 +43,19 @@ class InvalidateCacheTest extends \PHPUnit_Framework_TestCase $this->subjectMock = $this->getMock('Magento\Indexer\Model\Processor', [], [], '', false); $this->contextMock = $this->getMock('Magento\Framework\Indexer\CacheContext', [], [], '', false); $this->eventManagerMock = $this->getMock('Magento\Framework\Event\Manager', [], [], '', false); - $this->moduleManager = $this->getMock('Magento\Framework\Module\Manager', [], [], '', false); - $this->plugin = new \Magento\Indexer\Model\Processor\InvalidateCache( + $this->plugin = new \Magento\Indexer\Model\Processor\CleanCache( $this->contextMock, - $this->eventManagerMock, - $this->moduleManager + $this->eventManagerMock ); } /** - * Test afterUpdateMview with enabled PageCache module + * Test afterUpdateMview * * @return void */ - public function testAfterUpdateMviewPageCacheEnabled() + public function testAfterUpdateMview() { - $this->moduleManager->expects($this->once()) - ->method('isEnabled') - ->with($this->equalTo('Magento_PageCache')) - ->will($this->returnValue(true)); $this->eventManagerMock->expects($this->once()) ->method('dispatch') ->with( @@ -78,20 +64,4 @@ class InvalidateCacheTest extends \PHPUnit_Framework_TestCase ); $this->plugin->afterUpdateMview($this->subjectMock); } - - /** - * Test afterUpdateMview with disabled PageCache module - * - * @return void - */ - public function testAfterUpdateMviewPageCacheDisabled() - { - $this->moduleManager->expects($this->once()) - ->method('isEnabled') - ->with($this->equalTo('Magento_PageCache')) - ->will($this->returnValue(false)); - $this->eventManagerMock->expects($this->never()) - ->method('dispatch'); - $this->plugin->afterUpdateMview($this->subjectMock); - } } diff --git a/app/code/Magento/Indexer/composer.json b/app/code/Magento/Indexer/composer.json index e70c92907e7aee19d7de54688b4696ae89df57c4..2c374d77111510c9d14a5ed3f846e09be67d3891 100644 --- a/app/code/Magento/Indexer/composer.json +++ b/app/code/Magento/Indexer/composer.json @@ -4,7 +4,6 @@ "require": { "php": "~5.5.22|~5.6.0|~7.0.0", "magento/module-backend": "100.0.*", - "magento/module-page-cache": "100.0.*", "magento/framework": "100.0.*" }, "type": "magento2-module", diff --git a/app/code/Magento/Indexer/etc/di.xml b/app/code/Magento/Indexer/etc/di.xml index 87a99325e4ae60d9bee2056a988064848ff4a322..019dad475a5531e87f14c8e4f52d8456bb0a49b7 100644 --- a/app/code/Magento/Indexer/etc/di.xml +++ b/app/code/Magento/Indexer/etc/di.xml @@ -39,8 +39,8 @@ </arguments> </type> <type name="Magento\Indexer\Model\Processor"> - <plugin name="page-cache-indexer-reindex-invalidate" - type="Magento\Indexer\Model\Processor\InvalidateCache" sortOrder="10"/> + <plugin name="page-cache-indexer-reindex-clean-cache" + type="Magento\Indexer\Model\Processor\CleanCache" sortOrder="10"/> </type> <type name="Magento\Framework\Console\CommandList"> diff --git a/app/code/Magento/PageCache/Observer/InvalidateCacheIfChanged.php b/app/code/Magento/PageCache/Observer/InvalidateCacheIfChanged.php deleted file mode 100644 index c8f2d0e23f20d3da3ff7aa291ca5067e11e1133e..0000000000000000000000000000000000000000 --- a/app/code/Magento/PageCache/Observer/InvalidateCacheIfChanged.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\PageCache\Observer; - -use Magento\Framework\Event\ObserverInterface; - -/** - * An observer to invalidate full page cache when the content given is changed - */ -class InvalidateCacheIfChanged implements ObserverInterface -{ - /** - * @var \Magento\Framework\App\Cache\TypeListInterface - */ - protected $typeList; - - /** - * Application config object - * - * @var \Magento\PageCache\Model\Config - */ - protected $config; - - /** - * @param \Magento\PageCache\Model\Config $config - * @param \Magento\Framework\App\Cache\TypeListInterface $typeList - */ - public function __construct( - \Magento\PageCache\Model\Config $config, - \Magento\Framework\App\Cache\TypeListInterface $typeList - ) { - $this->config = $config; - $this->typeList = $typeList; - } - - /** - * Invalidate full page cache if content is changed - * - * @param \Magento\Framework\Event\Observer $observer - * @return void - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - if ($this->config->isEnabled()) { - $object = $observer->getEvent()->getObject(); - if ($object instanceof \Magento\Framework\DataObject\IdentityInterface) { - if ($object->getIdentities()) { - $this->typeList->invalidate('full_page'); - } - } - } - } -} diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/InvalidateCacheIfChangedTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/InvalidateCacheIfChangedTest.php deleted file mode 100644 index 9e6af22857295967436f6ce7d944b8aeb6d42040..0000000000000000000000000000000000000000 --- a/app/code/Magento/PageCache/Test/Unit/Observer/InvalidateCacheIfChangedTest.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php -/** - * - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\PageCache\Test\Unit\Observer; - -class InvalidateCacheIfChangedTest extends \PHPUnit_Framework_TestCase -{ - /** @var \Magento\PageCache\Observer\InvalidateCacheIfChanged */ - protected $model; - - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Config */ - protected $configMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\Cache\TypeListInterface */ - protected $typeListMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Event\Observer */ - protected $observerMock; - - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\DataObject\IdentityInterface */ - protected $objectMock; - - /** - * Set up all mocks and data for test - */ - protected function setUp() - { - $this->configMock = $this->getMock( - 'Magento\PageCache\Model\Config', - ['getType', 'isEnabled'], - [], - '', - false - ); - $this->typeListMock = $this->getMock('Magento\Framework\App\Cache\TypeList', [], [], '', false); - - $this->model = new \Magento\PageCache\Observer\InvalidateCacheIfChanged( - $this->configMock, - $this->typeListMock - ); - - $this->observerMock = $this->getMock('Magento\Framework\Event\Observer', [], [], '', false); - $eventMock = $this->getMock('Magento\Framework\Event', ['getObject'], [], '', false); - $this->objectMock = $this->getMockForAbstractClass( - 'Magento\Framework\DataObject\IdentityInterface', - [], - '', - false - ); - $eventMock->expects($this->any())->method('getObject')->willReturn($this->objectMock); - $this->observerMock->expects($this->any())->method('getEvent')->willReturn($eventMock); - } - - /** - * @dataProvider invalidateCacheDataProvider - * @param bool $cacheState - */ - public function testExecuteChanged($cacheState) - { - $this->configMock->expects($this->once())->method('isEnabled')->will($this->returnValue($cacheState)); - - if ($cacheState) { - $this->typeListMock->expects($this->once())->method('invalidate')->with($this->equalTo('full_page')); - $this->objectMock->expects($this->once())->method('getIdentities')->will($this->returnValue(['tag_1'])); - } - $this->model->execute($this->observerMock); - } - - /** - * @dataProvider invalidateCacheDataProvider - * @param bool $cacheState - */ - public function testExecuteNoChanged($cacheState) - { - $this->configMock->expects($this->once())->method('isEnabled')->will($this->returnValue($cacheState)); - $this->typeListMock->expects($this->never())->method('invalidate'); - - if ($cacheState) { - $this->objectMock->expects($this->once())->method('getIdentities')->will($this->returnValue([])); - } - $this->model->execute($this->observerMock); - } - - public function invalidateCacheDataProvider() - { - return [[true], [false]]; - } -} diff --git a/app/code/Magento/PageCache/etc/events.xml b/app/code/Magento/PageCache/etc/events.xml index 617709c400a1500c59bafa3f0b11ded2ddbcdf06..7ac67a931c7ac8bbbe3b5e397a7ade7b3a039633 100644 --- a/app/code/Magento/PageCache/etc/events.xml +++ b/app/code/Magento/PageCache/etc/events.xml @@ -13,7 +13,7 @@ <observer name="invalidate_builtin" instance="Magento\PageCache\Observer\FlushCacheByTags" /> </event> <event name="clean_cache_after_reindex"> - <observer name="reindex_cache_flush" instance="Magento\PageCache\Observer\InvalidateCacheIfChanged" /> + <observer name="reindex_cache_flush" instance="Magento\PageCache\Observer\FlushCacheByTags" /> </event> <event name="adminhtml_cache_flush_system"> <observer name="flush_system_pagecache" instance="Magento\PageCache\Observer\FlushAllCache" /> diff --git a/app/code/Magento/Paypal/Controller/Express/AbstractExpress/PlaceOrder.php b/app/code/Magento/Paypal/Controller/Express/AbstractExpress/PlaceOrder.php index a824adbd2bf82ed733841813ba42d72622bfdf39..04e149cae270e1ff5077e55ce0283f9a51066e82 100644 --- a/app/code/Magento/Paypal/Controller/Express/AbstractExpress/PlaceOrder.php +++ b/app/code/Magento/Paypal/Controller/Express/AbstractExpress/PlaceOrder.php @@ -62,15 +62,21 @@ class PlaceOrder extends \Magento\Paypal\Controller\Express\AbstractExpress */ public function execute() { - try { - if ($this->isValidationRequired() && - !$this->agreementsValidator->isValid(array_keys($this->getRequest()->getPost('agreement', []))) - ) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Please agree to all the terms and conditions before placing the order.') - ); - } + if ($this->isValidationRequired() && + !$this->agreementsValidator->isValid(array_keys($this->getRequest()->getPost('agreement', []))) + ) { + $e = new \Magento\Framework\Exception\LocalizedException( + __('Please agree to all the terms and conditions before placing the order.') + ); + $this->messageManager->addExceptionMessage( + $e, + $e->getMessage() + ); + $this->_redirect('*/*/review'); + return; + } + try { $this->_initCheckout(); $this->_checkout->place($this->_initToken()); @@ -108,12 +114,6 @@ class PlaceOrder extends \Magento\Paypal\Controller\Express\AbstractExpress return; } catch (ApiProcessableException $e) { $this->_processPaypalApiError($e); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addExceptionMessage( - $e, - $e->getMessage() - ); - $this->_redirect('*/*/review'); } catch (\Exception $e) { $this->messageManager->addExceptionMessage( $e, @@ -141,6 +141,9 @@ class PlaceOrder extends \Magento\Paypal\Controller\Express\AbstractExpress case ApiProcessableException::API_DO_EXPRESS_CHECKOUT_FAIL: $this->_redirectSameToken(); break; + case ApiProcessableException::API_ADDRESS_MATCH_FAIL: + $this->redirectToOrderReviewPageAndShowError($exception->getUserMessage()); + break; case ApiProcessableException::API_UNABLE_TRANSACTION_COMPLETE: if ($this->_config->getPaymentAction() == \Magento\Payment\Model\Method\AbstractMethod::ACTION_ORDER) { $paypalTransactionData = $this->_getCheckoutSession()->getPaypalTransactionData(); @@ -182,6 +185,18 @@ class PlaceOrder extends \Magento\Paypal\Controller\Express\AbstractExpress $this->_redirect('checkout/cart'); } + /** + * Redirect customer to the paypal order review page and show error message + * + * @param string $errorMessage + * @return void + */ + private function redirectToOrderReviewPageAndShowError($errorMessage) + { + $this->messageManager->addErrorMessage($errorMessage); + $this->_redirect('*/*/review'); + } + /** * Return true if agreements validation required * diff --git a/app/code/Magento/Paypal/Controller/Express/AbstractExpress/Start.php b/app/code/Magento/Paypal/Controller/Express/AbstractExpress/Start.php index 20c9b5f7fb08fbcd78fc46cfa7c2e8ebce3436a1..aef1f4aa87920d3dc1a801ff5ee47b2c33ff84c5 100644 --- a/app/code/Magento/Paypal/Controller/Express/AbstractExpress/Start.php +++ b/app/code/Magento/Paypal/Controller/Express/AbstractExpress/Start.php @@ -21,7 +21,6 @@ class Start extends GetToken public function execute() { try { - $token = $this->getToken(); if ($token === null) { diff --git a/app/code/Magento/Paypal/Controller/Express/GetToken.php b/app/code/Magento/Paypal/Controller/Express/GetToken.php index c96cdb420bb3aca6bacfe83a9eb281a574e37c39..a7c00bf1f38322c811422f3e4b26f84934ed8bfd 100644 --- a/app/code/Magento/Paypal/Controller/Express/GetToken.php +++ b/app/code/Magento/Paypal/Controller/Express/GetToken.php @@ -53,8 +53,9 @@ class GetToken extends AbstractExpress if ($token === null) { $token = false; } + $url = $this->_checkout->getRedirectUrl(); $this->_initToken($token); - $controllerResult->setData(['token' => $token]); + $controllerResult->setData(['url' => $url]); } catch (LocalizedException $exception) { $this->messageManager->addExceptionMessage( $exception, diff --git a/app/code/Magento/Paypal/Model/Api/Nvp.php b/app/code/Magento/Paypal/Model/Api/Nvp.php index 521887b6c3c524e4f82fb348bfcc47d63f2db99e..d1cc47a06ff8ff89d4f1b0d95748184ad0244e4d 100644 --- a/app/code/Magento/Paypal/Model/Api/Nvp.php +++ b/app/code/Magento/Paypal/Model/Api/Nvp.php @@ -1288,9 +1288,10 @@ class Nvp extends \Magento\Paypal\Model\Api\AbstractApi $exceptionPhrase = __('PayPal gateway has rejected request. %1', $errorMessages); /** @var \Magento\Framework\Exception\LocalizedException $exception */ - $exception = count($errors) == 1 && $this->_isProcessableError($errors[0]['code']) + $firstError = $errors[0]['code']; + $exception = $this->_isProcessableError($firstError) ? $this->_processableExceptionFactory->create( - ['phrase' => $exceptionPhrase, 'code' => $errors[0]['code']] + ['phrase' => $exceptionPhrase, 'code' => $firstError] ) : $this->_frameworkExceptionFactory->create( ['phrase' => $exceptionPhrase] diff --git a/app/code/Magento/Paypal/Model/Api/ProcessableException.php b/app/code/Magento/Paypal/Model/Api/ProcessableException.php index a44e9ae5f3e753ef8c8a2e99a1bf768de8f16bfb..2a7ce0f104203c1a2803e1d77314f3c04cefbf0e 100644 --- a/app/code/Magento/Paypal/Model/Api/ProcessableException.php +++ b/app/code/Magento/Paypal/Model/Api/ProcessableException.php @@ -25,6 +25,7 @@ class ProcessableException extends LocalizedException const API_COUNTRY_FILTER_DECLINE = 10537; const API_MAXIMUM_AMOUNT_FILTER_DECLINE = 10538; const API_OTHER_FILTER_DECLINE = 10539; + const API_ADDRESS_MATCH_FAIL = 10736; /**#@-*/ /** @@ -61,8 +62,13 @@ class ProcessableException extends LocalizedException 'I\'m sorry - but we are not able to complete your transaction. Please contact us so we can assist you.' ); break; + case self::API_ADDRESS_MATCH_FAIL: + $message = __( + 'A match of the Shipping Address City, State, and Postal Code failed.' + ); + break; default: - $message = __($this->getMessage()); + $message = __('We can\'t place the order.'); break; } return $message; diff --git a/app/code/Magento/Paypal/Model/Express.php b/app/code/Magento/Paypal/Model/Express.php index 2464ef48498ce967dc6b074e695f4b685bfc3bd0..a10a2654bf529b63a70c07247bba9b9c154368d0 100644 --- a/app/code/Magento/Paypal/Model/Express.php +++ b/app/code/Magento/Paypal/Model/Express.php @@ -266,6 +266,7 @@ class Express extends \Magento\Payment\Model\Method\AbstractMethod ApiProcessableException::API_COUNTRY_FILTER_DECLINE, ApiProcessableException::API_MAXIMUM_AMOUNT_FILTER_DECLINE, ApiProcessableException::API_OTHER_FILTER_DECLINE, + ApiProcessableException::API_ADDRESS_MATCH_FAIL, ] ); } diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Api/ProcessableExceptionTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Api/ProcessableExceptionTest.php index 63c6d364e82f43bddcc14fe7595560e494aef7fc..c90431dc4dc5979136d5f274517af4efe7c67c21 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Api/ProcessableExceptionTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Api/ProcessableExceptionTest.php @@ -5,19 +5,16 @@ */ namespace Magento\Paypal\Test\Unit\Model\Api; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Paypal\Model\Api\ProcessableException; class ProcessableExceptionTest extends \PHPUnit_Framework_TestCase { - /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager - */ - protected $objectManager; + const UNKNOWN_CODE = 10411; /** - * @var \Magento\Paypal\Model\Api\ProcessableException + * @var ProcessableException */ - protected $model; + private $model; /** * @param int $code @@ -27,8 +24,7 @@ class ProcessableExceptionTest extends \PHPUnit_Framework_TestCase */ public function testGetUserMessage($code, $msg) { - $this->objectManager = new ObjectManager($this); - $this->model = new \Magento\Paypal\Model\Api\ProcessableException(__($msg), null, $code); + $this->model = new ProcessableException(__($msg), null, $code); $this->assertEquals($msg, $this->model->getUserMessage()); } @@ -39,28 +35,35 @@ class ProcessableExceptionTest extends \PHPUnit_Framework_TestCase { return [ [ - 10001, + ProcessableException::API_INTERNAL_ERROR, "I'm sorry - but we were not able to process your payment. " . "Please try another payment method or contact us so we can assist you.", ], [ - 10417, + ProcessableException::API_UNABLE_PROCESS_PAYMENT_ERROR_CODE, "I'm sorry - but we were not able to process your payment. " . "Please try another payment method or contact us so we can assist you." ], [ - 10537, + ProcessableException::API_COUNTRY_FILTER_DECLINE, "I'm sorry - but we are not able to complete your transaction. Please contact us so we can assist you." ], [ - 10538, + ProcessableException::API_MAXIMUM_AMOUNT_FILTER_DECLINE, "I'm sorry - but we are not able to complete your transaction. Please contact us so we can assist you." ], [ - 10539, + ProcessableException::API_OTHER_FILTER_DECLINE, "I'm sorry - but we are not able to complete your transaction. Please contact us so we can assist you." ], - [10411, "something went wrong"] + [ + ProcessableException::API_ADDRESS_MATCH_FAIL, + 'A match of the Shipping Address City, State, and Postal Code failed.' + ], + [ + self::UNKNOWN_CODE, + "We can't place the order." + ] ]; } } diff --git a/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php b/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php index 288b01bd05d399f3599158e686295044d250d767..75f4c14d8517e23b08dc59f46850ae63df5d4830 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php @@ -22,6 +22,7 @@ class ExpressTest extends \PHPUnit_Framework_TestCase ApiProcessableException::API_COUNTRY_FILTER_DECLINE, ApiProcessableException::API_MAXIMUM_AMOUNT_FILTER_DECLINE, ApiProcessableException::API_OTHER_FILTER_DECLINE, + ApiProcessableException::API_ADDRESS_MATCH_FAIL ]; /** diff --git a/app/code/Magento/Paypal/view/frontend/web/js/in-context/express-checkout.js b/app/code/Magento/Paypal/view/frontend/web/js/in-context/express-checkout.js index e2f70141de02218a46bf0222af200e503ac35170..d70de53581276d34886263558deb455f03a55094 100644 --- a/app/code/Magento/Paypal/view/frontend/web/js/in-context/express-checkout.js +++ b/app/code/Magento/Paypal/view/frontend/web/js/in-context/express-checkout.js @@ -42,8 +42,8 @@ define( } ).done( function (response) { - if (response && response.token) { - paypalExpressCheckout.checkout.startFlow(response.token); + if (response && response.url) { + paypalExpressCheckout.checkout.startFlow(response.url); return; } diff --git a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/iframe-methods.js b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/iframe-methods.js index 9d8ba97f26775ae95714cf8f15a2b892e6150fba..b95c0a7839a6dca8c21040c518192049e16611c5 100644 --- a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/iframe-methods.js +++ b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/iframe-methods.js @@ -50,6 +50,7 @@ define( */ placePendingPaymentOrder: function () { if (this.placeOrder()) { + fullScreenLoader.startLoader(); this.isInAction(true); // capture all click events document.addEventListener('click', iframe.stopEventPropagation, true); @@ -61,6 +62,7 @@ define( return this._super() .fail( function () { + fullScreenLoader.stopLoader(); self.isInAction(false); document.removeEventListener('click', iframe.stopEventPropagation, true); } @@ -71,7 +73,15 @@ define( * After place order callback */ afterPlaceOrder: function () { + if (this.iframeIsLoaded) { + document.getElementById(this.getCode() + '-iframe') + .contentWindow.location.reload(); + } + this.paymentReady(true); + this.iframeIsLoaded = true; + this.isPlaceOrderActionAllowed(true); + fullScreenLoader.stopLoader(); }, /** diff --git a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/in-context/checkout-express.js b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/in-context/checkout-express.js index d93e0ea702fca98fcd447cc5786a830fa0f8df52..7538d3a02aa9b76a769845f5f32d471d110a7983 100644 --- a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/in-context/checkout-express.js +++ b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/in-context/checkout-express.js @@ -52,8 +52,8 @@ define( } ).done( function (response) { - if (response && response.token) { - paypalExpressCheckout.checkout.startFlow(response.token); + if (response && response.url) { + paypalExpressCheckout.checkout.startFlow(response.url); return; } diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index e841d9b7d76150499cfcc15ab41668468fbb4458..aeaa71fb8daad7c4fa8d832e66cad83dadb45f34 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -114,6 +114,7 @@ class Total extends \Magento\Framework\DataObject } //@codeCoverageIgnoreStart + /** * Get all total amount values * @@ -133,4 +134,33 @@ class Total extends \Magento\Framework\DataObject { return $this->baseTotalAmounts; } + + //@codeCoverageIgnoreEnd + + /** + * Set the full info, which is used to capture tax related information. + * If a string is used, it is assumed to be serialized. + * + * @param array|string $info + * @return $this + */ + public function setFullInfo($info) + { + $this->setData('full_info', $info); + return $this; + } + + /** + * Returns the full info, which is used to capture tax related information. + * + * @return array + */ + public function getFullInfo() + { + $fullInfo = $this->getData('full_info'); + if (is_string($fullInfo)) { + $fullInfo = unserialize($fullInfo); + } + return $fullInfo; + } } diff --git a/app/code/Magento/Quote/Model/Quote/Item/Compare.php b/app/code/Magento/Quote/Model/Quote/Item/Compare.php index f61dc25660c9d1dd68b132aaceedc0fd7356315e..10bb712025462fc11f99c2f55206c6cf10f8b854 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Compare.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Compare.php @@ -23,6 +23,9 @@ class Compare if (is_string($value) && is_array(@unserialize($value))) { $value = @unserialize($value); unset($value['qty'], $value['uenc']); + $value = array_filter($value, function ($optionValue) { + return !empty($optionValue); + }); } return $value; } diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index d8aedf4a8b986a45e237e460e127ba4d5060a8a0..f69e299b4542e7669d9bc23df733d9bd8b63b242 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -21,6 +21,8 @@ use Magento\Sales\Api\Data\OrderInterfaceFactory as OrderFactory; use Magento\Sales\Api\OrderManagementInterface as OrderManagement; use Magento\Store\Model\StoreManagerInterface; use Magento\Quote\Model\Quote\Address; +use Magento\Framework\App\ObjectManager; +use Magento\Quote\Model\QuoteIdMaskFactory; /** * Class QuoteManagement @@ -130,6 +132,11 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface */ protected $quoteFactory; + /** + * @var QuoteIdMaskFactory + */ + private $quoteIdMaskFactory; + /** * @param EventManager $eventManager * @param QuoteValidator $quoteValidator @@ -262,6 +269,12 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface $quote->setCustomer($customer); $quote->setCustomerIsGuest(0); + $quoteIdMaskFactory = $this->getQuoteIdMaskFactory(); + /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ + $quoteIdMask = $quoteIdMaskFactory->create()->load($cartId, 'quote_id'); + if ($quoteIdMask->getId()) { + $quoteIdMask->delete(); + } $this->quoteRepository->save($quote); return true; @@ -547,4 +560,16 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface $shipping->setIsDefaultBilling(true); } } + + /** + * @return QuoteIdMaskFactory + * @deprecated + */ + private function getQuoteIdMaskFactory() + { + if (!$this->quoteIdMaskFactory) { + $this->quoteIdMaskFactory = ObjectManager::getInstance()->get(QuoteIdMaskFactory::class); + } + return $this->quoteIdMaskFactory; + } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php index 00f653922db5c4c89e173a32fd9ce29bd34b5ece..efe6a81ce9242f510561f54cd6b484a336b37dbf 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php @@ -167,4 +167,48 @@ class TotalTest extends \PHPUnit_Framework_TestCase { $this->assertEquals(0, $this->model->getBaseTotalAmount('great')); } + + /** + * Verify handling of serialized, non-serialized input into and out of getFullInfo() + * + * @param $input + * @param $expected + * @dataProvider getFullInfoDataProvider + */ + public function testGetFullInfo($input, $expected) + { + $this->model->setFullInfo($input); + $this->assertEquals($expected, $this->model->getFullInfo()); + } + + /** + * @return array + */ + public function getFullInfoDataProvider() + { + $myArray = ['team' => 'kiwis']; + $serializedInput = serialize($myArray); + + return [ + 'simple array' => [ + $myArray, + $myArray, + ], + + 'serialized array' => [ + $serializedInput, + $myArray, + ], + + 'null input/output' => [ + null, + null, + ], + + 'float input' => [ + 1.23, + 1.23, + ], + ]; + } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php index a22d10aee9318aed5d07fa70768e8ecfa90ca940..07f9987a902522b5701c6fdab888274f664d2073 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/CompareTest.php @@ -187,4 +187,31 @@ class CompareTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue([])); $this->assertFalse($this->helper->compare($this->itemMock, $this->comparedMock)); } + + /** + * Verify that compare ignores empty options. + */ + public function testCompareWithEmptyValues() + { + $this->itemMock->expects($this->any()) + ->method('getProductId') + ->will($this->returnValue(1)); + $this->comparedMock->expects($this->any()) + ->method('getProductId') + ->will($this->returnValue(1)); + + $this->itemMock->expects($this->once())->method('getOptions')->willReturn([ + $this->getOptionMock('option-1', serialize([ + 'non-empty-option' => 'test', + 'empty_option' => '' + ])) + ]); + $this->comparedMock->expects($this->once())->method('getOptions')->willReturn([ + $this->getOptionMock('option-1', serialize([ + 'non-empty-option' => 'test' + ])) + ]); + + $this->assertTrue($this->helper->compare($this->itemMock, $this->comparedMock)); + } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index cd2a56b3b9d7b414b24530e17dbf409dfcf2fe14..2664e7ef21ad76da3bfd6b844caf42d8bbdb8900 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -124,7 +124,7 @@ class QuoteManagementTest extends \PHPUnit_Framework_TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $quoteFactoryMock; + private $quoteIdMock; /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -238,7 +238,6 @@ class QuoteManagementTest extends \PHPUnit_Framework_TestCase ); $this->quoteFactoryMock = $this->getMock('\Magento\Quote\Model\QuoteFactory', ['create'], [], '', false); - $this->model = $objectManager->getObject( '\Magento\Quote\Model\QuoteManagement', [ @@ -264,6 +263,12 @@ class QuoteManagementTest extends \PHPUnit_Framework_TestCase 'quoteFactory' => $this->quoteFactoryMock ] ); + + // Set the new dependency + $this->quoteIdMock = $this->getMock('Magento\Quote\Model\QuoteIdMask', [], [], '', false); + $quoteIdFactoryMock = $this->getMock(\Magento\Quote\Model\QuoteIdMaskFactory::class, ['create'], [], '', false); + $this->setPropertyValue($this->model, 'quoteIdMaskFactory', $quoteIdFactoryMock); + } public function testCreateEmptyCartAnonymous() @@ -508,6 +513,13 @@ class QuoteManagementTest extends \PHPUnit_Framework_TestCase $customerId = 455; $storeId = 5; + $this->getPropertyValue($this->model, 'quoteIdMaskFactory') + ->expects($this->once()) + ->method('create') + ->willReturn($this->quoteIdMock); + $this->quoteIdMock->expects($this->once())->method('load')->with($cartId, 'quote_id')->willReturnSelf(); + $this->quoteIdMock->expects($this->once())->method('getId')->willReturn(10); + $this->quoteIdMock->expects($this->once())->method('delete'); $quoteMock = $this->getMock( '\Magento\Quote\Model\Quote', ['getCustomerId', 'setCustomer', 'setCustomerIsGuest'], @@ -739,7 +751,7 @@ class QuoteManagementTest extends \PHPUnit_Framework_TestCase $this->checkoutSessionMock->expects($this->once())->method('setLastOrderId')->with($orderId); $this->checkoutSessionMock->expects($this->once())->method('setLastRealOrderId')->with($orderIncrementId); $this->checkoutSessionMock->expects($this->once())->method('setLastOrderStatus')->with($orderStatus); - + $this->assertEquals($orderId, $service->placeOrder($cartId)); } @@ -935,7 +947,7 @@ class QuoteManagementTest extends \PHPUnit_Framework_TestCase $order = $this->getMock( 'Magento\Sales\Model\Order', ['setShippingAddress', 'getAddressesCollection', 'getAddresses', 'getBillingAddress', 'addAddresses', - 'setBillingAddress', 'setAddresses', 'setPayment', 'setItems', 'setQuoteId'], + 'setBillingAddress', 'setAddresses', 'setPayment', 'setItems', 'setQuoteId'], [], '', false @@ -979,4 +991,37 @@ class QuoteManagementTest extends \PHPUnit_Framework_TestCase ->willReturn($cartMock); $this->assertEquals($cartMock, $this->model->getCartForCustomer($customerId)); } + + /** + * Get any object property value. + * + * @param $object + * @param $property + * @return mixed + */ + protected function getPropertyValue($object, $property) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + + return $reflectionProperty->getValue($object); + } + + /** + * Set object property value. + * + * @param $object + * @param $property + * @param $value + */ + protected function setPropertyValue(&$object, $property, $value) + { + $reflection = new \ReflectionClass(get_class($object)); + $reflectionProperty = $reflection->getProperty($property); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($object, $value); + + return $object; + } } diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/totals/tax.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/totals/tax.phtml index ad0e314930e095aa0cdeca00280924c35b525342..acf10ccbaa08726485065b67733c2cd52263c477 100755 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/totals/tax.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/totals/tax.phtml @@ -11,11 +11,11 @@ $taxAmount = $block->getTotal()->getValue(); <?php if (($taxAmount == 0 && $this->helper('Magento\Tax\Helper\Data')->displayZeroTax()) || ($taxAmount > 0)): ?> <?php global $taxIter; $taxIter++; ?> <?php if ($this->helper('Magento\Tax\Helper\Data')->displayFullSummary()): ?> -<?php $isTop = 1; ?> - <?php foreach ($block->getTotal()->getFullInfo() as $info): ?> + <?php $isTop = 1; ?> + <?php foreach ($block->getTotal()->getFullInfo() as $info): ?> <?php if (isset($info['hidden']) && $info['hidden']) { - continue; -} ?> + continue; + } ?> <?php $percent = $info['percent']; ?> <?php $amount = $info['amount']; ?> <?php $rates = $info['rates']; ?> @@ -39,7 +39,7 @@ $taxAmount = $block->getTotal()->getValue(); <?php $isFirst = 0; ?> <?php $isTop = 0; ?> <?php endforeach; ?> - <?php endforeach; ?> + <?php endforeach; ?> <?php endif;?> <?php $class = "{$block->getTotal()->getCode()} " . ($this->helper('Magento\Tax\Helper\Data')->displayFullSummary() ? 'summary-total' : ''); ?> <tr<?php if ($this->helper('Magento\Tax\Helper\Data')->displayFullSummary()): ?> onclick="expandDetails(this, '.summary-details-<?php /* @escapeNotVerified */ echo $taxIter;?>')"<?php endif; ?> class="<?php /* @escapeNotVerified */ echo $class;?> row-totals"> diff --git a/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php b/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php index 93c628c9a2e2603e3deecadc37a00eafcd419e3f..09f925f4f6ecdbf6d6c857e3529605c5cf74250a 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php +++ b/app/code/Magento/SalesRule/Model/Rule/Metadata/ValueProvider.php @@ -78,7 +78,7 @@ class ValueProvider $applyOptions = [ ['label' => __('Percent of product price discount'), 'value' => Rule::BY_PERCENT_ACTION], ['label' => __('Fixed amount discount'), 'value' => Rule::BY_FIXED_ACTION], - ['label' => __('Fixed amount discount for whole cart'), 'value' => Rule::BY_PERCENT_ACTION], + ['label' => __('Fixed amount discount for whole cart'), 'value' => Rule::CART_FIXED_ACTION], ['label' => __('Buy X get Y free (discount amount is Y)'), 'value' => Rule::BUY_X_GET_Y_ACTION] ]; diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php index d93c504af9e71c7490c304f6b5fd43e87c335daa..ec3d3c86d7288b7292fe630478812669286bbc8e 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Metadata/_files/MetaData.php @@ -1,4 +1,9 @@ <?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + return [ 'rule_information' => [ @@ -101,7 +106,7 @@ return [ ], [ 'label' => __('Fixed amount discount for whole cart'), - 'value' => 'by_percent', + 'value' => 'cart_fixed', ], [ 'label' => __( diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js b/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js index b636a613d622ffe50570117408c6be86b414df0a..606fe4013ea762c5460c49202907463d7c7952f2 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js +++ b/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js @@ -13,26 +13,24 @@ define( 'jquery', 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/resource-url-manager', - 'Magento_Checkout/js/model/payment-service', 'Magento_Checkout/js/model/error-processor', 'Magento_SalesRule/js/model/payment/discount-messages', 'mage/storage', - 'Magento_Checkout/js/action/get-totals', 'mage/translate', - 'Magento_Checkout/js/model/payment/method-list' + 'Magento_Checkout/js/action/get-payment-information', + 'Magento_Checkout/js/model/totals' ], function ( ko, $, quote, urlManager, - paymentService, errorProcessor, messageContainer, storage, - getTotalsAction, $t, - paymentMethodList + getPaymentInformationAction, + totals ) { 'use strict'; return function (couponCode, isApplied, isLoading) { @@ -49,11 +47,10 @@ define( var deferred = $.Deferred(); isLoading(false); isApplied(true); - getTotalsAction([], deferred); - $.when(deferred).done(function() { - paymentService.setPaymentMethods( - paymentMethodList() - ); + totals.isLoading(true); + getPaymentInformationAction(deferred); + $.when(deferred).done(function () { + totals.isLoading(false); }); messageContainer.addSuccessMessage({'message': message}); } @@ -61,6 +58,7 @@ define( ).fail( function (response) { isLoading(false); + totals.isLoading(false); errorProcessor.process(response, messageContainer); } ); diff --git a/app/code/Magento/Security/Model/ResourceModel/AdminSessionInfo/Collection.php b/app/code/Magento/Security/Model/ResourceModel/AdminSessionInfo/Collection.php index f85e2d308d7d1205110833074e33a864267745a9..5c6537030396357851e4b73984e5302119d64bde 100644 --- a/app/code/Magento/Security/Model/ResourceModel/AdminSessionInfo/Collection.php +++ b/app/code/Magento/Security/Model/ResourceModel/AdminSessionInfo/Collection.php @@ -16,7 +16,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab protected $_idFieldName = 'id'; /** - * @var \Magento\Framework\Stdlib\DateTime + * @var \Magento\Framework\Stdlib\DateTime\DateTime */ protected $dateTime; @@ -25,7 +25,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy * @param \Magento\Framework\Event\ManagerInterface $eventManager - * @param \Magento\Framework\Stdlib\DateTime $dateTime + * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb|null $resource */ @@ -34,7 +34,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab \Psr\Log\LoggerInterface $logger, \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Framework\Stdlib\DateTime $dateTime, + \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null ) { @@ -108,9 +108,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab */ public function filterExpiredSessions($sessionLifeTime) { + $connection = $this->getConnection(); + $gmtTimestamp = $this->dateTime->gmtTimestamp(); $this->addFieldToFilter( 'updated_at', - ['gt' => $this->dateTime->formatDate($this->dateTime->gmDate('U', null) - $sessionLifeTime)] + ['gt' => $connection->formatDate($gmtTimestamp - $sessionLifeTime)] ); return $this; } diff --git a/app/code/Magento/Security/Model/ResourceModel/PasswordResetRequestEvent/Collection.php b/app/code/Magento/Security/Model/ResourceModel/PasswordResetRequestEvent/Collection.php index f1c1a867717d1d778570708a05be21ba3d40cc3c..5b455f8c686e3a6e76e065a03ee34c45a44160ce 100644 --- a/app/code/Magento/Security/Model/ResourceModel/PasswordResetRequestEvent/Collection.php +++ b/app/code/Magento/Security/Model/ResourceModel/PasswordResetRequestEvent/Collection.php @@ -16,7 +16,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab protected $_idFieldName = 'id'; /** - * @var \Magento\Framework\Stdlib\DateTime + * @var \Magento\Framework\Stdlib\DateTime\DateTime */ protected $dateTime; @@ -25,7 +25,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy * @param \Magento\Framework\Event\ManagerInterface $eventManager - * @param \Magento\Framework\Stdlib\DateTime $dateTime + * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb|null $resource */ @@ -34,7 +34,7 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab \Psr\Log\LoggerInterface $logger, \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Framework\Stdlib\DateTime $dateTime, + \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null ) { @@ -102,9 +102,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab */ public function filterByLifetime($lifetime) { + $connection = $this->getConnection(); + $gmtTimestamp = $this->dateTime->gmtTimestamp(); $this->addFieldToFilter( 'created_at', - ['gt' => $this->dateTime->formatDate($this->dateTime->gmDate('U', null) - $lifetime)] + ['gt' => $connection->formatDate($gmtTimestamp - $lifetime)] ); return $this; diff --git a/app/code/Magento/Security/Setup/InstallSchema.php b/app/code/Magento/Security/Setup/InstallSchema.php index 02143411a5d50c0e78311b6dfe42578a25cf7a93..db090066d9cbd054989d6b2c87dfffc74e94e622 100644 --- a/app/code/Magento/Security/Setup/InstallSchema.php +++ b/app/code/Magento/Security/Setup/InstallSchema.php @@ -88,9 +88,9 @@ class InstallSchema implements InstallSchemaInterface ) ->addColumn( 'ip', - \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, - null, - ['unsigned' => true, 'nullable' => false], + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 15, + ['nullable' => false], 'Remote user IP' ) ->addIndex( @@ -151,9 +151,9 @@ class InstallSchema implements InstallSchemaInterface ) ->addColumn( 'ip', - \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, - null, - ['unsigned' => true, 'nullable' => false], + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 15, + ['nullable' => false], 'Remote user IP' ) ->addIndex( diff --git a/app/code/Magento/Security/Setup/UpgradeSchema.php b/app/code/Magento/Security/Setup/UpgradeSchema.php new file mode 100644 index 0000000000000000000000000000000000000000..f17f0f496024832f0abb52de893a20adf9e2978c --- /dev/null +++ b/app/code/Magento/Security/Setup/UpgradeSchema.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Security\Setup; + +use Magento\Framework\Setup\UpgradeSchemaInterface; +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\SchemaSetupInterface; + +/** + * Upgrade the Catalog module DB scheme + */ +class UpgradeSchema implements UpgradeSchemaInterface +{ + /** + * {@inheritdoc} + */ + public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context) + { + $setup->startSetup(); + + if (version_compare($context->getVersion(), '2.0.1', '<')) { + $setup->getConnection()->changeColumn( + $setup->getTable(\Magento\Security\Setup\InstallSchema::ADMIN_SESSIONS_DB_TABLE_NAME), + 'ip', + 'ip', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 'length' => 15, + 'nullable' => false, + 'comment' => 'Remote user IP' + ] + ); + + $setup->getConnection()->changeColumn( + $setup->getTable(\Magento\Security\Setup\InstallSchema::PASSWORD_RESET_REQUEST_EVENT_DB_TABLE_NAME), + 'ip', + 'ip', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 'length' => 15, + 'nullable' => false, + 'comment' => 'Remote user IP' + ] + ); + } + + $setup->endSetup(); + } +} diff --git a/app/code/Magento/Security/Test/Unit/Model/ResourceModel/AdminSessionInfo/CollectionTest.php b/app/code/Magento/Security/Test/Unit/Model/ResourceModel/AdminSessionInfo/CollectionTest.php index 633ab04dd03b5802440b9d0342a415784df9c9ba..c5229e573d08e6446bae09e647b85fc69f2d2134 100644 --- a/app/code/Magento/Security/Test/Unit/Model/ResourceModel/AdminSessionInfo/CollectionTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/ResourceModel/AdminSessionInfo/CollectionTest.php @@ -6,8 +6,6 @@ namespace Magento\Security\Test\Unit\Model\ResourceModel\AdminSessionInfo; -use Magento\Security\Model\ConfigInterface; - /** * Test class for \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection testing */ @@ -16,7 +14,7 @@ class CollectionTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection */ protected $collectionMock; - /** @var \Magento\Framework\Stdlib\DateTime */ + /** @var \Magento\Framework\Stdlib\DateTime\DateTime */ protected $dateTimeMock; /** @var \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ @@ -29,7 +27,7 @@ class CollectionTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->dateTimeMock = $this->getMock( - '\Magento\Framework\Stdlib\DateTime', + \Magento\Framework\Stdlib\DateTime\DateTime::class, [], [], '', @@ -136,14 +134,14 @@ class CollectionTest extends \PHPUnit_Framework_TestCase $timestamp = time(); $this->dateTimeMock->expects($this->once()) - ->method('gmDate') + ->method('gmtTimestamp') ->willReturn($timestamp); $this->collectionMock->expects($this->once()) ->method('addFieldToFilter') ->with( 'updated_at', - ['gt' => $this->dateTimeMock->formatDate($timestamp - $sessionLifeTime)] + ['gt' => $this->collectionMock->getConnection()->formatDate($timestamp - $sessionLifeTime)] ) ->willReturnSelf(); diff --git a/app/code/Magento/Security/Test/Unit/Model/ResourceModel/PasswordResetRequestEvent/CollectionTest.php b/app/code/Magento/Security/Test/Unit/Model/ResourceModel/PasswordResetRequestEvent/CollectionTest.php index 4492326851582eef1b340f1bef52026a09a8ee35..6f9664031bbf70bb1033be719b7ad5322a6914ba 100644 --- a/app/code/Magento/Security/Test/Unit/Model/ResourceModel/PasswordResetRequestEvent/CollectionTest.php +++ b/app/code/Magento/Security/Test/Unit/Model/ResourceModel/PasswordResetRequestEvent/CollectionTest.php @@ -6,8 +6,6 @@ namespace Magento\Security\Test\Unit\Model\ResourceModel\PasswordResetRequestEvent; -use Magento\Security\Model\ConfigInterface; - /** * Test class for \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection testing */ @@ -16,7 +14,7 @@ class CollectionTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Security\Model\ResourceModel\PasswordResetRequestEvent\Collection */ protected $collectionMock; - /** @var \Magento\Framework\Stdlib\DateTime */ + /** @var \Magento\Framework\Stdlib\DateTime\DateTime */ protected $dateTimeMock; /** @var \Magento\Framework\DB\Select */ @@ -61,7 +59,7 @@ class CollectionTest extends \PHPUnit_Framework_TestCase ); $this->dateTimeMock = $this->getMock( - '\Magento\Framework\Stdlib\DateTime', + '\Magento\Framework\Stdlib\DateTime\DateTime', [], [], '', @@ -175,14 +173,14 @@ class CollectionTest extends \PHPUnit_Framework_TestCase $timestamp = time(); $this->dateTimeMock->expects($this->once()) - ->method('gmDate') + ->method('gmtTimestamp') ->willReturn($timestamp); $this->collectionMock->expects($this->once()) ->method('addFieldToFilter') ->with( 'created_at', - ['gt' => $this->dateTimeMock->formatDate($timestamp - $lifetime)] + ['gt' => $this->collectionMock->getConnection()->formatDate($timestamp - $lifetime)] ) ->willReturnSelf(); diff --git a/app/code/Magento/Security/etc/module.xml b/app/code/Magento/Security/etc/module.xml index d0bdba4a6f79cbf0bfe0cff841ef91d0c3faaa0a..85ad00b5c0729e90016f938afeef49e9ddf32f44 100644 --- a/app/code/Magento/Security/etc/module.xml +++ b/app/code/Magento/Security/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Security" setup_version="2.0.0"> + <module name="Magento_Security" setup_version="2.0.1"> <sequence> <module name="Magento_Backend"/> <module name="Magento_Store"/> diff --git a/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml b/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml index 92d0b199ae7071b6f482aa9d153a68362d6e1f0c..4cdfbac691e6aadf18530ac41203b7790788bc39 100644 --- a/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml @@ -39,10 +39,10 @@ <?php if ($this->helper('Magento\Tax\Helper\Data')->displayFullSummary() && $_value != 0): ?> <?php $isTop = 1; ?> - <?php foreach (unserialize($block->getTotal()->getFullInfo()) as $info): ?> + <?php foreach ($block->getTotal()->getFullInfo() as $info): ?> <?php if (isset($info['hidden']) && $info['hidden']) { - continue; -} ?> + continue; + } ?> <?php $percent = $info['percent']; ?> <?php $amount = $info['amount']; ?> <?php $rates = $info['rates']; ?> diff --git a/app/code/Magento/Theme/Block/Html/Topmenu.php b/app/code/Magento/Theme/Block/Html/Topmenu.php index d0205f51453bc4d003e6347af7b5754ac40d6d4c..52a4714fea8db053b49790a548538644866744cc 100644 --- a/app/code/Magento/Theme/Block/Html/Topmenu.php +++ b/app/code/Magento/Theme/Block/Html/Topmenu.php @@ -330,7 +330,9 @@ class Topmenu extends Template implements IdentityInterface */ public function addIdentity($identity) { - $this->identities[] = $identity; + if (!in_array($identity, $this->identities)) { + $this->identities[] = $identity; + } } /** @@ -364,4 +366,14 @@ class Topmenu extends Template implements IdentityInterface { return array_merge(parent::getCacheTags(), $this->getIdentities()); } + + /** + * Get menu object. + * + * @return Node + */ + public function getMenu() + { + return $this->_menu; + } } diff --git a/app/code/Magento/Ui/view/base/templates/stepswizard.phtml b/app/code/Magento/Ui/view/base/templates/stepswizard.phtml index b6f74dda267eb9dcbf30932a6659989a934b8046..82cc34b42b12088f781629fac0a8a53948437d0e 100644 --- a/app/code/Magento/Ui/view/base/templates/stepswizard.phtml +++ b/app/code/Magento/Ui/view/base/templates/stepswizard.phtml @@ -64,7 +64,7 @@ "component": "Magento_Ui/js/lib/step-wizard", "initData": <?= /* @escapeNotVerified */ $this->helper("Magento\Framework\Json\Helper\Data")->jsonEncode($block->getInitData()) ?>, "stepsNames": <?= /* @escapeNotVerified */ $this->helper("Magento\Framework\Json\Helper\Data")->jsonEncode($block->getStepComponents()) ?>, - "modalClass": "<?= /* @noEscape */ $block->getData('config/modal')?>" + "modalClass": "<?= /* @noEscape */ $block->getData('config/dataScope')?>" } } } diff --git a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php index efcfda9cb67bbc8c76d3775d4ba8f3fe4ac2882d..d91faf2da5dd699989cc87cd7794b1a395516598 100644 --- a/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php +++ b/app/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword.php @@ -6,6 +6,8 @@ */ namespace Magento\User\Controller\Adminhtml\Auth; +use Magento\Security\Model\SecurityManager; + class Forgotpassword extends \Magento\User\Controller\Adminhtml\Auth { /** @@ -31,12 +33,15 @@ class Forgotpassword extends \Magento\User\Controller\Adminhtml\Auth * Forgot administrator password action * * @return void + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() { $email = (string)$this->getRequest()->getParam('email'); $params = $this->getRequest()->getParams(); + /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); if (!empty($email) && !empty($params)) { // Validate received data to be an email address if (\Zend_Validate::is($email, 'EmailAddress')) { @@ -47,7 +52,6 @@ class Forgotpassword extends \Magento\User\Controller\Adminhtml\Auth ); } catch (\Magento\Framework\Exception\SecurityViolationException $exception) { $this->messageManager->addErrorMessage($exception->getMessage()); - $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('admin'); } $collection = $this->_objectManager->get('Magento\User\Model\ResourceModel\User\Collection'); @@ -55,20 +59,28 @@ class Forgotpassword extends \Magento\User\Controller\Adminhtml\Auth $collection->addFieldToFilter('email', $email); $collection->load(false); - if ($collection->getSize() > 0) { - foreach ($collection as $item) { - /** @var \Magento\User\Model\User $user */ - $user = $this->_userFactory->create()->load($item->getId()); - if ($user->getId()) { - $newPassResetToken = $this->_objectManager->get( - 'Magento\User\Helper\Data' - )->generateResetPasswordLinkToken(); - $user->changeResetPasswordLinkToken($newPassResetToken); - $user->save(); - $user->sendPasswordResetConfirmationEmail(); + try { + if ($collection->getSize() > 0) { + foreach ($collection as $item) { + /** @var \Magento\User\Model\User $user */ + $user = $this->_userFactory->create()->load($item->getId()); + if ($user->getId()) { + $newPassResetToken = $this->_objectManager->get( + 'Magento\User\Helper\Data' + )->generateResetPasswordLinkToken(); + $user->changeResetPasswordLinkToken($newPassResetToken); + $user->save(); + $user->sendPasswordResetConfirmationEmail(); + } + break; } - break; } + } catch (\Exception $exception) { + $this->messageManager->addExceptionMessage( + $exception, + __('We\'re unable to send the password reset email.') + ); + return $resultRedirect->setPath('admin'); } // @codingStandardsIgnoreStart $this->messageManager->addSuccess(__('We\'ll email you a link to reset your password.')); diff --git a/app/code/Magento/Webapi/Model/Soap/Wsdl/Generator.php b/app/code/Magento/Webapi/Model/Soap/Wsdl/Generator.php index d5c0bcc678f6a4af813fe88d6f2419f9561a5134..038f5cdcfc444802259049eac10e371fba17edec 100644 --- a/app/code/Magento/Webapi/Model/Soap/Wsdl/Generator.php +++ b/app/code/Magento/Webapi/Model/Soap/Wsdl/Generator.php @@ -12,6 +12,7 @@ use Magento\Webapi\Model\Soap\Wsdl; use Magento\Webapi\Model\Soap\WsdlFactory; use Magento\Framework\Webapi\Authorization; use Magento\Webapi\Model\ServiceMetadata; +use Magento\Framework\Exception\AuthorizationException; /** * WSDL generator. @@ -364,4 +365,21 @@ class Generator extends AbstractSchemaGenerator { return $this->serviceMetadata->getServiceMetadata($serviceName); } + + /** + * {@inheritdoc} + */ + protected function getAllowedServicesMetadata($requestedServices) + { + $allowedServicesMetadata = parent::getAllowedServicesMetadata($requestedServices); + if (!$allowedServicesMetadata) { + throw new AuthorizationException( + __( + AuthorizationException::NOT_AUTHORIZED, + ['resources' => implode(', ', $requestedServices)] + ) + ); + } + return $allowedServicesMetadata; + } } diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index fbf2485d37542a489d3a8e6b8bf447a6d94e81c5..2235df9aff0772f05eb7ea71d0ebf60010893570 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -309,7 +309,7 @@ class Widget } elseif (trim($value) == '') { $widget = $this->getConfigAsObject($type); $parameters = $widget->getParameters(); - if (is_object($parameters[$name])) { + if (isset($parameters[$name]) && is_object($parameters[$name])) { $value = $parameters[$name]->getValue(); } } diff --git a/composer.json b/composer.json index 73115b15a9f32a8af1a49a14a7658cd5daf9804c..3734cdd372bba9951c78de4769fa6773429b1290 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "zendframework/zend-http": "~2.4.6", "magento/zendframework1": "1.12.16", "colinmollenhour/credis": "1.6", - "colinmollenhour/php-redis-session-abstract": "1.0", + "colinmollenhour/php-redis-session-abstract": "1.1", "composer/composer": "1.0.0-beta1", "monolog/monolog": "1.16.0", "oyejorge/less.php": "1.7.0.3", diff --git a/composer.lock b/composer.lock index 4aaae63b403d43bf5ce7a75886dc6c8cedb6adb0..2f0873b997487361830407de6878ee657eab5928 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "d81a16c234a62403d9b5b06057393d9d", - "content-hash": "cd415001aebab7eb87bb62e2223509fc", + "hash": "666640f73320778f40be99a86695ef1c", + "content-hash": "1a3c1d3316e4448159bbcc436015ad2b", "packages": [ { "name": "braintree/braintree_php", @@ -95,16 +95,16 @@ }, { "name": "colinmollenhour/php-redis-session-abstract", - "version": "v1.0", + "version": "v1.1", "source": { "type": "git", "url": "https://github.com/colinmollenhour/php-redis-session-abstract.git", - "reference": "1308ddc08e2adbe303f7f8b8ead9beb5f2f2adf9" + "reference": "95330b7f29663dab81f53d1a438e4d927b6c5f66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/php-redis-session-abstract/zipball/1308ddc08e2adbe303f7f8b8ead9beb5f2f2adf9", - "reference": "1308ddc08e2adbe303f7f8b8ead9beb5f2f2adf9", + "url": "https://api.github.com/repos/colinmollenhour/php-redis-session-abstract/zipball/95330b7f29663dab81f53d1a438e4d927b6c5f66", + "reference": "95330b7f29663dab81f53d1a438e4d927b6c5f66", "shasum": "" }, "require": { @@ -129,7 +129,7 @@ ], "description": "A Redis-based session handler with optimistic locking", "homepage": "https://github.com/colinmollenhour/php-redis-session-abstract", - "time": "2016-01-14 16:04:27" + "time": "2016-02-03 18:13:49" }, { "name": "composer/composer", @@ -208,16 +208,16 @@ }, { "name": "composer/semver", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "df4463baa9f44fe6cf0a6da4fde2934d4c0a2747" + "reference": "84c47f3d8901440403217afc120683c7385aecb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/df4463baa9f44fe6cf0a6da4fde2934d4c0a2747", - "reference": "df4463baa9f44fe6cf0a6da4fde2934d4c0a2747", + "url": "https://api.github.com/repos/composer/semver/zipball/84c47f3d8901440403217afc120683c7385aecb8", + "reference": "84c47f3d8901440403217afc120683c7385aecb8", "shasum": "" }, "require": { @@ -266,28 +266,28 @@ "validation", "versioning" ], - "time": "2016-02-25 22:23:39" + "time": "2016-03-30 13:16:03" }, { "name": "composer/spdx-licenses", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "9e1c3926bb0842812967213d7c92827bc5883671" + "reference": "547659c3cacd3ccfe1b4714c2ff88cafc6b6793b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/9e1c3926bb0842812967213d7c92827bc5883671", - "reference": "9e1c3926bb0842812967213d7c92827bc5883671", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/547659c3cacd3ccfe1b4714c2ff88cafc6b6793b", + "reference": "547659c3cacd3ccfe1b4714c2ff88cafc6b6793b", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^5.3.2 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "~2.3" + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" }, "type": "library", "extra": { @@ -327,7 +327,7 @@ "spdx", "validator" ], - "time": "2015-10-05 11:27:42" + "time": "2016-03-25 10:57:10" }, { "name": "justinrainbow/json-schema", @@ -1181,16 +1181,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.8.3", + "version": "v2.8.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "78c468665c9568c3faaa9c416a7134308f2d85c3" + "reference": "47d2d8cade9b1c3987573d2943bb9352536cdb87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/78c468665c9568c3faaa9c416a7134308f2d85c3", - "reference": "78c468665c9568c3faaa9c416a7134308f2d85c3", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/47d2d8cade9b1c3987573d2943bb9352536cdb87", + "reference": "47d2d8cade9b1c3987573d2943bb9352536cdb87", "shasum": "" }, "require": { @@ -1237,20 +1237,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2016-01-27 05:14:19" + "time": "2016-03-07 14:04:32" }, { "name": "symfony/filesystem", - "version": "v2.8.3", + "version": "v2.8.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "65cb36b6539b1d446527d60457248f30d045464d" + "reference": "f08ffdf229252cd2745558cb2112df43903bcae4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/65cb36b6539b1d446527d60457248f30d045464d", - "reference": "65cb36b6539b1d446527d60457248f30d045464d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/f08ffdf229252cd2745558cb2112df43903bcae4", + "reference": "f08ffdf229252cd2745558cb2112df43903bcae4", "shasum": "" }, "require": { @@ -1286,20 +1286,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2016-02-22 15:02:30" + "time": "2016-03-27 10:20:16" }, { "name": "symfony/finder", - "version": "v3.0.3", + "version": "v3.0.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "623bda0abd9aa29e529c8e9c08b3b84171914723" + "reference": "c54e407b35bc098916704e9fd090da21da4c4f52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/623bda0abd9aa29e529c8e9c08b3b84171914723", - "reference": "623bda0abd9aa29e529c8e9c08b3b84171914723", + "url": "https://api.github.com/repos/symfony/finder/zipball/c54e407b35bc098916704e9fd090da21da4c4f52", + "reference": "c54e407b35bc098916704e9fd090da21da4c4f52", "shasum": "" }, "require": { @@ -1335,20 +1335,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-01-27 05:14:46" + "time": "2016-03-10 11:13:05" }, { "name": "symfony/process", - "version": "v2.8.3", + "version": "v2.8.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "7dedd5b60550f33dca16dd7e94ef8aca8b67bbfe" + "reference": "fb467471952ef5cf8497c029980e556b47545333" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7dedd5b60550f33dca16dd7e94ef8aca8b67bbfe", - "reference": "7dedd5b60550f33dca16dd7e94ef8aca8b67bbfe", + "url": "https://api.github.com/repos/symfony/process/zipball/fb467471952ef5cf8497c029980e556b47545333", + "reference": "fb467471952ef5cf8497c029980e556b47545333", "shasum": "" }, "require": { @@ -1384,7 +1384,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2016-02-02 13:33:15" + "time": "2016-03-23 13:11:46" }, { "name": "tedivm/jshrink", @@ -4112,16 +4112,16 @@ }, { "name": "symfony/config", - "version": "v2.8.3", + "version": "v2.8.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "0f8f94e6a32b5c480024eed5fa5cbd2790d0ad19" + "reference": "5273f4724dc5288fe7a33cb08077ab9852621f2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/0f8f94e6a32b5c480024eed5fa5cbd2790d0ad19", - "reference": "0f8f94e6a32b5c480024eed5fa5cbd2790d0ad19", + "url": "https://api.github.com/repos/symfony/config/zipball/5273f4724dc5288fe7a33cb08077ab9852621f2c", + "reference": "5273f4724dc5288fe7a33cb08077ab9852621f2c", "shasum": "" }, "require": { @@ -4161,20 +4161,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2016-02-22 16:12:45" + "time": "2016-03-04 07:54:35" }, { "name": "symfony/dependency-injection", - "version": "v2.8.3", + "version": "v2.8.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "62251761a7615435b22ccf562384c588b431be44" + "reference": "f7b4a498e679fa440b16facb934680a1527ed48c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/62251761a7615435b22ccf562384c588b431be44", - "reference": "62251761a7615435b22ccf562384c588b431be44", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f7b4a498e679fa440b16facb934680a1527ed48c", + "reference": "f7b4a498e679fa440b16facb934680a1527ed48c", "shasum": "" }, "require": { @@ -4223,20 +4223,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2016-02-28 16:34:46" + "time": "2016-03-21 07:27:21" }, { "name": "symfony/stopwatch", - "version": "v3.0.3", + "version": "v3.0.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "4a204804952ff267ace88cf499e0b4bb302a475e" + "reference": "6015187088421e9499d8f8316bdb396f8b806c06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/4a204804952ff267ace88cf499e0b4bb302a475e", - "reference": "4a204804952ff267ace88cf499e0b4bb302a475e", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/6015187088421e9499d8f8316bdb396f8b806c06", + "reference": "6015187088421e9499d8f8316bdb396f8b806c06", "shasum": "" }, "require": { @@ -4272,20 +4272,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2016-01-03 15:35:16" + "time": "2016-03-04 07:55:57" }, { "name": "symfony/yaml", - "version": "v2.8.3", + "version": "v2.8.4", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "2a4ee40acb880c56f29fb1b8886e7ffe94f3b995" + "reference": "584e52cb8f788a887553ba82db6caacb1d6260bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/2a4ee40acb880c56f29fb1b8886e7ffe94f3b995", - "reference": "2a4ee40acb880c56f29fb1b8886e7ffe94f3b995", + "url": "https://api.github.com/repos/symfony/yaml/zipball/584e52cb8f788a887553ba82db6caacb1d6260bb", + "reference": "584e52cb8f788a887553ba82db6caacb1d6260bb", "shasum": "" }, "require": { @@ -4321,7 +4321,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-02-23 07:41:20" + "time": "2016-03-04 07:54:35" }, { "name": "theseer/fdomdocument", diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartManagementTest.php index c67470fd69ae592503f7b18dba7eee920ba133a1..a201724eaaa6824e9c0500b2108699be44e33285 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartManagementTest.php @@ -70,9 +70,9 @@ class GuestCartManagementTest extends WebapiAbstract $quote = $this->objectManager->create('Magento\Quote\Model\Quote')->load('test01', 'reserved_order_id'); $cartId = $quote->getId(); /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ - $quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create('Magento\Quote\Model\QuoteIdMaskFactory') - ->create(); + $quoteIdMaskFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create('Magento\Quote\Model\QuoteIdMaskFactory'); + $quoteIdMask = $quoteIdMaskFactory->create(); $quoteIdMask->load($cartId, 'quote_id'); //Use masked cart Id $cartId = $quoteIdMask->getMaskedId(); @@ -110,6 +110,7 @@ class GuestCartManagementTest extends WebapiAbstract $this->assertEquals($customer->getId(), $quote->getCustomerId()); $this->assertEquals($customer->getFirstname(), $quote->getCustomerFirstname()); $this->assertEquals($customer->getLastname(), $quote->getCustomerLastname()); + $this->assertNull($quoteIdMaskFactory->create()->load($cartId, 'masked_id')->getId()); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/BaseService.php b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/BaseService.php index eff0dbba735f6295e4084f10bb8228f56ee04539..88d4e559e16d52967f1d452bfd95791765cd1255 100644 --- a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/BaseService.php +++ b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/BaseService.php @@ -25,7 +25,7 @@ abstract class BaseService extends \Magento\TestFramework\TestCase\WebapiAbstrac $this->_assertSoapException( $serviceInfo, $requestData, - 'SOAP-ERROR: Parsing WSDL: Couldn\'t bind to service' + 'Consumer is not authorized to access %resources' ); } elseif (TESTS_WEB_API_ADAPTER == self::ADAPTER_REST) { $this->_assertRestUnauthorizedException($serviceInfo, $requestData); @@ -111,7 +111,7 @@ abstract class BaseService extends \Magento\TestFramework\TestCase\WebapiAbstrac } if ($expectedMessage) { - $this->assertEquals($expectedMessage, $e->getMessage()); + $this->assertContains($expectedMessage, $e->getMessage()); } } } diff --git a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/SoapErrorHandlingTest.php b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/SoapErrorHandlingTest.php index c7ef1d00e02fad6a9311240065b3c32a247e2065..8cd95ef23a47e75b7915e7c114c1968833d81125 100644 --- a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/SoapErrorHandlingTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/SoapErrorHandlingTest.php @@ -110,8 +110,8 @@ class SoapErrorHandlingTest extends \Magento\TestFramework\TestCase\WebapiAbstra } catch (\SoapFault $e) { $this->checkSoapFault( $e, - 'SOAP-ERROR: Parsing WSDL: Couldn\'t bind to service', - 'WSDL' + 'Consumer is not authorized to access %resources', + 'env:Sender' ); } } diff --git a/dev/tests/api-functional/testsuite/Magento/Webapi/WsdlGenerationFromDataObjectTest.php b/dev/tests/api-functional/testsuite/Magento/Webapi/WsdlGenerationFromDataObjectTest.php index 2593e9d12f5f94c4e18f12e3f9720747994e1b56..9afdb1f26067a769726fd4d44f235d3c5958ba48 100644 --- a/dev/tests/api-functional/testsuite/Magento/Webapi/WsdlGenerationFromDataObjectTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Webapi/WsdlGenerationFromDataObjectTest.php @@ -65,6 +65,16 @@ class WsdlGenerationFromDataObjectTest extends \Magento\TestFramework\TestCase\W $this->_checkFaultsDeclaration($wsdlContent); } + public function testNoAuthorizedServices() + { + $wsdlUrl = $this->_getBaseWsdlUrl() . 'testModule5AllSoapAndRestV2'; + $connection = curl_init($wsdlUrl); + curl_setopt($connection, CURLOPT_RETURNTRANSFER, 1); + $responseContent = curl_exec($connection); + $this->assertEquals(curl_getinfo($connection, CURLINFO_HTTP_CODE), 401); + $this->assertContains("Consumer is not authorized to access %resources", $responseContent); + } + public function testInvalidWsdlUrlNoServices() { $responseContent = $this->_getWsdlContent($this->_getBaseWsdlUrl()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/_files/products_advanced.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/_files/products_advanced.php index 8146a04b34398a758c815c787feb9cfe047c786d..2ae37ed9b71946b9924064a3cd1fa0a30e6b3c88 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/_files/products_advanced.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/_files/products_advanced.php @@ -80,6 +80,11 @@ foreach ($prices as $price) { 'simple-' . $productId )->setPrice( $price + )->setStockData( + [ + 'qty' => 100, + 'is_in_stock' => 1, + ] )->setWeight( 18 )->setCategoryIds( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual.php index 4992805824538bc35ec803abea5056b6456bb134..7ec85f110a5b58bc9c721593e50e2c6fb4fbb682 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual.php @@ -16,4 +16,9 @@ $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL) ->setTaxClassId(0) ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->save(); + ->setStockData( + [ + 'qty' => 100, + 'is_in_stock' => 1, + ] + )->save(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Config/Backend/ManagestockTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Config/Backend/ManagestockTest.php deleted file mode 100644 index 12aae4429203d85c9095ba607dfe25f620852353..0000000000000000000000000000000000000000 --- a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Config/Backend/ManagestockTest.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\CatalogInventory\Model\Config\Backend; - -use Magento\TestFramework\Helper\Bootstrap as Bootstrap; - -/** - * Class ManagestockTest - */ -class ManagestockTest extends \PHPUnit_Framework_TestCase -{ - /** - * Data provider for testSaveAndRebuildIndex - * @return array - */ - public function saveAndRebuildIndexDataProvider() - { - return [ - [1, 1], - [0, 0], - ]; - } - - /** - * Test rebuild stock indexer on stock status config save - * - * @dataProvider saveAndRebuildIndexDataProvider - * - * @magentoAppIsolation enabled - * @magentoDbIsolation enabled - * @magentoConfigFixture default/cataloginventory/item_options/manage_stock 0 - * - * @param int $newStockValue new value for stock status - * @param int $callCount count matcher - */ - public function testSaveAndRebuildIndex($newStockValue, $callCount) - { - /** @var \Magento\CatalogInventory\Model\StockIndex */ - $stockManagement = $this->getMock( - '\Magento\CatalogInventory\Model\StockIndex', - ['rebuild'], - [], - '', - false - ); - - $stockManagement->expects($this->exactly($callCount)) - ->method('rebuild'); - - $manageStock = new Managestock( - Bootstrap::getObjectManager()->get('Magento\Framework\Model\Context'), - Bootstrap::getObjectManager()->get('Magento\Framework\Registry'), - Bootstrap::getObjectManager()->get('Magento\Framework\App\Config\ScopeConfigInterface'), - Bootstrap::getObjectManager()->get('Magento\Framework\App\Cache\TypeListInterface'), - $stockManagement, - Bootstrap::getObjectManager()->get('Magento\CatalogInventory\Model\Indexer\Stock\Processor'), - Bootstrap::getObjectManager()->get('Magento\Config\Model\ResourceModel\Config') - ); - - $manageStock->setPath('cataloginventory/item_options/manage_stock'); - $manageStock->setScope('default'); - $manageStock->setScopeId(0); - $manageStock->setValue($newStockValue); - - // assert - $manageStock->save(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStockTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStockTest.php index 8fbcad5267cf774401910a63b7dcf7b2c8bcd427..02ddb8e23b9924bc2acd357831724196ae1fbcf8 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStockTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStockTest.php @@ -17,12 +17,19 @@ class DefaultStockTest extends \PHPUnit_Framework_TestCase */ private $indexer; + /** + * @var \Magento\CatalogInventory\Api\StockConfigurationInterface + */ + private $stockConfiguration; + protected function setUp() { $this->indexer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock::class ); - + $this->stockConfiguration = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\CatalogInventory\Api\StockConfigurationInterface::class + ); } /** @@ -53,7 +60,7 @@ class DefaultStockTest extends \PHPUnit_Framework_TestCase ); $criteria = $criteriaFactory->create(); $criteria->setProductsFilter([$product->getId()]); - $criteria->addFilter('website', 'website_id', $testWebsite->getId()); + $criteria->addFilter('website', 'website_id', $this->stockConfiguration->getDefaultScopeId()); $items = $stockStatusRepository->getList($criteria)->getItems(); $this->assertEquals($product->getId(), $items[$product->getId()]->getProductId()); } diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php index 7ccfba9b3c1b7cf743cfeb30512ffc11902ddf80..4c15a2a9e08877c2b3516ad86f5458df7ed5acd3 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php @@ -17,7 +17,13 @@ $product ->setPrice(10) ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setLinksPurchasedSeparately(true); + ->setLinksPurchasedSeparately(true) + ->setStockData( + [ + 'qty' => 100, + 'is_in_stock' => 1, + ] + ); /** * @var \Magento\Downloadable\Api\Data\LinkInterfaceFactory $linkFactory diff --git a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payment_express.php b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payment_express.php index c94078c41d741682c467819a704610ec5cdad799..8fb3c96eae0b5993b4ec93b3eac13cfccb2de97e 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payment_express.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payment_express.php @@ -28,7 +28,12 @@ $product->setTypeId('simple') ->setPrice(10) ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->save(); + ->setStockData( + [ + 'qty' => 100, + 'is_in_stock' => 1, + ] + )->save(); $product->load(1); $billingData = [ diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/quote.php b/dev/tests/integration/testsuite/Magento/Sales/_files/quote.php index 2304dcdc21399e3dbe7936c83fc880ab06b38139..95f387213d16ccb97ffb4d41321cd2aa941f8fef 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/quote.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/quote.php @@ -17,7 +17,12 @@ $product->setTypeId('simple') ->setMetaDescription('meta description') ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->save(); + ->setStockData( + [ + 'qty' => 100, + 'is_in_stock' => 1, + ] + )->save(); $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create('Magento\Catalog\Api\ProductRepositoryInterface'); diff --git a/lib/internal/Magento/Framework/Code/Minifier/Adapter/Css/CSSmin.php b/lib/internal/Magento/Framework/Code/Minifier/Adapter/Css/CSSmin.php index 44eedd2c9fc40de783f778ccfa7568531db64907..90de5d2832b53cbaef102f2dd818032bba614aae 100644 --- a/lib/internal/Magento/Framework/Code/Minifier/Adapter/Css/CSSmin.php +++ b/lib/internal/Magento/Framework/Code/Minifier/Adapter/Css/CSSmin.php @@ -3,7 +3,6 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Framework\Code\Minifier\Adapter\Css; use CSSmin as CssMinLibrary; @@ -26,10 +25,24 @@ class CSSmin implements AdapterInterface /** * @param CssMinLibrary $cssMinifier + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct(CssMinLibrary $cssMinifier) { - $this->cssMinifier = $cssMinifier; + // TODO: set $cssMinifier in constructor once MAGETWO-51176 is resolved. + } + + /** + * Get CSS Minifier + * + * @return \CSSMin + */ + private function getCssMin() + { + if (!($this->cssMinifier instanceof \CSSMin)) { + $this->cssMinifier = new \CSSmin(false); + } + return $this->cssMinifier; } /** @@ -42,7 +55,7 @@ class CSSmin implements AdapterInterface { $pcreRecursionLimit = ini_get('pcre.recursion_limit'); ini_set('pcre.recursion_limit', self::PCRE_RECURSION_LIMIT); - $result = $this->cssMinifier->run($content); + $result = $this->getCssMin()->run($content); ini_set('pcre.recursion_limit', $pcreRecursionLimit); return $result; } diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/Minifier/Adapter/Css/CssMinTest.php b/lib/internal/Magento/Framework/Code/Test/Unit/Minifier/Adapter/Css/CssMinTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f6cc3c489206be68d5809017b01e6b727fa8462a --- /dev/null +++ b/lib/internal/Magento/Framework/Code/Test/Unit/Minifier/Adapter/Css/CssMinTest.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Code\Test\Unit\Minifier\Adapter\Css; + +class CssMinTest extends \PHPUnit_Framework_TestCase +{ + public function testMinify() + { + $cssMinMock = $this->getMockBuilder(\CSSmin::class) + ->disableOriginalConstructor() + ->getMock(); + $cssMinAdapter = new \Magento\Framework\Code\Minifier\Adapter\Css\CSSmin($cssMinMock); + $property = new \ReflectionProperty(\Magento\Framework\Code\Minifier\Adapter\Css\CSSmin::class, 'cssMinifier'); + $property->setAccessible(true); + $property->setValue($cssMinAdapter, $cssMinMock); + + $expectedResult = 'minified content'; + $cssMinMock->expects($this->once())->method('run')->willReturn($expectedResult); + $this->assertEquals($expectedResult, $cssMinAdapter->minify('not minified')); + } +} diff --git a/lib/internal/Magento/Framework/Console/Cli.php b/lib/internal/Magento/Framework/Console/Cli.php index 6e9110de1ab75200dc859f39018b7010deeb8423..9742acbbd237cdce6f379f2fbb218526d3f60f62 100644 --- a/lib/internal/Magento/Framework/Console/Cli.php +++ b/lib/internal/Magento/Framework/Console/Cli.php @@ -3,22 +3,23 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Framework\Console; -use Magento\Framework\Filesystem\Driver\File; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Application as SymfonyApplication; use Magento\Framework\App\Bootstrap; +use Magento\Framework\Filesystem\Driver\File; use Magento\Framework\Shell\ComplexParameter; -use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Magento\Framework\Setup\FilePermissions; +use Magento\Setup\Console\CompilerPreparation; /** - * Magento2 CLI Application. This is the hood for all command line tools supported by Magento. + * Magento 2 CLI Application. This is the hood for all command line tools supported by Magento * * {@inheritdoc} + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Cli extends SymfonyApplication { @@ -27,7 +28,9 @@ class Cli extends SymfonyApplication */ const INPUT_KEY_BOOTSTRAP = 'bootstrap'; - /** @var \Zend\ServiceManager\ServiceManager */ + /** + * @var \Zend\ServiceManager\ServiceManager + */ private $serviceManager; /** @@ -47,23 +50,6 @@ class Cli extends SymfonyApplication */ public function doRun(InputInterface $input, OutputInterface $output) { - // Check to make sure var/generation/Magento folder dir have read/execute permission for the current user - /** @var \Magento\Setup\Model\ObjectManagerProvider $omProvider */ - $omProvider = $this->serviceManager->get('Magento\Setup\Model\ObjectManagerProvider'); - /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ - $objectManager = $omProvider->get(); - /** @var \Magento\Framework\Setup\Filepermissions $filePermissions */ - $filePermissions = $objectManager->get('Magento\Framework\Setup\FilePermissions'); - if ($filePermissions->checkDirectoryPermissionForCLIUser() === false) { - $output->writeln( - "<error>Command line user (" - . get_current_user() - . ") may not have proper read+execute permissions for directories under \"var/generation/\" . " - . "Please address this issue before using Magento command line." - ); - return 0; - } - $exitCode = parent::doRun($input, $output); if ($this->initException) { $output->writeln( @@ -76,21 +62,31 @@ class Cli extends SymfonyApplication } /** - * @param string $name The name of the application - * @param string $version The version of the application + * @param string $name application name + * @param string $version application version + * @SuppressWarnings(PHPMD.ExitExpression) */ public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') { $this->serviceManager = \Zend\Mvc\Application::init(require BP . '/setup/config/application.config.php') ->getServiceManager(); + $generationDirectoryAccess = new GenerationDirectoryAccess($this->serviceManager); + if (!$generationDirectoryAccess->check()) { + $output = new ConsoleOutput(); + $output->writeln( + '<error>Command line user does not have read and write permissions on var/generation directory. Please' + . ' address this issue before using Magento command line.</error>' + ); + exit(0); + } /** - * Temporary workaround until the compiler is able to clear the generation directory. (MAGETWO-44493) + * Temporary workaround until the compiler is able to clear the generation directory + * @todo remove after MAGETWO-44493 resolved */ - if (class_exists('Magento\Setup\Console\CompilerPreparation')) { - (new \Magento\Setup\Console\CompilerPreparation($this->serviceManager, new ArgvInput(), new File())) - ->handleCompilerEnvironment(); + if (class_exists(CompilerPreparation::class)) { + $compilerPreparation = new CompilerPreparation($this->serviceManager, new ArgvInput(), new File()); + $compilerPreparation->handleCompilerEnvironment(); } - parent::__construct($name, $version); } diff --git a/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php b/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php new file mode 100644 index 0000000000000000000000000000000000000000..873669bcf8fff35aeac7874e922ad73943853953 --- /dev/null +++ b/lib/internal/Magento/Framework/Console/GenerationDirectoryAccess.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Console; + +use Magento\Framework\App\Bootstrap; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem\DriverPool; +use Magento\Framework\Filesystem\File\WriteFactory; +use Magento\Framework\Filesystem\Directory\Write; +use Zend\ServiceManager\ServiceManager; +use Magento\Setup\Mvc\Bootstrap\InitParamListener; + +/** + * Check var/generation read and write access + */ +class GenerationDirectoryAccess +{ + /** + * @var ServiceManager + */ + private $serviceManager; + + /** + * @param ServiceManager $serviceManager + */ + public function __construct( + ServiceManager $serviceManager + ) { + $this->serviceManager = $serviceManager; + } + + /** + * Check var/generation read and write access + * + * @return bool + */ + public function check() + { + $initParams = $this->serviceManager->get(InitParamListener::BOOTSTRAP_PARAM); + $filesystemDirPaths = isset($initParams[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS]) + ? $initParams[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] + : []; + $directoryList = new DirectoryList(BP, $filesystemDirPaths); + $generationDirectoryPath = $directoryList->getPath(DirectoryList::GENERATION); + $driverPool = new DriverPool(); + $fileWriteFactory = new WriteFactory($driverPool); + /** @var \Magento\Framework\Filesystem\DriverInterface $driver */ + $driver = $driverPool->getDriver(DriverPool::FILE); + $directoryWrite = new Write($fileWriteFactory, $driver, $generationDirectoryPath); + if ($directoryWrite->isExist()) { + if ($directoryWrite->isDirectory() + || $directoryWrite->isReadable() + ) { + try { + $probeFilePath = $generationDirectoryPath . DIRECTORY_SEPARATOR . time(); + $fileWriteFactory->create($probeFilePath, DriverPool::FILE, 'w'); + $directoryWrite->delete($probeFilePath); + } catch (\Exception $e) { + return false; + } + } else { + return false; + } + } else { + try { + $directoryWrite->create(); + } catch (\Exception $e) { + return false; + } + } + return true; + } +} diff --git a/lib/internal/Magento/Framework/Indexer/CacheContext.php b/lib/internal/Magento/Framework/Indexer/CacheContext.php index cf4c4bc5ee2334b64bc668af495e9b083e5b87f4..230adc543d4371a203fcf330c5a8de403e2a2150 100644 --- a/lib/internal/Magento/Framework/Indexer/CacheContext.php +++ b/lib/internal/Magento/Framework/Indexer/CacheContext.php @@ -15,6 +15,11 @@ class CacheContext implements \Magento\Framework\DataObject\IdentityInterface */ protected $entities = []; + /** + * @var array + */ + private $tags = []; + /** * Register entity Ids * @@ -29,6 +34,18 @@ class CacheContext implements \Magento\Framework\DataObject\IdentityInterface return $this; } + /** + * Register entity tags + * + * @param string $cacheTag + * @return $this + */ + public function registerTags($cacheTags) + { + $this->tags = array_merge($this->tags, $cacheTags); + return $this; + } + /** * Returns registered entities * @@ -57,6 +74,6 @@ class CacheContext implements \Magento\Framework\DataObject\IdentityInterface $identities[] = $cacheTag . '_' . $id; } } - return $identities; + return array_merge($identities, array_unique($this->tags)); } } diff --git a/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Config.php b/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Config.php index d4dce686583b060b6e98a8335bf96d9c04608598..1a23e2974f568d90c33d49067cc228f8666d6705 100644 --- a/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Config.php +++ b/lib/internal/Magento/Framework/Session/SaveHandler/Redis/Config.php @@ -5,9 +5,14 @@ */ namespace Magento\Framework\Session\SaveHandler\Redis; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface as StoreScopeInterface; use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\State; +/** + * Redis session save handler + */ class Config implements \Cm\RedisSession\Handler\ConfigInterface { /** @@ -95,6 +100,21 @@ class Config implements \Cm\RedisSession\Handler\ConfigInterface */ const PARAM_BREAK_AFTER = 'session/redis/break_after'; + /** + * Cookie lifetime config path + */ + const XML_PATH_COOKIE_LIFETIME = 'web/cookie/cookie_lifetime'; + + /** + * Admin session lifetime config path + */ + const XML_PATH_ADMIN_SESSION_LIFETIME = 'admin/security/session_lifetime'; + + /** + * Session max lifetime + */ + const SESSION_MAX_LIFETIME = 31536000; + /** * Deployment config * @@ -102,14 +122,24 @@ class Config implements \Cm\RedisSession\Handler\ConfigInterface */ private $deploymentConfig; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @param DeploymentConfig $deploymentConfig * @param State $appState + * @param ScopeConfigInterface $scopeConfig */ - public function __construct(DeploymentConfig $deploymentConfig, State $appState) - { + public function __construct( + DeploymentConfig $deploymentConfig, + State $appState, + ScopeConfigInterface $scopeConfig + ) { $this->deploymentConfig = $deploymentConfig; $this->appState = $appState; + $this->scopeConfig = $scopeConfig; } /** @@ -197,7 +227,7 @@ class Config implements \Cm\RedisSession\Handler\ConfigInterface */ public function getMaxLifetime() { - return $this->deploymentConfig->get(self::PARAM_MAX_LIFETIME); + return self::SESSION_MAX_LIFETIME; } /** @@ -247,4 +277,15 @@ class Config implements \Cm\RedisSession\Handler\ConfigInterface { return $this->deploymentConfig->get(self::PARAM_BREAK_AFTER . '_' . $this->appState->getAreaCode()); } + + /** + * {@inheritdoc} + */ + public function getLifetime() + { + if ($this->appState->getAreaCode() == \Magento\Framework\App\Area::AREA_ADMINHTML) { + return (int)$this->scopeConfig->getValue(self::XML_PATH_ADMIN_SESSION_LIFETIME); + } + return (int)$this->scopeConfig->getValue(self::XML_PATH_COOKIE_LIFETIME, StoreScopeInterface::SCOPE_STORE); + } } diff --git a/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/ConfigTest.php b/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/ConfigTest.php index f34ef8b756d3343620ce736529a089032ca0a403..c66c31d2fcdff67dcf11b21f35cb3a26c45950db 100644 --- a/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/ConfigTest.php +++ b/lib/internal/Magento/Framework/Session/Test/Unit/SaveHandler/Redis/ConfigTest.php @@ -5,35 +5,44 @@ */ namespace Magento\Framework\Session\Test\Unit\SaveHandler\Redis; +use Magento\Store\Model\ScopeInterface; use Magento\Framework\Session\SaveHandler\Redis\Config; class ConfigTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\Framework\App\DeploymentConfig + * @var \Magento\Framework\App\DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject */ - protected $deploymentConfig; + private $deploymentConfigMock; /** - * @var \Magento\Framework\App\State + * @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject */ - protected $appState; + private $appStateMock; /** - * @var \Magento\Framework\Session\SaveHandler\Redis\Config + * @var \Magento\Framework\App\Config|\PHPUnit_Framework_MockObject_MockObject */ - protected $config; + private $scopeConfigMock; + + /** + * @var Config + */ + private $config; public function setUp() { - $this->deploymentConfig = $this->getMock('Magento\Framework\App\DeploymentConfig', [], [], '', false); - $this->appState = $this->getMock('Magento\Framework\App\State', [], [], '', false); + $this->deploymentConfigMock = $this->getMock(\Magento\Framework\App\DeploymentConfig::class, [], [], '', false); + $this->appStateMock = $this->getMock(\Magento\Framework\App\State::class, [], [], '', false); + $this->scopeConfigMock = $this->getMock(\Magento\Framework\App\Config::class, [], [], '', false); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->config = $objectManager->getObject( - 'Magento\Framework\Session\SaveHandler\Redis\Config', + Config::class, [ - 'deploymentConfig' => $this->deploymentConfig, - 'appState' => $this->appState + 'deploymentConfig' => $this->deploymentConfigMock, + 'appState' => $this->appStateMock, + 'scopeConfig' => $this->scopeConfigMock ] ); } @@ -41,7 +50,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetLogLevel() { $expected = 2; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_LOG_LEVEL) ->willReturn($expected); @@ -51,7 +60,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetHost() { $expected = '127.0.0.1'; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_HOST) ->willReturn($expected); @@ -61,7 +70,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetPort() { $expected = 1234; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_PORT) ->willReturn($expected); @@ -71,7 +80,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetDatabase() { $expected = 2; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_DATABASE) ->willReturn($expected); @@ -81,7 +90,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetPassword() { $expected = 'password'; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_PASSWORD) ->willReturn($expected); @@ -91,7 +100,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetTimeout() { $expected = 10; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_TIMEOUT) ->willReturn($expected); @@ -101,7 +110,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetPersistentIdentifier() { $expected = 'sess01'; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_PERSISTENT_IDENTIFIER) ->willReturn($expected); @@ -111,7 +120,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetCompressionThreshold() { $expected = 2; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_COMPRESSION_THRESHOLD) ->willReturn($expected); @@ -121,7 +130,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetCompressionLibrary() { $expected = 'gzip'; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_COMPRESSION_LIBRARY) ->willReturn($expected); @@ -131,7 +140,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetMaxConcurrency() { $expected = 6; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_MAX_CONCURRENCY) ->willReturn($expected); @@ -140,18 +149,13 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetMaxLifetime() { - $expected = 30; - $this->deploymentConfig->expects($this->once()) - ->method('get') - ->with(Config::PARAM_MAX_LIFETIME) - ->willReturn($expected); - $this->assertEquals($this->config->getMaxLifetime(), $expected); + $this->assertEquals($this->config->getMaxLifetime(), Config::SESSION_MAX_LIFETIME); } public function testGetMinLifetime() { $expected = 30; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_MIN_LIFETIME) ->willReturn($expected); @@ -161,7 +165,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetDisableLocking() { $expected = false; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_DISABLE_LOCKING) ->willReturn($expected); @@ -171,7 +175,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetBotLifetime() { $expected = 30; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_BOT_LIFETIME) ->willReturn($expected); @@ -181,7 +185,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetBotFirstLifetime() { $expected = 30; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_BOT_FIRST_LIFETIME) ->willReturn($expected); @@ -191,7 +195,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function testGetFirstLifetime() { $expected = 30; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_FIRST_LIFETIME) ->willReturn($expected); @@ -202,13 +206,44 @@ class ConfigTest extends \PHPUnit_Framework_TestCase { $areaCode = 'frontend'; $breakAfter = 5; - $this->deploymentConfig->expects($this->once()) + $this->deploymentConfigMock->expects($this->once()) ->method('get') ->with(Config::PARAM_BREAK_AFTER . '_' . $areaCode) ->willReturn($breakAfter); - $this->appState->expects($this->once()) + $this->appStateMock->expects($this->once()) ->method('getAreaCode') ->willReturn($areaCode); $this->assertEquals($this->config->getBreakAfter(), $breakAfter); } + + public function testGetLifetimeAdmin() + { + $areaCode = 'adminhtml'; + $expectedLifetime = 123; + $this->appStateMock->expects($this->once()) + ->method('getAreaCode') + ->willReturn($areaCode); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(Config::XML_PATH_ADMIN_SESSION_LIFETIME) + ->willReturn($expectedLifetime); + $this->assertEquals($this->config->getLifetime(), $expectedLifetime); + } + + public function testGetLifetimeFrontend() + { + $areaCode = 'frontend'; + $expectedLifetime = 234; + $this->appStateMock->expects($this->once()) + ->method('getAreaCode') + ->willReturn($areaCode); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with( + Config::XML_PATH_COOKIE_LIFETIME, + ScopeInterface::SCOPE_STORE + ) + ->willReturn($expectedLifetime); + $this->assertEquals($this->config->getLifetime(), $expectedLifetime); + } } diff --git a/lib/internal/Magento/Framework/Setup/FilePermissions.php b/lib/internal/Magento/Framework/Setup/FilePermissions.php index df65ae89296fc685d3fa240720a6ea3463307670..4bb0816a34752969ae843d791e8823512f88610c 100644 --- a/lib/internal/Magento/Framework/Setup/FilePermissions.php +++ b/lib/internal/Magento/Framework/Setup/FilePermissions.php @@ -3,15 +3,12 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Framework\Setup; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Backup\Filesystem\Iterator\Filter; use Magento\Framework\Filesystem\Filter\ExcludeFilter; use Magento\Framework\Filesystem; -use Magento\Framework\Filesystem\Driver\File; -use Magento\Framework\OsInfo; class FilePermissions { @@ -25,11 +22,6 @@ class FilePermissions */ protected $directoryList; - /** - * @var File - */ - protected $driverFile; - /** * List of required writable directories for installation * @@ -65,27 +57,16 @@ class FilePermissions */ protected $nonWritablePathsInDirectories = []; - /** - * @var \Magento\Framework\OsInfo - */ - protected $osInfo; - /** * @param Filesystem $filesystem * @param DirectoryList $directoryList - * @param File $driverFile - * @param OsInfo $osInfo */ public function __construct( Filesystem $filesystem, - DirectoryList $directoryList, - File $driverFile, - OsInfo $osInfo + DirectoryList $directoryList ) { $this->filesystem = $filesystem; $this->directoryList = $directoryList; - $this->driverFile = $driverFile; - $this->osInfo = $osInfo; } /** @@ -230,25 +211,6 @@ class FilePermissions return $this->isReadableDirectory($directory) && !$directory->isWritable(); } - /** - * Checks if var/generation/* has read and execute permissions - * - * @return bool - */ - public function checkDirectoryPermissionForCLIUser() - { - $varGenerationDir = $this->directoryList->getPath(DirectoryList::GENERATION); - $dirs = $this->driverFile->readDirectory($varGenerationDir); - array_unshift($dirs, $varGenerationDir); - - foreach ($dirs as $dir) { - if (!$this->directoryPermissionForCLIUserValid($dir)) { - return false; - } - } - return true; - } - /** * Checks if directory exists and is readable * @@ -308,16 +270,4 @@ class FilePermissions $current = $this->getApplicationCurrentNonWritableDirectories(); return array_diff($required, $current); } - - /** - * Checks if directory has permissions needed for CLI user (valid directory, readable, and executable.) - * Ignores executable permission for Windows. - * - * @param string $dir - * @return bool - */ - private function directoryPermissionForCLIUserValid($dir) - { - return (is_dir($dir) && is_readable($dir) && (is_executable($dir) || $this->osInfo->isWindows())); - } } diff --git a/lib/internal/Magento/Framework/Setup/Test/Unit/FilePermissionsTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/FilePermissionsTest.php index 3788a1ede33cfef1b935159fa8c74eeac7d8fadb..cff6323802083c6000f009833415562eee6c3957 100644 --- a/lib/internal/Magento/Framework/Setup/Test/Unit/FilePermissionsTest.php +++ b/lib/internal/Magento/Framework/Setup/Test/Unit/FilePermissionsTest.php @@ -3,7 +3,6 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Framework\Setup\Test\Unit; use \Magento\Framework\Setup\FilePermissions; @@ -26,16 +25,6 @@ class FilePermissionsTest extends \PHPUnit_Framework_TestCase */ private $directoryListMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Filesystem\Driver\File - */ - private $driverFileMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\OsInfo - */ - private $osInfoMock; - /** * @var FilePermissions */ @@ -45,8 +34,6 @@ class FilePermissionsTest extends \PHPUnit_Framework_TestCase { $this->directoryWriteMock = $this->getMock('Magento\Framework\Filesystem\Directory\Write', [], [], '', false); $this->filesystemMock = $this->getMock('Magento\Framework\Filesystem', [], [], '', false); - $this->driverFileMock = $this->getMock('Magento\Framework\Filesystem\Driver\File', [], [], '', false); - $this->osInfoMock = $this->getMock('Magento\Framework\OsInfo', [], [], '', false); $this->filesystemMock ->expects($this->any()) @@ -56,9 +43,7 @@ class FilePermissionsTest extends \PHPUnit_Framework_TestCase $this->filePermissions = new FilePermissions( $this->filesystemMock, - $this->directoryListMock, - $this->driverFileMock, - $this->osInfoMock + $this->directoryListMock ); } @@ -212,55 +197,6 @@ class FilePermissionsTest extends \PHPUnit_Framework_TestCase ]; } - /** - * Directories have executable permission, not Windows - */ - public function testCheckDirectoryPermissionForCLIUser() - { - $this->directoryListMock->expects($this->once())->method('getPath')->willReturn('/var/generation'); - $this->driverFileMock->expects($this->once()) - ->method('readDirectory') - ->willReturn(['/var/generation/Composer', '/var/gen/Magento']); - // Should never check for OS if executable - $this->osInfoMock->expects($this->never())->method('isWindows'); - $this->assertTrue($this->filePermissions->checkDirectoryPermissionForCLIUser()); - } - - /** - * Directories do not have executable permissions, is Windows - */ - public function testCheckDirectoryPermissionForCLIUserWin() - { - $this->directoryListMock->expects($this->once())->method('getPath')->willReturn('/var/generationNotExec'); - $this->driverFileMock->expects($this->once()) - ->method('readDirectory') - ->willReturn(['/var/generation/ComposerNotExec', '/var/generation/MagentoNotExec']); - // Contains a 'NotExec', so is_executable will return false, isWindows should be called once for each - // directory (including parent) and return true - $this->osInfoMock->expects($this->exactly(3))->method('isWindows')->willReturn(true); - $this->assertTrue($this->filePermissions->checkDirectoryPermissionForCLIUser()); - } - - /** - * One directory does not have executable permission, is not Windows - */ - public function testCheckDirectoryPermissionForCLIUserNotExecutable() - { - $this->directoryListMock->expects($this->once())->method('getPath')->willReturn('/var/generation'); - $this->driverFileMock->expects($this->once()) - ->method('readDirectory') - ->willReturn(['/var/generation/ComposerNotExec', '/var/gen/Magento']); - // Contains a 'NotExec', so is_executable will return false, isWindows should be called and return false - $this->osInfoMock->expects($this->once())->method('isWindows')->willReturn(false); - $this->assertFalse($this->filePermissions->checkDirectoryPermissionForCLIUser()); - } - - /* - * exec directory, unix - * non-exec directory, windows - * non-exec directory, unix - */ - public function setUpDirectoryListInstallation() { $this->directoryListMock @@ -340,46 +276,3 @@ class FilePermissionsTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue(false)); } } - -namespace Magento\Framework\Setup; - -/** - * Overriding the built-in PHP function is_dir, always returns true, - * allows unit test of this code without having to setup special directories. - * - * @param string $filename - * @return true - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ -function is_dir($filename) -{ - return true; -} - -/** - * Overriding the built-in PHP function is_readable, always returns true, - * allows unit test of this code without having to setup special directories. - * - * @param string $filename - * @return true - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ -function is_readable($filename) -{ - return true; -} - -/** - * Overriding the built-in PHP function is_executable, will return false if directory name contains 'NotExec' - * Allows unit test of this code without having to setup a special directory with non-executable permission. - * - * @param string $filename - * @return bool - */ -function is_executable($filename) -{ - if (strpos($filename, 'NotExec') !== false) { - return false; - } - return true; -} diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php index bad169e114754ee24dd850c636b2f27b3e9e76d0..ee5a42069fb0cca56c7bac2065ec7f3ac6607182 100644 --- a/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/ClassesScanner.php @@ -6,7 +6,7 @@ namespace Magento\Setup\Module\Di\Code\Reader; use Magento\Framework\Exception\FileSystemException; -use Zend\Code\Scanner\FileScanner; +use Magento\Setup\Module\Di\Code\Reader\FileScanner; class ClassesScanner implements ClassesScannerInterface { diff --git a/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php new file mode 100644 index 0000000000000000000000000000000000000000..5293b782064242a571cb7580835abb6586d2a454 --- /dev/null +++ b/setup/src/Magento/Setup/Module/Di/Code/Reader/FileScanner.php @@ -0,0 +1,367 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +namespace Magento\Setup\Module\Di\Code\Reader; + +/** + * @SuppressWarnings(PHPMD) + */ +class FileScanner extends \Zend\Code\Scanner\FileScanner +{ + /** + * @var int + */ + private $tokenType; + + /** + * {@inheritdoc} + */ + protected function scan() + { + if ($this->isScanned) { + return; + } + + if (!$this->tokens) { + throw new \Zend\Code\Exception\RuntimeException('No tokens were provided'); + } + + /** + * Define PHP 5.4 'trait' token constant. + */ + if (!defined('T_TRAIT')) { + define('T_TRAIT', 42001); + } + + /** + * Variables & Setup + */ + + $tokens = &$this->tokens; // localize + $infos = &$this->infos; // localize + $tokenIndex = null; + $token = null; + $this->tokenType = null; + $tokenContent = null; + $tokenLine = null; + $namespace = null; + $docCommentIndex = false; + $infoIndex = 0; + + /* + * MACRO creation + */ + $MACRO_TOKEN_ADVANCE = function () use (&$tokens, &$tokenIndex, &$token, &$tokenContent, &$tokenLine) { + $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1; + if (!isset($tokens[$tokenIndex])) { + $token = false; + $tokenContent = false; + $this->tokenType = false; + $tokenLine = false; + + return false; + } + if (is_string($tokens[$tokenIndex]) && $tokens[$tokenIndex] === '"') { + do { + $tokenIndex++; + } while (!(is_string($tokens[$tokenIndex]) && $tokens[$tokenIndex] === '"')); + } + $token = $tokens[$tokenIndex]; + if (is_array($token)) { + list($this->tokenType, $tokenContent, $tokenLine) = $token; + } else { + $this->tokenType = null; + $tokenContent = $token; + } + + return $tokenIndex; + }; + $MACRO_TOKEN_LOGICAL_START_INDEX = function () use (&$tokenIndex, &$docCommentIndex) { + return ($docCommentIndex === false) ? $tokenIndex : $docCommentIndex; + }; + $MACRO_DOC_COMMENT_START = function () use (&$tokenIndex, &$docCommentIndex) { + $docCommentIndex = $tokenIndex; + + return $docCommentIndex; + }; + $MACRO_DOC_COMMENT_VALIDATE = function () use (&$docCommentIndex) { + static $validTrailingTokens = null; + if ($validTrailingTokens === null) { + $validTrailingTokens = array(T_WHITESPACE, T_FINAL, T_ABSTRACT, T_INTERFACE, T_CLASS, T_FUNCTION); + } + if ($docCommentIndex !== false && !in_array($this->tokenType, $validTrailingTokens)) { + $docCommentIndex = false; + } + + return $docCommentIndex; + }; + $MACRO_INFO_ADVANCE = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) { + $infos[$infoIndex]['tokenEnd'] = $tokenIndex; + $infos[$infoIndex]['lineEnd'] = $tokenLine; + $infoIndex++; + + return $infoIndex; + }; + + /** + * START FINITE STATE MACHINE FOR SCANNING TOKENS + */ + + // Initialize token + $MACRO_TOKEN_ADVANCE(); + + SCANNER_TOP: + + if ($token === false) { + goto SCANNER_END; + } + + // Validate current doc comment index + $MACRO_DOC_COMMENT_VALIDATE(); + + switch ($this->tokenType) { + + case T_DOC_COMMENT: + + $MACRO_DOC_COMMENT_START(); + goto SCANNER_CONTINUE; + //goto no break needed + + case T_NAMESPACE: + + $infos[$infoIndex] = array( + 'type' => 'namespace', + 'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(), + 'tokenEnd' => null, + 'lineStart' => $token[2], + 'lineEnd' => null, + 'namespace' => null, + ); + + // start processing with next token + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + + SCANNER_NAMESPACE_TOP: + + if ($this->tokenType === null && $tokenContent === ';' || $tokenContent === '{') { + goto SCANNER_NAMESPACE_END; + } + + if ($this->tokenType === T_WHITESPACE) { + goto SCANNER_NAMESPACE_CONTINUE; + } + + if ($this->tokenType === T_NS_SEPARATOR || $this->tokenType === T_STRING) { + $infos[$infoIndex]['namespace'] .= $tokenContent; + } + + SCANNER_NAMESPACE_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_NAMESPACE_TOP; + + SCANNER_NAMESPACE_END: + + $namespace = $infos[$infoIndex]['namespace']; + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CONTINUE; + //goto no break needed + + case T_USE: + + $infos[$infoIndex] = array( + 'type' => 'use', + 'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(), + 'tokenEnd' => null, + 'lineStart' => $tokens[$tokenIndex][2], + 'lineEnd' => null, + 'namespace' => $namespace, + 'statements' => array(0 => array('use' => null, + 'as' => null)), + ); + + $useStatementIndex = 0; + $useAsContext = false; + + // start processing with next token + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + + SCANNER_USE_TOP: + + if ($this->tokenType === null) { + if ($tokenContent === ';') { + goto SCANNER_USE_END; + } elseif ($tokenContent === ',') { + $useAsContext = false; + $useStatementIndex++; + $infos[$infoIndex]['statements'][$useStatementIndex] = array('use' => null, + 'as' => null); + } + } + + // ANALYZE + if ($this->tokenType !== null) { + if ($this->tokenType == T_AS) { + $useAsContext = true; + goto SCANNER_USE_CONTINUE; + } + + if ($this->tokenType == T_NS_SEPARATOR || $this->tokenType == T_STRING) { + if ($useAsContext == false) { + $infos[$infoIndex]['statements'][$useStatementIndex]['use'] .= $tokenContent; + } else { + $infos[$infoIndex]['statements'][$useStatementIndex]['as'] = $tokenContent; + } + } + } + + SCANNER_USE_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_USE_TOP; + + SCANNER_USE_END: + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CONTINUE; + //goto no break needed + + case T_INCLUDE: + case T_INCLUDE_ONCE: + case T_REQUIRE: + case T_REQUIRE_ONCE: + + // Static for performance + static $includeTypes = array( + T_INCLUDE => 'include', + T_INCLUDE_ONCE => 'include_once', + T_REQUIRE => 'require', + T_REQUIRE_ONCE => 'require_once' + ); + + $infos[$infoIndex] = array( + 'type' => 'include', + 'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(), + 'tokenEnd' => null, + 'lineStart' => $tokens[$tokenIndex][2], + 'lineEnd' => null, + 'includeType' => $includeTypes[$tokens[$tokenIndex][0]], + 'path' => '', + ); + + // start processing with next token + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + + SCANNER_INCLUDE_TOP: + + if ($this->tokenType === null && $tokenContent === ';') { + goto SCANNER_INCLUDE_END; + } + + $infos[$infoIndex]['path'] .= $tokenContent; + + SCANNER_INCLUDE_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_INCLUDE_TOP; + + SCANNER_INCLUDE_END: + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CONTINUE; + //goto no break needed + + case T_FUNCTION: + case T_FINAL: + case T_ABSTRACT: + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + + $infos[$infoIndex] = array( + 'type' => ($this->tokenType === T_FUNCTION) ? 'function' : 'class', + 'tokenStart' => $MACRO_TOKEN_LOGICAL_START_INDEX(), + 'tokenEnd' => null, + 'lineStart' => $tokens[$tokenIndex][2], + 'lineEnd' => null, + 'namespace' => $namespace, + 'uses' => $this->getUsesNoScan($namespace), + 'name' => null, + 'shortName' => null, + ); + + $classBraceCount = 0; + + // start processing with current token + + SCANNER_CLASS_TOP: + + // process the name + if ($infos[$infoIndex]['shortName'] == '' + && (($this->tokenType === T_CLASS || $this->tokenType === T_INTERFACE || $this->tokenType === T_TRAIT) && $infos[$infoIndex]['type'] === 'class' + || ($this->tokenType === T_FUNCTION && $infos[$infoIndex]['type'] === 'function')) + ) { + $infos[$infoIndex]['shortName'] = $tokens[$tokenIndex + 2][1]; + $infos[$infoIndex]['name'] = (($namespace !== null) ? $namespace . '\\' : '') . $infos[$infoIndex]['shortName']; + } + + if ($this->tokenType === null) { + if ($tokenContent == '{') { + $classBraceCount++; + } + if ($tokenContent == '}') { + $classBraceCount--; + if ($classBraceCount === 0) { + goto SCANNER_CLASS_END; + } + } + } + + SCANNER_CLASS_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_CLASS_TOP; + + SCANNER_CLASS_END: + + $MACRO_INFO_ADVANCE(); + goto SCANNER_CONTINUE; + + } + + SCANNER_CONTINUE: + + if ($MACRO_TOKEN_ADVANCE() === false) { + goto SCANNER_END; + } + goto SCANNER_TOP; + + SCANNER_END: + + /** + * END FINITE STATE MACHINE FOR SCANNING TOKENS + */ + + $this->isScanned = true; + } +} \ No newline at end of file diff --git a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php index 135e03ae69b800d593bf6c0592f7454c7b9f1da2..2d98bc58f789ad4b8476fa057e615c27d7093253 100644 --- a/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php +++ b/setup/src/Magento/Setup/Mvc/Bootstrap/InitParamListener.php @@ -123,16 +123,20 @@ class InitParamListener implements ListenerAggregateInterface, FactoryInterface /** @var \Magento\Framework\App\State $adminAppState */ $adminAppState = $objectManager->get('Magento\Framework\App\State'); $adminAppState->setAreaCode(\Magento\Framework\App\Area::AREA_ADMIN); + /** @var \Magento\Backend\Model\Session\AdminConfig $sessionConfig */ + $sessionConfig = $objectManager->get(\Magento\Backend\Model\Session\AdminConfig::class); + $cookiePath = $this->getSetupCookiePath($objectManager); + $sessionConfig->setCookiePath($cookiePath); /** @var \Magento\Backend\Model\Auth\Session $adminSession */ $adminSession = $objectManager->create( \Magento\Backend\Model\Auth\Session::class, [ - 'sessionConfig' => $objectManager->get(\Magento\Backend\Model\Session\AdminConfig::class), + 'sessionConfig' => $sessionConfig, 'appState' => $adminAppState ] ); if (!$objectManager->get(\Magento\Backend\Model\Auth::class)->isLoggedIn()) { - $adminSession->expireSessionCookie(); + $adminSession->destroy(); $response = $event->getResponse(); $response->getHeaders()->addHeaderLine('Location', 'index.php/session/unlogin'); $response->setStatusCode(302); @@ -145,6 +149,25 @@ class InitParamListener implements ListenerAggregateInterface, FactoryInterface return false; } + /** + * Get cookie path + * + * @param \Magento\Framework\ObjectManagerInterface $objectManager + * @return string + */ + private function getSetupCookiePath(\Magento\Framework\ObjectManagerInterface $objectManager) + { + /** @var \Magento\Backend\App\BackendAppList $backendAppList */ + $backendAppList = $objectManager->get(\Magento\Backend\App\BackendAppList::class); + $backendApp = $backendAppList->getBackendApp('setup'); + /** @var \Magento\Backend\Model\UrlFactory $backendUrlFactory */ + $backendUrlFactory = $objectManager->get(\Magento\Backend\Model\UrlFactory::class); + $baseUrl = parse_url($backendUrlFactory->create()->getBaseUrl(), PHP_URL_PATH); + $baseUrl = \Magento\Framework\App\Request\Http::getUrlNoScript($baseUrl); + $cookiePath = $baseUrl . $backendApp->getCookiePath(); + return $cookiePath; + } + /** * {@inheritdoc} */