Skip to content

Commit 799f9a1

Browse files
authored
ENGCOM-6918: Remove media gallery assets metadata when a directory removed #26015
2 parents 4a31e42 + 8a78108 commit 799f9a1

File tree

9 files changed

+429
-12
lines changed

9 files changed

+429
-12
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\MediaGallery\Model\Asset\Command;
9+
10+
use Magento\Framework\App\ResourceConnection;
11+
use Magento\Framework\DB\Adapter\AdapterInterface;
12+
use Magento\Framework\Exception\CouldNotDeleteException;
13+
use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface;
14+
use Psr\Log\LoggerInterface;
15+
16+
/**
17+
* Class DeleteByDirectoryPath
18+
*
19+
* Remove asset(s) that correspond the provided directory path
20+
*/
21+
class DeleteByDirectoryPath implements DeleteByDirectoryPathInterface
22+
{
23+
private const TABLE_MEDIA_GALLERY_ASSET = 'media_gallery_asset';
24+
25+
private const MEDIA_GALLERY_ASSET_PATH = 'path';
26+
27+
/**
28+
* @var ResourceConnection
29+
*/
30+
private $resourceConnection;
31+
32+
/**
33+
* @var LoggerInterface
34+
*/
35+
private $logger;
36+
37+
/**
38+
* DeleteById constructor.
39+
*
40+
* @param ResourceConnection $resourceConnection
41+
* @param LoggerInterface $logger
42+
*/
43+
public function __construct(
44+
ResourceConnection $resourceConnection,
45+
LoggerInterface $logger
46+
) {
47+
$this->resourceConnection = $resourceConnection;
48+
$this->logger = $logger;
49+
}
50+
51+
/**
52+
* Delete media asset(s) by path
53+
*
54+
* @param string $directoryPath
55+
*
56+
* @return void
57+
*
58+
* @throws CouldNotDeleteException
59+
*/
60+
public function execute(string $directoryPath): void
61+
{
62+
$this->validateDirectoryPath($directoryPath);
63+
try {
64+
// Make sure that the path has a trailing slash
65+
$directoryPath = rtrim($directoryPath, '/') . '/';
66+
67+
/** @var AdapterInterface $connection */
68+
$connection = $this->resourceConnection->getConnection();
69+
$tableName = $this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET);
70+
$connection->delete($tableName, [self::MEDIA_GALLERY_ASSET_PATH . ' LIKE ?' => $directoryPath . '%']);
71+
} catch (\Exception $exception) {
72+
$this->logger->critical($exception);
73+
$message = __(
74+
'Could not delete media assets by path %path: %error',
75+
['path' => $directoryPath, 'error' => $exception->getMessage()]
76+
);
77+
throw new CouldNotDeleteException($message, $exception);
78+
}
79+
}
80+
81+
/**
82+
* Validate the directory path
83+
*
84+
* @param string $directoryPath
85+
*
86+
* @throws CouldNotDeleteException
87+
*/
88+
private function validateDirectoryPath(string $directoryPath): void
89+
{
90+
if (!$directoryPath || trim($directoryPath) === '') {
91+
throw new CouldNotDeleteException(__('Cannot remove assets, the directory path does not exist'));
92+
}
93+
}
94+
}

app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88

99
namespace Magento\MediaGallery\Plugin\Wysiwyg\Images;
1010

11+
use Magento\Framework\Exception\CouldNotDeleteException;
12+
use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByDirectoryPathInterface;
1113
use Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface;
1214
use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface;
1315
use Magento\Cms\Model\Wysiwyg\Images\Storage as StorageSubject;
1416
use Magento\Framework\App\Filesystem\DirectoryList;
1517
use Magento\Framework\Filesystem;
16-
use Magento\Framework\Exception\ValidatorException;
1718
use Psr\Log\LoggerInterface;
19+
use Magento\Framework\Exception\ValidatorException;
1820

1921
/**
2022
* Ensures that metadata is removed from the database when a file is deleted and it is an image
@@ -31,6 +33,11 @@ class Storage
3133
*/
3234
private $deleteMediaAssetByPath;
3335

36+
/**
37+
* @var DeleteByDirectoryPathInterface
38+
*/
39+
private $deleteMediaAssetByDirectoryPath;
40+
3441
/**
3542
* @var Filesystem
3643
*/
@@ -46,17 +53,20 @@ class Storage
4653
*
4754
* @param GetByPathInterface $getMediaAssetByPath
4855
* @param DeleteByPathInterface $deleteMediaAssetByPath
56+
* @param DeleteByDirectoryPathInterface $deleteByDirectoryPath
4957
* @param Filesystem $filesystem
5058
* @param LoggerInterface $logger
5159
*/
5260
public function __construct(
5361
GetByPathInterface $getMediaAssetByPath,
5462
DeleteByPathInterface $deleteMediaAssetByPath,
63+
DeleteByDirectoryPathInterface $deleteByDirectoryPath,
5564
Filesystem $filesystem,
5665
LoggerInterface $logger
5766
) {
5867
$this->getMediaAssetByPath = $getMediaAssetByPath;
5968
$this->deleteMediaAssetByPath = $deleteMediaAssetByPath;
69+
$this->deleteMediaAssetByDirectoryPath = $deleteByDirectoryPath;
6070
$this->filesystem = $filesystem;
6171
$this->logger = $logger;
6272
}
@@ -69,7 +79,6 @@ public function __construct(
6979
* @param string $target
7080
*
7181
* @return StorageSubject
72-
* @throws ValidatorException
7382
*
7483
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
7584
*/
@@ -92,4 +101,32 @@ public function afterDeleteFile(StorageSubject $subject, StorageSubject $result,
92101

93102
return $result;
94103
}
104+
105+
/**
106+
* Delete media data after the folder delete action from Wysiwyg
107+
*
108+
* @param StorageSubject $subject
109+
* @param mixed $result
110+
* @param string $path
111+
*
112+
* @return null
113+
*
114+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
115+
*/
116+
public function afterDeleteDirectory(StorageSubject $subject, $result, $path)
117+
{
118+
if (!is_string($path)) {
119+
return $result;
120+
}
121+
122+
try {
123+
$mediaDirectoryRead = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA);
124+
$relativePath = $mediaDirectoryRead->getRelativePath($path);
125+
$this->deleteMediaAssetByDirectoryPath->execute($relativePath);
126+
} catch (ValidatorException $exception) {
127+
$this->logger->critical($exception);
128+
}
129+
130+
return $result;
131+
}
95132
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\MediaGallery\Test\Unit\Model\Asset\Command;
9+
10+
use Magento\MediaGallery\Model\Asset\Command\DeleteByDirectoryPath;
11+
use Magento\Framework\App\ResourceConnection;
12+
use Magento\Framework\DB\Adapter\AdapterInterface;
13+
use Magento\Framework\Exception\CouldNotDeleteException;
14+
use PHPUnit\Framework\MockObject\MockObject;
15+
use PHPUnit\Framework\TestCase;
16+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
17+
use Psr\Log\LoggerInterface;
18+
19+
/**
20+
* Test the DeleteByDirectoryPath command model
21+
*/
22+
class DeleteByDirectoryPathTest extends TestCase
23+
{
24+
private const TABLE_NAME = 'media_gallery_asset';
25+
private const DIRECTORY_PATH = 'test-directory-path/';
26+
27+
/**
28+
* @var ResourceConnection|MockObject
29+
*/
30+
private $resourceConnection;
31+
32+
/**
33+
* @var DeleteByDirectoryPath
34+
*/
35+
private $deleteMediaAssetByDirectoryPath;
36+
37+
/**
38+
* @var AdapterInterface|MockObject
39+
*/
40+
private $adapter;
41+
42+
/**
43+
* @var LoggerInterface|MockObject
44+
*/
45+
private $logger;
46+
47+
/**
48+
* Initialize basic test class mocks
49+
*/
50+
protected function setUp(): void
51+
{
52+
$this->logger = $this->createMock(LoggerInterface::class);
53+
$this->resourceConnection = $this->createMock(ResourceConnection::class);
54+
55+
$this->deleteMediaAssetByDirectoryPath = (new ObjectManager($this))->getObject(
56+
DeleteByDirectoryPath::class,
57+
[
58+
'resourceConnection' => $this->resourceConnection,
59+
'logger' => $this->logger,
60+
]
61+
);
62+
63+
$this->adapter = $this->createMock(AdapterInterface::class);
64+
}
65+
66+
/**
67+
* Test delete media asset by path command
68+
*
69+
* @param string $directoryPath
70+
* @throws CouldNotDeleteException
71+
* @dataProvider directoryPathDataProvider
72+
*/
73+
public function testDeleteByDirectoryPath(string $directoryPath): void
74+
{
75+
if (!empty($directoryPath)) {
76+
$this->resourceConnection->expects($this->once())
77+
->method('getConnection')
78+
->willReturn($this->adapter);
79+
$this->resourceConnection->expects($this->once())
80+
->method('getTableName')
81+
->with(self::TABLE_NAME)
82+
->willReturn('prefix_' . self::TABLE_NAME);
83+
$this->adapter->expects($this->once())
84+
->method('delete')
85+
->with('prefix_' . self::TABLE_NAME, ['path LIKE ?' => self::DIRECTORY_PATH . '%']);
86+
} else {
87+
self::expectException('\Magento\Framework\Exception\CouldNotDeleteException');
88+
}
89+
90+
$this->deleteMediaAssetByDirectoryPath->execute($directoryPath);
91+
}
92+
93+
/**
94+
* Data provider for directory path
95+
*
96+
* @return array
97+
*/
98+
public function directoryPathDataProvider(): array
99+
{
100+
return [
101+
'Existing path' => [self::DIRECTORY_PATH],
102+
'Empty path' => ['']
103+
];
104+
}
105+
}

0 commit comments

Comments
 (0)