Skip to content

Commit 0c70667

Browse files
Merge remote-tracking branch '39672/fix-for-issue-38933' into blprs
2 parents ade8ea8 + 6847456 commit 0c70667

File tree

6 files changed

+399
-120
lines changed

6 files changed

+399
-120
lines changed

app/code/Magento/Csp/Model/Collector/CspWhitelistXml/FileResolver.php

+29-22
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2020 Adobe
4+
* All Rights Reserved.
55
*/
66

77
declare(strict_types=1);
88

99
namespace Magento\Csp\Model\Collector\CspWhitelistXml;
1010

11+
use Magento\Framework\App\Filesystem\DirectoryList;
12+
use Magento\Framework\Config\CompositeFileIteratorFactory;
1113
use Magento\Framework\Config\FileResolverInterface;
1214
use Magento\Framework\Filesystem;
13-
use Magento\Framework\View\Design\ThemeInterface;
14-
use Magento\Framework\View\DesignInterface;
15+
use Magento\Framework\Filesystem\Directory\ReadInterface as DirectoryRead;
1516
use Magento\Framework\View\Design\Theme\CustomizationInterface;
1617
use Magento\Framework\View\Design\Theme\CustomizationInterfaceFactory;
17-
use Magento\Framework\App\Filesystem\DirectoryList;
18-
use Magento\Framework\Filesystem\Directory\ReadInterface as DirectoryRead;
19-
use Magento\Framework\Config\CompositeFileIteratorFactory;
18+
use Magento\Framework\View\Design\ThemeInterface;
19+
use Magento\Framework\View\DesignInterface;
2020

2121
/**
2222
* Combines configuration files from both modules and current theme.
@@ -74,22 +74,29 @@ public function __construct(
7474
*/
7575
public function get($filename, $scope)
7676
{
77-
$configs = $this->moduleFileResolver->get($filename, $scope);
78-
if ($scope === 'global') {
79-
$files = [];
80-
$theme = $this->theme;
81-
while ($theme) {
82-
/** @var CustomizationInterface $info */
83-
$info = $this->themeInfoFactory->create(['theme' => $theme]);
84-
$file = $info->getThemeFilesPath() .'/etc/' .$filename;
85-
if ($this->rootDir->isExist($file)) {
86-
$files[] = $file;
77+
$configs = $this->moduleFileResolver->get($filename, $scope);
78+
79+
switch ($scope) {
80+
case 'frontend':
81+
case 'adminhtml':
82+
$files = [];
83+
$theme = $this->theme;
84+
while ($theme) {
85+
/** @var CustomizationInterface $info */
86+
$info = $this->themeInfoFactory->create(['theme' => $theme]);
87+
$file = $info->getThemeFilesPath() . '/etc/' . $filename;
88+
if ($this->rootDir->isExist($file)) {
89+
$files[] = $file;
90+
}
91+
$theme = $theme->getParentTheme();
8792
}
88-
$theme = $theme->getParentTheme();
89-
}
90-
$configs = $this->iteratorFactory->create(
91-
['paths' => array_reverse($files), 'existingIterator' => $configs]
92-
);
93+
$configs = $this->iteratorFactory->create(
94+
['paths' => array_reverse($files), 'existingIterator' => $configs]
95+
);
96+
break;
97+
case 'global':
98+
default:
99+
break;
93100
}
94101

95102
return $configs;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Csp\Test\Unit\Model\Collector\CspWhitelistXml;
10+
11+
use Magento\Framework\Filesystem;
12+
use Magento\Framework\View\Design\Theme\CustomizationInterface;
13+
use Magento\Framework\View\Design\ThemeInterface;
14+
use PHPUnit\Framework\TestCase;
15+
use Magento\Framework\Config\FileResolverInterface;
16+
use Magento\Csp\Model\Collector\CspWhitelistXml\FileResolver;
17+
use Magento\Framework\View\DesignInterface;
18+
use Magento\Framework\Config\CompositeFileIteratorFactory;
19+
use Magento\Framework\View\Design\Theme\CustomizationInterfaceFactory;
20+
use Magento\Framework\Filesystem\Directory\ReadInterface;
21+
use Magento\Framework\App\Filesystem\DirectoryList;
22+
23+
class FileResolverTest extends TestCase
24+
{
25+
/**
26+
* @var FileResolver
27+
*/
28+
private $model;
29+
30+
/**
31+
* @var FileResolverInterface
32+
*/
33+
private $moduleFileResolverMock;
34+
35+
/**
36+
* @var DesignInterface
37+
*/
38+
private $designMock;
39+
40+
/**
41+
* @var CustomizationInterfaceFactory
42+
*/
43+
private $customizationFactoryMock;
44+
45+
/**
46+
* @var Filesystem
47+
*/
48+
private $filesystemMock;
49+
50+
/**
51+
* @var CompositeFileIteratorFactory
52+
*/
53+
private $iteratorFactoryMock;
54+
55+
/**
56+
* @var ReadInterface
57+
*/
58+
private $readInterfaceMock;
59+
60+
/**
61+
* @var ThemeInterface
62+
*/
63+
private $themeInterFaceMock;
64+
65+
/**
66+
* @var CustomizationInterface
67+
*/
68+
private $customizationInterfaceMock;
69+
70+
protected function setUp(): void
71+
{
72+
$this->moduleFileResolverMock = $this->getMockBuilder(FileResolverInterface::class)
73+
->disableOriginalConstructor()
74+
->getMock();
75+
76+
$this->designMock = $this->getMockBuilder(DesignInterface::class)
77+
->disableOriginalConstructor()
78+
->getMock();
79+
80+
$this->themeInterFaceMock = $this->getMockBuilder(ThemeInterface::class)
81+
->disableOriginalConstructor()
82+
->getMock();
83+
84+
$this->designMock->expects($this->once())
85+
->method('getDesignTheme')
86+
->willReturn($this->themeInterFaceMock);
87+
88+
$this->customizationFactoryMock = $this->getMockBuilder(CustomizationInterfaceFactory::class)
89+
->disableOriginalConstructor()
90+
->onlyMethods(['create'])
91+
->getMock();
92+
93+
$this->customizationInterfaceMock = $this->getMockBuilder(CustomizationInterface::class)
94+
->disableOriginalConstructor()
95+
->getMock();
96+
97+
$this->filesystemMock = $this->createPartialMock(Filesystem::class, ['getDirectoryRead']);
98+
99+
$this->readInterfaceMock = $this->getMockBuilder(ReadInterface::class)
100+
->disableOriginalConstructor()
101+
->getMock();
102+
103+
$this->filesystemMock->expects($this->once())
104+
->method('getDirectoryRead')
105+
->with(DirectoryList::ROOT)
106+
->willReturn($this->readInterfaceMock);
107+
108+
$this->iteratorFactoryMock = $this->getMockBuilder(CompositeFileIteratorFactory::class)
109+
->disableOriginalConstructor()
110+
->getMock();
111+
112+
$this->model = new FileResolver(
113+
$this->moduleFileResolverMock,
114+
$this->designMock,
115+
$this->customizationFactoryMock,
116+
$this->filesystemMock,
117+
$this->iteratorFactoryMock
118+
);
119+
}
120+
121+
/**
122+
* Test for get method with frontend scope.
123+
*
124+
* @param string $scope
125+
* @param string $fileName
126+
* @param array $fileList
127+
* @param string $themeFilesPath
128+
*
129+
* @return void
130+
* @dataProvider providerGetFrontend
131+
*/
132+
public function testGetFrontend(string $scope, string $fileName, array $fileList, string $themeFilesPath): void
133+
{
134+
$this->moduleFileResolverMock->expects($this->once())
135+
->method('get')
136+
->with($fileName, $scope)
137+
->willReturn($fileList);
138+
139+
$this->customizationFactoryMock->expects($this->any())
140+
->method('create')
141+
->with(['theme' => $this->themeInterFaceMock])
142+
->willReturn($this->customizationInterfaceMock);
143+
144+
$this->customizationInterfaceMock->expects($this->once())
145+
->method('getThemeFilesPath')
146+
->willReturn($themeFilesPath);
147+
148+
$this->readInterfaceMock->expects($this->once())
149+
->method('isExist')
150+
->with($themeFilesPath.'/etc/'.$fileName)
151+
->willReturn(true);
152+
153+
$this->iteratorFactoryMock->expects($this->once())
154+
->method('create')
155+
->with(
156+
[
157+
'paths' => array_reverse([$themeFilesPath.'/etc/'.$fileName]),
158+
'existingIterator' => $fileList
159+
]
160+
)
161+
->willReturn($fileList);
162+
163+
$this->assertEquals($fileList, $this->model->get($fileName, $scope));
164+
}
165+
166+
/**
167+
* Test for get method with global scope.
168+
*
169+
* @param string $scope
170+
* @param string $fileName
171+
* @param array $fileList
172+
*
173+
* @return void
174+
* @dataProvider providerGetGlobal
175+
*/
176+
public function testGetGlobal(string $scope, string $fileName, array $fileList): void
177+
{
178+
$this->moduleFileResolverMock->expects($this->once())
179+
->method('get')
180+
->with($fileName, $scope)
181+
->willReturn($fileList);
182+
$this->assertEquals($fileList, $this->model->get($fileName, $scope));
183+
}
184+
185+
/**
186+
* Data provider for get global scope tests.
187+
*
188+
* @return array
189+
*/
190+
public static function providerGetGlobal(): array
191+
{
192+
return [
193+
[
194+
'global',
195+
'csp_whitelist.xml',
196+
['anyvendor/anymodule/etc/csp_whitelist.xml']
197+
]
198+
];
199+
}
200+
201+
/**
202+
* Data provider for get frontend & adminhtml scope tests.
203+
*
204+
* @return array
205+
*/
206+
public static function providerGetFrontend(): array
207+
{
208+
return [
209+
[
210+
'frontend',
211+
'csp_whitelist.xml',
212+
['themevendor/theme/etc/csp_whitelist.xml'],
213+
'themevendor/theme'
214+
],
215+
[
216+
'adminhtml',
217+
'csp_whitelist.xml',
218+
['adminthemevendor/admintheme/etc/csp_whitelist.xml'],
219+
'adminthemevendor/admintheme'
220+
]
221+
];
222+
}
223+
}

app/code/Magento/Email/Test/Fixture/FileTransport.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818
class FileTransport implements RevertibleDataFixtureInterface
1919
{
2020
private const DEFAULT_DATA = [
21-
'directory' => DirectoryList::VAR_DIR,
22-
'path' => 'mail/%uniqid%',
23-
'data' => 'Bienvenue sur Le Site de Paris.'
21+
'directory' => DirectoryList::TMP,
22+
'path' => 'mail/%uniqid%'
2423
];
2524

2625
private const CONFIG_FILE = 'mail-transport-config.json';

dev/tests/api-functional/_files/Magento/TestModuleEmail/Model/Transport/File.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ public function send(SymfonyMessage|RawMessage $message, ?Envelope $envelope = n
6363
$config = $this->json->unserialize($directory->readFile(self::CONFIG_FILE));
6464
$directory = $this->filesystem->getDirectoryWrite($config['directory']);
6565
$mail = $message->toString();
66-
$addresses = $this->message->getHeaders()->get('To')?->getAddresses() ?? [];
66+
$addresses = $message->getHeaders()->get('To')?->getAddresses() ?? [];
6767
foreach ($addresses as $address) {
6868
$index = 1;
69-
$filename = preg_replace('/[^a-z0-9_]/', '__', strtolower($address->getEmail()));
69+
$filename = preg_replace('/[^a-z0-9_]/', '__', strtolower($address->getAddress()));
7070
$basePath = $config['path']. DIRECTORY_SEPARATOR . $filename;
7171
$path = $basePath . '.eml';
7272
while ($directory->isExist($path)) {

dev/tests/api-functional/testsuite/Magento/Customer/Api/AsyncBulkAccountManagementTest.php

+9-13
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use Magento\Directory\Helper\Data as LocaleConfig;
1212
use Magento\Email\Test\Fixture\FileTransport as FileTransportFixture;
1313
use Magento\Framework\Filesystem;
14-
use Magento\Framework\App\Filesystem\DirectoryList;
1514
use Magento\Store\Test\Fixture\Group as StoreGroupFixture;
1615
use Magento\Store\Test\Fixture\Store as StoreFixture;
1716
use Magento\Store\Test\Fixture\Website as WebsiteFixture;
@@ -139,34 +138,31 @@ function (array $data) {
139138
$this->fail("Customer was not created");
140139
}
141140

141+
$mailConfig = $fixtures->get('mail_transport_config')->getData();
142142
$filesystem = $this->objectManager->get(Filesystem::class);
143-
$directory = $filesystem->getDirectoryRead(DirectoryList::VAR_DIR);
143+
$directory = $filesystem->getDirectoryRead($mailConfig['directory']);
144144

145145
// wait until a mail is sent
146146
try {
147147
$this->publisherConsumerController->waitForAsynchronousResult(
148-
function (\Magento\Framework\Filesystem\Directory\ReadInterface $directory) {
149-
return count($directory->read('')) > 0;
148+
function (\Magento\Framework\Filesystem\Directory\ReadInterface $directory, string $path) {
149+
return $directory->isExist($path) && count($directory->read($path)) > 0;
150150
},
151-
[$directory]
151+
[$directory, $mailConfig['path']]
152152
);
153153
} catch (PreconditionFailedException $e) {
154154
$this->fail("No mail was sent");
155155
}
156156

157-
$mailPaths = $directory->read(DirectoryList::VAR_DIR);
157+
$mailPaths = $directory->read($mailConfig['path']);
158158
$sentMails = count($mailPaths);
159159
$this->assertCount(1, $mailPaths, "Only 1 mail was expected to be sent, actually $sentMails were sent.");
160-
$mailContent = $directory->readFile('mail-transport-config.json');
160+
$mailContent = $directory->readFile($mailPaths[0]);
161161
$parser = $this->objectManager->get(Parser::class);
162-
$message = $parser->fromString((string)$mailContent)->getSymfonyMessage()->getBody();
163-
$decoded_data = base64_decode($message->bodyToString());
164-
$parts = explode("\r\n", $decoded_data);
165-
$count = count($parts);
166-
$mergedString = base64_decode($parts[$count - 2] . $parts[$count - 1]);
162+
$message = $parser->fromString($mailContent);
167163
$this->assertStringContainsString(
168164
'Bienvenue sur Le Site de Paris.',
169-
$mergedString
165+
quoted_printable_decode($message->getSymfonyMessage()->getBody()->bodyToString())
170166
);
171167
}
172168

0 commit comments

Comments
 (0)