diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/ByIdContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdContentApiController.cs similarity index 96% rename from src/Umbraco.Cms.Api.Delivery/Controllers/ByIdContentApiController.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdContentApiController.cs index fc2e91e1ba..d16afc4d6d 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/ByIdContentApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdContentApiController.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Services; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Content; [ApiVersion("1.0")] public class ByIdContentApiController : ContentApiItemControllerBase diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/ByIdsContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdsContentApiController.cs similarity index 96% rename from src/Umbraco.Cms.Api.Delivery/Controllers/ByIdsContentApiController.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdsContentApiController.cs index ca2deb360d..5d415fffe6 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/ByIdsContentApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdsContentApiController.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Services; using Umbraco.Extensions; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Content; [ApiVersion("1.0")] public class ByIdsContentApiController : ContentApiItemControllerBase diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/ByRouteContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs similarity index 98% rename from src/Umbraco.Cms.Api.Delivery/Controllers/ByRouteContentApiController.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs index 04900f52d2..2d22887637 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/ByRouteContentApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs @@ -1,4 +1,3 @@ -using System.Net; using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -9,7 +8,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Services; using Umbraco.Extensions; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Content; [ApiVersion("1.0")] public class ByRouteContentApiController : ContentApiItemControllerBase diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/ContentApiControllerBase.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ContentApiControllerBase.cs similarity index 97% rename from src/Umbraco.Cms.Api.Delivery/Controllers/ContentApiControllerBase.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Content/ContentApiControllerBase.cs index 07439505e0..ebfa32c479 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/ContentApiControllerBase.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ContentApiControllerBase.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Api.Delivery.Routing; using Umbraco.Cms.Core.DeliveryApi; using Umbraco.Cms.Core.Services.OperationStatus; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Content; [DeliveryApiAccess] [VersionedDeliveryApiRoute("content")] diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/ContentApiItemControllerBase.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ContentApiItemControllerBase.cs similarity index 94% rename from src/Umbraco.Cms.Api.Delivery/Controllers/ContentApiItemControllerBase.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Content/ContentApiItemControllerBase.cs index 8c8e2b5fd7..dad11de009 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/ContentApiItemControllerBase.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ContentApiItemControllerBase.cs @@ -2,7 +2,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Services; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Content; public abstract class ContentApiItemControllerBase : ContentApiControllerBase { diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/QueryContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/QueryContentApiController.cs similarity index 97% rename from src/Umbraco.Cms.Api.Delivery/Controllers/QueryContentApiController.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Content/QueryContentApiController.cs index e46204f85e..d4db82d9be 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/QueryContentApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/QueryContentApiController.cs @@ -10,7 +10,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Services.OperationStatus; using Umbraco.Extensions; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Content; [ApiVersion("1.0")] public class QueryContentApiController : ContentApiControllerBase diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/ByIdMediaApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdMediaApiController.cs similarity index 89% rename from src/Umbraco.Cms.Api.Delivery/Controllers/ByIdMediaApiController.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdMediaApiController.cs index 423d70fd5b..b0242bea5d 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/ByIdMediaApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdMediaApiController.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Infrastructure.DeliveryApi; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Media; [ApiVersion("1.0")] public class ByIdMediaApiController : MediaApiControllerBase @@ -23,7 +23,7 @@ public class ByIdMediaApiController : MediaApiControllerBase /// The media item or not found result. [HttpGet("item/{id:guid}")] [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(ApiMediaWithCropsResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(IApiMediaWithCropsResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task ById(Guid id) { diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/ByIdsMediaApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdsMediaApiController.cs similarity index 85% rename from src/Umbraco.Cms.Api.Delivery/Controllers/ByIdsMediaApiController.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdsMediaApiController.cs index b8327e95c3..a9421eaa0c 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/ByIdsMediaApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdsMediaApiController.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Infrastructure.DeliveryApi; using Umbraco.Extensions; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Media; [ApiVersion("1.0")] public class ByIdsMediaApiController : MediaApiControllerBase @@ -24,7 +24,7 @@ public class ByIdsMediaApiController : MediaApiControllerBase /// The media items. [HttpGet("item")] [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task Item([FromQuery(Name = "id")] HashSet ids) { IPublishedContent[] mediaItems = ids @@ -32,7 +32,7 @@ public class ByIdsMediaApiController : MediaApiControllerBase .WhereNotNull() .ToArray(); - ApiMediaWithCropsResponse[] apiMediaItems = mediaItems + IApiMediaWithCropsResponse[] apiMediaItems = mediaItems .Select(BuildApiMediaWithCrops) .ToArray(); diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/ByPathMediaApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByPathMediaApiController.cs similarity index 91% rename from src/Umbraco.Cms.Api.Delivery/Controllers/ByPathMediaApiController.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByPathMediaApiController.cs index 947dd820a1..1d725ac5ab 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/ByPathMediaApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByPathMediaApiController.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Infrastructure.DeliveryApi; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Media; [ApiVersion("1.0")] public class ByPathMediaApiController : MediaApiControllerBase @@ -28,7 +28,7 @@ public class ByPathMediaApiController : MediaApiControllerBase /// The media item or not found result. [HttpGet("item/{*path}")] [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(ApiMediaWithCropsResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(IApiMediaWithCropsResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task ByPath(string path) { diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/MediaApiControllerBase.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/MediaApiControllerBase.cs similarity index 94% rename from src/Umbraco.Cms.Api.Delivery/Controllers/MediaApiControllerBase.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Media/MediaApiControllerBase.cs index dc279cf703..73a385fd2e 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/MediaApiControllerBase.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/MediaApiControllerBase.cs @@ -9,7 +9,7 @@ using Umbraco.Cms.Core.Services.OperationStatus; using Umbraco.Cms.Infrastructure.DeliveryApi; using Umbraco.Extensions; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Media; [DeliveryApiMediaAccess] [VersionedDeliveryApiRoute("media")] @@ -30,7 +30,7 @@ public abstract class MediaApiControllerBase : DeliveryApiControllerBase ??= _publishedSnapshotAccessor.GetRequiredPublishedSnapshot().Media ?? throw new InvalidOperationException("Could not obtain the published media cache"); - protected ApiMediaWithCropsResponse BuildApiMediaWithCrops(IPublishedContent media) + protected IApiMediaWithCropsResponse BuildApiMediaWithCrops(IPublishedContent media) => _apiMediaWithCropsResponseBuilder.Build(media); protected IActionResult ApiMediaQueryOperationStatusResult(ApiMediaQueryOperationStatus status) => diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/QueryMediaApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/QueryMediaApiController.cs similarity index 91% rename from src/Umbraco.Cms.Api.Delivery/Controllers/QueryMediaApiController.cs rename to src/Umbraco.Cms.Api.Delivery/Controllers/Media/QueryMediaApiController.cs index 98110e9589..5d962ea4bf 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/QueryMediaApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/QueryMediaApiController.cs @@ -12,7 +12,7 @@ using Umbraco.Cms.Core.Services.OperationStatus; using Umbraco.Cms.Infrastructure.DeliveryApi; using Umbraco.Extensions; -namespace Umbraco.Cms.Api.Delivery.Controllers; +namespace Umbraco.Cms.Api.Delivery.Controllers.Media; [ApiVersion("1.0")] public class QueryMediaApiController : MediaApiControllerBase @@ -37,7 +37,7 @@ public class QueryMediaApiController : MediaApiControllerBase /// The paged result of the media item(s). [HttpGet] [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] public async Task Query( string? fetch, @@ -56,7 +56,7 @@ public class QueryMediaApiController : MediaApiControllerBase PagedModel pagedResult = queryAttempt.Result; IPublishedContent[] mediaItems = pagedResult.Items.Select(PublishedMediaCache.GetById).WhereNotNull().ToArray(); - var model = new PagedViewModel + var model = new PagedViewModel { Total = pagedResult.Total, Items = mediaItems.Select(BuildApiMediaWithCrops) diff --git a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs index 8b3c946873..7c42a3d0aa 100644 --- a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs +++ b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerContentDocumentationFilter.cs @@ -3,6 +3,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using Umbraco.Cms.Api.Delivery.Configuration; using Umbraco.Cms.Api.Delivery.Controllers; +using Umbraco.Cms.Api.Delivery.Controllers.Content; namespace Umbraco.Cms.Api.Delivery.Filters; diff --git a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerMediaDocumentationFilter.cs b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerMediaDocumentationFilter.cs index af22914f78..8529178888 100644 --- a/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerMediaDocumentationFilter.cs +++ b/src/Umbraco.Cms.Api.Delivery/Filters/SwaggerMediaDocumentationFilter.cs @@ -3,6 +3,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using Umbraco.Cms.Api.Delivery.Configuration; using Umbraco.Cms.Api.Delivery.Controllers; +using Umbraco.Cms.Api.Delivery.Controllers.Media; namespace Umbraco.Cms.Api.Delivery.Filters; diff --git a/src/Umbraco.Core/Models/DeliveryApi/IApiMediaWithCrops.cs b/src/Umbraco.Core/Models/DeliveryApi/IApiMediaWithCrops.cs new file mode 100644 index 0000000000..073ea89ca3 --- /dev/null +++ b/src/Umbraco.Core/Models/DeliveryApi/IApiMediaWithCrops.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Cms.Core.Models.DeliveryApi; + +public interface IApiMediaWithCrops : IApiMedia +{ + public ImageFocalPoint? FocalPoint { get; } + + public IEnumerable? Crops { get; } +} diff --git a/src/Umbraco.Core/Models/DeliveryApi/IApiMediaWithCropsResponse.cs b/src/Umbraco.Core/Models/DeliveryApi/IApiMediaWithCropsResponse.cs new file mode 100644 index 0000000000..34912bbec7 --- /dev/null +++ b/src/Umbraco.Core/Models/DeliveryApi/IApiMediaWithCropsResponse.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Core.Models.DeliveryApi; + +public interface IApiMediaWithCropsResponse : IApiMediaWithCrops +{ + public string Path { get; } + + public DateTime CreateDate { get; } + + public DateTime UpdateDate { get; } +} diff --git a/src/Umbraco.Core/Models/DeliveryApi/ImageCrop.cs b/src/Umbraco.Core/Models/DeliveryApi/ImageCrop.cs new file mode 100644 index 0000000000..eda02d8fa7 --- /dev/null +++ b/src/Umbraco.Core/Models/DeliveryApi/ImageCrop.cs @@ -0,0 +1,20 @@ +namespace Umbraco.Cms.Core.Models.DeliveryApi; + +public class ImageCrop +{ + public ImageCrop(string? alias, int width, int height, ImageCropCoordinates? coordinates) + { + Alias = alias; + Width = width; + Height = height; + Coordinates = coordinates; + } + + public string? Alias { get; } + + public int Width { get; } + + public int Height { get; } + + public ImageCropCoordinates? Coordinates { get; } +} diff --git a/src/Umbraco.Core/Models/DeliveryApi/ImageCropCoordinates.cs b/src/Umbraco.Core/Models/DeliveryApi/ImageCropCoordinates.cs new file mode 100644 index 0000000000..48ad0b6201 --- /dev/null +++ b/src/Umbraco.Core/Models/DeliveryApi/ImageCropCoordinates.cs @@ -0,0 +1,20 @@ +namespace Umbraco.Cms.Core.Models.DeliveryApi; + +public class ImageCropCoordinates +{ + public ImageCropCoordinates(decimal x1, decimal y1, decimal x2, decimal y2) + { + X1 = x1; + Y1 = y1; + X2 = x2; + Y2 = y2; + } + + public decimal X1 { get; } + + public decimal Y1 { get; } + + public decimal X2 { get; } + + public decimal Y2 { get; } +} diff --git a/src/Umbraco.Core/Models/DeliveryApi/ImageFocalPoint.cs b/src/Umbraco.Core/Models/DeliveryApi/ImageFocalPoint.cs new file mode 100644 index 0000000000..d3ae5e65cd --- /dev/null +++ b/src/Umbraco.Core/Models/DeliveryApi/ImageFocalPoint.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Cms.Core.Models.DeliveryApi; + +public class ImageFocalPoint +{ + public ImageFocalPoint(decimal left, decimal top) + { + Left = left; + Top = top; + } + + public decimal Left { get; } + + public decimal Top { get; } +} diff --git a/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsBuilder.cs b/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsBuilder.cs index ab9d7943b4..d3490dcb13 100644 --- a/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsBuilder.cs +++ b/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsBuilder.cs @@ -1,21 +1,20 @@ using Umbraco.Cms.Core.DeliveryApi; using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors.ValueConverters; namespace Umbraco.Cms.Infrastructure.DeliveryApi; -internal sealed class ApiMediaWithCropsBuilder : ApiMediaWithCropsBuilderBase, IApiMediaWithCropsBuilder +internal sealed class ApiMediaWithCropsBuilder : ApiMediaWithCropsBuilderBase, IApiMediaWithCropsBuilder { public ApiMediaWithCropsBuilder(IApiMediaBuilder apiMediaBuilder, IPublishedValueFallback publishedValueFallback) : base(apiMediaBuilder, publishedValueFallback) { } - protected override ApiMediaWithCrops Create( + protected override IApiMediaWithCrops Create( IPublishedContent media, IApiMedia inner, - ImageCropperValue.ImageCropperFocalPoint? focalPoint, - IEnumerable? crops) => - new ApiMediaWithCrops(inner, focalPoint, crops); + ImageFocalPoint? focalPoint, + IEnumerable? crops) + => new ApiMediaWithCrops(inner, focalPoint, crops); } diff --git a/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsBuilderBase.cs b/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsBuilderBase.cs index 8754eea976..d8ee9c9289 100644 --- a/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsBuilderBase.cs +++ b/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsBuilderBase.cs @@ -9,7 +9,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.DeliveryApi; internal abstract class ApiMediaWithCropsBuilderBase - where T : IApiMedia + where T : IApiMediaWithCrops { private readonly IApiMediaBuilder _apiMediaBuilder; private readonly IPublishedValueFallback _publishedValueFallback; @@ -23,8 +23,8 @@ internal abstract class ApiMediaWithCropsBuilderBase protected abstract T Create( IPublishedContent media, IApiMedia inner, - ImageCropperValue.ImageCropperFocalPoint? focalPoint, - IEnumerable? crops); + ImageFocalPoint? focalPoint, + IEnumerable? crops); public T Build(MediaWithCrops media) { @@ -38,7 +38,7 @@ internal abstract class ApiMediaWithCropsBuilderBase localCrops = localCrops.Merge(mediaCrops); } - return Create(media.Content, inner, localCrops.FocalPoint, localCrops.Crops); + return Create(media.Content, inner, localCrops.GetImageFocalPoint(), localCrops.GetImageCrops()); } public T Build(IPublishedContent media) diff --git a/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsResponseBuilder.cs b/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsResponseBuilder.cs index 68c73304da..3fd3ca34ef 100644 --- a/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsResponseBuilder.cs +++ b/src/Umbraco.Infrastructure/DeliveryApi/ApiMediaWithCropsResponseBuilder.cs @@ -1,22 +1,21 @@ using Umbraco.Cms.Core.DeliveryApi; using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors.ValueConverters; namespace Umbraco.Cms.Infrastructure.DeliveryApi; -internal sealed class ApiMediaWithCropsResponseBuilder : ApiMediaWithCropsBuilderBase, IApiMediaWithCropsResponseBuilder +internal sealed class ApiMediaWithCropsResponseBuilder : ApiMediaWithCropsBuilderBase, IApiMediaWithCropsResponseBuilder { public ApiMediaWithCropsResponseBuilder(IApiMediaBuilder apiMediaBuilder, IPublishedValueFallback publishedValueFallback) : base(apiMediaBuilder, publishedValueFallback) { } - protected override ApiMediaWithCropsResponse Create( + protected override IApiMediaWithCropsResponse Create( IPublishedContent media, IApiMedia inner, - ImageCropperValue.ImageCropperFocalPoint? focalPoint, - IEnumerable? crops) + ImageFocalPoint? focalPoint, + IEnumerable? crops) { var path = $"/{string.Join("/", PathSegments(media).Reverse())}/"; return new ApiMediaWithCropsResponse(inner, focalPoint, crops, path, media.CreateDate, media.UpdateDate); diff --git a/src/Umbraco.Infrastructure/DeliveryApi/IApiMediaWithCropsBuilder.cs b/src/Umbraco.Infrastructure/DeliveryApi/IApiMediaWithCropsBuilder.cs index 63c2a0d218..5d56a7c6f1 100644 --- a/src/Umbraco.Infrastructure/DeliveryApi/IApiMediaWithCropsBuilder.cs +++ b/src/Umbraco.Infrastructure/DeliveryApi/IApiMediaWithCropsBuilder.cs @@ -6,7 +6,7 @@ namespace Umbraco.Cms.Infrastructure.DeliveryApi; public interface IApiMediaWithCropsBuilder { - ApiMediaWithCrops Build(MediaWithCrops media); + IApiMediaWithCrops Build(MediaWithCrops media); - ApiMediaWithCrops Build(IPublishedContent media); + IApiMediaWithCrops Build(IPublishedContent media); } diff --git a/src/Umbraco.Infrastructure/DeliveryApi/IApiMediaWithCropsResponseBuilder.cs b/src/Umbraco.Infrastructure/DeliveryApi/IApiMediaWithCropsResponseBuilder.cs index 62e2cc7156..9c6377b4ea 100644 --- a/src/Umbraco.Infrastructure/DeliveryApi/IApiMediaWithCropsResponseBuilder.cs +++ b/src/Umbraco.Infrastructure/DeliveryApi/IApiMediaWithCropsResponseBuilder.cs @@ -5,5 +5,5 @@ namespace Umbraco.Cms.Infrastructure.DeliveryApi; public interface IApiMediaWithCropsResponseBuilder { - ApiMediaWithCropsResponse Build(IPublishedContent media); + IApiMediaWithCropsResponse Build(IPublishedContent media); } diff --git a/src/Umbraco.Infrastructure/Extensions/DeliveryApiImageExtensions.cs b/src/Umbraco.Infrastructure/Extensions/DeliveryApiImageExtensions.cs new file mode 100644 index 0000000000..ba23ed36de --- /dev/null +++ b/src/Umbraco.Infrastructure/Extensions/DeliveryApiImageExtensions.cs @@ -0,0 +1,21 @@ +using Umbraco.Cms.Core.Models.DeliveryApi; +using Umbraco.Cms.Core.PropertyEditors.ValueConverters; + +namespace Umbraco.Extensions; + +public static class DeliveryApiImageExtensions +{ + public static ImageFocalPoint? GetImageFocalPoint(this ImageCropperValue imageCropperValue) + => imageCropperValue.FocalPoint is not null + ? new ImageFocalPoint(imageCropperValue.FocalPoint.Left, imageCropperValue.FocalPoint.Top) + : null; + + public static IEnumerable? GetImageCrops(this ImageCropperValue imageCropperValue) + => imageCropperValue.Crops?.Select(crop => new ImageCrop( + crop.Alias, + crop.Width, + crop.Height, + crop.Coordinates is not null + ? new ImageCropCoordinates(crop.Coordinates.X1, crop.Coordinates.Y1, crop.Coordinates.X2, crop.Coordinates.Y2) + : null)); +} diff --git a/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiImageCropperValue.cs b/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiImageCropperValue.cs index 5cb6ee535e..275c88c619 100644 --- a/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiImageCropperValue.cs +++ b/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiImageCropperValue.cs @@ -1,10 +1,8 @@ -using Umbraco.Cms.Core.PropertyEditors.ValueConverters; - namespace Umbraco.Cms.Core.Models.DeliveryApi; internal sealed class ApiImageCropperValue { - public ApiImageCropperValue(string url, ImageCropperValue.ImageCropperFocalPoint? focalPoint, IEnumerable? crops) + public ApiImageCropperValue(string url, ImageFocalPoint? focalPoint, IEnumerable? crops) { Url = url; FocalPoint = focalPoint; @@ -13,7 +11,7 @@ internal sealed class ApiImageCropperValue public string Url { get; } - public ImageCropperValue.ImageCropperFocalPoint? FocalPoint { get; } + public ImageFocalPoint? FocalPoint { get; } - public IEnumerable? Crops { get; } + public IEnumerable? Crops { get; } } diff --git a/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiMediaWithCrops.cs b/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiMediaWithCrops.cs index d51a34e27d..9b7f60d2ba 100644 --- a/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiMediaWithCrops.cs +++ b/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiMediaWithCrops.cs @@ -1,15 +1,13 @@ -using Umbraco.Cms.Core.PropertyEditors.ValueConverters; - namespace Umbraco.Cms.Core.Models.DeliveryApi; -public class ApiMediaWithCrops : IApiMedia +internal class ApiMediaWithCrops : IApiMediaWithCrops { private readonly IApiMedia _inner; public ApiMediaWithCrops( IApiMedia inner, - ImageCropperValue.ImageCropperFocalPoint? focalPoint, - IEnumerable? crops) + ImageFocalPoint? focalPoint, + IEnumerable? crops) { _inner = inner; FocalPoint = focalPoint; @@ -34,7 +32,7 @@ public class ApiMediaWithCrops : IApiMedia public IDictionary Properties => _inner.Properties; - public ImageCropperValue.ImageCropperFocalPoint? FocalPoint { get; } + public ImageFocalPoint? FocalPoint { get; } - public IEnumerable? Crops { get; } + public IEnumerable? Crops { get; } } diff --git a/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiMediaWithCropsResponse.cs b/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiMediaWithCropsResponse.cs index e1c1f09344..b7c056a395 100644 --- a/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiMediaWithCropsResponse.cs +++ b/src/Umbraco.Infrastructure/Models/DeliveryApi/ApiMediaWithCropsResponse.cs @@ -1,13 +1,11 @@ -using Umbraco.Cms.Core.PropertyEditors.ValueConverters; +namespace Umbraco.Cms.Core.Models.DeliveryApi; -namespace Umbraco.Cms.Core.Models.DeliveryApi; - -public sealed class ApiMediaWithCropsResponse : ApiMediaWithCrops +internal sealed class ApiMediaWithCropsResponse : ApiMediaWithCrops, IApiMediaWithCropsResponse { public ApiMediaWithCropsResponse( IApiMedia inner, - ImageCropperValue.ImageCropperFocalPoint? focalPoint, - IEnumerable? crops, + ImageFocalPoint? focalPoint, + IEnumerable? crops, string path, DateTime createDate, DateTime updateDate) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs index 37a4b406fc..1f6dfc9578 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs @@ -73,7 +73,10 @@ public class ImageCropperValueConverter : PropertyValueConverterBase, IDeliveryA public Type GetDeliveryApiPropertyValueType(IPublishedPropertyType propertyType) => typeof(ApiImageCropperValue); public object? ConvertIntermediateToDeliveryApiObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview, bool expanding) - => inter is ImageCropperValue {Src: { }} imageCropperValue - ? new ApiImageCropperValue(imageCropperValue.Src, imageCropperValue.FocalPoint, imageCropperValue.Crops) + => inter is ImageCropperValue { Src: { } } imageCropperValue + ? new ApiImageCropperValue( + imageCropperValue.Src, + imageCropperValue.GetImageFocalPoint(), + imageCropperValue.GetImageCrops()) : null; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs index 7e0829a399..817d48687a 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs @@ -151,13 +151,13 @@ public class MediaPickerWithCropsValueConverter : PropertyValueConverterBase, ID public PropertyCacheLevel GetDeliveryApiPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Elements; - public Type GetDeliveryApiPropertyValueType(IPublishedPropertyType propertyType) => typeof(IEnumerable); + public Type GetDeliveryApiPropertyValueType(IPublishedPropertyType propertyType) => typeof(IEnumerable); public object? ConvertIntermediateToDeliveryApiObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview, bool expanding) { var isMultiple = IsMultipleDataType(propertyType.DataType); - ApiMediaWithCrops ToApiMedia(MediaWithCrops media) => _apiMediaWithCropsBuilder.Build(media); + IApiMediaWithCrops ToApiMedia(MediaWithCrops media) => _apiMediaWithCropsBuilder.Build(media); // NOTE: eventually we might implement this explicitly instead of piggybacking on the default object conversion. however, this only happens once per cache rebuild, // and the performance gain from an explicit implementation is negligible, so... at least for the time being this will do just fine. @@ -171,7 +171,7 @@ public class MediaPickerWithCropsValueConverter : PropertyValueConverterBase, ID return new [] { ToApiMedia(mediaWithCrops) }; } - return Array.Empty(); + return Array.Empty(); } private bool IsMultipleDataType(PublishedDataType dataType) => diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MediaPickerWithCropsValueConverterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MediaPickerWithCropsValueConverterTests.cs index 9e0284ffa8..365e48a325 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MediaPickerWithCropsValueConverterTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MediaPickerWithCropsValueConverterTests.cs @@ -43,7 +43,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes var serializer = new JsonNetSerializer(); var valueConverter = MediaPickerWithCropsValueConverter(); - Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType)); + Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType)); var inter = serializer.Serialize(new[] { @@ -63,7 +63,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes } }); - var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; + var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; Assert.NotNull(result); Assert.AreEqual(1, result.Count()); var first = result.Single(); @@ -87,7 +87,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes var serializer = new JsonNetSerializer(); var valueConverter = MediaPickerWithCropsValueConverter(); - Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType)); + Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType)); var inter = serializer.Serialize(new[] { @@ -121,7 +121,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes } }); - var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; + var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; Assert.NotNull(result); Assert.AreEqual(2, result.Count()); var first = result.First(); @@ -169,7 +169,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes var serializer = new JsonNetSerializer(); var valueConverter = MediaPickerWithCropsValueConverter(); - Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType)); + Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType)); var inter = serializer.Serialize(new[] { @@ -188,7 +188,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes } }); - var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; + var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; Assert.NotNull(result); Assert.AreEqual(1, result.Count()); var mediaWithCrops = result.Single(); @@ -206,7 +206,6 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes ValidateCrop(mediaWithCrops.Crops.Last(), "mediaOne", 111, 222, 2m, 4m, 20m, 40m); } - [Test] public void MediaPickerWithCropsValueConverter_LocalCropsAndFocalPointTakesPrecedenceOverMediaCropsAndFocalPoint() { @@ -230,7 +229,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes var serializer = new JsonNetSerializer(); var valueConverter = MediaPickerWithCropsValueConverter(); - Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType)); + Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType)); var inter = serializer.Serialize(new[] { @@ -250,7 +249,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes } }); - var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; + var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; Assert.NotNull(result); Assert.AreEqual(1, result.Count()); var mediaWithCrops = result.Single(); @@ -277,7 +276,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes var valueConverter = MediaPickerWithCropsValueConverter(); - var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; + var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; Assert.NotNull(result); Assert.IsEmpty(result); } @@ -292,12 +291,11 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes var valueConverter = MediaPickerWithCropsValueConverter(); - var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; + var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; Assert.NotNull(result); Assert.IsEmpty(result); } - private IPublishedPropertyType SetupMediaPropertyType(bool multiSelect) { var publishedDataType = new PublishedDataType(123, "test", new Lazy(() => new MediaPicker3Configuration @@ -361,7 +359,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes } private void ValidateMedia( - ApiMediaWithCrops actual, + IApiMediaWithCrops actual, string expectedName, string expectedUrl, string expectedExtension, @@ -378,7 +376,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes } - private void ValidateFocalPoint(ImageCropperValue.ImageCropperFocalPoint? actual, decimal expectedLeft, decimal expectedTop) + private void ValidateFocalPoint(ImageFocalPoint? actual, decimal expectedLeft, decimal expectedTop) { Assert.NotNull(actual); Assert.AreEqual(expectedLeft, actual.Left); @@ -386,7 +384,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes } private void ValidateCrop( - ImageCropperValue.ImageCropperCrop actual, + ImageCrop actual, string expectedAlias, int expectedWidth, int expectedHeight,