2022-04-29 13:16:24 +02:00
|
|
|
using System.Globalization;
|
|
|
|
|
using Microsoft.AspNetCore.WebUtilities;
|
2023-05-11 11:01:03 +02:00
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2022-04-29 13:16:24 +02:00
|
|
|
using Microsoft.Extensions.Primitives;
|
|
|
|
|
using SixLabors.ImageSharp.Web.Processors;
|
2023-05-11 11:01:03 +02:00
|
|
|
using Umbraco.Cms.Core.DependencyInjection;
|
2022-04-29 13:16:24 +02:00
|
|
|
using Umbraco.Cms.Core.Media;
|
|
|
|
|
using Umbraco.Cms.Core.Models;
|
2022-09-27 14:22:34 +02:00
|
|
|
using Umbraco.Cms.Imaging.ImageSharp.ImageProcessors;
|
2022-04-29 13:16:24 +02:00
|
|
|
using static Umbraco.Cms.Core.Models.ImageUrlGenerationOptions;
|
|
|
|
|
|
2022-09-27 14:22:34 +02:00
|
|
|
namespace Umbraco.Cms.Imaging.ImageSharp.Media;
|
2022-05-09 09:39:46 +02:00
|
|
|
|
|
|
|
|
/// <summary>
|
2023-05-11 11:01:03 +02:00
|
|
|
/// Exposes a method that generates an image URL based on the specified options that can be processed by ImageSharp.
|
2022-05-09 09:39:46 +02:00
|
|
|
/// </summary>
|
|
|
|
|
/// <seealso cref="IImageUrlGenerator" />
|
2022-09-27 14:22:34 +02:00
|
|
|
public sealed class ImageSharpImageUrlGenerator : IImageUrlGenerator
|
2022-04-29 13:16:24 +02:00
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
private readonly RequestAuthorizationUtilities? _requestAuthorizationUtilities;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref="ImageSharpImageUrlGenerator" /> class.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="configuration">The ImageSharp configuration.</param>
|
|
|
|
|
/// <param name="requestAuthorizationUtilities">Contains helpers that allow authorization of image requests.</param>
|
|
|
|
|
public ImageSharpImageUrlGenerator(Configuration configuration, RequestAuthorizationUtilities? requestAuthorizationUtilities)
|
|
|
|
|
: this(configuration.ImageFormats.SelectMany(f => f.FileExtensions).ToArray(), requestAuthorizationUtilities)
|
|
|
|
|
{ }
|
2022-09-27 14:22:34 +02:00
|
|
|
|
2022-04-29 13:16:24 +02:00
|
|
|
/// <summary>
|
2023-05-11 11:01:03 +02:00
|
|
|
/// Initializes a new instance of the <see cref="ImageSharpImageUrlGenerator" /> class.
|
2022-05-09 09:39:46 +02:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="configuration">The ImageSharp configuration.</param>
|
2023-05-11 11:01:03 +02:00
|
|
|
[Obsolete("Use ctor with all params - This will be removed in Umbraco 13.")]
|
2022-05-09 09:39:46 +02:00
|
|
|
public ImageSharpImageUrlGenerator(Configuration configuration)
|
2023-05-11 11:01:03 +02:00
|
|
|
: this(configuration, StaticServiceProvider.Instance.GetService<RequestAuthorizationUtilities>())
|
2022-09-27 14:22:34 +02:00
|
|
|
{ }
|
2022-05-09 09:39:46 +02:00
|
|
|
|
|
|
|
|
/// <summary>
|
2023-05-11 11:01:03 +02:00
|
|
|
/// Initializes a new instance of the <see cref="ImageSharpImageUrlGenerator" /> class.
|
2022-04-29 13:16:24 +02:00
|
|
|
/// </summary>
|
2022-05-09 09:39:46 +02:00
|
|
|
/// <param name="supportedImageFileTypes">The supported image file types/extensions.</param>
|
2023-05-11 11:01:03 +02:00
|
|
|
/// <param name="requestAuthorizationUtilities">Contains helpers that allow authorization of image requests.</param>
|
2022-05-09 09:39:46 +02:00
|
|
|
/// <remarks>
|
2023-05-11 11:01:03 +02:00
|
|
|
/// This constructor is only used for testing.
|
2022-05-09 09:39:46 +02:00
|
|
|
/// </remarks>
|
2023-05-11 11:01:03 +02:00
|
|
|
internal ImageSharpImageUrlGenerator(IEnumerable<string> supportedImageFileTypes, RequestAuthorizationUtilities? requestAuthorizationUtilities = null)
|
|
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
SupportedImageFileTypes = supportedImageFileTypes;
|
2023-05-11 11:01:03 +02:00
|
|
|
_requestAuthorizationUtilities = requestAuthorizationUtilities;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public IEnumerable<string> SupportedImageFileTypes { get; }
|
2022-05-09 09:39:46 +02:00
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public string? GetImageUrl(ImageUrlGenerationOptions? options)
|
2022-04-29 13:16:24 +02:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
if (options?.ImageUrl == null)
|
2022-04-29 13:16:24 +02:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
return null;
|
2022-04-29 13:16:24 +02:00
|
|
|
}
|
2022-05-09 09:39:46 +02:00
|
|
|
|
|
|
|
|
var queryString = new Dictionary<string, string?>();
|
2022-11-10 13:56:29 +01:00
|
|
|
Dictionary<string, StringValues> furtherOptions = QueryHelpers.ParseQuery(options.FurtherOptions);
|
2022-05-09 09:39:46 +02:00
|
|
|
|
2023-05-11 11:01:03 +02:00
|
|
|
if (options.Crop is CropCoordinates crop)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
queryString.Add(CropWebProcessor.Coordinates, FormattableString.Invariant($"{crop.Left},{crop.Top},{crop.Right},{crop.Bottom}"));
|
2022-05-09 09:39:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 11:01:03 +02:00
|
|
|
if (options.FocalPoint is FocalPointPosition focalPoint)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
queryString.Add(ResizeWebProcessor.Xy, FormattableString.Invariant($"{focalPoint.Left},{focalPoint.Top}"));
|
2022-05-09 09:39:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 11:01:03 +02:00
|
|
|
if (options.ImageCropMode is ImageCropMode imageCropMode)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
queryString.Add(ResizeWebProcessor.Mode, imageCropMode.ToString().ToLowerInvariant());
|
2022-05-09 09:39:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 11:01:03 +02:00
|
|
|
if (options.ImageCropAnchor is ImageCropAnchor imageCropAnchor)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
queryString.Add(ResizeWebProcessor.Anchor, imageCropAnchor.ToString().ToLowerInvariant());
|
2022-05-09 09:39:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 11:01:03 +02:00
|
|
|
if (options.Width is int width)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
queryString.Add(ResizeWebProcessor.Width, width.ToString(CultureInfo.InvariantCulture));
|
2022-05-09 09:39:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 11:01:03 +02:00
|
|
|
if (options.Height is int height)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
queryString.Add(ResizeWebProcessor.Height, height.ToString(CultureInfo.InvariantCulture));
|
2022-05-09 09:39:46 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-10 13:56:29 +01:00
|
|
|
if (furtherOptions.Remove(FormatWebProcessor.Format, out StringValues format))
|
|
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
queryString.Add(FormatWebProcessor.Format, format.ToString());
|
2022-11-10 13:56:29 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 11:01:03 +02:00
|
|
|
if (options.Quality is int quality)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
queryString.Add(QualityWebProcessor.Quality, quality.ToString(CultureInfo.InvariantCulture));
|
2022-05-09 09:39:46 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-10 13:56:29 +01:00
|
|
|
foreach (KeyValuePair<string, StringValues> kvp in furtherOptions)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
|
|
|
|
queryString.Add(kvp.Key, kvp.Value);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-11 11:01:03 +02:00
|
|
|
if (options.CacheBusterValue is string cacheBusterValue && !string.IsNullOrEmpty(cacheBusterValue))
|
|
|
|
|
{
|
|
|
|
|
queryString.Add("v", cacheBusterValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_requestAuthorizationUtilities is not null)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
2023-05-11 11:01:03 +02:00
|
|
|
var uri = QueryHelpers.AddQueryString(options.ImageUrl, queryString);
|
|
|
|
|
if (_requestAuthorizationUtilities.ComputeHMAC(uri, CommandHandling.Sanitize) is string token && !string.IsNullOrEmpty(token))
|
|
|
|
|
{
|
|
|
|
|
queryString.Add(RequestAuthorizationUtilities.TokenCommand, token);
|
|
|
|
|
}
|
2022-05-09 09:39:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QueryHelpers.AddQueryString(options.ImageUrl, queryString);
|
2022-04-29 13:16:24 +02:00
|
|
|
}
|
|
|
|
|
}
|