Skip to content

Commit 8c4a1a5

Browse files
committed
Merge remote-tracking branch 'magento2/develop' into MPI-BUGFIXES
2 parents 9a72e57 + 66883d5 commit 8c4a1a5

File tree

52 files changed

+1234
-475
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1234
-475
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ php:
1414
env:
1515
global:
1616
- COMPOSER_BIN_DIR=~/bin
17-
- INTEGRATION_SETS=2
17+
- INTEGRATION_SETS=3
1818
matrix:
1919
- TEST_SUITE=unit
2020
- TEST_SUITE=integration INTEGRATION_INDEX=1
2121
- TEST_SUITE=integration INTEGRATION_INDEX=2
22+
- TEST_SUITE=integration INTEGRATION_INDEX=3
2223
- TEST_SUITE=static
2324
cache:
2425
apt: true
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Backend\Model\Setup;
7+
8+
use Magento\Backend\Model\Menu;
9+
use Magento\Backend\Model\Menu\Builder;
10+
use Magento\Framework\App\DocRootLocator;
11+
12+
/**
13+
* Plugin class to remove web setup wizard from menu if application root is pub/ and no setup url variable is specified.
14+
*/
15+
class MenuBuilder
16+
{
17+
/**
18+
* @var DocRootLocator
19+
*/
20+
protected $docRootLocator;
21+
22+
/**
23+
* MenuBuilder constructor.
24+
*
25+
* @param DocRootLocator $docRootLocator
26+
*/
27+
public function __construct(DocRootLocator $docRootLocator)
28+
{
29+
$this->docRootLocator = $docRootLocator;
30+
}
31+
32+
/**
33+
* Removes 'Web Setup Wizard' from the menu if doc root is pub and no setup url variable is specified.
34+
*
35+
* @param Builder $subject
36+
* @param Menu $menu
37+
* @return Menu
38+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
39+
*/
40+
public function afterGetResult(Builder $subject, Menu $menu)
41+
{
42+
if ($this->docRootLocator->isPub()) {
43+
$menu->remove('Magento_Backend::setup_wizard');
44+
}
45+
return $menu;
46+
}
47+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Backend\Test\Unit\Model;
8+
9+
use Magento\Backend\Model\Setup\MenuBuilder;
10+
11+
class MenuBuilderTest extends \PHPUnit_Framework_TestCase
12+
{
13+
/**
14+
* @dataProvider afterGetResultDataProvider
15+
*
16+
* @param string $isPub
17+
* @param int $times
18+
* @param bool $result
19+
*/
20+
public function testAfterGetResult($isPub, $times)
21+
{
22+
$docRootLocator = $this->getMock('\Magento\Framework\App\DocRootLocator', [], [], '', false);
23+
$docRootLocator->expects($this->once())->method('isPub')->willReturn($isPub);
24+
$model = new MenuBuilder($docRootLocator);
25+
/** @var \Magento\Backend\Model\Menu $menu */
26+
$menu = $this->getMock('\Magento\Backend\Model\Menu', [], [], '', false);
27+
$menu->expects($this->exactly($times))->method('remove')->willReturn(true);
28+
29+
/** @var \Magento\Backend\Model\Menu\Builder $menuBuilder */
30+
$menuBuilder = $this->getMock('\Magento\Backend\Model\Menu\Builder', [], [], '', false);
31+
32+
$this->assertInstanceOf(
33+
'\Magento\Backend\Model\Menu',
34+
$model->afterGetResult($menuBuilder, $menu)
35+
);
36+
}
37+
38+
public function afterGetResultDataProvider()
39+
{
40+
return [[true, 1], [false, 0],];
41+
}
42+
}

app/code/Magento/Backend/etc/adminhtml/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,7 @@
138138
<argument name="isIncludesAvailable" xsi:type="boolean">false</argument>
139139
</arguments>
140140
</type>
141+
<type name="Magento\Backend\Model\Menu\Builder">
142+
<plugin name="SetupMenuBuilder" type="Magento\Backend\Model\Setup\MenuBuilder" />
143+
</type>
141144
</config>

app/code/Magento/Catalog/Model/CategoryManagement.php

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,20 @@ class CategoryManagement implements \Magento\Catalog\Api\CategoryManagementInter
2121
*/
2222
protected $categoryTree;
2323

24+
/**
25+
* @var \Magento\Framework\App\ScopeResolverInterface
26+
*/
27+
private $scopeResolver;
28+
29+
/**
30+
* @var \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory
31+
*/
32+
private $categoriesFactory;
33+
2434
/**
2535
* @param \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository
2636
* @param Category\Tree $categoryTree
27-
* @param CollectionFactory $categoriesFactory
37+
* @param \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoriesFactory
2838
*/
2939
public function __construct(
3040
\Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository,
@@ -45,11 +55,49 @@ public function getTree($rootCategoryId = null, $depth = null)
4555
if ($rootCategoryId !== null) {
4656
/** @var \Magento\Catalog\Model\Category $category */
4757
$category = $this->categoryRepository->get($rootCategoryId);
58+
} elseif ($this->isAdminStore()) {
59+
$category = $this->getTopLevelCategory();
4860
}
4961
$result = $this->categoryTree->getTree($this->categoryTree->getRootNode($category), $depth);
5062
return $result;
5163
}
5264

65+
/**
66+
* Check is request use default scope
67+
*
68+
* @return bool
69+
*/
70+
private function isAdminStore()
71+
{
72+
return $this->getScopeResolver()->getScope()->getCode() == \Magento\Store\Model\Store::ADMIN_CODE;
73+
}
74+
75+
/**
76+
* Get store manager for operations with admin code
77+
*
78+
* @return \Magento\Framework\App\ScopeResolverInterface
79+
*/
80+
private function getScopeResolver()
81+
{
82+
if ($this->scopeResolver == null) {
83+
$this->scopeResolver = \Magento\Framework\App\ObjectManager::getInstance()
84+
->get(\Magento\Framework\App\ScopeResolverInterface::class);
85+
}
86+
87+
return $this->scopeResolver;
88+
}
89+
90+
/**
91+
* Get top level hidden root category
92+
*
93+
* @return \Magento\Catalog\Model\Category
94+
*/
95+
private function getTopLevelCategory()
96+
{
97+
$categoriesCollection = $this->categoriesFactory->create();
98+
return $categoriesCollection->addFilter('level', ['eq' => 0])->getFirstItem();
99+
}
100+
53101
/**
54102
* {@inheritdoc}
55103
*/

app/code/Magento/Catalog/Model/ImageUploader.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,8 @@ public function saveFileToTmpDir($fileId)
244244
->getStore()
245245
->getBaseUrl(
246246
\Magento\Framework\UrlInterface::URL_TYPE_MEDIA
247-
) . $this->getFilePath($baseTmpPath, $result['name']);
247+
) . $this->getFilePath($baseTmpPath, $result['file']);
248+
$result['name'] = $result['file'];
248249

249250
if (isset($result['file'])) {
250251
try {

app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ abstract class AbstractAction
9797
*/
9898
protected $metadataPool;
9999

100+
/**
101+
* @var string
102+
*/
103+
protected $tempTreeIndexTableName;
104+
100105
/**
101106
* @param ResourceConnection $resource
102107
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
@@ -368,6 +373,8 @@ protected function createAnchorSelect(\Magento\Store\Model\Store $store)
368373
$rootCatIds = explode('/', $this->getPathFromCategoryId($store->getRootCategoryId()));
369374
array_pop($rootCatIds);
370375

376+
$temporaryTreeTable = $this->makeTempCategoryTreeIndex();
377+
371378
$productMetadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
372379
$categoryMetadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\CategoryInterface::class);
373380
$productLinkField = $productMetadata->getLinkField();
@@ -377,17 +384,15 @@ protected function createAnchorSelect(\Magento\Store\Model\Store $store)
377384
['cc' => $this->getTable('catalog_category_entity')],
378385
[]
379386
)->joinInner(
380-
['cc2' => $this->getTable('catalog_category_entity')],
381-
'cc2.path LIKE ' . $this->connection->getConcatSql(
382-
[$this->connection->quoteIdentifier('cc.path'), $this->connection->quote('/%')]
383-
) . ' AND cc.entity_id NOT IN (' . implode(
387+
['cc2' => $temporaryTreeTable],
388+
'cc2.parent_id = cc.entity_id AND cc.entity_id NOT IN (' . implode(
384389
',',
385390
$rootCatIds
386391
) . ')',
387392
[]
388393
)->joinInner(
389394
['ccp' => $this->getTable('catalog_category_product')],
390-
'ccp.category_id = cc2.entity_id',
395+
'ccp.category_id = cc2.child_id',
391396
[]
392397
)->joinInner(
393398
['cpe' => $this->getTable('catalog_product_entity')],
@@ -459,6 +464,92 @@ protected function createAnchorSelect(\Magento\Store\Model\Store $store)
459464
);
460465
}
461466

467+
/**
468+
* Get temporary table name for concurrent indexing in persistent connection
469+
* Temp table name is NOT shared between action instances and each action has it's own temp tree index
470+
*
471+
* @return string
472+
*/
473+
protected function getTemporaryTreeIndexTableName()
474+
{
475+
if (empty($this->tempTreeIndexTableName)) {
476+
$this->tempTreeIndexTableName = $this->connection->getTableName('temp_catalog_category_tree_index')
477+
. '_'
478+
. substr(md5(time() . rand(0, 999999999)), 0, 8);
479+
}
480+
481+
return $this->tempTreeIndexTableName;
482+
}
483+
484+
/**
485+
* Build and populate the temporary category tree index table
486+
*
487+
* Returns the name of the temporary table to use in queries.
488+
*
489+
* @return string
490+
*/
491+
protected function makeTempCategoryTreeIndex()
492+
{
493+
// Note: this temporary table is per-connection, so won't conflict by prefix.
494+
$temporaryName = $this->getTemporaryTreeIndexTableName();
495+
496+
$temporaryTable = $this->connection->newTable($temporaryName);
497+
$temporaryTable->addColumn(
498+
'parent_id',
499+
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
500+
null,
501+
['nullable' => false, 'unsigned' => true]
502+
);
503+
$temporaryTable->addColumn(
504+
'child_id',
505+
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
506+
null,
507+
['nullable' => false, 'unsigned' => true]
508+
);
509+
// Each entry will be unique.
510+
$temporaryTable->addIndex(
511+
'idx_primary',
512+
['parent_id', 'child_id'],
513+
['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_PRIMARY]
514+
);
515+
516+
// Drop the temporary table in case it already exists on this (persistent?) connection.
517+
$this->connection->dropTemporaryTable($temporaryName);
518+
$this->connection->createTemporaryTable($temporaryTable);
519+
520+
$this->fillTempCategoryTreeIndex($temporaryName);
521+
522+
return $temporaryName;
523+
}
524+
525+
/**
526+
* Populate the temporary category tree index table
527+
*
528+
* @param string $temporaryName
529+
*/
530+
protected function fillTempCategoryTreeIndex($temporaryName)
531+
{
532+
// This finds all children (cc2) that descend from a parent (cc) by path.
533+
// For example, cc.path may be '1/2', and cc2.path may be '1/2/3/4/5'.
534+
$temporarySelect = $this->connection->select()->from(
535+
['cc' => $this->getTable('catalog_category_entity')],
536+
['parent_id' => 'entity_id']
537+
)->joinInner(
538+
['cc2' => $this->getTable('catalog_category_entity')],
539+
'cc2.path LIKE ' . $this->connection->getConcatSql(
540+
[$this->connection->quoteIdentifier('cc.path'), $this->connection->quote('/%')]
541+
),
542+
['child_id' => 'entity_id']
543+
);
544+
545+
$this->connection->query(
546+
$temporarySelect->insertFromSelect(
547+
$temporaryName,
548+
['parent_id', 'child_id']
549+
)
550+
);
551+
}
552+
462553
/**
463554
* Retrieve select for reindex products of non anchor categories
464555
*

app/code/Magento/Catalog/Model/ResourceModel/Collection/AbstractCollection.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,12 @@ public function setStore($store)
9797
/**
9898
* Set store scope
9999
*
100-
* @param int|string|\Magento\Store\Model\Store $storeId
100+
* @param int|string|\Magento\Store\Api\Data\StoreInterface $storeId
101101
* @return $this
102102
*/
103103
public function setStoreId($storeId)
104104
{
105-
if ($storeId instanceof \Magento\Store\Model\Store) {
105+
if ($storeId instanceof \Magento\Store\Api\Data\StoreInterface) {
106106
$storeId = $storeId->getId();
107107
}
108108
$this->_storeId = (int)$storeId;

0 commit comments

Comments
 (0)