From d18bce0ea7b35920c2eeea69b102872e5845782f Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 14 May 2024 10:42:33 +0200 Subject: [PATCH] V14: Resize url endpoint (#16275) * Add image resizing endpoint * Rename factory * Actually use height and width --------- Co-authored-by: nikolajlauridsen --- .../UrlSegment/ImagingControllerBase.cs | 13 +++ .../UrlSegment/ResizeImagingController.cs | 32 +++++++ .../MediaBuilderExtensions.cs | 1 + .../Factories/IReziseImageUrlFactory.cs | 9 ++ .../Factories/ReziseImageUrlFactory.cs | 72 +++++++++++++++ src/Umbraco.Cms.Api.Management/OpenApi.json | 88 +++++++++++++++++++ 6 files changed, 215 insertions(+) create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ImagingControllerBase.cs create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ResizeImagingController.cs create mode 100644 src/Umbraco.Cms.Api.Management/Factories/IReziseImageUrlFactory.cs create mode 100644 src/Umbraco.Cms.Api.Management/Factories/ReziseImageUrlFactory.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ImagingControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ImagingControllerBase.cs new file mode 100644 index 0000000000..3d62a16878 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ImagingControllerBase.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Routing; +using Umbraco.Cms.Web.Common.Authorization; + +namespace Umbraco.Cms.Api.Management.Controllers.UrlSegment; + +[VersionedApiBackOfficeRoute("imaging")] +[ApiExplorerSettings(GroupName = "Imaging")] +[Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] +public class ImagingControllerBase : ManagementApiControllerBase +{ +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ResizeImagingController.cs b/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ResizeImagingController.cs new file mode 100644 index 0000000000..2c2dba9a19 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ResizeImagingController.cs @@ -0,0 +1,32 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Factories; +using Umbraco.Cms.Api.Management.ViewModels.Media; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.UrlSegment; + +[ApiVersion("1.0")] +public class ResizeImagingController : ImagingControllerBase +{ + private readonly IMediaService _mediaService; + private readonly IReziseImageUrlFactory _reziseImageUrlFactory; + + public ResizeImagingController(IMediaService mediaService, IReziseImageUrlFactory reziseImageUrlFactory) + { + _mediaService = mediaService; + _reziseImageUrlFactory = reziseImageUrlFactory; + } + + [MapToApiVersion("1.0")] + [HttpGet("resize/urls")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task Urls([FromQuery(Name = "id")] HashSet ids, int height = 200, int width = 200, ImageCropMode? mode = null) + { + IEnumerable items = _mediaService.GetByIds(ids); + + return await Task.FromResult(Ok(_reziseImageUrlFactory.CreateUrlSets(items, height, width, mode))); + } +} diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/MediaBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/MediaBuilderExtensions.cs index 59dc5063ee..e72992c209 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/MediaBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/MediaBuilderExtensions.cs @@ -16,6 +16,7 @@ internal static class MediaBuilderExtensions builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); builder.Services.AddScoped(); builder.WithCollectionBuilder().Add(); diff --git a/src/Umbraco.Cms.Api.Management/Factories/IReziseImageUrlFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IReziseImageUrlFactory.cs new file mode 100644 index 0000000000..5ff625209c --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Factories/IReziseImageUrlFactory.cs @@ -0,0 +1,9 @@ +using Umbraco.Cms.Api.Management.ViewModels.Media; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Api.Management.Factories; + +public interface IReziseImageUrlFactory +{ + IEnumerable CreateUrlSets(IEnumerable mediaItems, int height, int width, ImageCropMode? mode); +} diff --git a/src/Umbraco.Cms.Api.Management/Factories/ReziseImageUrlFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/ReziseImageUrlFactory.cs new file mode 100644 index 0000000000..d3ca79f8cf --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Factories/ReziseImageUrlFactory.cs @@ -0,0 +1,72 @@ +using Microsoft.Extensions.Options; +using Umbraco.Cms.Api.Management.Routing; +using Umbraco.Cms.Api.Management.ViewModels.Media; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Media; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Api.Management.Factories; + +public class ReziseImageUrlFactory : IReziseImageUrlFactory +{ + private readonly IImageUrlGenerator _imageUrlGenerator; + private readonly ContentSettings _contentSettings; + private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; + private readonly IAbsoluteUrlBuilder _absoluteUrlBuilder; + + public ReziseImageUrlFactory(IImageUrlGenerator imageUrlGenerator, IOptions contentSettings, MediaUrlGeneratorCollection mediaUrlGenerators, IAbsoluteUrlBuilder absoluteUrlBuilder) + { + _imageUrlGenerator = imageUrlGenerator; + _contentSettings = contentSettings.Value; + _mediaUrlGenerators = mediaUrlGenerators; + _absoluteUrlBuilder = absoluteUrlBuilder; + } + + public IEnumerable CreateUrlSets(IEnumerable mediaItems, int height, int width, ImageCropMode? mode) + { + return mediaItems.Select(media => new MediaUrlInfoResponseModel(media.Key, CreateUrls(media, height, width, mode))).ToArray(); + } + + private IEnumerable CreateUrls(IMedia media, int height, int width, ImageCropMode? mode) + { + IEnumerable urls = media + .GetUrls(_contentSettings, _mediaUrlGenerators) + .WhereNotNull(); + + return CreateThumbnailUrls(urls, height, width, mode); + } + + private IEnumerable CreateThumbnailUrls(IEnumerable urls, int height, int width, ImageCropMode? mode) + { + foreach (var url in urls) + { + // Have to remove first char here, as it always contains the "." + var extension = Path.GetExtension(url).Remove(0, 1); + if (_imageUrlGenerator.SupportedImageFileTypes.InvariantContains(extension) is false) + { + continue; + } + + var options = new ImageUrlGenerationOptions(url) + { + Height = height, + Width = width, + ImageCropMode = mode, + }; + + var relativeUrl = _imageUrlGenerator.GetImageUrl(options); + if (relativeUrl is null) + { + continue; + } + + yield return new MediaUrlInfo + { + Culture = null, + Url = _absoluteUrlBuilder.ToAbsoluteUrl(relativeUrl).ToString(), + }; + } + } +} diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 42e9aeb4d1..e145fc3aab 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -10816,6 +10816,83 @@ ] } }, + "/umbraco/management/api/v1/imaging/resize/urls": { + "get": { + "tags": [ + "Imaging" + ], + "operationId": "GetImagingResizeUrls", + "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "height", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 200 + } + }, + { + "name": "width", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 200 + } + }, + { + "name": "mode", + "in": "query", + "schema": { + "$ref": "#/components/schemas/ImageCropModeModel" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/MediaUrlInfoResponseModel" + } + ] + } + } + } + } + }, + "401": { + "description": "The resource is protected and requires an authentication token" + }, + "403": { + "description": "The authenticated user do not have access to this resource" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, "/umbraco/management/api/v1/indexer": { "get": { "tags": [ @@ -36709,6 +36786,17 @@ }, "additionalProperties": false }, + "ImageCropModeModel": { + "enum": [ + "Crop", + "Max", + "Stretch", + "Pad", + "BoxPad", + "Min" + ], + "type": "string" + }, "ImportDictionaryRequestModel": { "required": [ "temporaryFile"