diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml index 241e8f24fc..b467eac21d 100644 --- a/.idea/jsonSchemas.xml +++ b/.idea/jsonSchemas.xml @@ -797,6 +797,25 @@ + + + + + + + diff --git a/.vscode/settings.json b/.vscode/settings.json index 333c511351..cdefcea218 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -334,6 +334,12 @@ ], "url": "./schema/metadata/app/language.json" }, + { + "fileMatch": [ + "*/Resources/metadata/app/layouts.json" + ], + "url": "./schema/metadata/app/layouts.json" + }, { "fileMatch": [ "*/Resources/metadata/app/linkManager.json" diff --git a/application/Espo/Resources/metadata/app/layouts.json b/application/Espo/Resources/metadata/app/layouts.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/application/Espo/Resources/metadata/app/layouts.json @@ -0,0 +1 @@ +{} diff --git a/application/Espo/Tools/Layout/LayoutProvider.php b/application/Espo/Tools/Layout/LayoutProvider.php index 6f1b6657f0..48087a45cf 100644 --- a/application/Espo/Tools/Layout/LayoutProvider.php +++ b/application/Espo/Tools/Layout/LayoutProvider.php @@ -32,6 +32,7 @@ namespace Espo\Tools\Layout; use Espo\Core\InjectableFactory; use Espo\Core\Utils\File\Manager as FileManager; use Espo\Core\Utils\Json; +use Espo\Core\Utils\Metadata; use Espo\Core\Utils\Resource\FileReader; use Espo\Core\Utils\Resource\FileReader\Params as FileReaderParams; use RuntimeException; @@ -46,6 +47,7 @@ class LayoutProvider public function __construct( private FileManager $fileManager, private InjectableFactory $injectableFactory, + private Metadata $metadata, FileReader $fileReader ) { $this->fileReader = $fileReader; @@ -62,8 +64,15 @@ class LayoutProvider $path = 'layouts/' . $scope . '/' . $name . '.json'; - $params = FileReaderParams::create() - ->withScope($scope); + $params = FileReaderParams::create()->withScope($scope); + + $module = $this->getLayoutLocationModule($scope, $name); + + if ($module) { + $params = $params + ->withScope(null) + ->withModuleName($module); + } if ($this->fileReader->exists($path, $params)) { return $this->fileReader->read($path, $params); @@ -72,6 +81,11 @@ class LayoutProvider return $this->getDefault($scope, $name); } + private function getLayoutLocationModule(string $scope, string $name): ?string + { + return $this->metadata->get("app.layouts.{$scope}.{$name}.module"); + } + private function getDefault(string $scope, string $name): ?string { $defaultImplClassName = 'Espo\\Custom\\Classes\\DefaultLayouts\\' . ucfirst($name) . 'Type'; diff --git a/schema/metadata/app/layouts.json b/schema/metadata/app/layouts.json new file mode 100644 index 0000000000..9439fc151b --- /dev/null +++ b/schema/metadata/app/layouts.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://www.espocrm.com/schema/metadata/app/layouts.json", + "title": "app/layouts", + "description": "Layouts.", + "type": "object", + "propertyNames": { + "anyOf": [ + {"type": "string"} + ] + }, + "additionalProperties": { + "type": "object", + "description": "A scope name.", + "additionalProperties": { + "type": "object", + "description": "A layout name.", + "properties": { + "module": { + "type": "string", + "description": "A module in which the layout is located." + } + } + } + } +} diff --git a/tests/unit/Espo/Core/Utils/LayoutTest.php b/tests/unit/Espo/Core/Utils/LayoutTest.php index 87740d50ff..c51d8df2f0 100644 --- a/tests/unit/Espo/Core/Utils/LayoutTest.php +++ b/tests/unit/Espo/Core/Utils/LayoutTest.php @@ -29,43 +29,38 @@ namespace tests\unit\Espo\Core\Utils; +use Espo\Core\Utils\Metadata; use Espo\Tools\Layout\LayoutProvider; use Espo\Core\Utils\File\Manager as FileManager; use Espo\Core\InjectableFactory; -use Espo\Core\{ - Utils\Resource\FileReader, - Utils\Resource\FileReader\Params as FileReaderParams, -}; +use Espo\Core\Utils\Resource\FileReader; +use Espo\Core\Utils\Resource\FileReader\Params as FileReaderParams; class LayoutTest extends \PHPUnit\Framework\TestCase { - /** - * @var LayoutProvider - */ + /** @var LayoutProvider */ private $layout; - - /** - * @var InjectableFactory - */ + /** @var InjectableFactory */ private $injectableFactory; - - /** - * @var FileManager - */ + /** @var FileManager */ private $fileManager; - private $fileReader; protected function setUp(): void { $this->fileManager = $this->createMock(FileManager::class); - $this->injectableFactory = $this->createMock(InjectableFactory::class); - $this->fileReader = $this->createMock(FileReader::class); - $this->layout = new \Espo\Tools\Layout\LayoutProvider($this->fileManager, $this->injectableFactory, $this->fileReader); + $metadata = $this->createMock(Metadata::class); + + $this->layout = new LayoutProvider( + $this->fileManager, + $this->injectableFactory, + $metadata, + $this->fileReader + ); } public function testGet1(): void