diff --git a/src/Designer/backend/src/Designer/Controllers/UiFoldersController.cs b/src/Designer/backend/src/Designer/Controllers/UiFoldersController.cs new file mode 100644 index 00000000000..52c32e6ef20 --- /dev/null +++ b/src/Designer/backend/src/Designer/Controllers/UiFoldersController.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Altinn.Studio.Designer.Filters; +using Altinn.Studio.Designer.Helpers; +using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Models.Dto; +using Altinn.Studio.Designer.Services.Interfaces; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Altinn.Studio.Designer.Controllers; + +/// +/// Controller for handling UI folder related operations for v9 and newer, such as fetching and saving settings for validation on navigation and task navigation. +/// +[ApiController] +[Authorize] +[AutoValidateAntiforgeryToken] +[Route("designer/api/{org}/{app:regex(^(?!datamodels$)[[a-z]][[a-z0-9-]]{{1,28}}[[a-z0-9]]$)}/ui-folders")] +public class UiFoldersController : Controller +{ + private readonly IUiFoldersService _uiFoldersService; + + public UiFoldersController(IUiFoldersService uiFoldersService) + { + _uiFoldersService = uiFoldersService; + } + + private AltinnRepoEditingContext CreateContext(string org, string app) + { + string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); + return AltinnRepoEditingContext.FromOrgRepoDeveloper(org, app, developer); + } + + [HttpGet("settings/validation-on-navigation")] + [UseSystemTextJson] + public async Task GetGlobalValidationOnNavigation( + string org, + string app, + CancellationToken cancellationToken + ) + { + AltinnRepoEditingContext editingContext = CreateContext(org, app); + ValidationOnNavigation? config = await _uiFoldersService.GetGlobalValidationOnNavigation( + editingContext, + cancellationToken + ); + return Ok(config); + } + + [HttpPost("settings/validation-on-navigation")] + [UseSystemTextJson] + public async Task SaveGlobalValidationOnNavigation( + string org, + string app, + [FromBody] ValidationOnNavigation config, + CancellationToken cancellationToken + ) + { + AltinnRepoEditingContext editingContext = CreateContext(org, app); + await _uiFoldersService.SaveGlobalValidationOnNavigation(editingContext, config, cancellationToken); + return Ok(); + } + + [HttpDelete("settings/validation-on-navigation")] + public async Task DeleteGlobalValidationOnNavigation( + string org, + string app, + CancellationToken cancellationToken + ) + { + AltinnRepoEditingContext editingContext = CreateContext(org, app); + await _uiFoldersService.SaveGlobalValidationOnNavigation(editingContext, null, cancellationToken); + return Ok(); + } + + [HttpGet("settings/task-navigation")] + [UseSystemTextJson] + public async Task GetGlobalTaskNavigation( + string org, + string app, + CancellationToken cancellationToken + ) + { + AltinnRepoEditingContext editingContext = CreateContext(org, app); + IEnumerable result = await _uiFoldersService.GetGlobalTaskNavigationDto( + editingContext, + cancellationToken + ); + + return Ok(result); + } + + [HttpPost("settings/task-navigation")] + [UseSystemTextJson] + public async Task UpdateGlobalTaskNavigation( + string org, + string app, + [FromBody] IEnumerable taskNavigationGroupDtoList, + CancellationToken cancellationToken + ) + { + try + { + AltinnRepoEditingContext editingContext = CreateContext(org, app); + await _uiFoldersService.UpdateGlobalTaskNavigation( + editingContext, + taskNavigationGroupDtoList, + cancellationToken + ); + + return NoContent(); + } + catch (ArgumentException exception) + { + return BadRequest(exception.Message); + } + } +} diff --git a/src/Designer/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs b/src/Designer/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs index e731a94cfef..c207746688e 100644 --- a/src/Designer/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs +++ b/src/Designer/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs @@ -42,7 +42,7 @@ public class AltinnAppGitRepository : AltinnGitRepository private const string CshtmlPath = "App/views/Home/Index.cshtml"; private const string ServiceConfigFilename = "config.json"; - private const string LayoutSettingsFilename = "Settings.json"; + private const string SettingsFilename = "Settings.json"; private const string AppMetadataFilename = "applicationmetadata.json"; private const string LayoutSetsFilename = "layout-sets.json"; private const string FooterFilename = "footer.json"; @@ -617,6 +617,26 @@ public async Task SaveLayoutSets(LayoutSets layoutSets) } } + public async Task GetGlobalSettingsFile(CancellationToken cancellationToken = default) + { + string globalSettingsFilePath = GetPathToGlobalSettingsFile(); + cancellationToken.ThrowIfCancellationRequested(); + if (!FileExistsByRelativePath(globalSettingsFilePath)) + { + return null; + } + string fileContent = await ReadTextByRelativePathAsync(globalSettingsFilePath, cancellationToken); + UiSettings globalSettingsFile = JsonSerializer.Deserialize(fileContent, s_jsonOptions); + return globalSettingsFile; + } + + public async Task SaveGlobalSettingsFile(UiSettings globalSettings) + { + string globalSettingsFilePath = GetPathToGlobalSettingsFile(); + string globalSettingsString = JsonSerializer.Serialize(globalSettings, s_jsonOptions); + await WriteTextByRelativePathAsync(globalSettingsFilePath, globalSettingsString); + } + public async Task GetFooter(CancellationToken cancellationToken = default) { string footerFilePath = GetPathToFooterFile(); @@ -1022,8 +1042,8 @@ private static string GetPathToLayoutFile(string layoutSetName, string layoutNam private static string GetPathToLayoutSettings(string layoutSetName) { return string.IsNullOrEmpty(layoutSetName) - ? Path.Combine(LayoutsFolderName, LayoutSettingsFilename) - : Path.Combine(LayoutsFolderName, layoutSetName, LayoutSettingsFilename); + ? Path.Combine(LayoutsFolderName, SettingsFilename) + : Path.Combine(LayoutsFolderName, layoutSetName, SettingsFilename); } private static string GetPathToLayoutSetsFile() @@ -1031,6 +1051,11 @@ private static string GetPathToLayoutSetsFile() return Path.Combine(LayoutsFolderName, LayoutSetsFilename); } + private static string GetPathToGlobalSettingsFile() + { + return Path.Combine(LayoutsFolderName, SettingsFilename); + } + private static string GetPathToFooterFile() { return Path.Combine(LayoutsFolderName, FooterFilename); diff --git a/src/Designer/backend/src/Designer/Infrastructure/ServiceRegistration.cs b/src/Designer/backend/src/Designer/Infrastructure/ServiceRegistration.cs index 81435799c4c..0c827103802 100644 --- a/src/Designer/backend/src/Designer/Infrastructure/ServiceRegistration.cs +++ b/src/Designer/backend/src/Designer/Infrastructure/ServiceRegistration.cs @@ -110,6 +110,7 @@ IConfiguration configuration services.AddHttpClient(); services.AddHttpClient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Designer/backend/src/Designer/Services/Implementation/UiFoldersService.cs b/src/Designer/backend/src/Designer/Services/Implementation/UiFoldersService.cs new file mode 100644 index 00000000000..617d58d4434 --- /dev/null +++ b/src/Designer/backend/src/Designer/Services/Implementation/UiFoldersService.cs @@ -0,0 +1,133 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Altinn.App.Core.Internal.Process.Elements; +using Altinn.Studio.Designer.Infrastructure.GitRepository; +using Altinn.Studio.Designer.Mappers; +using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Models.Dto; +using Altinn.Studio.Designer.Services.Interfaces; + +public class UiFoldersService : IUiFoldersService +{ + private readonly IAltinnGitRepositoryFactory _altinnGitRepositoryFactory; + + public UiFoldersService(IAltinnGitRepositoryFactory altinnGitRepositoryFactory) + { + _altinnGitRepositoryFactory = altinnGitRepositoryFactory; + } + + public async Task GetGlobalValidationOnNavigation( + AltinnRepoEditingContext altinnRepoEditingContext, + CancellationToken cancellationToken + ) + { + cancellationToken.ThrowIfCancellationRequested(); + AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository( + altinnRepoEditingContext.Org, + altinnRepoEditingContext.Repo, + altinnRepoEditingContext.Developer + ); + + UiSettings globalSettingsFile = await altinnAppGitRepository.GetGlobalSettingsFile(cancellationToken); + return globalSettingsFile?.ValidationOnNavigation; + } + + public async Task SaveGlobalValidationOnNavigation( + AltinnRepoEditingContext altinnRepoEditingContext, + ValidationOnNavigation? config, + CancellationToken cancellationToken + ) + { + cancellationToken.ThrowIfCancellationRequested(); + AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository( + altinnRepoEditingContext.Org, + altinnRepoEditingContext.Repo, + altinnRepoEditingContext.Developer + ); + + UiSettings globalSettingsFile = + await altinnAppGitRepository.GetGlobalSettingsFile(cancellationToken) ?? new UiSettings(); + + globalSettingsFile.ValidationOnNavigation = config; + await altinnAppGitRepository.SaveGlobalSettingsFile(globalSettingsFile); + } + + public async Task> GetGlobalTaskNavigationDto( + AltinnRepoEditingContext editingContext, + CancellationToken cancellationToken + ) + { + IEnumerable taskNavigationGroups = await GetGlobalTaskNavigation( + editingContext, + cancellationToken + ); + + IEnumerable tasks = GetTasks(editingContext, cancellationToken); + + Dictionary taskTypesById = tasks.ToDictionary( + task => task.Id, + task => task.ExtensionElements?.TaskExtension?.TaskType + ); + + return taskNavigationGroups.Select(group => group.ToDto(taskId => taskTypesById.GetValueOrDefault(taskId))); + } + + public async Task> GetGlobalTaskNavigation( + AltinnRepoEditingContext altinnRepoEditingContext, + CancellationToken cancellationToken + ) + { + cancellationToken.ThrowIfCancellationRequested(); + + AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository( + altinnRepoEditingContext.Org, + altinnRepoEditingContext.Repo, + altinnRepoEditingContext.Developer + ); + + UiSettings globalSettingsFile = await altinnAppGitRepository.GetGlobalSettingsFile(cancellationToken); + return globalSettingsFile?.TaskNavigation?.ToList() ?? []; + } + + public IEnumerable GetTasks( + AltinnRepoEditingContext altinnRepoEditingContext, + CancellationToken cancellationToken + ) + { + cancellationToken.ThrowIfCancellationRequested(); + AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository( + altinnRepoEditingContext.Org, + altinnRepoEditingContext.Repo, + altinnRepoEditingContext.Developer + ); + + Definitions definitions = altinnAppGitRepository.GetProcessDefinitions(); + return definitions.Process.Tasks; + } + + public async Task UpdateGlobalTaskNavigation( + AltinnRepoEditingContext altinnRepoEditingContext, + IEnumerable taskNavigationGroupDtoList, + CancellationToken cancellationToken + ) + { + cancellationToken.ThrowIfCancellationRequested(); + + AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository( + altinnRepoEditingContext.Org, + altinnRepoEditingContext.Repo, + altinnRepoEditingContext.Developer + ); + + IEnumerable taskNavigationGroupList = taskNavigationGroupDtoList.Select(x => x.ToDomain()); + + UiSettings globalSettingsFile = + await altinnAppGitRepository.GetGlobalSettingsFile(cancellationToken) ?? new UiSettings(); + + globalSettingsFile.TaskNavigation = taskNavigationGroupList.Any() ? taskNavigationGroupList : null; + + await altinnAppGitRepository.SaveGlobalSettingsFile(globalSettingsFile); + } +} diff --git a/src/Designer/backend/src/Designer/Services/Interfaces/IUiFoldersService.cs b/src/Designer/backend/src/Designer/Services/Interfaces/IUiFoldersService.cs new file mode 100644 index 00000000000..ef5b6985774 --- /dev/null +++ b/src/Designer/backend/src/Designer/Services/Interfaces/IUiFoldersService.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Models.Dto; + +namespace Altinn.Studio.Designer.Services.Interfaces; + +public interface IUiFoldersService +{ + public Task GetGlobalValidationOnNavigation( + AltinnRepoEditingContext context, + CancellationToken cancellationToken + ); + + public Task SaveGlobalValidationOnNavigation( + AltinnRepoEditingContext editingContext, + ValidationOnNavigation? validationOnNavigation, + CancellationToken cancellationToken + ); + + public Task> GetGlobalTaskNavigationDto( + AltinnRepoEditingContext editingContext, + CancellationToken cancellationToken + ); + + public Task UpdateGlobalTaskNavigation( + AltinnRepoEditingContext editingContext, + IEnumerable taskNavigationGroupDtoList, + CancellationToken cancellationToken + ); +}