Skip to content

Commit 15390c4

Browse files
authored
MC-37460: Support by Magento CMS (#6202)
* MC-37479: Support by Magento Content Design
1 parent 4e40bc1 commit 15390c4

File tree

23 files changed

+828
-89
lines changed

23 files changed

+828
-89
lines changed

app/code/Magento/AwsS3/Driver/AwsS3.php

+132-13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
/**
1616
* Driver for AWS S3 IO operations.
17+
*
18+
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
1719
*/
1820
class AwsS3 implements DriverInterface
1921
{
@@ -173,21 +175,15 @@ public function filePutContents($path, $content, $mode = null, $context = null):
173175
*/
174176
public function readDirectoryRecursively($path = null): array
175177
{
176-
return $this->adapter->listContents(
177-
$this->normalizeRelativePath($path),
178-
true
179-
);
178+
return $this->readPath($path, true);
180179
}
181180

182181
/**
183182
* @inheritDoc
184183
*/
185184
public function readDirectory($path): array
186185
{
187-
return $this->adapter->listContents(
188-
$this->normalizeRelativePath($path),
189-
false
190-
);
186+
return $this->readPath($path, false);
191187
}
192188

193189
/**
@@ -402,11 +398,11 @@ public function stat($path): array
402398
'ctime' => 0,
403399
'blksize' => 0,
404400
'blocks' => 0,
405-
'size' => $metaInfo['size'],
406-
'type' => $metaInfo['type'],
407-
'mtime' => $metaInfo['timestamp'],
401+
'size' => $metaInfo['size'] ?? 0,
402+
'type' => $metaInfo['type'] ?? 0,
403+
'mtime' => $metaInfo['timestamp'] ?? 0,
408404
'disposition' => null,
409-
'mimetype' => $metaInfo['mimetype']
405+
'mimetype' => $metaInfo['mimetype'] ?? 0
410406
];
411407
}
412408

@@ -415,7 +411,36 @@ public function stat($path): array
415411
*/
416412
public function search($pattern, $path): array
417413
{
418-
throw new FileSystemException(__('Method %1 is not supported', __METHOD__));
414+
return $this->glob(rtrim($path, '/') . '/' . ltrim($pattern, '/'));
415+
}
416+
417+
/**
418+
* Emulate php glob function for AWS S3 storage
419+
*
420+
* @param string $pattern
421+
* @return array
422+
* @throws FileSystemException
423+
*/
424+
private function glob(string $pattern): array
425+
{
426+
$directoryContent = [];
427+
428+
$patternFound = preg_match('(\*|\?|\[.+\])', $pattern, $parentPattern, PREG_OFFSET_CAPTURE);
429+
if ($patternFound) {
430+
// phpcs:ignore Magento2.Functions.DiscouragedFunction
431+
$parentDirectory = \dirname(substr($pattern, 0, $parentPattern[0][1] + 1));
432+
$leftover = substr($pattern, $parentPattern[0][1]);
433+
$index = strpos($leftover, '/');
434+
$searchPattern = $this->getSearchPattern($pattern, $parentPattern, $parentDirectory, $index);
435+
436+
if ($this->isDirectory($parentDirectory . '/')) {
437+
$directoryContent = $this->getDirectoryContent($parentDirectory, $searchPattern, $leftover, $index);
438+
}
439+
} elseif ($this->isDirectory($pattern) || $this->isFile($pattern)) {
440+
$directoryContent[] = $pattern;
441+
}
442+
443+
return $directoryContent;
419444
}
420445

421446
/**
@@ -630,4 +655,98 @@ private function getWarningMessage(): ?string
630655

631656
return null;
632657
}
658+
659+
/**
660+
* Read directory by path and is recursive flag
661+
*
662+
* @param string $path
663+
* @param bool $isRecursive
664+
* @return array
665+
*/
666+
private function readPath(string $path, $isRecursive = false): array
667+
{
668+
$relativePath = $this->normalizeRelativePath($path);
669+
$contentsList = $this->adapter->listContents(
670+
$relativePath,
671+
$isRecursive
672+
);
673+
$itemsList = [];
674+
foreach ($contentsList as $item) {
675+
if (isset($item['path'])
676+
&& $item['path'] !== $relativePath
677+
&& strpos($item['path'], $relativePath) === 0) {
678+
$itemsList[] = $item['path'];
679+
}
680+
}
681+
682+
return $itemsList;
683+
}
684+
685+
/**
686+
* Get search pattern for directory
687+
*
688+
* @param string $pattern
689+
* @param array $parentPattern
690+
* @param string $parentDirectory
691+
* @param int|bool $index
692+
* @return string
693+
*/
694+
private function getSearchPattern(string $pattern, array $parentPattern, string $parentDirectory, $index): string
695+
{
696+
$parentLength = \strlen($parentDirectory);
697+
if ($index !== false) {
698+
$searchPattern = substr(
699+
$pattern,
700+
$parentLength + 1,
701+
$parentPattern[0][1] - $parentLength + $index - 1
702+
);
703+
} else {
704+
$searchPattern = substr($pattern, $parentLength + 1);
705+
}
706+
707+
$replacement = [
708+
'/\*/' => '.*',
709+
'/\?/' => '.',
710+
'/\//' => '\/'
711+
];
712+
return preg_replace(array_keys($replacement), array_values($replacement), $searchPattern);
713+
}
714+
715+
/**
716+
* Get directory content by given search pattern
717+
*
718+
* @param string $parentDirectory
719+
* @param string $searchPattern
720+
* @param string $leftover
721+
* @param int|bool $index
722+
* @return array
723+
* @throws FileSystemException
724+
*/
725+
private function getDirectoryContent(
726+
string $parentDirectory,
727+
string $searchPattern,
728+
string $leftover,
729+
$index
730+
): array {
731+
$items = $this->readDirectory($parentDirectory . '/');
732+
$directoryContent = [];
733+
foreach ($items as $item) {
734+
if (preg_match('/' . $searchPattern . '$/', $item)
735+
// phpcs:ignore Magento2.Functions.DiscouragedFunction
736+
&& strpos(basename($item), '.') !== 0) {
737+
if ($index === false || \strlen($leftover) === $index + 1) {
738+
$directoryContent[] = $this->isDirectory($item)
739+
? rtrim($item, '/') . '/'
740+
: $item;
741+
} elseif (strlen($leftover) > $index + 1) {
742+
// phpcs:ignore Magento2.Performance.ForeachArrayMerge
743+
$directoryContent = array_merge(
744+
$directoryContent,
745+
$this->glob("{$parentDirectory}/{$item}" . substr($leftover, $index))
746+
);
747+
}
748+
}
749+
}
750+
return $directoryContent;
751+
}
633752
}

app/code/Magento/AwsS3/Test/Unit/Driver/AwsS3Test.php

+51
Original file line numberDiff line numberDiff line change
@@ -363,4 +363,55 @@ public function getRealPathSafetyDataProvider(): array
363363
]
364364
];
365365
}
366+
367+
/**
368+
* @throws FileSystemException
369+
*/
370+
public function testSearchDirectory(): void
371+
{
372+
$expression = '/*';
373+
$path = 'path/';
374+
$subPaths = [
375+
['path' => 'path/1'],
376+
['path' => 'path/2']
377+
];
378+
$expectedResult = ['path/1', 'path/2'];
379+
$this->adapterMock->expects(self::atLeastOnce())->method('has')
380+
->willReturnMap([
381+
[$path, true]
382+
]);
383+
$this->adapterMock->expects(self::atLeastOnce())->method('getMetadata')
384+
->willReturnMap([
385+
[$path, ['type' => AwsS3::TYPE_DIR]]
386+
]);
387+
$this->adapterMock->expects(self::atLeastOnce())->method('listContents')->with($path, false)
388+
->willReturn($subPaths);
389+
self::assertEquals($expectedResult, $this->driver->search($expression, $path));
390+
}
391+
392+
/**
393+
* @throws FileSystemException
394+
*/
395+
public function testSearchFiles(): void
396+
{
397+
$expression = "/*";
398+
$path = 'path/';
399+
$subPaths = [
400+
['path' => 'path/1.jpg'],
401+
['path' => 'path/2.png']
402+
];
403+
$expectedResult = ['path/1.jpg', 'path/2.png'];
404+
405+
$this->adapterMock->expects(self::atLeastOnce())->method('has')
406+
->willReturnMap([
407+
[$path, true],
408+
]);
409+
$this->adapterMock->expects(self::atLeastOnce())->method('getMetadata')
410+
->willReturnMap([
411+
[$path, ['type' => AwsS3::TYPE_DIR]],
412+
]);
413+
$this->adapterMock->expects(self::atLeastOnce())->method('listContents')->with($path, false)
414+
->willReturn($subPaths);
415+
self::assertEquals($expectedResult, $this->driver->search($expression, $path));
416+
}
366417
}

app/code/Magento/Backup/Model/Fs/Collection.php

+14-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
namespace Magento\Backup\Model\Fs;
77

88
use Magento\Framework\App\Filesystem\DirectoryList;
9+
use Magento\Framework\Config\DocumentRoot;
10+
use Magento\Framework\Filesystem\Directory\TargetDirectory;
911

1012
/**
1113
* Backup data collection
@@ -40,20 +42,30 @@ class Collection extends \Magento\Framework\Data\Collection\Filesystem
4042
*/
4143
protected $_backup = null;
4244

45+
/**
46+
* @var \Magento\Framework\Filesystem
47+
*/
48+
protected $_filesystem;
49+
4350
/**
4451
* @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
4552
* @param \Magento\Backup\Helper\Data $backupData
4653
* @param \Magento\Framework\Filesystem $filesystem
4754
* @param \Magento\Backup\Model\Backup $backup
55+
* @param TargetDirectory|null $targetDirectory
56+
* @param DocumentRoot|null $documentRoot
57+
* @throws \Magento\Framework\Exception\FileSystemException
4858
*/
4959
public function __construct(
5060
\Magento\Framework\Data\Collection\EntityFactory $entityFactory,
5161
\Magento\Backup\Helper\Data $backupData,
5262
\Magento\Framework\Filesystem $filesystem,
53-
\Magento\Backup\Model\Backup $backup
63+
\Magento\Backup\Model\Backup $backup,
64+
TargetDirectory $targetDirectory = null,
65+
DocumentRoot $documentRoot = null
5466
) {
5567
$this->_backupData = $backupData;
56-
parent::__construct($entityFactory);
68+
parent::__construct($entityFactory, $targetDirectory, $documentRoot);
5769

5870
$this->_filesystem = $filesystem;
5971
$this->_backup = $backup;

app/code/Magento/Backup/Test/Unit/Model/Fs/CollectionTest.php

+16-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Magento\Backup\Helper\Data;
1111
use Magento\Backup\Model\Fs\Collection;
1212
use Magento\Framework\Filesystem;
13+
use Magento\Framework\Filesystem\Directory\TargetDirectory;
1314
use Magento\Framework\Filesystem\Directory\WriteInterface;
1415
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
1516
use PHPUnit\Framework\TestCase;
@@ -36,10 +37,23 @@ public function testConstructor()
3637

3738
$directoryWrite->expects($this->any())->method('create')->with('backups');
3839
$directoryWrite->expects($this->any())->method('getAbsolutePath')->with('backups');
39-
40+
$directoryWrite->expects($this->any())->method('isDirectory')->willReturn(true);
41+
$targetDirectory = $this->getMockBuilder(TargetDirectory::class)
42+
->disableOriginalConstructor()
43+
->getMock();
44+
$targetDirectoryWrite = $this->getMockBuilder(WriteInterface::class)
45+
->disableOriginalConstructor()
46+
->getMock();
47+
$targetDirectoryWrite->expects($this->any())->method('isDirectory')->willReturn(true);
48+
$targetDirectory->expects($this->any())->method('getDirectoryWrite')->willReturn($targetDirectoryWrite);
4049
$classObject = $helper->getObject(
4150
Collection::class,
42-
['filesystem' => $filesystem, 'backupData' => $backupData]
51+
[
52+
'filesystem' => $filesystem,
53+
'backupData' => $backupData,
54+
'directoryWrite' => $directoryWrite,
55+
'targetDirectory' => $targetDirectory
56+
]
4357
);
4458
$this->assertNotNull($classObject);
4559
}

app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Tree.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public function __construct(
5858
* Json tree builder
5959
*
6060
* @return string
61+
* @throws \Magento\Framework\Exception\ValidatorException
6162
*/
6263
public function getTreeJson()
6364
{
@@ -75,8 +76,8 @@ public function getTreeJson()
7576
'path' => substr($item->getFilename(), strlen($storageRoot)),
7677
'cls' => 'folder',
7778
];
78-
79-
$hasNestedDirectories = count(glob($item->getFilename() . '/*', GLOB_ONLYDIR)) > 0;
79+
$nestedDirectories = $this->getMediaDirectory()->readRecursively($item->getFilename());
80+
$hasNestedDirectories = count($nestedDirectories) > 0;
8081

8182
// if no nested directories inside dir, add 'leaf' state so that jstree hides dropdown arrow next to dir
8283
if (!$hasNestedDirectories) {

0 commit comments

Comments
 (0)