From 955f0c8d6350f14587084a6b6e642ae9243aabd8 Mon Sep 17 00:00:00 2001 From: Laura Neto <12862535+lauraneto@users.noreply.github.com> Date: Tue, 9 Sep 2025 09:48:17 +0200 Subject: [PATCH] New document segments endpoint (#20088) * Added `cultures` property to the Segment models * Added new endpoint to return the segments of a specific document. * Mark additional properties and methods as obsolete * Small indentation fix Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Document/AvailableSegmentsController.cs | 85 +++++++++++++++++++ .../Mapping/Segment/SegmentMapDefinition.cs | 5 +- .../Segment/SegmentResponseModel.cs | 5 +- src/Umbraco.Core/Models/Segment.cs | 3 + src/Umbraco.Core/Services/ISegmentService.cs | 24 +++++- 5 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Document/AvailableSegmentsController.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/AvailableSegmentsController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/AvailableSegmentsController.cs new file mode 100644 index 0000000000..bb83d578c9 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/AvailableSegmentsController.cs @@ -0,0 +1,85 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Common.ViewModels.Pagination; +using Umbraco.Cms.Api.Management.ViewModels.Segment; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Actions; +using Umbraco.Cms.Core.Mapping; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Security.Authorization; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; +using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Api.Management.Controllers.Document; + +[Obsolete("This controller is temporary and will be removed in a future release (planned for v20). A more permanent solution will follow.")] +[ApiVersion("1.0")] +public class AvailableSegmentsController : DocumentControllerBase +{ + private readonly IAuthorizationService _authorizationService; + private readonly ISegmentService _segmentService; + private readonly IUmbracoMapper _umbracoMapper; + + public AvailableSegmentsController( + IAuthorizationService authorizationService, + ISegmentService segmentService, + IUmbracoMapper umbracoMapper) + { + _authorizationService = authorizationService; + _segmentService = segmentService; + _umbracoMapper = umbracoMapper; + } + + [HttpGet("{id:guid}/available-segment-options")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] + public async Task GetAvailableSegmentOptions( + Guid id, + CancellationToken cancellationToken, + int skip = 0, + int take = 100) + { + AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync( + User, + ContentPermissionResource.WithKeys(ActionBrowse.ActionLetter, id), + AuthorizationPolicies.ContentPermissionByResource); + + if (!authorizationResult.Succeeded) + { + return Forbidden(); + } + + Attempt?, SegmentOperationStatus> pagedAttempt = + await _segmentService.GetPagedSegmentsForDocumentAsync(id, skip, take); + + if (pagedAttempt.Success is false) + { + return MapFailure(pagedAttempt.Status); + } + + var viewModel = new PagedViewModel + { + Items = _umbracoMapper.MapEnumerable(pagedAttempt.Result!.Items), + Total = pagedAttempt.Result!.Total, + }; + + return Ok(viewModel); + } + + private IActionResult MapFailure(SegmentOperationStatus status) + => OperationStatusResult( + status, + problemDetailsBuilder => status switch + { + _ => StatusCode( + StatusCodes.Status500InternalServerError, + problemDetailsBuilder + .WithTitle("Unknown segment operation status.") + .Build()), + }); +} diff --git a/src/Umbraco.Cms.Api.Management/Mapping/Segment/SegmentMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/Segment/SegmentMapDefinition.cs index 903d460119..e1beb7c846 100644 --- a/src/Umbraco.Cms.Api.Management/Mapping/Segment/SegmentMapDefinition.cs +++ b/src/Umbraco.Cms.Api.Management/Mapping/Segment/SegmentMapDefinition.cs @@ -5,12 +5,15 @@ namespace Umbraco.Cms.Api.Management.Mapping.Segment; public class SegmentMapDefinition : IMapDefinition { - public void DefineMaps(IUmbracoMapper mapper) => mapper.Define((_, _) => new SegmentResponseModel { Name = string.Empty, Alias = string.Empty }, Map); + public void DefineMaps(IUmbracoMapper mapper) => mapper.Define( + (_, _) => new SegmentResponseModel { Name = string.Empty, Alias = string.Empty, Cultures = null }, + Map); // Umbraco.Code.MapAll private static void Map(Core.Models.Segment source, SegmentResponseModel target, MapperContext context) { target.Name = source.Name; target.Alias = source.Alias; + target.Cultures = source.Cultures; } } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Segment/SegmentResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Segment/SegmentResponseModel.cs index 4932104206..fe93827b48 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Segment/SegmentResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Segment/SegmentResponseModel.cs @@ -1,5 +1,3 @@ -using System.ComponentModel.DataAnnotations; - namespace Umbraco.Cms.Api.Management.ViewModels.Segment; public class SegmentResponseModel @@ -7,4 +5,7 @@ public class SegmentResponseModel public required string Name { get; set; } = string.Empty; public required string Alias { get; set; } = string.Empty; + + [Obsolete("This property is temporary and will be removed in a future release (planned for v20). A more permanent solution will follow.")] + public IEnumerable? Cultures { get; set; } = null; } diff --git a/src/Umbraco.Core/Models/Segment.cs b/src/Umbraco.Core/Models/Segment.cs index 117b886d64..2bb02d90f2 100644 --- a/src/Umbraco.Core/Models/Segment.cs +++ b/src/Umbraco.Core/Models/Segment.cs @@ -5,4 +5,7 @@ public class Segment public required string Name { get; set; } public required string Alias { get; set; } + + [Obsolete("This property is temporary and will be removed in a future release (planned for v20). A more permanent solution will follow.")] + public IEnumerable? Cultures { get; set; } = null; } diff --git a/src/Umbraco.Core/Services/ISegmentService.cs b/src/Umbraco.Core/Services/ISegmentService.cs index f5af487a73..c31cbf9f5e 100644 --- a/src/Umbraco.Core/Services/ISegmentService.cs +++ b/src/Umbraco.Core/Services/ISegmentService.cs @@ -5,5 +5,27 @@ namespace Umbraco.Cms.Core.Services; public interface ISegmentService { - Task?, SegmentOperationStatus>> GetPagedSegmentsAsync(int skip = 0, int take = 100); + /// + /// Gets a paged list of segments. + /// + /// The number of items to skip. + /// The number of items to take. + /// The paged list of segments. + Task?, SegmentOperationStatus>> GetPagedSegmentsAsync( + int skip = 0, + int take = 100); + + /// + /// Gets a paged list of segments for a specific document. + /// + /// The document unique identifier. + /// The number of items to skip. + /// The number of items to take. + /// The paged list of segments. + [Obsolete("This method is temporary and will be removed in a future release (planned for v20). A more permanent solution will follow.")] + Task?, SegmentOperationStatus>> GetPagedSegmentsForDocumentAsync( + Guid id, + int skip = 0, + int take = 100) + => GetPagedSegmentsAsync(skip, take); }