diff --git a/backend/src/Designer/Controllers/AppDevelopmentController.cs b/backend/src/Designer/Controllers/AppDevelopmentController.cs index ae927c4eb05..8d4bcb73d66 100644 --- a/backend/src/Designer/Controllers/AppDevelopmentController.cs +++ b/backend/src/Designer/Controllers/AppDevelopmentController.cs @@ -159,15 +159,24 @@ await _mediator.Publish(new LayoutPageAddedEvent /// Application identifier which is unique within an organisation. /// The name of the layout set the specific layout belongs to /// The form layout to be deleted + /// A that observes if operation is cancelled. /// A success message if the save was successful [HttpDelete] [Route("form-layout/{layoutName}")] - public ActionResult DeleteFormLayout(string org, string app, [FromQuery] string layoutSetName, [FromRoute] string layoutName) + public async Task DeleteFormLayout(string org, string app, [FromQuery] string layoutSetName, [FromRoute] string layoutName, CancellationToken cancellationToken) { try { string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, app, developer); + + await _mediator.Publish(new LayoutPageDeletedEvent + { + EditingContext = editingContext, + LayoutSetName = layoutSetName, + LayoutName = layoutName, + }, cancellationToken); + _appDevelopmentService.DeleteFormLayout(editingContext, layoutSetName, layoutName); return Ok(); } diff --git a/backend/src/Designer/EventHandlers/LayoutPageDeleted/LayoutPageDeleterHandler.cs b/backend/src/Designer/EventHandlers/LayoutPageDeleted/LayoutPageDeleterHandler.cs new file mode 100644 index 00000000000..17696840be4 --- /dev/null +++ b/backend/src/Designer/EventHandlers/LayoutPageDeleted/LayoutPageDeleterHandler.cs @@ -0,0 +1,86 @@ + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Altinn.Studio.Designer.Events; +using Altinn.Studio.Designer.Hubs.SyncHub; +using Altinn.Studio.Designer.Infrastructure.GitRepository; +using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Services.Interfaces; +using MediatR; +using System.Text.Json.Nodes; +using Altinn.App.Core.Helpers; + +namespace Altinn.Studio.Designer.EventHandlers.LayoutPageDeleted; + +public class LayoutPageDeletedHandler(IAltinnGitRepositoryFactory altinnGitRepositoryFactory, + IFileSyncHandlerExecutor fileSyncHandlerExecutor) : INotificationHandler +{ + public async Task Handle(LayoutPageDeletedEvent notification, CancellationToken cancellationToken) + { + AltinnAppGitRepository altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository( + notification.EditingContext.Org, + notification.EditingContext.Repo, + notification.EditingContext.Developer); + + LayoutSets layoutSets = await altinnAppGitRepository.GetLayoutSetsFile(cancellationToken); + + await fileSyncHandlerExecutor.ExecuteWithExceptionHandlingAndConditionalNotification( + notification.EditingContext, + SyncErrorCodes.LayoutPageDeletedSyncError, + "layouts", + async () => + { + bool hasChanges = false; + foreach (LayoutSetConfig layoutSet in layoutSets.Sets) + { + Dictionary formLayouts = await altinnAppGitRepository.GetFormLayouts(layoutSet.Id, cancellationToken); + foreach (var formLayout in formLayouts) + { + hasChanges |= await RemoveComponentsReferencingLayout( + notification, + altinnAppGitRepository, + layoutSets.Sets, + layoutSet.Id, + formLayout, + cancellationToken); + } + } + return hasChanges; + }); + } + + private static async Task RemoveComponentsReferencingLayout(LayoutPageDeletedEvent notification, AltinnAppGitRepository altinnAppGitRepository, List layoutSets, string layoutSetName, KeyValuePair formLayout, CancellationToken cancellationToken) + { + if (formLayout.Value["data"] is not JsonObject data || data["layout"] is not JsonArray layoutArray) + { + return false; + } + + bool hasChanges = false; + layoutArray.RemoveAll(jsonNode => + { + if (jsonNode["type"]?.GetValue() == "Summary2" && jsonNode["target"] is JsonObject targetObject) + { + string summaryType = targetObject["type"]?.GetValue(); + string taskId = targetObject["taskId"]?.GetValue(); + string layouSetId = string.IsNullOrEmpty(taskId) ? layoutSetName : layoutSets?.FirstOrDefault(item => item.Tasks?.Contains(taskId) ?? false)?.Id; + bool hasLayoutSet = summaryType == "layout" && layouSetId == notification.LayoutSetName; + if (hasLayoutSet) + { + hasChanges = true; + return true; + } + } + + return false; + }); + + if (hasChanges) + { + await altinnAppGitRepository.SaveLayout(layoutSetName, $"{formLayout.Key}.json", formLayout.Value, cancellationToken); + } + return hasChanges; + } +} diff --git a/backend/src/Designer/Events/LayoutPageDeletedEvent.cs b/backend/src/Designer/Events/LayoutPageDeletedEvent.cs new file mode 100644 index 00000000000..8411d583169 --- /dev/null +++ b/backend/src/Designer/Events/LayoutPageDeletedEvent.cs @@ -0,0 +1,11 @@ +using Altinn.Studio.Designer.Models; +using MediatR; + +namespace Altinn.Studio.Designer.Events; + +public class LayoutPageDeletedEvent : INotification +{ + public AltinnRepoEditingContext EditingContext { get; set; } + public string LayoutSetName { get; set; } + public string LayoutName { get; set; } +} diff --git a/backend/src/Designer/Hubs/SyncHub/SyncErrorCodes.cs b/backend/src/Designer/Hubs/SyncHub/SyncErrorCodes.cs index 23556793c7b..76d1b5bbddd 100644 --- a/backend/src/Designer/Hubs/SyncHub/SyncErrorCodes.cs +++ b/backend/src/Designer/Hubs/SyncHub/SyncErrorCodes.cs @@ -13,4 +13,5 @@ public static class SyncErrorCodes public const string LayoutSetSubFormButtonSyncError = nameof(LayoutSetSubFormButtonSyncError); public const string SettingsComponentIdSyncError = nameof(SettingsComponentIdSyncError); public const string LayoutPageAddSyncError = nameof(LayoutPageAddSyncError); + public const string LayoutPageDeletedSyncError = nameof(LayoutPageDeletedSyncError); }