> */ protected $changedData = []; public function __construct( protected FileManager $fileManager, protected LayoutProvider $layoutProvider ) {} /** * Get layout in string format. */ public function get(string $scope, string $name): ?string { return $this->layoutProvider->get($scope, $name); } /** * Set layout data. * * @param mixed $data * @throws Error */ public function set($data, string $scope, string $name): void { $scope = $this->sanitizeInput($scope); $name = $this->sanitizeInput($name); if (empty($scope) || empty($name)) { throw new Error("Error while setting layout."); } $this->changedData[$scope][$name] = $data; } public function resetToDefault(string $scope, string $name): ?string { $scope = $this->sanitizeInput($scope); $name = $this->sanitizeInput($name); $filePath = 'custom/Espo/Custom/Resources/layouts/' . $scope . '/' . $name . '.json'; if ($this->fileManager->isFile($filePath)) { $this->fileManager->removeFile($filePath); } if (!empty($this->changedData[$scope]) && !empty($this->changedData[$scope][$name])) { unset($this->changedData[$scope][$name]); } return $this->get($scope, $name); } /** * Save changes. * * @throws Error */ public function save(): void { $result = true; if (empty($this->changedData)) { return; } foreach ($this->changedData as $scope => $rowData) { $dirPath = 'custom/Espo/Custom/Resources/layouts/' . $scope . '/'; foreach ($rowData as $layoutName => $layoutData) { if (empty($scope) || empty($layoutName)) { continue; } $data = Json::encode($layoutData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); $path = $dirPath . $layoutName . '.json'; $result &= $this->fileManager->putContents($path, $data); } } if (!$result) { throw new Error("Error while saving layout."); } $this->clearChanges(); } /** * Clear unsaved changes. */ public function clearChanges(): void { $this->changedData = []; } protected function sanitizeInput(string $name): string { /** @var string */ return preg_replace("([.]{2,})", '', $name); } }