From 7332d0aa379b59295d56e7ab4804febf7d49ca0d Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Thu, 24 Jun 2021 16:06:19 +0300 Subject: [PATCH] layout use file reader --- application/Espo/Core/Portal/Utils/Layout.php | 27 ++-- application/Espo/Core/Utils/Layout.php | 78 ++++------ .../Espo/Core/Utils/Resource/FileReader.php | 116 ++++++++++++++ .../Core/Utils/Resource/FileReaderParams.php | 53 +++++++ .../Tools/LayoutManager/LayoutManager.php | 11 +- tests/unit/Espo/Core/Utils/LayoutTest.php | 145 ++++++++++-------- 6 files changed, 287 insertions(+), 143 deletions(-) create mode 100644 application/Espo/Core/Utils/Resource/FileReader.php create mode 100644 application/Espo/Core/Utils/Resource/FileReaderParams.php diff --git a/application/Espo/Core/Portal/Utils/Layout.php b/application/Espo/Core/Portal/Utils/Layout.php index 46abf5aa3c..feb3a666a4 100644 --- a/application/Espo/Core/Portal/Utils/Layout.php +++ b/application/Espo/Core/Portal/Utils/Layout.php @@ -30,19 +30,15 @@ namespace Espo\Core\Portal\Utils; use Espo\Core\{ - Utils\Util, - Utils\Json, Utils\Layout as LayoutBase, Exceptions\Error, + Utils\Resource\FileReaderParams, }; class Layout extends LayoutBase { public function get(string $scope, string $name): ?string { - $originalScope = $scope; - $originalName = $name; - if ( $this->sanitizeInput($scope) !== $scope || $this->sanitizeInput($name) !== $name @@ -50,20 +46,15 @@ class Layout extends LayoutBase throw new Error("Bad parameters."); } - if (isset($this->changedData[$scope][$name])) { - return Json::encode($this->changedData[$scope][$name]); + $path = 'layouts/' . $scope . '/portal/' . $name . '.json'; + + $params = FileReaderParams::create() + ->withScope($scope); + + if ($this->fileReader->exists($path, $params)) { + return $this->fileReader->read($path, $params); } - $filePath = Util::concatPath($this->getDirPath($scope, true), 'portal/' . $name . '.json'); - - if (!file_exists($filePath)) { - $filePath = Util::concatPath($this->getDirPath($scope), 'portal/' . $name . '.json'); - } - - if (file_exists($filePath)) { - return $this->fileManager->getContents($filePath); - } - - return parent::get($originalScope, $originalName); + return parent::get($scope, $name); } } diff --git a/application/Espo/Core/Utils/Layout.php b/application/Espo/Core/Utils/Layout.php index e8c88032c2..549cdcbd11 100644 --- a/application/Espo/Core/Utils/Layout.php +++ b/application/Espo/Core/Utils/Layout.php @@ -31,28 +31,33 @@ namespace Espo\Core\Utils; use Espo\Core\{ Utils\File\Manager as FileManager, - Utils\Metadata, + InjectableFactory, Exceptions\Error, + Utils\Resource\FileReader, + Utils\Resource\FileReaderParams, }; class Layout { private $defaultPath = 'application/Espo/Resources/defaults/layouts'; - private $paths = [ - 'corePath' => 'application/Espo/Resources/layouts', - 'modulePath' => 'application/Espo/Modules/{*}/Resources/layouts', - 'customPath' => 'custom/Espo/Custom/Resources/layouts', - ]; - private $fileManager; - private $metadata; + private $injectableFactory; - public function __construct(FileManager $fileManager, Metadata $metadata) - { + /** + * @internal Used by the portal layout util. + */ + protected $fileReader; + + public function __construct( + FileManager $fileManager, + InjectableFactory $injectableFactory, + FileReader $fileReader + ) { $this->fileManager = $fileManager; - $this->metadata = $metadata; + $this->injectableFactory = $injectableFactory; + $this->fileReader = $fileReader; } public function get(string $scope, string $name): ?string @@ -64,27 +69,16 @@ class Layout throw new Error("Bad parameters."); } - if (isset($this->changedData[$scope][$name])) { - return Json::encode($this->changedData[$scope][$name]); + $path = 'layouts/' . $scope . '/' . $name . '.json'; + + $params = FileReaderParams::create() + ->withScope($scope); + + if ($this->fileReader->exists($path, $params)) { + return $this->fileReader->read($path, $params); } - $filePath = Util::concatPath( - $this->getDirPath($scope, true), - $name . '.json' - ); - - if (!file_exists($filePath)) { - $filePath = Util::concatPath( - $this->getDirPath($scope), - $name . '.json' - ); - } - - if (!file_exists($filePath)) { - return $this->getDefault($scope, $name); - } - - return $this->fileManager->getContents($filePath); + return $this->getDefault($scope, $name); } private function getDefault(string $scope, string $name): ?string @@ -97,7 +91,7 @@ class Layout if (class_exists($defaultImplClassName)) { // @todo Use factory and interface. - $defaultImpl = new $defaultImplClassName($this->metadata); + $defaultImpl = $this->injectableFactory->create($defaultImplClassName); $data = $defaultImpl->get($scope); @@ -109,33 +103,13 @@ class Layout $name . '.json' ); - if (!file_exists($filePath)) { + if (!$this->fileManager->isFile($filePath)) { return null; } return $this->fileManager->getContents($filePath); } - public function getDirPath(string $entityType, bool $isCustom = false): string - { - $path = $this->paths['customPath']; - - if (!$isCustom) { - $moduleName = $this->metadata->getScopeModuleName($entityType); - - $path = $this->paths['corePath']; - - if ($moduleName) { - $path = str_replace('{*}', $moduleName, $this->paths['modulePath']); - } - } - - return Util::concatPath( - Util::fixPath($path), - $entityType - ); - } - protected function sanitizeInput(string $name): string { return preg_replace("([\.]{2,})", '', $name); diff --git a/application/Espo/Core/Utils/Resource/FileReader.php b/application/Espo/Core/Utils/Resource/FileReader.php new file mode 100644 index 0000000000..9f17553ff2 --- /dev/null +++ b/application/Espo/Core/Utils/Resource/FileReader.php @@ -0,0 +1,116 @@ + 'application/Espo/Resources/', + 'modulePath' => 'application/Espo/Modules/{*}/Resources/', + 'customPath' => 'custom/Espo/Custom/Resources/', + ]; + + private $fileManager; + + private $metadata; + + public function __construct(FileManager $fileManager, Metadata $metadata) + { + $this->fileManager = $fileManager; + $this->metadata = $metadata; + } + + /** + * Read a resource file. Returns NULL if the file does not exists. + * + * @throws RuntimeException If the resource does not exist. + */ + public function read(string $path, FileReaderParams $params): string + { + $exactPath = $this->findExactPath($path, $params); + + if (!$exactPath) { + throw new RuntimeException("Resource file '{$path}' does not exist."); + } + + return $this->fileManager->getContents($exactPath); + } + + /** + * Whether a resource file exists. + */ + public function exists(string $path, FileReaderParams $params): bool + { + return $this->findExactPath($path, $params) !== null; + } + + private function findExactPath(string $path, FileReaderParams $params): ?string + { + $customPath = $this->paths['customPath'] . $path; + + if ($this->fileManager->isFile($customPath)) { + return $customPath; + } + + $moduleName = null; + + if ($params->getScope()) { + $moduleName = $this->metadata->getScopeModuleName($params->getScope()); + } + + if ($moduleName) { + $modulePath = str_replace( + '{*}', + $moduleName, + $this->paths['modulePath'] . $path + ); + + if ($this->fileManager->isFile($modulePath)) { + return $modulePath; + } + } + + $corePath = $this->paths['corePath'] . $path; + + if ($this->fileManager->isFile($corePath)) { + return $corePath; + } + + return null; + } +} diff --git a/application/Espo/Core/Utils/Resource/FileReaderParams.php b/application/Espo/Core/Utils/Resource/FileReaderParams.php new file mode 100644 index 0000000000..82bd59e7cf --- /dev/null +++ b/application/Espo/Core/Utils/Resource/FileReaderParams.php @@ -0,0 +1,53 @@ +scope = $scope; + + return $obj; + } + + public function getScope(): ?string + { + return $this->scope; + } +} diff --git a/application/Espo/Tools/LayoutManager/LayoutManager.php b/application/Espo/Tools/LayoutManager/LayoutManager.php index cd4b8f0250..35c4df3c58 100644 --- a/application/Espo/Tools/LayoutManager/LayoutManager.php +++ b/application/Espo/Tools/LayoutManager/LayoutManager.php @@ -110,16 +110,16 @@ class LayoutManager } foreach ($this->changedData as $scope => $rowData) { + $dirPath = 'custom/Espo/Custom/Resources/layouts/' . $scope . '/'; + foreach ($rowData as $layoutName => $layoutData) { if (empty($scope) || empty($layoutName)) { continue; } - $layoutPath = $this->getDirPath($scope, true); - $data = Json::encode($layoutData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); - $path = $layoutPath . '/' . $layoutName . '.json'; + $path = $dirPath . $layoutName . '.json'; $result &= $this->fileManager->putContents($path, $data); } @@ -140,11 +140,6 @@ class LayoutManager $this->changedData = []; } - protected function getDirPath(string $entityType, bool $isCustom = false): string - { - return $this->layout->getDirPath($entityType, $isCustom); - } - protected function sanitizeInput(string $name): string { return preg_replace("([\.]{2,})", '', $name); diff --git a/tests/unit/Espo/Core/Utils/LayoutTest.php b/tests/unit/Espo/Core/Utils/LayoutTest.php index 28c13bea20..a2a028b605 100644 --- a/tests/unit/Espo/Core/Utils/LayoutTest.php +++ b/tests/unit/Espo/Core/Utils/LayoutTest.php @@ -29,92 +29,107 @@ namespace tests\unit\Espo\Core\Utils; -use tests\unit\ReflectionHelper; - -use Espo\Core\Utils\Util; use Espo\Core\Utils\Layout; +use Espo\Core\Utils\File\Manager as FileManager; +use Espo\Core\InjectableFactory; + +use Espo\Core\{ + Utils\Resource\FileReader, + Utils\Resource\FileReaderParams, +}; class LayoutTest extends \PHPUnit\Framework\TestCase { - protected $object; + /** + * @var Layout + */ + private $layout; - protected $objects; + /** + * @var InjectableFactory + */ + private $injectableFactory; - protected $reflection; + /** + * @var FileManager + */ + private $fileManager; - protected $filesPath= 'tests/unit/testData/FileManager'; + private $fileReader; protected function setUp() : void { - $this->fileManager = $this->getMockBuilder('Espo\\Core\\Utils\\File\\Manager') - ->disableOriginalConstructor()->getMock(); + $this->fileManager = $this->createMock(FileManager::class); - $this->metadata = $this->getMockBuilder('Espo\\Core\\Utils\\Metadata') - ->disableOriginalConstructor()->getMock(); + $this->injectableFactory = $this->createMock(InjectableFactory::class); - $this->user = $this->getMockBuilder('Espo\\Entities\\User') - ->disableOriginalConstructor()->getMock(); + $this->fileReader = $this->createMock(FileReader::class); - $this->object = new Layout($this->fileManager, $this->metadata, $this->user); - - $this->reflection = new ReflectionHelper($this->object); + $this->layout = new Layout($this->fileManager, $this->injectableFactory, $this->fileReader); } - protected function tearDown() : void + public function testGet1(): void { - $this->object = NULL; + $this->fileReader + ->expects($this->once()) + ->method('exists') + ->with( + 'layouts/Test/test.json', + $this->callback( + function (FileReaderParams $params): bool { + return $params->getScope() === 'Test'; + } + ) + ) + ->willReturn(true); + + $this->fileReader + ->expects($this->once()) + ->method('read') + ->with( + 'layouts/Test/test.json', + $this->callback( + function (FileReaderParams $params): bool { + return $params->getScope() === 'Test'; + } + ) + ) + ->willReturn('["test"]'); + + $result = $this->layout->get('Test', 'test'); + + $this->assertEquals('["test"]', $result); } - function testGetLayoutPathCore() + public function testGetDefault(): void { - $this->metadata - ->expects($this->exactly(1)) - ->method('getScopeModuleName') - ->will($this->returnValue(null)); - - $this->assertEquals(Util::fixPath('application/Espo/Resources/layouts/User'), - $this->reflection->invokeMethod('getDirPath', ['User']) - ); - $this->assertEquals( - Util::fixPath('custom/Espo/Custom/Resources/layouts/User'), - $this->reflection->invokeMethod('getDirPath', ['User', true]) - ); - } - - function testGetLayoutPathModule() - { - $this->metadata - ->expects($this->exactly(1)) - ->method('getScopeModuleName') - ->will($this->returnValue('Crm')); - - $this->assertEquals( - Util::fixPath('application/Espo/Modules/Crm/Resources/layouts/Call'), - $this->reflection->invokeMethod('getDirPath', array('Call')) - ); - $this->assertEquals( - Util::fixPath('custom/Espo/Custom/Resources/layouts/Call'), - $this->reflection->invokeMethod('getDirPath', array('Call', true)) - ); - } - - function testGet() - { - $result = - '[{"label":"Overview","rows":[[{"name":"userName"}," .' . - '"{"name":"isAdmin"}],[{"name":"name"},{"name":"title"}]," .' . - '"[{"name":"defaultTeam"}],[{"name":"emailAddress"},{"name":"phone"}]]}]'; - - $this->metadata - ->expects($this->exactly(1)) - ->method('getScopeModuleName') - ->will($this->returnValue(null)); + $this->fileReader + ->expects($this->once()) + ->method('exists') + ->with( + 'layouts/Test/test.json', + $this->callback( + function (FileReaderParams $params): bool { + return $params->getScope() === 'Test'; + } + ) + ) + ->willReturn(false); $this->fileManager - ->expects($this->exactly(1)) - ->method('getContents') - ->will($this->returnValue($result)); + ->expects($this->once()) + ->method('isFile') + ->with('application/Espo/Resources/defaults/layouts/test.json') + ->willReturn(true); - $this->assertEquals($result, $this->object->get('Note', 'detail')); + $this->fileManager + ->expects($this->once()) + ->method('getContents') + ->with('application/Espo/Resources/defaults/layouts/test.json') + ->willReturn('["test"]'); + + $result = $this->layout->get('Test', 'test'); + + $this->assertEquals('["test"]', $result); } }