Skip to content

Commit ca5b2f9

Browse files
committed
ACP2E-3324: allow cli config set with and w/o lock for design theme configs and bypass saving design config that is locked
1 parent 5ebdfb5 commit ca5b2f9

File tree

5 files changed

+310
-11
lines changed

5 files changed

+310
-11
lines changed

app/code/Magento/Theme/Model/Data/Design/ConfigFactory.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
namespace Magento\Theme\Model\Data\Design;
88

9+
use Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker;
10+
use Magento\Framework\App\ObjectManager;
911
use Magento\Framework\App\ScopeValidatorInterface;
1012
use Magento\Framework\Exception\LocalizedException;
1113
use Magento\Store\Model\StoreManagerInterface;
@@ -60,21 +62,24 @@ class ConfigFactory
6062
* @param DesignConfigExtensionFactory $configExtensionFactory
6163
* @param ScopeValidatorInterface $scopeValidator
6264
* @param StoreManagerInterface $storeManager
65+
* @param SettingChecker|null $settingChecker
6366
*/
6467
public function __construct(
6568
DesignConfigInterfaceFactory $designConfigFactory,
6669
MetadataProviderInterface $metadataProvider,
6770
DesignConfigDataInterfaceFactory $designConfigDataFactory,
6871
DesignConfigExtensionFactory $configExtensionFactory,
6972
ScopeValidatorInterface $scopeValidator,
70-
StoreManagerInterface $storeManager
73+
StoreManagerInterface $storeManager,
74+
private ?SettingChecker $settingChecker = null
7175
) {
7276
$this->designConfigFactory = $designConfigFactory;
7377
$this->metadataProvider = $metadataProvider;
7478
$this->designConfigDataFactory = $designConfigDataFactory;
7579
$this->configExtensionFactory = $configExtensionFactory;
7680
$this->scopeValidator = $scopeValidator;
7781
$this->storeManager = $storeManager;
82+
$this->settingChecker = $this->settingChecker ?? ObjectManager::getInstance()->get(SettingChecker::class);
7883
}
7984

8085
/**
@@ -100,7 +105,7 @@ public function create($scope, $scopeId, array $data = [])
100105
$configDataObject = $this->designConfigDataFactory->create();
101106
$configDataObject->setPath($metadata['path']);
102107
$configDataObject->setFieldConfig($metadata);
103-
if (isset($data[$name])) {
108+
if (isset($data[$name]) && !$this->settingChecker->isReadOnly($metadata['path'], $scope, $scopeId)) {
104109
$configDataObject->setValue($data[$name]);
105110
}
106111
$configData[] = $configDataObject;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Theme\Plugin;
9+
10+
use Magento\Config\Model\Config\PathValidator;
11+
use Magento\Config\Model\Config\Structure;
12+
use Magento\Config\Model\Config\Structure\Element\Field;
13+
use Magento\Framework\Exception\ValidatorException;
14+
use Magento\Theme\Model\DesignConfigRepository;
15+
16+
class ValidateDesignConfig
17+
{
18+
/**
19+
* @param Structure $structure
20+
* @param DesignConfigRepository $designConfigRepository
21+
*/
22+
public function __construct(
23+
private readonly Structure $structure,
24+
private readonly DesignConfigRepository $designConfigRepository
25+
) {
26+
}
27+
28+
/**
29+
* Allow setting design configuration from cli
30+
*
31+
* @param PathValidator $subject
32+
* @param callable $proceed
33+
* @param string $path
34+
* @return true
35+
* @throws ValidatorException
36+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
37+
*/
38+
public function aroundValidate(PathValidator $subject, callable $proceed, $path): bool
39+
{
40+
if (stripos($path, 'design/') !== 0) {
41+
return $proceed($path);
42+
}
43+
44+
$element = $this->structure->getElementByConfigPath($path);
45+
if ($element instanceof Field && $element->getConfigPath()) {
46+
$path = $element->getConfigPath();
47+
}
48+
49+
$allPaths = array_merge($this->structure->getFieldPaths(), $this->getDesignFieldPaths());
50+
51+
if (!array_key_exists($path, $allPaths)) {
52+
throw new ValidatorException(__('The "%1" path doesn\'t exist. Verify and try again.', $path));
53+
}
54+
return true;
55+
}
56+
57+
/**
58+
* Get design configuration field paths
59+
*
60+
* @return array
61+
*/
62+
private function getDesignFieldPaths(): array
63+
{
64+
$designConfig = $this->designConfigRepository->getByScope('default', null);
65+
$fieldsData = $designConfig->getExtensionAttributes()->getDesignConfigData();
66+
$data = [];
67+
foreach ($fieldsData as $fieldData) {
68+
$data[$fieldData->getFieldConfig()['path']] = [$fieldData->getFieldConfig()['path']];
69+
}
70+
return $data;
71+
}
72+
}

app/code/Magento/Theme/Test/Unit/Model/Data/Design/ConfigFactoryTest.php

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace Magento\Theme\Test\Unit\Model\Data\Design;
99

10+
use Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker;
1011
use Magento\Framework\App\ScopeValidatorInterface;
1112
use Magento\Store\Api\Data\WebsiteInterface;
1213
use Magento\Store\Model\StoreManagerInterface;
@@ -52,6 +53,11 @@ class ConfigFactoryTest extends TestCase
5253
/** @var WebsiteInterface|MockObject */
5354
protected $website;
5455

56+
/**
57+
* @var SettingChecker|MockObject
58+
*/
59+
private $settingChecker;
60+
5561
protected function setUp(): void
5662
{
5763
$this->designConfigFactory = $this->createPartialMock(
@@ -99,14 +105,18 @@ protected function setUp(): void
99105
->getMockForAbstractClass();
100106
$this->website = $this->getMockBuilder(WebsiteInterface::class)
101107
->getMockForAbstractClass();
108+
$this->settingChecker = $this->getMockBuilder(SettingChecker::class)
109+
->disableOriginalConstructor()
110+
->getMock();
102111

103112
$this->factory = new ConfigFactory(
104113
$this->designConfigFactory,
105114
$this->metadataProvider,
106115
$this->designConfigDataFactory,
107116
$this->configExtensionFactory,
108117
$this->scopeValidator,
109-
$this->storeManager
118+
$this->storeManager,
119+
$this->settingChecker
110120
);
111121
}
112122

@@ -158,14 +168,18 @@ public function testCreate()
158168
return null;
159169
}
160170
});
161-
$this->designConfigData->expects($this->exactly(2))
162-
->method('setFieldConfig')
163-
->willReturnCallback(function ($config) {
164-
if ($config['path'] == 'design/header/default_title' ||
165-
$config['path']== 'design/head/default_description') {
166-
return null;
167-
}
168-
});
171+
$this->designConfigData->expects($this->exactly(2))
172+
->method('setFieldConfig')
173+
->willReturnCallback(function ($config) {
174+
if ($config['path'] == 'design/header/default_title' ||
175+
$config['path'] == 'design/head/default_description') {
176+
return null;
177+
}
178+
});
179+
$this->settingChecker->expects($this->once())
180+
->method('isReadOnly')
181+
->with('design/header/default_title', 'default', 0)
182+
->willReturn(false);
169183
$this->designConfigData->expects($this->once())
170184
->method('setValue')
171185
->with('value');
@@ -242,6 +256,10 @@ public function testCreateInSingleStoreMode()
242256
return null;
243257
}
244258
});
259+
$this->settingChecker->expects($this->once())
260+
->method('isReadOnly')
261+
->with('design/header/default_title', 'default', 0)
262+
->willReturn(false);
245263
$this->designConfigData->expects($this->once())
246264
->method('setValue')
247265
->with('value');
@@ -256,4 +274,80 @@ public function testCreateInSingleStoreMode()
256274
->with($this->designConfigExtension);
257275
$this->assertSame($this->designConfig, $this->factory->create($scope, $scopeId, $data));
258276
}
277+
278+
public function testBypassSettingLockedConfig() {
279+
$scope = 'default';
280+
$scopeId = 0;
281+
$data = [
282+
'header_default_title' => 'value'
283+
];
284+
$metadata = [
285+
'header_default_title' => [
286+
'path' => 'design/header/default_title',
287+
'fieldset' => 'head'
288+
],
289+
'head_default_description' => [
290+
'path' => 'design/head/default_description',
291+
'fieldset' => 'head'
292+
],
293+
];
294+
295+
$this->scopeValidator->expects($this->once())
296+
->method('isValidScope')
297+
->with($scope, $scopeId)
298+
->willReturn(true);
299+
$this->storeManager->expects($this->once())
300+
->method('isSingleStoreMode')
301+
->willReturn(true);
302+
$this->storeManager->expects($this->once())
303+
->method('getWebsites')
304+
->willReturn([$this->website]);
305+
$this->website->expects($this->once())
306+
->method('getId')
307+
->willReturn(1);
308+
309+
$this->designConfigFactory->expects($this->once())
310+
->method('create')
311+
->willReturn($this->designConfig);
312+
$this->designConfig->expects($this->once())
313+
->method('setScope')
314+
->willReturn('websites');
315+
$this->designConfig->expects($this->once())
316+
->method('setScopeId')
317+
->willReturn(1);
318+
$this->metadataProvider->expects($this->once())
319+
->method('get')
320+
->willReturn($metadata);
321+
$this->designConfigDataFactory->expects($this->exactly(2))
322+
->method('create')
323+
->willReturn($this->designConfigData);
324+
$this->designConfigData->expects($this->exactly(2))
325+
->method('setPath')
326+
->willReturnCallback(function ($arg1) {
327+
if ($arg1 == 'design/header/default_title' && $arg1 == 'design/head/default_description') {
328+
return null;
329+
}
330+
});
331+
$this->designConfigData->expects($this->exactly(2))
332+
->method('setFieldConfig')
333+
->willReturnCallback(function ($arg1) {
334+
if ($arg1 == 'design/header/default_title' && $arg1 == 'design/head/default_description') {
335+
return null;
336+
}
337+
});
338+
$this->settingChecker->expects($this->once())
339+
->method('isReadOnly')
340+
->with('design/header/default_title', 'default', 0)
341+
->willReturn(true);
342+
$this->configExtensionFactory->expects($this->once())
343+
->method('create')
344+
->willReturn($this->designConfigExtension);
345+
$this->designConfigExtension->expects($this->once())
346+
->method('setDesignConfigData')
347+
->with([$this->designConfigData, $this->designConfigData]);
348+
$this->designConfig->expects($this->once())
349+
->method('setExtensionAttributes')
350+
->with($this->designConfigExtension);
351+
$this->assertSame($this->designConfig, $this->factory->create($scope, $scopeId, $data));
352+
}
259353
}

0 commit comments

Comments
 (0)