Merge remote-tracking branch 'origin/v9/dev' into v9/bugfix/Refractor_UmbracoContextAccessor
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -193,6 +193,8 @@ src/Umbraco.Web.UI/Umbraco/telemetrics-id.umb
|
||||
/src/Umbraco.Web.UI.NetCore/[Uu]mbraco/[Ll]ogs
|
||||
/src/Umbraco.Web.UI.NetCore/[Uu]mbraco/[Dd]ata/*
|
||||
/src/Umbraco.Web.UI.NetCore/[Uu]mbraco/[Mm]odels/*
|
||||
/src/Umbraco.Web.UI.NetCore/appsettings.json
|
||||
/src/Umbraco.Web.UI.NetCore/appsettings.Development.json
|
||||
|
||||
|
||||
src/Umbraco.Tests.Integration/umbraco/Data/
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"version": {
|
||||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"defaultValue": "9.0.0-rc001",
|
||||
"defaultValue": "9.0.0-rc002",
|
||||
"description": "The version of Umbraco to load using NuGet",
|
||||
"replaces": "UMBRACO_VERSION_FROM_TEMPLATE"
|
||||
},
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"version": {
|
||||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"defaultValue": "9.0.0-rc001",
|
||||
"defaultValue": "9.0.0-rc002",
|
||||
"description": "The version of Umbraco to load using NuGet",
|
||||
"replaces": "UMBRACO_VERSION_FROM_TEMPLATE"
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<Version>9.0.0</Version>
|
||||
<AssemblyVersion>9.0.0</AssemblyVersion>
|
||||
<InformationalVersion>9.0.0-rc001</InformationalVersion>
|
||||
<InformationalVersion>9.0.0-rc002</InformationalVersion>
|
||||
<FileVersion>9.0.0</FileVersion>
|
||||
<LangVersion Condition="'$(LangVersion)' == ''">9.0</LangVersion>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Cms.Core
|
||||
namespace Umbraco.Cms.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
@@ -78,6 +78,12 @@
|
||||
/// </summary>
|
||||
public static readonly char[] TildeForwardSlash = new char[] { '~', '/' };
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Char array containing ~ / \
|
||||
/// </summary>
|
||||
public static readonly char[] TildeForwardSlashBackSlash = new char[] { '~', '/', '\\' };
|
||||
|
||||
/// <summary>
|
||||
/// Char array containing only ?
|
||||
/// </summary>
|
||||
|
||||
@@ -79,26 +79,28 @@ namespace Umbraco.Cms.Core.Diagnostics
|
||||
|
||||
private static bool Write(IMarchal marchal, SafeHandle fileHandle, Option options, bool withException = false)
|
||||
{
|
||||
var currentProcess = Process.GetCurrentProcess();
|
||||
var currentProcessHandle = currentProcess.Handle;
|
||||
var currentProcessId = (uint)currentProcess.Id;
|
||||
|
||||
MiniDumpExceptionInformation exp;
|
||||
|
||||
exp.ThreadId = GetCurrentThreadId();
|
||||
exp.ClientPointers = false;
|
||||
exp.ExceptionPointers = IntPtr.Zero;
|
||||
|
||||
if (withException)
|
||||
using (var currentProcess = Process.GetCurrentProcess())
|
||||
{
|
||||
exp.ExceptionPointers = marchal.GetExceptionPointers();
|
||||
var currentProcessHandle = currentProcess.Handle;
|
||||
var currentProcessId = (uint)currentProcess.Id;
|
||||
|
||||
MiniDumpExceptionInformation exp;
|
||||
|
||||
exp.ThreadId = GetCurrentThreadId();
|
||||
exp.ClientPointers = false;
|
||||
exp.ExceptionPointers = IntPtr.Zero;
|
||||
|
||||
if (withException)
|
||||
{
|
||||
exp.ExceptionPointers = marchal.GetExceptionPointers();
|
||||
}
|
||||
|
||||
var bRet = exp.ExceptionPointers == IntPtr.Zero
|
||||
? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)
|
||||
: MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, ref exp, IntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
var bRet = exp.ExceptionPointers == IntPtr.Zero
|
||||
? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)
|
||||
: MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, ref exp, IntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
public static bool Dump(IMarchal marchal, IHostingEnvironment hostingEnvironment, Option options = Option.WithFullMemory, bool withException = false)
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Media
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes a method that generates an image URL based on the specified options.
|
||||
/// </summary>
|
||||
public interface IImageUrlGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the supported image file types/extensions.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The supported image file types/extensions.
|
||||
/// </value>
|
||||
IEnumerable<string> SupportedImageFileTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image URL based on the specified <paramref name="options" />.
|
||||
/// </summary>
|
||||
/// <param name="options">The image URL generation options.</param>
|
||||
/// <returns>
|
||||
/// The generated image URL.
|
||||
/// </returns>
|
||||
string GetImageUrl(ImageUrlGenerationOptions options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Umbraco.Cms.Core.Models
|
||||
{
|
||||
public enum ImageCropRatioMode
|
||||
{
|
||||
Width,
|
||||
Height
|
||||
}
|
||||
}
|
||||
@@ -1,66 +1,68 @@
|
||||
namespace Umbraco.Cms.Core.Models
|
||||
namespace Umbraco.Cms.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// These are options that are passed to the IImageUrlGenerator implementation to determine
|
||||
/// the propery URL that is needed
|
||||
/// These are options that are passed to the IImageUrlGenerator implementation to determine the URL that is generated.
|
||||
/// </summary>
|
||||
public class ImageUrlGenerationOptions
|
||||
{
|
||||
public ImageUrlGenerationOptions (string imageUrl)
|
||||
{
|
||||
ImageUrl = imageUrl;
|
||||
}
|
||||
public ImageUrlGenerationOptions(string imageUrl) => ImageUrl = imageUrl;
|
||||
|
||||
public string ImageUrl { get; }
|
||||
|
||||
public int? Width { get; set; }
|
||||
|
||||
public int? Height { get; set; }
|
||||
public decimal? WidthRatio { get; set; }
|
||||
public decimal? HeightRatio { get; set; }
|
||||
|
||||
public int? Quality { get; set; }
|
||||
|
||||
public ImageCropMode? ImageCropMode { get; set; }
|
||||
|
||||
public ImageCropAnchor? ImageCropAnchor { get; set; }
|
||||
public bool DefaultCrop { get; set; }
|
||||
|
||||
public FocalPointPosition FocalPoint { get; set; }
|
||||
|
||||
public CropCoordinates Crop { get; set; }
|
||||
|
||||
public string CacheBusterValue { get; set; }
|
||||
|
||||
public string FurtherOptions { get; set; }
|
||||
public bool UpScale { get; set; } = true;
|
||||
public string AnimationProcessMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The focal point position, in whatever units the registered IImageUrlGenerator uses,
|
||||
/// typically a percentage of the total image from 0.0 to 1.0.
|
||||
/// The focal point position, in whatever units the registered IImageUrlGenerator uses, typically a percentage of the total image from 0.0 to 1.0.
|
||||
/// </summary>
|
||||
public class FocalPointPosition
|
||||
{
|
||||
public FocalPointPosition (decimal top, decimal left)
|
||||
public FocalPointPosition(decimal left, decimal top)
|
||||
{
|
||||
Left = left;
|
||||
Top = top;
|
||||
}
|
||||
|
||||
public decimal Left { get; }
|
||||
|
||||
public decimal Top { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bounds of the crop within the original image, in whatever units the registered
|
||||
/// IImageUrlGenerator uses, typically a percentage between 0 and 100.
|
||||
/// The bounds of the crop within the original image, in whatever units the registered IImageUrlGenerator uses, typically a percentage between 0.0 and 1.0.
|
||||
/// </summary>
|
||||
public class CropCoordinates
|
||||
{
|
||||
public CropCoordinates (decimal x1, decimal y1, decimal x2, decimal y2)
|
||||
public CropCoordinates(decimal left, decimal top, decimal right, decimal bottom)
|
||||
{
|
||||
X1 = x1;
|
||||
Y1 = y1;
|
||||
X2 = x2;
|
||||
Y2 = y2;
|
||||
Left = left;
|
||||
Top = top;
|
||||
Right = right;
|
||||
Bottom = bottom;
|
||||
}
|
||||
|
||||
public decimal X1 { get; }
|
||||
public decimal Y1 { get; }
|
||||
public decimal X2 { get; }
|
||||
public decimal Y2 { get; }
|
||||
public decimal Left { get; }
|
||||
|
||||
public decimal Top { get; }
|
||||
|
||||
public decimal Right { get; }
|
||||
|
||||
public decimal Bottom { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace Umbraco.Cms.Core.Routing
|
||||
// and then the comparisons in IsMatch can be way faster - and allocate way less strings
|
||||
const string propertyAlias = Constants.Conventions.Content.UrlAlias;
|
||||
|
||||
var test1 = alias.TrimStart('/') + ",";
|
||||
var test1 = alias.TrimStart(Constants.CharArrays.ForwardSlash) + ",";
|
||||
var test2 = ",/" + test1; // test2 is ",/alias,"
|
||||
test1 = "," + test1; // test1 is ",alias,"
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Umbraco.Cms.Core.Routing
|
||||
public UmbracoRequestPaths(IOptions<GlobalSettings> globalSettings, IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
var applicationPath = hostingEnvironment.ApplicationVirtualPath;
|
||||
_appPath = applicationPath.TrimStart('/');
|
||||
_appPath = applicationPath.TrimStart(Constants.CharArrays.ForwardSlash);
|
||||
|
||||
_backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment)
|
||||
.EnsureStartsWith('/').TrimStart(_appPath.EnsureStartsWith('/')).EnsureStartsWith('/');
|
||||
@@ -71,7 +71,7 @@ namespace Umbraco.Cms.Core.Routing
|
||||
/// </remarks>
|
||||
public bool IsBackOfficeRequest(string absPath)
|
||||
{
|
||||
var fullUrlPath = absPath.TrimStart('/');
|
||||
var fullUrlPath = absPath.TrimStart(Constants.CharArrays.ForwardSlash);
|
||||
var urlPath = fullUrlPath.TrimStart(_appPath).EnsureStartsWith('/');
|
||||
|
||||
// check if this is in the umbraco back office
|
||||
@@ -108,7 +108,7 @@ namespace Umbraco.Cms.Core.Routing
|
||||
// Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id}
|
||||
// so if the path contains at a minimum 3 parts: Umbraco + MYPLUGINAREA + MYCONTROLLERNAME then we will have to assume it is a
|
||||
// plugin controller for the front-end.
|
||||
if (urlPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Length >= 3)
|
||||
if (urlPath.Split(Constants.CharArrays.ForwardSlash, StringSplitOptions.RemoveEmptyEntries).Length >= 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Cms.Core
|
||||
{
|
||||
// http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx
|
||||
// https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-6-asynclock/
|
||||
//
|
||||
// notes:
|
||||
// - this is NOT a reader/writer lock
|
||||
|
||||
@@ -145,8 +145,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
builder.PropertyValueConverters()
|
||||
.Remove<SimpleTinyMceValueConverter>();
|
||||
|
||||
builder.Services.AddUnique<IImageUrlGenerator, ImageSharpImageUrlGenerator>();
|
||||
|
||||
// register *all* checks, except those marked [HideFromTypeFinder] of course
|
||||
builder.Services.AddUnique<IMarkdownToHtmlConverter, MarkdownToHtmlConverter>();
|
||||
|
||||
@@ -183,7 +181,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
|
||||
builder.Services.AddUnique<ICronTabParser, NCronTabParser>();
|
||||
|
||||
// Add default ImageSharp configuration and service implementations
|
||||
builder.Services.AddUnique(SixLabors.ImageSharp.Configuration.Default);
|
||||
builder.Services.AddUnique<IImageDimensionExtractor, ImageDimensionExtractor>();
|
||||
builder.Services.AddUnique<IImageUrlGenerator, ImageSharpImageUrlGenerator>();
|
||||
|
||||
builder.Services.AddUnique<PackageDataInstallation>();
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
|
||||
if (useBackgroundThread)
|
||||
{
|
||||
_logger.LogInformation($"Starting async background thread for rebuilding index {indexName}.");
|
||||
_logger.LogInformation("Starting async background thread for rebuilding index {indexName}.",indexName);
|
||||
|
||||
_backgroundTaskQueue.QueueBackgroundWorkItem(
|
||||
cancellationToken => Task.Run(() => RebuildIndex(indexName, delay.Value, cancellationToken)));
|
||||
|
||||
@@ -8,6 +8,17 @@ namespace Umbraco.Cms.Infrastructure.Media
|
||||
{
|
||||
internal class ImageDimensionExtractor : IImageDimensionExtractor
|
||||
{
|
||||
/// <summary>
|
||||
/// The ImageSharp configuration.
|
||||
/// </summary>
|
||||
private readonly Configuration _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageDimensionExtractor" /> class.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The ImageSharp configuration.</param>
|
||||
public ImageDimensionExtractor(Configuration configuration) => _configuration = configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dimensions of an image.
|
||||
/// </summary>
|
||||
@@ -39,7 +50,7 @@ namespace Umbraco.Cms.Infrastructure.Media
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
using (var image = Image.Load(stream))
|
||||
using (var image = Image.Load(_configuration, stream))
|
||||
{
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
|
||||
@@ -1,68 +1,107 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using SixLabors.ImageSharp;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Media
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes a method that generates an image URL based on the specified options that can be processed by ImageSharp.
|
||||
/// </summary>
|
||||
/// <seealso cref="Umbraco.Cms.Core.Media.IImageUrlGenerator" />
|
||||
public class ImageSharpImageUrlGenerator : IImageUrlGenerator
|
||||
{
|
||||
public IEnumerable<string> SupportedImageFileTypes => new[] { "jpeg", "jpg", "gif", "bmp", "png" };
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> SupportedImageFileTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageSharpImageUrlGenerator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The ImageSharp configuration.</param>
|
||||
public ImageSharpImageUrlGenerator(Configuration configuration)
|
||||
: this(configuration.ImageFormats.SelectMany(f => f.FileExtensions).ToArray())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageSharpImageUrlGenerator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="supportedImageFileTypes">The supported image file types/extensions.</param>
|
||||
/// <remarks>
|
||||
/// This constructor is only used for testing.
|
||||
/// </remarks>
|
||||
internal ImageSharpImageUrlGenerator(IEnumerable<string> supportedImageFileTypes) => SupportedImageFileTypes = supportedImageFileTypes;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string GetImageUrl(ImageUrlGenerationOptions options)
|
||||
{
|
||||
if (options == null) return null;
|
||||
|
||||
var imageProcessorUrl = new StringBuilder(options.ImageUrl ?? string.Empty);
|
||||
|
||||
if (options.FocalPoint != null) AppendFocalPoint(imageProcessorUrl, options);
|
||||
else if (options.Crop != null) AppendCrop(imageProcessorUrl, options);
|
||||
else if (options.DefaultCrop) imageProcessorUrl.Append("?anchor=center&mode=crop");
|
||||
else
|
||||
if (options == null)
|
||||
{
|
||||
imageProcessorUrl.Append("?mode=").Append((options.ImageCropMode ?? ImageCropMode.Crop).ToString().ToLower());
|
||||
|
||||
if (options.ImageCropAnchor != null) imageProcessorUrl.Append("&anchor=").Append(options.ImageCropAnchor.ToString().ToLower());
|
||||
return null;
|
||||
}
|
||||
|
||||
var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&format=");
|
||||
var imageUrl = new StringBuilder(options.ImageUrl);
|
||||
|
||||
//Only put quality here, if we don't have a format specified.
|
||||
//Otherwise we need to put quality at the end to avoid it being overridden by the format.
|
||||
if (options.Quality.HasValue && hasFormat == false) imageProcessorUrl.Append("&quality=").Append(options.Quality);
|
||||
if (options.HeightRatio.HasValue) imageProcessorUrl.Append("&heightratio=").Append(options.HeightRatio.Value.ToString(CultureInfo.InvariantCulture));
|
||||
if (options.WidthRatio.HasValue) imageProcessorUrl.Append("&widthratio=").Append(options.WidthRatio.Value.ToString(CultureInfo.InvariantCulture));
|
||||
if (options.Width.HasValue) imageProcessorUrl.Append("&width=").Append(options.Width);
|
||||
if (options.Height.HasValue) imageProcessorUrl.Append("&height=").Append(options.Height);
|
||||
if (options.UpScale == false) imageProcessorUrl.Append("&upscale=false");
|
||||
if (!string.IsNullOrWhiteSpace(options.AnimationProcessMode)) imageProcessorUrl.Append("&animationprocessmode=").Append(options.AnimationProcessMode);
|
||||
if (!string.IsNullOrWhiteSpace(options.FurtherOptions)) imageProcessorUrl.Append(options.FurtherOptions);
|
||||
bool queryStringHasStarted = false;
|
||||
void AppendQueryString(string value)
|
||||
{
|
||||
imageUrl.Append(queryStringHasStarted ? '&' : '?');
|
||||
queryStringHasStarted = true;
|
||||
|
||||
//If furtherOptions contains a format, we need to put the quality after the format.
|
||||
if (options.Quality.HasValue && hasFormat) imageProcessorUrl.Append("&quality=").Append(options.Quality);
|
||||
if (!string.IsNullOrWhiteSpace(options.CacheBusterValue)) imageProcessorUrl.Append("&rnd=").Append(options.CacheBusterValue);
|
||||
imageUrl.Append(value);
|
||||
}
|
||||
void AddQueryString(string key, params IConvertible[] values)
|
||||
=> AppendQueryString(key + '=' + string.Join(",", values.Select(x => x.ToString(CultureInfo.InvariantCulture))));
|
||||
|
||||
return imageProcessorUrl.ToString();
|
||||
}
|
||||
if (options.FocalPoint != null)
|
||||
{
|
||||
AddQueryString("rxy", options.FocalPoint.Left, options.FocalPoint.Top);
|
||||
}
|
||||
|
||||
private void AppendFocalPoint(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options)
|
||||
{
|
||||
imageProcessorUrl.Append("?center=");
|
||||
imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture));
|
||||
imageProcessorUrl.Append("&mode=crop");
|
||||
}
|
||||
if (options.Crop != null)
|
||||
{
|
||||
AddQueryString("cc", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom);
|
||||
}
|
||||
|
||||
private void AppendCrop(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options)
|
||||
{
|
||||
imageProcessorUrl.Append("?crop=");
|
||||
imageProcessorUrl.Append(options.Crop.X1.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture));
|
||||
imageProcessorUrl.Append("&cropmode=percentage");
|
||||
if (options.ImageCropMode.HasValue)
|
||||
{
|
||||
AddQueryString("rmode", options.ImageCropMode.Value.ToString().ToLowerInvariant());
|
||||
}
|
||||
|
||||
if (options.ImageCropAnchor.HasValue)
|
||||
{
|
||||
AddQueryString("ranchor", options.ImageCropAnchor.Value.ToString().ToLowerInvariant());
|
||||
}
|
||||
|
||||
if (options.Width.HasValue)
|
||||
{
|
||||
AddQueryString("width", options.Width.Value);
|
||||
}
|
||||
|
||||
if (options.Height.HasValue)
|
||||
{
|
||||
AddQueryString("height", options.Height.Value);
|
||||
}
|
||||
|
||||
if (options.Quality.HasValue)
|
||||
{
|
||||
AddQueryString("quality", options.Quality.Value);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.FurtherOptions) == false)
|
||||
{
|
||||
AppendQueryString(options.FurtherOptions.TrimStart('?', '&'));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.CacheBusterValue) == false)
|
||||
{
|
||||
AddQueryString("rnd", options.CacheBusterValue);
|
||||
}
|
||||
|
||||
return imageUrl.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,8 +231,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
|
||||
// - 8.15 Final - Current state: {5C424554-A32D-4852-8ED1-A13508187901}
|
||||
// - 9.0 RC1 - Current state: {5060F3D2-88BE-4D30-8755-CF51F28EAD12}
|
||||
To<UpdateCmsPropertyGroupIdSeed>("{622E5172-42E1-4662-AD80-9504AF5A4E53}");
|
||||
|
||||
To<ExternalLoginTableIndexesFixup>("{10F7BB61-C550-426B-830B-7F954F689CDF}");
|
||||
To<DictionaryTablesIndexes>("{12DCDE7F-9AB7-4617-804F-AB66BF360980}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Expressions.Execute.Expressions;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
|
||||
{
|
||||
public class DictionaryTablesIndexes : MigrationBase
|
||||
{
|
||||
private const string IndexedDictionaryColumn = "key";
|
||||
private const string IndexedLanguageTextColumn = "languageId";
|
||||
|
||||
public DictionaryTablesIndexes(IMigrationContext context)
|
||||
: base(context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Migrate()
|
||||
{
|
||||
var indexDictionaryDto = $"IX_{DictionaryDto.TableName}_{IndexedDictionaryColumn}";
|
||||
var indexLanguageTextDto = $"IX_{LanguageTextDto.TableName}_{IndexedLanguageTextColumn}";
|
||||
|
||||
// Delete existing
|
||||
DeleteIndex<DictionaryDto>(indexDictionaryDto);
|
||||
// Re-create/Add
|
||||
AddUniqueConstraint<DictionaryDto>(new[] { IndexedDictionaryColumn }, indexDictionaryDto);
|
||||
|
||||
// Delete existing
|
||||
DeleteIndex<LanguageTextDto>(indexLanguageTextDto);
|
||||
|
||||
var langTextcolumns = new[] { IndexedLanguageTextColumn, "UniqueId" };
|
||||
|
||||
// Re-create/Add
|
||||
AddUniqueConstraint<LanguageTextDto>(langTextcolumns, indexLanguageTextDto);
|
||||
}
|
||||
|
||||
private void DeleteIndex<TDto>(string indexName)
|
||||
{
|
||||
var tableDef = DefinitionFactory.GetTableDefinition(typeof(TDto), Context.SqlContext.SqlSyntax);
|
||||
|
||||
if (IndexExists(indexName))
|
||||
{
|
||||
Delete.Index(indexName).OnTable(tableDef.Name).Do();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateIndex<TDto>(string indexName)
|
||||
{
|
||||
var tableDef = DefinitionFactory.GetTableDefinition(typeof(TDto), Context.SqlContext.SqlSyntax);
|
||||
|
||||
// get the definition by name
|
||||
var index = tableDef.Indexes.First(x => x.Name == indexName);
|
||||
new ExecuteSqlStatementExpression(Context) { SqlStatement = Context.SqlContext.SqlSyntax.Format(index) }.Execute();
|
||||
}
|
||||
|
||||
private void AddUniqueConstraint<TDto>(string[] columns, string index)
|
||||
{
|
||||
var tableDef = DefinitionFactory.GetTableDefinition(typeof(TDto), Context.SqlContext.SqlSyntax);
|
||||
|
||||
// Check the existing data to ensure the constraint can be successfully applied.
|
||||
// This seems to be better than relying on catching an exception as this leads to
|
||||
// transaction errors: "This SqlTransaction has completed; it is no longer usable".
|
||||
var columnsDescription = string.Join("], [", columns);
|
||||
if (ContainsDuplicates<TDto>(columns))
|
||||
{
|
||||
var message = $"Could not create unique constraint on [{tableDef.Name}] due to existing " +
|
||||
$"duplicate records across the column{(columns.Length > 1 ? "s" : string.Empty)}: [{columnsDescription}].";
|
||||
|
||||
LogIncompleteMigrationStep(message);
|
||||
return;
|
||||
}
|
||||
|
||||
CreateIndex<TDto>(index);
|
||||
}
|
||||
|
||||
private bool ContainsDuplicates<TDto>(string[] columns)
|
||||
{
|
||||
// Check for duplicates by comparing the total count of all records with the count of records distinct by the
|
||||
// provided column. If the former is greater than the latter, there's at least one duplicate record.
|
||||
int recordCount = GetRecordCount<TDto>();
|
||||
int distinctRecordCount = GetDistinctRecordCount<TDto>(columns);
|
||||
|
||||
return recordCount > distinctRecordCount;
|
||||
}
|
||||
|
||||
private int GetRecordCount<TDto>()
|
||||
{
|
||||
var countQuery = Database.SqlContext.Sql()
|
||||
.SelectCount()
|
||||
.From<TDto>();
|
||||
|
||||
return Database.ExecuteScalar<int>(countQuery);
|
||||
}
|
||||
|
||||
private int GetDistinctRecordCount<TDto>(string[] columns)
|
||||
{
|
||||
string columnSpecification;
|
||||
|
||||
// If using SQL CE, we don't have access to COUNT (DISTINCT *) or CONCAT, so will need to do this by querying all records.
|
||||
if (DatabaseType.IsSqlCe())
|
||||
{
|
||||
columnSpecification = columns.Length == 1
|
||||
? StringConvertedAndQuotedColumnName(columns[0])
|
||||
: $"{string.Join(" + ", columns.Select(x => StringConvertedAndQuotedColumnName(x)))}";
|
||||
|
||||
var allRecordsQuery = Database.SqlContext.Sql()
|
||||
.Select(columnSpecification)
|
||||
.From<TDto>();
|
||||
|
||||
var allRecords = Database.Fetch<string>(allRecordsQuery);
|
||||
|
||||
return allRecords.Distinct().Count();
|
||||
}
|
||||
|
||||
columnSpecification = columns.Length == 1
|
||||
? QuoteColumnName(columns[0])
|
||||
: $"CONCAT({string.Join(",", columns.Select(QuoteColumnName))})";
|
||||
|
||||
var distinctCountQuery = Database.SqlContext.Sql()
|
||||
.Select($"COUNT(DISTINCT({columnSpecification}))")
|
||||
.From<TDto>();
|
||||
|
||||
return Database.ExecuteScalar<int>(distinctCountQuery);
|
||||
}
|
||||
|
||||
private void LogIncompleteMigrationStep(string message) =>
|
||||
Logger.LogError($"Database migration step failed: {message}");
|
||||
|
||||
private string StringConvertedAndQuotedColumnName(string column) => $"CONVERT(nvarchar(1000),{QuoteColumnName(column)})";
|
||||
|
||||
private string QuoteColumnName(string column) => $"[{column}]";
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos
|
||||
public class DictionaryDto // public as required to be accessible from Deploy for the RepairDictionaryIdsWorkItem.
|
||||
{
|
||||
public const string TableName = Cms.Core.Constants.DatabaseSchema.Tables.DictionaryEntry;
|
||||
|
||||
[Column("pk")]
|
||||
[PrimaryKeyColumn]
|
||||
public int PrimaryKey { get; set; }
|
||||
@@ -25,10 +26,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Parent")]
|
||||
public Guid? Parent { get; set; }
|
||||
|
||||
// TODO: This needs to have a unique index.
|
||||
[Column("key")]
|
||||
[Length(450)]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_cmsDictionary_key")]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_key")]
|
||||
public string Key { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
|
||||
@@ -4,24 +4,26 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.Dtos
|
||||
{
|
||||
[TableName(Cms.Core.Constants.DatabaseSchema.Tables.DictionaryValue)]
|
||||
[TableName(TableName)]
|
||||
[PrimaryKey("pk")]
|
||||
[ExplicitColumns]
|
||||
public class LanguageTextDto
|
||||
{
|
||||
public const string TableName = Cms.Core.Constants.DatabaseSchema.Tables.DictionaryValue;
|
||||
|
||||
[Column("pk")]
|
||||
[PrimaryKeyColumn]
|
||||
public int PrimaryKey { get; set; }
|
||||
|
||||
[Column("languageId")]
|
||||
[ForeignKey(typeof(LanguageDto), Column = "id")]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_languageId", ForColumns = "languageId,UniqueId")]
|
||||
public int LanguageId { get; set; }
|
||||
|
||||
[Column("UniqueId")]
|
||||
[ForeignKey(typeof(DictionaryDto), Column = "id")]
|
||||
public Guid UniqueId { get; set; }
|
||||
|
||||
// TODO: Need a unique constraint on LanguageId, UniqueId, Value
|
||||
[Column("value")]
|
||||
[Length(1000)]
|
||||
public string Value { get; set; }
|
||||
|
||||
@@ -906,7 +906,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
return -1;
|
||||
}
|
||||
|
||||
var p = new Process
|
||||
using (var p = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
@@ -918,13 +918,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
}
|
||||
};
|
||||
p.Start();
|
||||
output = p.StandardOutput.ReadToEnd();
|
||||
error = p.StandardError.ReadToEnd();
|
||||
p.WaitForExit();
|
||||
})
|
||||
{
|
||||
p.Start();
|
||||
output = p.StandardOutput.ReadToEnd();
|
||||
error = p.StandardError.ReadToEnd();
|
||||
p.WaitForExit();
|
||||
|
||||
return p.ExitCode;
|
||||
return p.ExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
@@ -63,13 +63,11 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
: Crops.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
}
|
||||
|
||||
public ImageUrlGenerationOptions GetCropBaseOptions(string url, ImageCropperCrop crop, bool defaultCrop, bool preferFocalPoint)
|
||||
public ImageUrlGenerationOptions GetCropBaseOptions(string url, ImageCropperCrop crop, bool preferFocalPoint)
|
||||
{
|
||||
if (preferFocalPoint && HasFocalPoint()
|
||||
|| crop != null && crop.Coordinates == null && HasFocalPoint()
|
||||
|| defaultCrop && HasFocalPoint())
|
||||
if ((preferFocalPoint && HasFocalPoint()) || (crop != null && crop.Coordinates == null && HasFocalPoint()))
|
||||
{
|
||||
return new ImageUrlGenerationOptions(url) { FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(FocalPoint.Top, FocalPoint.Left) };
|
||||
return new ImageUrlGenerationOptions(url) { FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(FocalPoint.Left, FocalPoint.Top) };
|
||||
}
|
||||
else if (crop != null && crop.Coordinates != null && preferFocalPoint == false)
|
||||
{
|
||||
@@ -77,7 +75,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ImageUrlGenerationOptions(url) { DefaultCrop = true };
|
||||
return new ImageUrlGenerationOptions(url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +90,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
if (crop == null && !string.IsNullOrWhiteSpace(alias))
|
||||
return null;
|
||||
|
||||
var options = GetCropBaseOptions(string.Empty, crop, string.IsNullOrWhiteSpace(alias), useFocalPoint);
|
||||
var options = GetCropBaseOptions(null, crop, useFocalPoint || string.IsNullOrWhiteSpace(alias));
|
||||
|
||||
if (crop != null && useCropDimensions)
|
||||
{
|
||||
@@ -108,9 +106,9 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
/// <summary>
|
||||
/// Gets the value image URL for a specific width and height.
|
||||
/// </summary>
|
||||
public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, bool useFocalPoint = false, string cacheBusterValue = null)
|
||||
public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, string cacheBusterValue = null)
|
||||
{
|
||||
var options = GetCropBaseOptions(string.Empty, null, true, useFocalPoint);
|
||||
var options = GetCropBaseOptions(null, null, false);
|
||||
|
||||
options.Width = width;
|
||||
options.Height = height;
|
||||
|
||||
@@ -72,15 +72,17 @@ namespace Umbraco.Cms.Infrastructure.Sync
|
||||
GlobalSettings = globalSettings.Value;
|
||||
_lastPruned = _lastSync = DateTime.UtcNow;
|
||||
_syncIdle = new ManualResetEvent(true);
|
||||
|
||||
// See notes on _localIdentity
|
||||
LocalIdentity = Environment.MachineName // eg DOMAIN\SERVER
|
||||
+ "/" + hostingEnvironment.ApplicationId // eg /LM/S3SVC/11/ROOT
|
||||
+ " [P" + Process.GetCurrentProcess().Id // eg 1234
|
||||
+ "/D" + AppDomain.CurrentDomain.Id // eg 22
|
||||
+ "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique
|
||||
|
||||
using (var process = Process.GetCurrentProcess())
|
||||
{
|
||||
// See notes on _localIdentity
|
||||
LocalIdentity = Environment.MachineName // eg DOMAIN\SERVER
|
||||
+ "/" + hostingEnvironment.ApplicationId // eg /LM/S3SVC/11/ROOT
|
||||
+ " [P" + process.Id // eg 1234
|
||||
+ "/D" + AppDomain.CurrentDomain.Id // eg 22
|
||||
+ "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique
|
||||
}
|
||||
_initialized = new Lazy<SyncBootState?>(InitializeWithMainDom);
|
||||
|
||||
}
|
||||
|
||||
public GlobalSettings GlobalSettings { get; }
|
||||
|
||||
@@ -524,7 +524,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
parent = GetParentLink(kit.Node, null);
|
||||
if (parent == null)
|
||||
{
|
||||
_logger.LogWarning($"Skip item id={kit.Node.Id}, could not find parent id={kit.Node.ParentContentId}.");
|
||||
_logger.LogWarning("Skip item id={kit.Node.Id}, could not find parent id={kit.Node.ParentContentId}.", kit.Node.Id, kit.Node.ParentContentId);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -533,21 +533,21 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// because the data sort operation is by path.
|
||||
if (parent.Value == null)
|
||||
{
|
||||
_logger.LogWarning($"Skip item id={kit.Node.Id}, no Data assigned for linked node with path {kit.Node.Path} and parent id {kit.Node.ParentContentId}. This can indicate data corruption for the Path value for node {kit.Node.Id}. See the Health Check dashboard in Settings to resolve data integrity issues.");
|
||||
_logger.LogWarning("Skip item id={kit.Node.Id}, no Data assigned for linked node with path {kit.Node.Path} and parent id {kit.Node.ParentContentId}. This can indicate data corruption for the Path value for node {kit.Node.Id}. See the Health Check dashboard in Settings to resolve data integrity issues.", kit.Node.Id, kit.Node.ParentContentId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the kit is valid
|
||||
if (kit.DraftData == null && kit.PublishedData == null)
|
||||
{
|
||||
_logger.LogWarning($"Skip item id={kit.Node.Id}, both draft and published data are null.");
|
||||
_logger.LogWarning("Skip item id={kit.Node.Id}, both draft and published data are null.", kit.Node.Id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// unknown = bad
|
||||
if (_contentTypesById.TryGetValue(kit.ContentTypeId, out var link) == false || link.Value == null)
|
||||
{
|
||||
_logger.LogWarning($"Skip item id={kit.Node.Id}, could not find content type id={kit.ContentTypeId}.");
|
||||
_logger.LogWarning("Skip item id={kit.Node.Id}, could not find content type id={kit.ContentTypeId}.", kit.Node.Id, kit.ContentTypeId);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -723,7 +723,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
previousNode = null; // there is no previous sibling
|
||||
}
|
||||
|
||||
_logger.LogDebug($"Set {thisNode.Id} with parent {thisNode.ParentContentId}");
|
||||
_logger.LogDebug("Set {thisNode.Id} with parent {thisNode.ParentContentId}", thisNode.Id, thisNode.ParentContentId);
|
||||
SetValueLocked(_contentNodes, thisNode.Id, thisNode);
|
||||
|
||||
// if we are initializing from the database source ensure the local db is updated
|
||||
@@ -780,7 +780,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
ok = false;
|
||||
continue; // skip that one
|
||||
}
|
||||
_logger.LogDebug($"Set {kit.Node.Id} with parent {kit.Node.ParentContentId}");
|
||||
_logger.LogDebug("Set {kit.Node.Id} with parent {kit.Node.ParentContentId}", kit.Node.Id, kit.Node.ParentContentId);
|
||||
SetValueLocked(_contentNodes, kit.Node.Id, kit.Node);
|
||||
|
||||
if (_localDb != null) RegisterChange(kit.Node.Id, kit);
|
||||
|
||||
@@ -463,7 +463,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// Update: We will still return false here even though the above mentioned race condition has been fixed since we now
|
||||
// lock the entire operation of creating/populating the cache file with the same lock as releasing/closing the cache file
|
||||
|
||||
_logger.LogInformation($"Tried to load {entityType} from the local cache file but it was empty.");
|
||||
_logger.LogInformation("Tried to load {entityType} from the local cache file but it was empty.", entityType);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace Umbraco.Cms.Tests.Integration.Implementations
|
||||
{
|
||||
var globalSettings = new GlobalSettings();
|
||||
IOptionsMonitor<GlobalSettings> mockedOptionsMonitorOfGlobalSettings = Mock.Of<IOptionsMonitor<GlobalSettings>>(x => x.CurrentValue == globalSettings);
|
||||
_backOfficeInfo = new AspNetCoreBackOfficeInfo(mockedOptionsMonitorOfGlobalSettings);
|
||||
_backOfficeInfo = new AspNetCoreBackOfficeInfo(mockedOptionsMonitorOfGlobalSettings, GetHostingEnvironment());
|
||||
}
|
||||
|
||||
return _backOfficeInfo;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -766,7 +767,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Packaging
|
||||
new DictionaryItem("Parent")
|
||||
{
|
||||
// This matches what is in the package.xml file
|
||||
Key = new System.Guid("28f2e02a-8c66-4fcd-85e3-8524d551c0d3"),
|
||||
Key = new Guid("28f2e02a-8c66-4fcd-85e3-8524d551c0d3"),
|
||||
Translations = new List<IDictionaryTranslation>
|
||||
{
|
||||
new DictionaryTranslation(englishLanguage, expectedEnglishParentValue),
|
||||
@@ -782,6 +783,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Packaging
|
||||
LocalizationService.Save(
|
||||
new DictionaryItem("Parent")
|
||||
{
|
||||
// This matches what is in the package.xml file
|
||||
Key = new Guid("28f2e02a-8c66-4fcd-85e3-8524d551c0d3"),
|
||||
Translations = new List<IDictionaryTranslation>
|
||||
{
|
||||
new DictionaryTranslation(englishLanguage, expectedEnglishParentValue),
|
||||
|
||||
@@ -12,40 +12,40 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
{
|
||||
private const string MediaPath = "/media/1005/img_0671.jpg";
|
||||
private static readonly ImageUrlGenerationOptions.CropCoordinates s_crop = new ImageUrlGenerationOptions.CropCoordinates(0.58729977382575338m, 0.055768992440203169m, 0m, 0.32457553600198386m);
|
||||
private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus1 = new ImageUrlGenerationOptions.FocalPointPosition(0.80827067669172936m, 0.96m);
|
||||
private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus2 = new ImageUrlGenerationOptions.FocalPointPosition(0.41m, 0.4275m);
|
||||
private static readonly ImageSharpImageUrlGenerator s_generator = new ImageSharpImageUrlGenerator();
|
||||
private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus1 = new ImageUrlGenerationOptions.FocalPointPosition(0.96m, 0.80827067669172936m);
|
||||
private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus2 = new ImageUrlGenerationOptions.FocalPointPosition(0.4275m, 0.41m);
|
||||
private static readonly ImageSharpImageUrlGenerator s_generator = new ImageSharpImageUrlGenerator(new string[0]);
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_CropAliasTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, Height = 100 });
|
||||
Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
|
||||
Assert.AreEqual(MediaPath + "?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_WidthHeightTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 200, Height = 300 });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300", urlString);
|
||||
Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=200&height=300", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_FocalPointTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 100, Height = 100 });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=100&height=100", urlString);
|
||||
Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=100&height=100", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrlFurtherOptionsTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 200, Height = 300, FurtherOptions = "&filter=comic&roundedcorners=radius-26|bgcolor-fff" });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
|
||||
Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that if a crop alias has been specified that doesn't exist the method returns null
|
||||
/// Test that if options is null, the generated image URL is also null.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrlNullTest()
|
||||
@@ -55,13 +55,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that if a crop alias has been specified that doesn't exist the method returns null
|
||||
/// Test that if the image URL is null, the generated image URL is empty.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrlEmptyTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null));
|
||||
Assert.AreEqual("?mode=crop", urlString);
|
||||
Assert.AreEqual(string.Empty, urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,37 +71,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
public void GetBaseCropUrlFromModelTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null) { Crop = s_crop, Width = 100, Height = 100 });
|
||||
Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test the height ratio mode with predefined crop dimensions
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_CropAliasHeightRatioModeTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, HeightRatio = 1 });
|
||||
Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&heightratio=1&width=100", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test the height ratio mode with manual width/height dimensions
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_WidthHeightRatioModeTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 300, HeightRatio = 0.5m });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&heightratio=0.5&width=300", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test the height ratio mode with width/height dimensions
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_HeightWidthRatioModeTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Height = 150, WidthRatio = 2 });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&widthratio=2&height=150", urlString);
|
||||
Assert.AreEqual("?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -116,11 +86,11 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
var urlStringMax = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Max, Width = 300, Height = 150 });
|
||||
var urlStringStretch = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Stretch, Width = 300, Height = 150 });
|
||||
|
||||
Assert.AreEqual(MediaPath + "?mode=min&width=300&height=150", urlStringMin);
|
||||
Assert.AreEqual(MediaPath + "?mode=boxpad&width=300&height=150", urlStringBoxPad);
|
||||
Assert.AreEqual(MediaPath + "?mode=pad&width=300&height=150", urlStringPad);
|
||||
Assert.AreEqual(MediaPath + "?mode=max&width=300&height=150", urlStringMax);
|
||||
Assert.AreEqual(MediaPath + "?mode=stretch&width=300&height=150", urlStringStretch);
|
||||
Assert.AreEqual(MediaPath + "?rmode=min&width=300&height=150", urlStringMin);
|
||||
Assert.AreEqual(MediaPath + "?rmode=boxpad&width=300&height=150", urlStringBoxPad);
|
||||
Assert.AreEqual(MediaPath + "?rmode=pad&width=300&height=150", urlStringPad);
|
||||
Assert.AreEqual(MediaPath + "?rmode=max&width=300&height=150", urlStringMax);
|
||||
Assert.AreEqual(MediaPath + "?rmode=stretch&width=300&height=150", urlStringStretch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -130,7 +100,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
public void GetCropUrl_UploadTypeTest()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Crop, ImageCropAnchor = ImageCropAnchor.Center, Width = 100, Height = 270 });
|
||||
Assert.AreEqual(MediaPath + "?mode=crop&anchor=center&width=100&height=270", urlString);
|
||||
Assert.AreEqual(MediaPath + "?rmode=crop&ranchor=center&width=100&height=270", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -139,28 +109,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
[Test]
|
||||
public void GetCropUrl_PreferFocalPointCenter()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 300, Height = 150 });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=300&height=150", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check if height ratio is returned for a predefined crop without coordinates and focal point in centre when a width parameter is passed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidth()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200, HeightRatio = 0.5962962962962962962962962963m });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check if height ratio is returned for a predefined crop without coordinates and focal point is custom when a width parameter is passed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPoint()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus2, Width = 200, HeightRatio = 0.5962962962962962962962962963m });
|
||||
Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString);
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Width = 300, Height = 150 });
|
||||
Assert.AreEqual(MediaPath + "?width=300&height=150", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -170,17 +120,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPointIgnore()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus2, Width = 270, Height = 161 });
|
||||
Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&width=270&height=161", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check if width ratio is returned for a predefined crop without coordinates and focal point in centre when a height parameter is passed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_PreDefinedCropNoCoordinatesWithHeight()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200, WidthRatio = 1.6770186335403726708074534161m });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&widthratio=1.6770186335403726708074534161&height=200", urlString);
|
||||
Assert.AreEqual(MediaPath + "?rxy=0.4275,0.41&width=270&height=161", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -189,8 +129,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
[Test]
|
||||
public void GetCropUrl_WidthOnlyParameter()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200 });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=200", urlString);
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Width = 200 });
|
||||
Assert.AreEqual(MediaPath + "?width=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -199,8 +139,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
[Test]
|
||||
public void GetCropUrl_HeightOnlyParameter()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200 });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&height=200", urlString);
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Height = 200 });
|
||||
Assert.AreEqual(MediaPath + "?height=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -210,7 +150,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media
|
||||
public void GetCropUrl_BackgroundColorParameter()
|
||||
{
|
||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Pad, Width = 400, Height = 400, FurtherOptions = "&bgcolor=fff" });
|
||||
Assert.AreEqual(MediaPath + "?mode=pad&width=400&height=400&bgcolor=fff", urlString);
|
||||
Assert.AreEqual(MediaPath + "?rmode=pad&width=400&height=400&bgcolor=fff", urlString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Extensions
|
||||
{
|
||||
[TestFixture]
|
||||
public class ImageCropperTemplateCoreExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void GetCropUrl_WithCropSpecifiedButNotFound_ReturnsNull()
|
||||
{
|
||||
var imageUrl = "/test.jpg";
|
||||
Mock<IImageUrlGenerator> imageUrlGenerator = CreateMockImageUrlGenerator();
|
||||
var result = imageUrl.GetCropUrl(
|
||||
imageUrlGenerator.Object,
|
||||
new ImageCropperValue { },
|
||||
imageCropMode: ImageCropMode.Crop,
|
||||
cropAlias: "Missing");
|
||||
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_WithCropSpecifiedAndUsingCropDimensions_CallsImageGeneratorWithCorrectParameters()
|
||||
{
|
||||
var imageUrl = "/test.jpg";
|
||||
Mock<IImageUrlGenerator> imageUrlGenerator = CreateMockImageUrlGenerator();
|
||||
var result = imageUrl.GetCropUrl(
|
||||
imageUrlGenerator.Object,
|
||||
CreateImageCropperValueWithCrops(),
|
||||
imageCropMode: ImageCropMode.Crop,
|
||||
cropAlias: "TestCrop",
|
||||
useCropDimensions: true);
|
||||
|
||||
imageUrlGenerator
|
||||
.Verify(x => x.GetImageUrl(
|
||||
It.Is<ImageUrlGenerationOptions>(y => y.Width == 100 &&
|
||||
y.Height == 200)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_WithCropSpecifiedAndWidthAndHeightProvided_CallsImageGeneratorWithCorrectParameters()
|
||||
{
|
||||
var imageUrl = "/test.jpg";
|
||||
Mock<IImageUrlGenerator> imageUrlGenerator = CreateMockImageUrlGenerator();
|
||||
var result = imageUrl.GetCropUrl(
|
||||
imageUrlGenerator.Object,
|
||||
CreateImageCropperValueWithCrops(),
|
||||
imageCropMode: ImageCropMode.Crop,
|
||||
cropAlias: "TestCrop",
|
||||
width: 50,
|
||||
height: 80);
|
||||
|
||||
imageUrlGenerator
|
||||
.Verify(x => x.GetImageUrl(
|
||||
It.Is<ImageUrlGenerationOptions>(y => y.Width == 50 &&
|
||||
y.Height == 80)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_WithCropSpecifiedAndWidthOnlyProvided_CallsImageGeneratorWithCorrectParameters()
|
||||
{
|
||||
var imageUrl = "/test.jpg";
|
||||
Mock<IImageUrlGenerator> imageUrlGenerator = CreateMockImageUrlGenerator();
|
||||
var result = imageUrl.GetCropUrl(
|
||||
imageUrlGenerator.Object,
|
||||
CreateImageCropperValueWithCrops(),
|
||||
imageCropMode: ImageCropMode.Crop,
|
||||
cropAlias: "TestCrop",
|
||||
width: 50);
|
||||
|
||||
imageUrlGenerator
|
||||
.Verify(x => x.GetImageUrl(
|
||||
It.Is<ImageUrlGenerationOptions>(y => y.Width == 50 &&
|
||||
y.Height == 100)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_WithCropSpecifiedAndHeightOnlyProvided_CallsImageGeneratorWithCorrectParameters()
|
||||
{
|
||||
var imageUrl = "/test.jpg";
|
||||
Mock<IImageUrlGenerator> imageUrlGenerator = CreateMockImageUrlGenerator();
|
||||
var result = imageUrl.GetCropUrl(
|
||||
imageUrlGenerator.Object,
|
||||
CreateImageCropperValueWithCrops(),
|
||||
imageCropMode: ImageCropMode.Crop,
|
||||
cropAlias: "TestCrop",
|
||||
height: 50);
|
||||
|
||||
imageUrlGenerator
|
||||
.Verify(x => x.GetImageUrl(
|
||||
It.Is<ImageUrlGenerationOptions>(y => y.Width == 25 &&
|
||||
y.Height == 50)));
|
||||
}
|
||||
|
||||
private static Mock<IImageUrlGenerator> CreateMockImageUrlGenerator() => new Mock<IImageUrlGenerator>();
|
||||
|
||||
private static ImageCropperValue CreateImageCropperValueWithCrops() => new ImageCropperValue
|
||||
{
|
||||
Crops = new List<ImageCropperValue.ImageCropperCrop>
|
||||
{
|
||||
new ImageCropperValue.ImageCropperCrop { Alias = "TestCrop", Width = 100, Height = 200 },
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -130,21 +132,21 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
public void GetCropUrl_WidthHeightTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300", urlString);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=200&h=300", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_FocalPointTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "thumb", preferFocalPoint: true, useCropDimensions: true);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=100&h=100", urlString);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=100&h=100", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrlFurtherOptionsTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "&filter=comic&roundedcorners=radius-26|bgcolor-fff");
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "filter=comic&roundedcorners=radius-26|bgcolor-fff");
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=200&h=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -174,8 +176,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
[Test]
|
||||
public void GetCropUrl_CropAliasHeightRatioModeTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, ratioMode: ImageCropRatioMode.Height);
|
||||
Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&hr=1&w=100", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true);
|
||||
Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -184,8 +186,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
[Test]
|
||||
public void GetCropUrl_WidthHeightRatioModeTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Height);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&hr=0.5&w=300", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=300&h=150", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -194,8 +196,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
[Test]
|
||||
public void GetCropUrl_HeightWidthRatioModeTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Width);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&wr=2&h=150", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=300&h=150", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -236,7 +238,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\":\"thumb\",\"width\": 100,\"height\": 100,\"coordinates\": {\"x1\": 0.58729977382575338,\"y1\": 0.055768992440203169,\"x2\": 0,\"y2\": 0.32457553600198386}}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 300, height: 150, preferFocalPoint: true);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&w=300&h=150", urlString);
|
||||
Assert.AreEqual(MediaPath + "?w=300&h=150", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -248,7 +250,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&hr=0.5962962962962962962962962963&w=200", urlString);
|
||||
Assert.AreEqual(MediaPath + "?w=200&h=119", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -260,7 +262,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200);
|
||||
Assert.AreEqual(MediaPath + "?f=0.41x0.4275&hr=0.5962962962962962962962962963&w=200", urlString);
|
||||
Assert.AreEqual(MediaPath + "?f=0.41,0.4275&w=200&h=119", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -272,7 +274,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200, useCropDimensions: true);
|
||||
Assert.AreEqual(MediaPath + "?f=0.41x0.4275&w=270&h=161", urlString);
|
||||
Assert.AreEqual(MediaPath + "?f=0.41,0.4275&w=270&h=161", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -284,7 +286,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", height: 200);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&wr=1.6770186335403726708074534161&h=200", urlString);
|
||||
Assert.AreEqual(MediaPath + "?w=335&h=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -296,7 +298,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 200);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&w=200", urlString);
|
||||
Assert.AreEqual(MediaPath + "?w=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -308,7 +310,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, height: 200);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&h=200", urlString);
|
||||
Assert.AreEqual(MediaPath + "?h=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -319,98 +321,88 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common
|
||||
{
|
||||
var cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"" + MediaPath + "\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), 400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "&bgcolor=fff");
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), 400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "bgcolor=fff");
|
||||
Assert.AreEqual(MediaPath + "?m=pad&w=400&h=400&bgcolor=fff", urlString);
|
||||
}
|
||||
|
||||
internal class TestImageUrlGenerator : IImageUrlGenerator
|
||||
{
|
||||
public IEnumerable<string> SupportedImageFileTypes => new[] { "jpeg", "jpg", "gif", "bmp", "png", "tiff", "tif" };
|
||||
public IEnumerable<string> SupportedImageFileTypes => new[]
|
||||
{
|
||||
"jpeg",
|
||||
"jpg",
|
||||
"gif",
|
||||
"bmp",
|
||||
"png",
|
||||
"tiff",
|
||||
"tif"
|
||||
};
|
||||
|
||||
public string GetImageUrl(ImageUrlGenerationOptions options)
|
||||
{
|
||||
var imageProcessorUrl = new StringBuilder(options.ImageUrl ?? string.Empty);
|
||||
if (options == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var imageUrl = new StringBuilder(options.ImageUrl);
|
||||
|
||||
bool queryStringHasStarted = false;
|
||||
void AppendQueryString(string value)
|
||||
{
|
||||
imageUrl.Append(queryStringHasStarted ? '&' : '?');
|
||||
queryStringHasStarted = true;
|
||||
|
||||
imageUrl.Append(value);
|
||||
}
|
||||
void AddQueryString(string key, params IConvertible[] values)
|
||||
=> AppendQueryString(key + '=' + string.Join(",", values.Select(x => x.ToString(CultureInfo.InvariantCulture))));
|
||||
|
||||
if (options.FocalPoint != null)
|
||||
{
|
||||
imageProcessorUrl.Append("?f=");
|
||||
imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture));
|
||||
imageProcessorUrl.Append("x");
|
||||
imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture));
|
||||
AddQueryString("f", options.FocalPoint.Top, options.FocalPoint.Left);
|
||||
}
|
||||
else if (options.Crop != null)
|
||||
{
|
||||
imageProcessorUrl.Append("?c=");
|
||||
imageProcessorUrl.Append(options.Crop.X1.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
else if (options.DefaultCrop)
|
||||
{
|
||||
imageProcessorUrl.Append("?m=defaultcrop");
|
||||
}
|
||||
else
|
||||
{
|
||||
imageProcessorUrl.Append("?m=" + options.ImageCropMode.ToString().ToLower());
|
||||
if (options.ImageCropAnchor != null)
|
||||
{
|
||||
imageProcessorUrl.Append("&a=" + options.ImageCropAnchor.ToString().ToLower());
|
||||
}
|
||||
AddQueryString("c", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom);
|
||||
}
|
||||
|
||||
var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&f=");
|
||||
if (options.Quality != null && hasFormat == false)
|
||||
if (options.ImageCropMode.HasValue)
|
||||
{
|
||||
imageProcessorUrl.Append("&q=" + options.Quality);
|
||||
AddQueryString("m", options.ImageCropMode.Value.ToString().ToLowerInvariant());
|
||||
}
|
||||
|
||||
if (options.HeightRatio != null)
|
||||
if (options.ImageCropAnchor.HasValue)
|
||||
{
|
||||
imageProcessorUrl.Append("&hr=" + options.HeightRatio.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
if (options.WidthRatio != null)
|
||||
{
|
||||
imageProcessorUrl.Append("&wr=" + options.WidthRatio.Value.ToString(CultureInfo.InvariantCulture));
|
||||
AddQueryString("a", options.ImageCropAnchor.Value.ToString().ToLowerInvariant());
|
||||
}
|
||||
|
||||
if (options.Width != null)
|
||||
{
|
||||
imageProcessorUrl.Append("&w=" + options.Width);
|
||||
AddQueryString("w", options.Width.Value);
|
||||
}
|
||||
|
||||
if (options.Height != null)
|
||||
{
|
||||
imageProcessorUrl.Append("&h=" + options.Height);
|
||||
AddQueryString("h", options.Height.Value);
|
||||
}
|
||||
|
||||
if (options.UpScale == false)
|
||||
if (options.Quality.HasValue)
|
||||
{
|
||||
imageProcessorUrl.Append("&u=no");
|
||||
}
|
||||
|
||||
if (options.AnimationProcessMode != null)
|
||||
{
|
||||
imageProcessorUrl.Append("&apm=" + options.AnimationProcessMode);
|
||||
AddQueryString("q", options.Quality.Value);
|
||||
}
|
||||
|
||||
if (options.FurtherOptions != null)
|
||||
{
|
||||
imageProcessorUrl.Append(options.FurtherOptions);
|
||||
}
|
||||
|
||||
if (options.Quality != null && hasFormat)
|
||||
{
|
||||
imageProcessorUrl.Append("&q=" + options.Quality);
|
||||
AppendQueryString(options.FurtherOptions.TrimStart('?', '&'));
|
||||
}
|
||||
|
||||
if (options.CacheBusterValue != null)
|
||||
{
|
||||
imageProcessorUrl.Append("&r=").Append(options.CacheBusterValue);
|
||||
AddQueryString("r", options.CacheBusterValue);
|
||||
}
|
||||
|
||||
return imageProcessorUrl.ToString();
|
||||
return imageUrl.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Web;
|
||||
using SixLabors.ImageSharp.Web.Commands;
|
||||
using SixLabors.ImageSharp.Web.Commands.Converters;
|
||||
using Umbraco.Cms.Web.Common.ImageProcessors;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.ImageProcessors
|
||||
{
|
||||
[TestFixture]
|
||||
public class CropWebProcessorTests
|
||||
{
|
||||
[Test]
|
||||
public void CropWebProcessor_CropsImage()
|
||||
{
|
||||
var converters = new List<ICommandConverter>
|
||||
{
|
||||
CreateArrayConverterOfFloat(),
|
||||
CreateSimpleCommandConverterOfFloat(),
|
||||
};
|
||||
|
||||
var parser = new CommandParser(converters);
|
||||
CultureInfo culture = CultureInfo.InvariantCulture;
|
||||
|
||||
var commands = new Dictionary<string, string>
|
||||
{
|
||||
{ CropWebProcessor.Coordinates, "0.1,0.2,0.1,0.4" }, // left, top, right, bottom
|
||||
};
|
||||
|
||||
using var image = new Image<Rgba32>(50, 80);
|
||||
using FormattedImage formatted = CreateFormattedImage(image, PngFormat.Instance);
|
||||
new CropWebProcessor().Process(formatted, null, commands, parser, culture);
|
||||
|
||||
Assert.AreEqual(40, image.Width); // Cropped 5 pixels from each side.
|
||||
Assert.AreEqual(32, image.Height); // Cropped 16 pixels from the top and 32 from the bottom.
|
||||
}
|
||||
|
||||
private static ICommandConverter CreateArrayConverterOfFloat()
|
||||
{
|
||||
// ImageSharp.Web's ArrayConverter is internal, so we need to use reflection to instantiate.
|
||||
var type = Type.GetType("SixLabors.ImageSharp.Web.Commands.Converters.ArrayConverter`1, SixLabors.ImageSharp.Web");
|
||||
Type[] typeArgs = { typeof(float) };
|
||||
Type genericType = type.MakeGenericType(typeArgs);
|
||||
return (ICommandConverter)Activator.CreateInstance(genericType);
|
||||
}
|
||||
|
||||
private static ICommandConverter CreateSimpleCommandConverterOfFloat()
|
||||
{
|
||||
// ImageSharp.Web's SimpleCommandConverter is internal, so we need to use reflection to instantiate.
|
||||
var type = Type.GetType("SixLabors.ImageSharp.Web.Commands.Converters.SimpleCommandConverter`1, SixLabors.ImageSharp.Web");
|
||||
Type[] typeArgs = { typeof(float) };
|
||||
Type genericType = type.MakeGenericType(typeArgs);
|
||||
return (ICommandConverter)Activator.CreateInstance(genericType);
|
||||
}
|
||||
|
||||
private FormattedImage CreateFormattedImage(Image<Rgba32> image, PngFormat format)
|
||||
{
|
||||
// Again, the constructor of FormattedImage useful for tests is internal, so we need to use reflection.
|
||||
Type type = typeof(FormattedImage);
|
||||
var instance = type.Assembly.CreateInstance(
|
||||
type.FullName,
|
||||
false,
|
||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||||
null,
|
||||
new object[] { image, format },
|
||||
null,
|
||||
null);
|
||||
return (FormattedImage)instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,7 +365,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
},
|
||||
{
|
||||
"imageUrlGeneratorApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<ImageUrlGeneratorController>(
|
||||
controller => controller.GetCropUrl(null, null, null, null, null))
|
||||
controller => controller.GetCropUrl(null, null, null, null))
|
||||
},
|
||||
{
|
||||
"elementTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<ElementTypeController>(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Umbraco.Cms.Core.Media;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Web.Common.Attributes;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
@@ -21,20 +21,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
{
|
||||
private readonly IImageUrlGenerator _imageUrlGenerator;
|
||||
|
||||
public ImageUrlGeneratorController(IImageUrlGenerator imageUrlGenerator)
|
||||
{
|
||||
_imageUrlGenerator = imageUrlGenerator;
|
||||
}
|
||||
public ImageUrlGeneratorController(IImageUrlGenerator imageUrlGenerator) => _imageUrlGenerator = imageUrlGenerator;
|
||||
|
||||
public string GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null, string animationProcessMode = null)
|
||||
public string GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null) => _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(mediaPath)
|
||||
{
|
||||
return _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(mediaPath)
|
||||
{
|
||||
Width = width,
|
||||
Height = height,
|
||||
ImageCropMode = imageCropMode,
|
||||
AnimationProcessMode = animationProcessMode
|
||||
});
|
||||
}
|
||||
Width = width,
|
||||
Height = height,
|
||||
ImageCropMode = imageCropMode
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
@@ -76,9 +76,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : null;
|
||||
var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(imagePath)
|
||||
{
|
||||
UpScale = false,
|
||||
Width = width,
|
||||
AnimationProcessMode = "first",
|
||||
ImageCropMode = ImageCropMode.Max,
|
||||
CacheBusterValue = rnd
|
||||
});
|
||||
@@ -94,9 +92,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
/// <param name="height"></param>
|
||||
/// <param name="focalPointLeft"></param>
|
||||
/// <param name="focalPointTop"></param>
|
||||
/// <param name="animationProcessMode"></param>
|
||||
/// <param name="mode"></param>
|
||||
/// <param name="upscale"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If there is no media, image property or image file is found then this will return not found.
|
||||
@@ -106,9 +102,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
int? height = null,
|
||||
decimal? focalPointLeft = null,
|
||||
decimal? focalPointTop = null,
|
||||
string animationProcessMode = "first",
|
||||
ImageCropMode mode = ImageCropMode.Max,
|
||||
bool upscale = false,
|
||||
string cacheBusterValue = "",
|
||||
decimal? cropX1 = null,
|
||||
decimal? cropX2 = null,
|
||||
@@ -116,45 +110,24 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
decimal? cropY2 = null
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
var options = new ImageUrlGenerationOptions(imagePath)
|
||||
{
|
||||
AnimationProcessMode = animationProcessMode,
|
||||
CacheBusterValue = cacheBusterValue,
|
||||
Width = width,
|
||||
Height = height,
|
||||
ImageCropMode = mode,
|
||||
UpScale = upscale,
|
||||
Width = width,
|
||||
Crop = (cropX1.HasValue && cropX2.HasValue && cropY1.HasValue && cropY2.HasValue) ? new ImageUrlGenerationOptions.CropCoordinates(cropX1.Value, cropY1.Value, cropX2.Value, cropY2.Value) : null,
|
||||
FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(focalPointTop.GetValueOrDefault(0.5m), focalPointLeft.GetValueOrDefault(0.5m)),
|
||||
CacheBusterValue = cacheBusterValue
|
||||
};
|
||||
|
||||
if (focalPointLeft.HasValue && focalPointTop.HasValue)
|
||||
{
|
||||
options.FocalPoint =
|
||||
new ImageUrlGenerationOptions.FocalPointPosition(focalPointTop.Value, focalPointLeft.Value);
|
||||
options.FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(focalPointLeft.Value, focalPointTop.Value);
|
||||
}
|
||||
else if (cropX1.HasValue && cropX2.HasValue && cropY1.HasValue && cropY2.HasValue)
|
||||
{
|
||||
options.Crop = new ImageUrlGenerationOptions.CropCoordinates(cropX1.Value, cropY1.Value, cropX2.Value, cropY2.Value);
|
||||
}
|
||||
|
||||
return _imageUrlGenerator.GetImageUrl(options);
|
||||
}
|
||||
|
||||
public class FocalPointPositionModel
|
||||
{
|
||||
public decimal Left { get; set; }
|
||||
public decimal Top { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bounds of the crop within the original image, in whatever units the registered
|
||||
/// IImageUrlGenerator uses, typically a percentage between 0 and 100.
|
||||
/// </summary>
|
||||
public class CropCoordinatesModel
|
||||
{
|
||||
|
||||
public decimal X1 { get; set; }
|
||||
public decimal Y1 { get; set; }
|
||||
public decimal X2 { get; set;}
|
||||
public decimal Y2 { get; set;}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,38 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using static Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.AspNetCore
|
||||
{
|
||||
public class AspNetCoreBackOfficeInfo : IBackOfficeInfo
|
||||
{
|
||||
public AspNetCoreBackOfficeInfo(IOptionsMonitor<GlobalSettings> globalSettings)
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private string _getAbsoluteUrl;
|
||||
public AspNetCoreBackOfficeInfo(IOptionsMonitor<GlobalSettings> globalSettings, IHostingEnvironment hostingEnviroment)
|
||||
{
|
||||
GetAbsoluteUrl = globalSettings.CurrentValue.UmbracoPath;
|
||||
_globalSettings = globalSettings;
|
||||
_hostingEnvironment = hostingEnviroment;
|
||||
|
||||
}
|
||||
|
||||
public string GetAbsoluteUrl { get; } // TODO make absolute
|
||||
|
||||
public string GetAbsoluteUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_getAbsoluteUrl is null)
|
||||
{
|
||||
if(_hostingEnvironment.ApplicationMainUrl is null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
_getAbsoluteUrl = WebPath.Combine(_hostingEnvironment.ApplicationMainUrl.ToString(), _globalSettings.CurrentValue.UmbracoPath.TrimStart(CharArrays.TildeForwardSlash));
|
||||
}
|
||||
return _getAbsoluteUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace Umbraco.Cms.Web.Common.AspNetCore
|
||||
throw new ArgumentException("The path appears to already be fully qualified. Please remove the call to MapPath");
|
||||
}
|
||||
|
||||
return Path.Combine(root, newPath.TrimStart('~', '/', '\\'));
|
||||
return Path.Combine(root, newPath.TrimStart(Core.Constants.CharArrays.TildeForwardSlashBackSlash));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Web.Middleware;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the ImageSharp middleware options to use the registered configuration.
|
||||
/// </summary>
|
||||
/// <seealso cref="Microsoft.Extensions.Options.IConfigureOptions<SixLabors.ImageSharp.Web.Middleware.ImageSharpMiddlewareOptions>" />
|
||||
public sealed class ImageSharpConfigurationOptions : IConfigureOptions<ImageSharpMiddlewareOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// The ImageSharp configuration.
|
||||
/// </summary>
|
||||
private readonly Configuration _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageSharpConfigurationOptions" /> class.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The ImageSharp configuration.</param>
|
||||
public ImageSharpConfigurationOptions(Configuration configuration) => _configuration = configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked to configure a <typeparamref name="TOptions" /> instance.
|
||||
/// </summary>
|
||||
/// <param name="options">The options instance to configure.</param>
|
||||
public void Configure(ImageSharpMiddlewareOptions options) => options.Configuration = _configuration;
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,16 @@ using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SixLabors.ImageSharp.Memory;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SixLabors.ImageSharp.Web.Caching;
|
||||
using SixLabors.ImageSharp.Web.Commands;
|
||||
using SixLabors.ImageSharp.Web.DependencyInjection;
|
||||
using SixLabors.ImageSharp.Web.Middleware;
|
||||
using SixLabors.ImageSharp.Web.Processors;
|
||||
using SixLabors.ImageSharp.Web.Providers;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Cms.Web.Common.ImageProcessors;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
@@ -20,56 +22,44 @@ namespace Umbraco.Extensions
|
||||
/// </summary>
|
||||
public static IServiceCollection AddUmbracoImageSharp(this IUmbracoBuilder builder)
|
||||
{
|
||||
IConfiguration configuration = builder.Config;
|
||||
IServiceCollection services = builder.Services;
|
||||
|
||||
ImagingSettings imagingSettings = configuration.GetSection(Cms.Core.Constants.Configuration.ConfigImaging)
|
||||
ImagingSettings imagingSettings = builder.Config.GetSection(Cms.Core.Constants.Configuration.ConfigImaging)
|
||||
.Get<ImagingSettings>() ?? new ImagingSettings();
|
||||
|
||||
services.AddImageSharp(options =>
|
||||
builder.Services.AddImageSharp(options =>
|
||||
{
|
||||
options.Configuration = SixLabors.ImageSharp.Configuration.Default;
|
||||
// The configuration is set using ImageSharpConfigurationOptions
|
||||
options.BrowserMaxAge = imagingSettings.Cache.BrowserMaxAge;
|
||||
options.CacheMaxAge = imagingSettings.Cache.CacheMaxAge;
|
||||
options.CachedNameLength = imagingSettings.Cache.CachedNameLength;
|
||||
|
||||
// Use configurable maximum width and height (overwrite ImageSharps default)
|
||||
options.OnParseCommandsAsync = context =>
|
||||
{
|
||||
RemoveIntParamenterIfValueGreatherThen(context.Commands, ResizeWebProcessor.Width, imagingSettings.Resize.MaxWidth);
|
||||
RemoveIntParamenterIfValueGreatherThen(context.Commands, ResizeWebProcessor.Height, imagingSettings.Resize.MaxHeight);
|
||||
if (context.Commands.Count == 0)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
uint width = context.Parser.ParseValue<uint>(context.Commands.GetValueOrDefault(ResizeWebProcessor.Width), context.Culture);
|
||||
uint height = context.Parser.ParseValue<uint>(context.Commands.GetValueOrDefault(ResizeWebProcessor.Height), context.Culture);
|
||||
if (width > imagingSettings.Resize.MaxWidth || height > imagingSettings.Resize.MaxHeight)
|
||||
{
|
||||
context.Commands.Remove(ResizeWebProcessor.Width);
|
||||
context.Commands.Remove(ResizeWebProcessor.Height);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
options.OnBeforeSaveAsync = _ => Task.CompletedTask;
|
||||
options.OnProcessedAsync = _ => Task.CompletedTask;
|
||||
options.OnPrepareResponseAsync = _ => Task.CompletedTask;
|
||||
})
|
||||
.SetRequestParser<QueryCollectionRequestParser>()
|
||||
.Configure<PhysicalFileSystemCacheOptions>(options =>
|
||||
{
|
||||
options.CacheFolder = imagingSettings.Cache.CacheFolder;
|
||||
})
|
||||
.SetCache<PhysicalFileSystemCache>()
|
||||
.SetCacheHash<CacheHash>()
|
||||
.AddProvider<PhysicalFileSystemProvider>()
|
||||
.AddProcessor<ResizeWebProcessor>()
|
||||
.AddProcessor<FormatWebProcessor>()
|
||||
.AddProcessor<BackgroundColorWebProcessor>();
|
||||
.Configure<PhysicalFileSystemCacheOptions>(options => options.CacheFolder = imagingSettings.Cache.CacheFolder)
|
||||
// We need to add CropWebProcessor before ResizeWebProcessor (until https://github.com/SixLabors/ImageSharp.Web/issues/182 is fixed)
|
||||
.RemoveProcessor<ResizeWebProcessor>()
|
||||
.AddProcessor<CropWebProcessor>()
|
||||
.AddProcessor<ResizeWebProcessor>();
|
||||
|
||||
return services;
|
||||
}
|
||||
builder.Services.AddTransient<IConfigureOptions<ImageSharpMiddlewareOptions>, ImageSharpConfigurationOptions>();
|
||||
|
||||
private static void RemoveIntParamenterIfValueGreatherThen(IDictionary<string, string> commands, string parameter, int maxValue)
|
||||
{
|
||||
if (commands.TryGetValue(parameter, out var command))
|
||||
{
|
||||
if (int.TryParse(command, out var i))
|
||||
{
|
||||
if (i > maxValue)
|
||||
{
|
||||
commands.Remove(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.Services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -11,25 +11,17 @@ namespace Umbraco.Extensions
|
||||
{
|
||||
public static class FriendlyImageCropperTemplateExtensions
|
||||
{
|
||||
private static IImageUrlGenerator ImageUrlGenerator { get; } =
|
||||
StaticServiceProvider.Instance.GetRequiredService<IImageUrlGenerator>();
|
||||
private static IImageUrlGenerator ImageUrlGenerator { get; } = StaticServiceProvider.Instance.GetRequiredService<IImageUrlGenerator>();
|
||||
|
||||
private static IPublishedValueFallback PublishedValueFallback { get; } =
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedValueFallback>();
|
||||
|
||||
private static IPublishedUrlProvider PublishedUrlProvider { get; } =
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedUrlProvider>();
|
||||
private static IPublishedValueFallback PublishedValueFallback { get; } = StaticServiceProvider.Instance.GetRequiredService<IPublishedValueFallback>();
|
||||
|
||||
private static IPublishedUrlProvider PublishedUrlProvider { get; } = StaticServiceProvider.Instance.GetRequiredService<IPublishedUrlProvider>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item
|
||||
/// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item.
|
||||
/// </summary>
|
||||
/// <param name="mediaItem">
|
||||
/// The IPublishedContent item.
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias e.g. thumbnail
|
||||
/// </param>
|
||||
/// <param name="mediaItem">The IPublishedContent item.</param>
|
||||
/// <param name="cropAlias">The crop alias e.g. thumbnail.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -57,17 +49,11 @@ namespace Umbraco.Extensions
|
||||
=> ImageCropperTemplateCoreExtensions.GetCropUrl(mediaItem, imageCropperValue, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item.
|
||||
/// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the IPublishedContent item.
|
||||
/// </summary>
|
||||
/// <param name="mediaItem">
|
||||
/// The IPublishedContent item.
|
||||
/// </param>
|
||||
/// <param name="propertyAlias">
|
||||
/// The property alias of the property containing the Json data e.g. umbracoFile
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias e.g. thumbnail
|
||||
/// </param>
|
||||
/// <param name="mediaItem">The IPublishedContent item.</param>
|
||||
/// <param name="propertyAlias">The property alias of the property containing the JSON data e.g. umbracoFile.</param>
|
||||
/// <param name="cropAlias">The crop alias e.g. thumbnail.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -83,53 +69,21 @@ namespace Umbraco.Extensions
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL from the IPublishedContent item.
|
||||
/// </summary>
|
||||
/// <param name="mediaItem">
|
||||
/// The IPublishedContent item.
|
||||
/// </param>
|
||||
/// <param name="width">
|
||||
/// The width of the output image.
|
||||
/// </param>
|
||||
/// <param name="height">
|
||||
/// The height of the output image.
|
||||
/// </param>
|
||||
/// <param name="propertyAlias">
|
||||
/// Property alias of the property containing the Json data.
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias.
|
||||
/// </param>
|
||||
/// <param name="quality">
|
||||
/// Quality percentage of the output image.
|
||||
/// </param>
|
||||
/// <param name="imageCropMode">
|
||||
/// The image crop mode.
|
||||
/// </param>
|
||||
/// <param name="imageCropAnchor">
|
||||
/// The image crop anchor.
|
||||
/// </param>
|
||||
/// <param name="preferFocalPoint">
|
||||
/// Use focal point, to generate an output image using the focal point instead of the predefined crop
|
||||
/// </param>
|
||||
/// <param name="useCropDimensions">
|
||||
/// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
|
||||
/// </param>
|
||||
/// <param name="cacheBuster">
|
||||
/// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
|
||||
/// </param>
|
||||
/// <param name="furtherOptions">
|
||||
/// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example>
|
||||
/// <![CDATA[
|
||||
/// furtherOptions: "&bgcolor=fff"
|
||||
/// ]]>
|
||||
/// </example>
|
||||
/// </param>
|
||||
/// <param name="ratioMode">
|
||||
/// Use a dimension as a ratio
|
||||
/// </param>
|
||||
/// <param name="upScale">
|
||||
/// If the image should be upscaled to requested dimensions
|
||||
/// </param>
|
||||
/// <param name="mediaItem">The IPublishedContent item.</param>
|
||||
/// <param name="width">The width of the output image.</param>
|
||||
/// <param name="height">The height of the output image.</param>
|
||||
/// <param name="propertyAlias">Property alias of the property containing the JSON data.</param>
|
||||
/// <param name="cropAlias">The crop alias.</param>
|
||||
/// <param name="quality">Quality percentage of the output image.</param>
|
||||
/// <param name="imageCropMode">The image crop mode.</param>
|
||||
/// <param name="imageCropAnchor">The image crop anchor.</param>
|
||||
/// <param name="preferFocalPoint">Use focal point, to generate an output image using the focal point instead of the predefined crop.</param>
|
||||
/// <param name="useCropDimensions">Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.</param>
|
||||
/// <param name="cacheBuster">Add a serialized date of the last edit of the item to ensure client cache refresh when updated.</param>
|
||||
/// <param name="furtherOptions">These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example><![CDATA[
|
||||
/// furtherOptions: "bgcolor=fff"
|
||||
/// ]]></example></param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -145,9 +99,7 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true)
|
||||
string furtherOptions = null)
|
||||
=> mediaItem.GetCropUrl(
|
||||
ImageUrlGenerator,
|
||||
PublishedValueFallback,
|
||||
@@ -162,63 +114,29 @@ namespace Umbraco.Extensions
|
||||
preferFocalPoint,
|
||||
useCropDimensions,
|
||||
cacheBuster,
|
||||
furtherOptions,
|
||||
ratioMode,
|
||||
upScale
|
||||
furtherOptions
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL from the image path.
|
||||
/// </summary>
|
||||
/// <param name="imageUrl">
|
||||
/// The image URL.
|
||||
/// </param>
|
||||
/// <param name="width">
|
||||
/// The width of the output image.
|
||||
/// </param>
|
||||
/// <param name="height">
|
||||
/// The height of the output image.
|
||||
/// </param>
|
||||
/// <param name="imageCropperValue">
|
||||
/// The Json data from the Umbraco Core Image Cropper property editor
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias.
|
||||
/// </param>
|
||||
/// <param name="quality">
|
||||
/// Quality percentage of the output image.
|
||||
/// </param>
|
||||
/// <param name="imageCropMode">
|
||||
/// The image crop mode.
|
||||
/// </param>
|
||||
/// <param name="imageCropAnchor">
|
||||
/// The image crop anchor.
|
||||
/// </param>
|
||||
/// <param name="preferFocalPoint">
|
||||
/// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one
|
||||
/// </param>
|
||||
/// <param name="useCropDimensions">
|
||||
/// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters
|
||||
/// </param>
|
||||
/// <param name="cacheBusterValue">
|
||||
/// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
|
||||
/// </param>
|
||||
/// <param name="furtherOptions">
|
||||
/// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example>
|
||||
/// <![CDATA[
|
||||
/// furtherOptions: "&bgcolor=fff"
|
||||
/// ]]>
|
||||
/// </example>
|
||||
/// </param>
|
||||
/// <param name="ratioMode">
|
||||
/// Use a dimension as a ratio
|
||||
/// </param>
|
||||
/// <param name="upScale">
|
||||
/// If the image should be upscaled to requested dimensions
|
||||
/// </param>
|
||||
/// <param name="imageUrl">The image URL.</param>
|
||||
/// <param name="width">The width of the output image.</param>
|
||||
/// <param name="height">The height of the output image.</param>
|
||||
/// <param name="imageCropperValue">The JSON data from the Umbraco Core Image Cropper property editor.</param>
|
||||
/// <param name="cropAlias">The crop alias.</param>
|
||||
/// <param name="quality">Quality percentage of the output image.</param>
|
||||
/// <param name="imageCropMode">The image crop mode.</param>
|
||||
/// <param name="imageCropAnchor">The image crop anchor.</param>
|
||||
/// <param name="preferFocalPoint">Use focal point to generate an output image using the focal point instead of the predefined crop if there is one.</param>
|
||||
/// <param name="useCropDimensions">Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.</param>
|
||||
/// <param name="cacheBusterValue">Add a serialized date of the last edit of the item to ensure client cache refresh when updated.</param>
|
||||
/// <param name="furtherOptions">These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example><![CDATA[
|
||||
/// furtherOptions: "bgcolor=fff"
|
||||
/// ]]></example></param>
|
||||
/// <returns>
|
||||
/// The the URL of the cropped image.
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
public static string GetCropUrl(
|
||||
this string imageUrl,
|
||||
@@ -232,9 +150,7 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
string cacheBusterValue = null,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true)
|
||||
string furtherOptions = null)
|
||||
=> imageUrl.GetCropUrl(
|
||||
ImageUrlGenerator,
|
||||
width,
|
||||
@@ -247,61 +163,29 @@ namespace Umbraco.Extensions
|
||||
preferFocalPoint,
|
||||
useCropDimensions,
|
||||
cacheBusterValue,
|
||||
furtherOptions,
|
||||
ratioMode,
|
||||
upScale
|
||||
);
|
||||
furtherOptions
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL from the image path.
|
||||
/// </summary>
|
||||
/// <param name="imageUrl">
|
||||
/// The image URL.
|
||||
/// </param>
|
||||
/// <param name="cropDataSet"></param>
|
||||
/// <param name="width">
|
||||
/// The width of the output image.
|
||||
/// </param>
|
||||
/// <param name="height">
|
||||
/// The height of the output image.
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias.
|
||||
/// </param>
|
||||
/// <param name="quality">
|
||||
/// Quality percentage of the output image.
|
||||
/// </param>
|
||||
/// <param name="imageCropMode">
|
||||
/// The image crop mode.
|
||||
/// </param>
|
||||
/// <param name="imageCropAnchor">
|
||||
/// The image crop anchor.
|
||||
/// </param>
|
||||
/// <param name="preferFocalPoint">
|
||||
/// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one
|
||||
/// </param>
|
||||
/// <param name="useCropDimensions">
|
||||
/// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters
|
||||
/// </param>
|
||||
/// <param name="cacheBusterValue">
|
||||
/// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
|
||||
/// </param>
|
||||
/// <param name="furtherOptions">
|
||||
/// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example>
|
||||
/// <![CDATA[
|
||||
/// furtherOptions: "&bgcolor=fff"
|
||||
/// ]]>
|
||||
/// </example>
|
||||
/// </param>
|
||||
/// <param name="ratioMode">
|
||||
/// Use a dimension as a ratio
|
||||
/// </param>
|
||||
/// <param name="upScale">
|
||||
/// If the image should be upscaled to requested dimensions
|
||||
/// </param>
|
||||
/// <param name="imageUrl">The image URL.</param>
|
||||
/// <param name="cropDataSet">The crop data set.</param>
|
||||
/// <param name="width">The width of the output image.</param>
|
||||
/// <param name="height">The height of the output image.</param>
|
||||
/// <param name="cropAlias">The crop alias.</param>
|
||||
/// <param name="quality">Quality percentage of the output image.</param>
|
||||
/// <param name="imageCropMode">The image crop mode.</param>
|
||||
/// <param name="imageCropAnchor">The image crop anchor.</param>
|
||||
/// <param name="preferFocalPoint">Use focal point to generate an output image using the focal point instead of the predefined crop if there is one.</param>
|
||||
/// <param name="useCropDimensions">Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.</param>
|
||||
/// <param name="cacheBusterValue">Add a serialized date of the last edit of the item to ensure client cache refresh when updated.</param>
|
||||
/// <param name="furtherOptions">These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example><![CDATA[
|
||||
/// furtherOptions: "bgcolor=fff"
|
||||
/// ]]></example></param>
|
||||
/// <returns>
|
||||
/// The the URL of the cropped image.
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
public static string GetCropUrl(
|
||||
this string imageUrl,
|
||||
@@ -315,10 +199,7 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
string cacheBusterValue = null,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true,
|
||||
string animationProcessMode = null)
|
||||
string furtherOptions = null)
|
||||
=> imageUrl.GetCropUrl(
|
||||
ImageUrlGenerator,
|
||||
cropDataSet,
|
||||
@@ -330,10 +211,7 @@ namespace Umbraco.Extensions
|
||||
preferFocalPoint,
|
||||
useCropDimensions,
|
||||
cacheBusterValue,
|
||||
furtherOptions,
|
||||
ratioMode,
|
||||
upScale,
|
||||
animationProcessMode
|
||||
furtherOptions
|
||||
);
|
||||
|
||||
|
||||
@@ -341,10 +219,6 @@ namespace Umbraco.Extensions
|
||||
public static string GetLocalCropUrl(
|
||||
this MediaWithCrops mediaWithCrops,
|
||||
string alias,
|
||||
string cacheBusterValue = null)
|
||||
{
|
||||
return mediaWithCrops.LocalCrops.Src +
|
||||
mediaWithCrops.LocalCrops.GetCropUrl(alias, ImageUrlGenerator, cacheBusterValue: cacheBusterValue);
|
||||
}
|
||||
string cacheBusterValue = null) => mediaWithCrops.LocalCrops.Src + mediaWithCrops.LocalCrops.GetCropUrl(alias, ImageUrlGenerator, cacheBusterValue: cacheBusterValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -13,17 +13,13 @@ namespace Umbraco.Extensions
|
||||
public static class ImageCropperTemplateCoreExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item
|
||||
/// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item.
|
||||
/// </summary>
|
||||
/// <param name="mediaItem">
|
||||
/// The IPublishedContent item.
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias e.g. thumbnail
|
||||
/// </param>
|
||||
/// <param name="imageUrlGenerator">The image url generator.</param>
|
||||
/// <param name="mediaItem">The IPublishedContent item.</param>
|
||||
/// <param name="cropAlias">The crop alias e.g. thumbnail.</param>
|
||||
/// <param name="imageUrlGenerator">The image URL generator.</param>
|
||||
/// <param name="publishedValueFallback">The published value fallback.</param>
|
||||
/// <param name="publishedUrlProvider">The published url provider.</param>
|
||||
/// <param name="publishedUrlProvider">The published URL provider.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -32,20 +28,14 @@ namespace Umbraco.Extensions
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
return mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
|
||||
}
|
||||
IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
|
||||
|
||||
public static string GetCropUrl(
|
||||
this MediaWithCrops mediaWithCrops,
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
return mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
|
||||
}
|
||||
IPublishedUrlProvider publishedUrlProvider) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the crop URL by using only the specified <paramref name="imageCropperValue" />.
|
||||
@@ -54,6 +44,8 @@ namespace Umbraco.Extensions
|
||||
/// <param name="imageCropperValue">The image cropper value.</param>
|
||||
/// <param name="cropAlias">The crop alias.</param>
|
||||
/// <param name="imageUrlGenerator">The image URL generator.</param>
|
||||
/// <param name="publishedValueFallback">The published value fallback.</param>
|
||||
/// <param name="publishedUrlProvider">The published URL provider.</param>
|
||||
/// <returns>
|
||||
/// The image crop URL.
|
||||
/// </returns>
|
||||
@@ -63,26 +55,17 @@ namespace Umbraco.Extensions
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
return mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, imageCropperValue, true, cropAlias: cropAlias, useCropDimensions: true);
|
||||
}
|
||||
IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, imageCropperValue, true, cropAlias: cropAlias, useCropDimensions: true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item.
|
||||
/// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the IPublishedContent item.
|
||||
/// </summary>
|
||||
/// <param name="mediaItem">
|
||||
/// The IPublishedContent item.
|
||||
/// </param>
|
||||
/// <param name="propertyAlias">
|
||||
/// The property alias of the property containing the Json data e.g. umbracoFile
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias e.g. thumbnail
|
||||
/// </param>
|
||||
/// <param name="imageUrlGenerator">The image url generator.</param>
|
||||
/// <param name="mediaItem">The IPublishedContent item.</param>
|
||||
/// <param name="propertyAlias">The property alias of the property containing the JSON data e.g. umbracoFile.</param>
|
||||
/// <param name="cropAlias">The crop alias e.g. thumbnail.</param>
|
||||
/// <param name="imageUrlGenerator">The image URL generator.</param>
|
||||
/// <param name="publishedValueFallback">The published value fallback.</param>
|
||||
/// <param name="publishedUrlProvider">The published url provider.</param>
|
||||
/// <param name="publishedUrlProvider">The published URL provider.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -92,74 +75,36 @@ namespace Umbraco.Extensions
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider)
|
||||
{
|
||||
return mediaItem.GetCropUrl( imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
|
||||
}
|
||||
IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
|
||||
|
||||
public static string GetCropUrl(this MediaWithCrops mediaWithCrops,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider,
|
||||
string propertyAlias,
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator)
|
||||
{
|
||||
return mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
|
||||
}
|
||||
IImageUrlGenerator imageUrlGenerator) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL from the IPublishedContent item.
|
||||
/// </summary>
|
||||
/// <param name="mediaItem">
|
||||
/// The IPublishedContent item.
|
||||
/// </param>
|
||||
/// <param name="imageUrlGenerator">The image url generator.</param>
|
||||
/// <param name="mediaItem">The IPublishedContent item.</param>
|
||||
/// <param name="imageUrlGenerator">The image URL generator.</param>
|
||||
/// <param name="publishedValueFallback">The published value fallback.</param>
|
||||
/// <param name="publishedUrlProvider">The published url provider.</param>
|
||||
/// <param name="width">
|
||||
/// The width of the output image.
|
||||
/// </param>
|
||||
/// <param name="height">
|
||||
/// The height of the output image.
|
||||
/// </param>
|
||||
/// <param name="propertyAlias">
|
||||
/// Property alias of the property containing the Json data.
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias.
|
||||
/// </param>
|
||||
/// <param name="quality">
|
||||
/// Quality percentage of the output image.
|
||||
/// </param>
|
||||
/// <param name="imageCropMode">
|
||||
/// The image crop mode.
|
||||
/// </param>
|
||||
/// <param name="imageCropAnchor">
|
||||
/// The image crop anchor.
|
||||
/// </param>
|
||||
/// <param name="preferFocalPoint">
|
||||
/// Use focal point, to generate an output image using the focal point instead of the predefined crop
|
||||
/// </param>
|
||||
/// <param name="useCropDimensions">
|
||||
/// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
|
||||
/// </param>
|
||||
/// <param name="cacheBuster">
|
||||
/// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
|
||||
/// </param>
|
||||
/// <param name="furtherOptions">
|
||||
/// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example:
|
||||
/// <example>
|
||||
/// <![CDATA[
|
||||
/// furtherOptions: "&bgcolor=fff"
|
||||
/// ]]>
|
||||
/// </example>
|
||||
/// </param>
|
||||
/// <param name="ratioMode">
|
||||
/// Use a dimension as a ratio
|
||||
/// </param>
|
||||
/// <param name="upScale">
|
||||
/// If the image should be upscaled to requested dimensions
|
||||
/// </param>
|
||||
/// <param name="publishedUrlProvider">The published URL provider.</param>
|
||||
/// <param name="width">The width of the output image.</param>
|
||||
/// <param name="height">The height of the output image.</param>
|
||||
/// <param name="propertyAlias">Property alias of the property containing the JSON data.</param>
|
||||
/// <param name="cropAlias">The crop alias.</param>
|
||||
/// <param name="quality">Quality percentage of the output image.</param>
|
||||
/// <param name="imageCropMode">The image crop mode.</param>
|
||||
/// <param name="imageCropAnchor">The image crop anchor.</param>
|
||||
/// <param name="preferFocalPoint">Use focal point, to generate an output image using the focal point instead of the predefined crop.</param>
|
||||
/// <param name="useCropDimensions">Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.</param>
|
||||
/// <param name="cacheBuster">Add a serialized date of the last edit of the item to ensure client cache refresh when updated.</param>
|
||||
/// <param name="furtherOptions">These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example:
|
||||
/// <example><![CDATA[
|
||||
/// furtherOptions: "bgcolor=fff"
|
||||
/// ]]></example></param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -178,12 +123,7 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true)
|
||||
{
|
||||
return mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, upScale);
|
||||
}
|
||||
string furtherOptions = null) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions);
|
||||
|
||||
public static string GetCropUrl(
|
||||
this MediaWithCrops mediaWithCrops,
|
||||
@@ -200,13 +140,14 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true)
|
||||
string furtherOptions = null)
|
||||
{
|
||||
if (mediaWithCrops == null) throw new ArgumentNullException(nameof(mediaWithCrops));
|
||||
if (mediaWithCrops == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mediaWithCrops));
|
||||
}
|
||||
|
||||
return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, upScale);
|
||||
return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions);
|
||||
}
|
||||
|
||||
private static string GetCropUrl(
|
||||
@@ -226,16 +167,17 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true)
|
||||
string furtherOptions = null)
|
||||
{
|
||||
if (mediaItem == null) throw new ArgumentNullException(nameof(mediaItem));
|
||||
|
||||
var cacheBusterValue = cacheBuster ? mediaItem.UpdateDate.ToFileTimeUtc().ToString(CultureInfo.InvariantCulture) : null;
|
||||
if (mediaItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mediaItem));
|
||||
}
|
||||
|
||||
if (mediaItem.HasProperty(propertyAlias) == false || mediaItem.HasValue(propertyAlias) == false)
|
||||
return string.Empty;
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var mediaItemUrl = mediaItem.MediaUrl(publishedUrlProvider, propertyAlias: propertyAlias);
|
||||
|
||||
@@ -269,63 +211,34 @@ namespace Umbraco.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
var cacheBusterValue = cacheBuster ? mediaItem.UpdateDate.ToFileTimeUtc().ToString(CultureInfo.InvariantCulture) : null;
|
||||
|
||||
return GetCropUrl(
|
||||
mediaItemUrl, imageUrlGenerator, localCrops, width, height, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions,
|
||||
cacheBusterValue, furtherOptions, ratioMode, upScale);
|
||||
cacheBusterValue, furtherOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL from the image path.
|
||||
/// </summary>
|
||||
/// <param name="imageUrl">
|
||||
/// The image URL.
|
||||
/// </param>
|
||||
/// <param name="width">
|
||||
/// The width of the output image.
|
||||
/// </param>
|
||||
/// <param name="height">
|
||||
/// The height of the output image.
|
||||
/// </param>
|
||||
/// <param name="imageCropperValue">
|
||||
/// The Json data from the Umbraco Core Image Cropper property editor
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias.
|
||||
/// </param>
|
||||
/// <param name="quality">
|
||||
/// Quality percentage of the output image.
|
||||
/// </param>
|
||||
/// <param name="imageCropMode">
|
||||
/// The image crop mode.
|
||||
/// </param>
|
||||
/// <param name="imageCropAnchor">
|
||||
/// The image crop anchor.
|
||||
/// </param>
|
||||
/// <param name="preferFocalPoint">
|
||||
/// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one
|
||||
/// </param>
|
||||
/// <param name="useCropDimensions">
|
||||
/// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters
|
||||
/// </param>
|
||||
/// <param name="cacheBusterValue">
|
||||
/// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
|
||||
/// </param>
|
||||
/// <param name="furtherOptions">
|
||||
/// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example>
|
||||
/// <![CDATA[
|
||||
/// furtherOptions: "&bgcolor=fff"
|
||||
/// ]]>
|
||||
/// </example>
|
||||
/// </param>
|
||||
/// <param name="ratioMode">
|
||||
/// Use a dimension as a ratio
|
||||
/// </param>
|
||||
/// <param name="upScale">
|
||||
/// If the image should be upscaled to requested dimensions
|
||||
/// </param>
|
||||
/// <param name="imageUrl">The image URL.</param>
|
||||
/// <param name="imageUrlGenerator">The image URL generator.</param>
|
||||
/// <param name="width">The width of the output image.</param>
|
||||
/// <param name="height">The height of the output image.</param>
|
||||
/// <param name="imageCropperValue">The Json data from the Umbraco Core Image Cropper property editor.</param>
|
||||
/// <param name="cropAlias">The crop alias.</param>
|
||||
/// <param name="quality">Quality percentage of the output image.</param>
|
||||
/// <param name="imageCropMode">The image crop mode.</param>
|
||||
/// <param name="imageCropAnchor">The image crop anchor.</param>
|
||||
/// <param name="preferFocalPoint">Use focal point to generate an output image using the focal point instead of the predefined crop if there is one.</param>
|
||||
/// <param name="useCropDimensions">Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.</param>
|
||||
/// <param name="cacheBusterValue">Add a serialized date of the last edit of the item to ensure client cache refresh when updated.</param>
|
||||
/// <param name="furtherOptions">These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example><![CDATA[
|
||||
/// furtherOptions: "bgcolor=fff"
|
||||
/// ]]></example></param>
|
||||
/// <returns>
|
||||
/// The the URL of the cropped image.
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
public static string GetCropUrl(
|
||||
this string imageUrl,
|
||||
@@ -340,11 +253,12 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
string cacheBusterValue = null,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true)
|
||||
string furtherOptions = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(imageUrl)) return string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(imageUrl))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ImageCropperValue cropDataSet = null;
|
||||
if (string.IsNullOrEmpty(imageCropperValue) == false && imageCropperValue.DetectIsJson() && (imageCropMode == ImageCropMode.Crop || imageCropMode == null))
|
||||
@@ -354,62 +268,30 @@ namespace Umbraco.Extensions
|
||||
|
||||
return GetCropUrl(
|
||||
imageUrl, imageUrlGenerator, cropDataSet, width, height, cropAlias, quality, imageCropMode,
|
||||
imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, upScale);
|
||||
imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL from the image path.
|
||||
/// </summary>
|
||||
/// <param name="imageUrl">
|
||||
/// The image URL.
|
||||
/// </param>
|
||||
/// <param name="imageUrlGenerator">
|
||||
/// The generator that will process all the options and the image URL to return a full image URLs with all processing options appended
|
||||
/// </param>
|
||||
/// <param name="cropDataSet"></param>
|
||||
/// <param name="width">
|
||||
/// The width of the output image.
|
||||
/// </param>
|
||||
/// <param name="height">
|
||||
/// The height of the output image.
|
||||
/// </param>
|
||||
/// <param name="cropAlias">
|
||||
/// The crop alias.
|
||||
/// </param>
|
||||
/// <param name="quality">
|
||||
/// Quality percentage of the output image.
|
||||
/// </param>
|
||||
/// <param name="imageCropMode">
|
||||
/// The image crop mode.
|
||||
/// </param>
|
||||
/// <param name="imageCropAnchor">
|
||||
/// The image crop anchor.
|
||||
/// </param>
|
||||
/// <param name="preferFocalPoint">
|
||||
/// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one
|
||||
/// </param>
|
||||
/// <param name="useCropDimensions">
|
||||
/// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters
|
||||
/// </param>
|
||||
/// <param name="cacheBusterValue">
|
||||
/// Add a serialized date of the last edit of the item to ensure client cache refresh when updated
|
||||
/// </param>
|
||||
/// <param name="furtherOptions">
|
||||
/// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example>
|
||||
/// <![CDATA[
|
||||
/// furtherOptions: "&bgcolor=fff"
|
||||
/// ]]>
|
||||
/// </example>
|
||||
/// </param>
|
||||
/// <param name="ratioMode">
|
||||
/// Use a dimension as a ratio
|
||||
/// </param>
|
||||
/// <param name="upScale">
|
||||
/// If the image should be upscaled to requested dimensions
|
||||
/// </param>
|
||||
/// <param name="imageUrl">The image URL.</param>
|
||||
/// <param name="imageUrlGenerator">The generator that will process all the options and the image URL to return a full image URLs with all processing options appended.</param>
|
||||
/// <param name="cropDataSet">The crop data set.</param>
|
||||
/// <param name="width">The width of the output image.</param>
|
||||
/// <param name="height">The height of the output image.</param>
|
||||
/// <param name="cropAlias">The crop alias.</param>
|
||||
/// <param name="quality">Quality percentage of the output image.</param>
|
||||
/// <param name="imageCropMode">The image crop mode.</param>
|
||||
/// <param name="imageCropAnchor">The image crop anchor.</param>
|
||||
/// <param name="preferFocalPoint">Use focal point to generate an output image using the focal point instead of the predefined crop if there is one.</param>
|
||||
/// <param name="useCropDimensions">Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.</param>
|
||||
/// <param name="cacheBusterValue">Add a serialized date of the last edit of the item to ensure client cache refresh when updated.</param>
|
||||
/// <param name="furtherOptions">These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example:
|
||||
/// <example><![CDATA[
|
||||
/// furtherOptions: "bgcolor=fff"
|
||||
/// ]]></example></param>
|
||||
/// <returns>
|
||||
/// The <see cref="string"/>.
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
public static string GetCropUrl(
|
||||
this string imageUrl,
|
||||
@@ -424,72 +306,57 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
string cacheBusterValue = null,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true,
|
||||
string animationProcessMode = null)
|
||||
string furtherOptions = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(imageUrl)) return string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(imageUrl))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ImageUrlGenerationOptions options;
|
||||
|
||||
if (cropDataSet != null && (imageCropMode == ImageCropMode.Crop || imageCropMode == null))
|
||||
{
|
||||
var crop = cropDataSet.GetCrop(cropAlias);
|
||||
ImageCropperValue.ImageCropperCrop crop = cropDataSet.GetCrop(cropAlias);
|
||||
|
||||
// if a crop was specified, but not found, return null
|
||||
// If a crop was specified, but not found, return null
|
||||
if (crop == null && !string.IsNullOrWhiteSpace(cropAlias))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
options = cropDataSet.GetCropBaseOptions(imageUrl, crop, string.IsNullOrWhiteSpace(cropAlias), preferFocalPoint);
|
||||
options = cropDataSet.GetCropBaseOptions(imageUrl, crop, preferFocalPoint || string.IsNullOrWhiteSpace(cropAlias));
|
||||
|
||||
if (crop != null & useCropDimensions)
|
||||
if (crop != null && useCropDimensions)
|
||||
{
|
||||
width = crop.Width;
|
||||
height = crop.Height;
|
||||
}
|
||||
|
||||
// If a predefined crop has been specified & there are no coordinates & no ratio mode, but a width parameter has been passed we can get the crop ratio for the height
|
||||
if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null && width != null && height == null)
|
||||
// Calculate missing dimension if a predefined crop has been specified, but has no coordinates
|
||||
if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null)
|
||||
{
|
||||
options.HeightRatio = (decimal)crop.Height / crop.Width;
|
||||
}
|
||||
|
||||
// If a predefined crop has been specified & there are no coordinates & no ratio mode, but a height parameter has been passed we can get the crop ratio for the width
|
||||
if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null && width == null && height != null)
|
||||
{
|
||||
options.WidthRatio = (decimal)crop.Width / crop.Height;
|
||||
if (width != null && height == null)
|
||||
{
|
||||
height = (int)MathF.Round(width.Value * ((float)crop.Height / crop.Width));
|
||||
}
|
||||
else if (width == null && height != null)
|
||||
{
|
||||
width = (int)MathF.Round(height.Value * ((float)crop.Width / crop.Height));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
options = new ImageUrlGenerationOptions (imageUrl)
|
||||
options = new ImageUrlGenerationOptions(imageUrl)
|
||||
{
|
||||
ImageCropMode = (imageCropMode ?? ImageCropMode.Pad),
|
||||
ImageCropMode = (imageCropMode ?? ImageCropMode.Pad), // Not sure why we default to Pad
|
||||
ImageCropAnchor = imageCropAnchor
|
||||
};
|
||||
}
|
||||
|
||||
options.Quality = quality;
|
||||
options.Width = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Width ? null : width;
|
||||
options.Height = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Height ? null : height;
|
||||
options.AnimationProcessMode = animationProcessMode;
|
||||
|
||||
if (ratioMode == ImageCropRatioMode.Width && height != null)
|
||||
{
|
||||
// if only height specified then assume a square
|
||||
if (width == null) width = height;
|
||||
options.WidthRatio = (decimal)width / (decimal)height;
|
||||
}
|
||||
|
||||
if (ratioMode == ImageCropRatioMode.Height && width != null)
|
||||
{
|
||||
// if only width specified then assume a square
|
||||
if (height == null) height = width;
|
||||
options.HeightRatio = (decimal)height / (decimal)width;
|
||||
}
|
||||
|
||||
options.UpScale = upScale;
|
||||
options.Width = width;
|
||||
options.Height = height;
|
||||
options.FurtherOptions = furtherOptions;
|
||||
options.CacheBusterValue = cacheBusterValue;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -246,8 +246,6 @@ namespace Umbraco.Extensions
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true,
|
||||
bool htmlEncode = true)
|
||||
{
|
||||
if (mediaItem == null)
|
||||
@@ -256,8 +254,8 @@ namespace Umbraco.Extensions
|
||||
}
|
||||
|
||||
var url = mediaItem.GetCropUrl(width, height, propertyAlias, cropAlias, quality, imageCropMode,
|
||||
imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode,
|
||||
upScale);
|
||||
imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions);
|
||||
|
||||
return CreateHtmlString(url, htmlEncode);
|
||||
}
|
||||
|
||||
@@ -273,16 +271,14 @@ namespace Umbraco.Extensions
|
||||
bool useCropDimensions = true,
|
||||
string cacheBusterValue = null,
|
||||
string furtherOptions = null,
|
||||
ImageCropRatioMode? ratioMode = null,
|
||||
bool upScale = true,
|
||||
bool htmlEncode = true)
|
||||
{
|
||||
if (imageCropperValue == null) return HtmlString.Empty;
|
||||
|
||||
var imageUrl = imageCropperValue.Src;
|
||||
var url = imageUrl.GetCropUrl(imageCropperValue, width, height, cropAlias, quality, imageCropMode,
|
||||
imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode,
|
||||
upScale);
|
||||
imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions);
|
||||
|
||||
return CreateHtmlString(url, htmlEncode);
|
||||
}
|
||||
|
||||
|
||||
64
src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs
Normal file
64
src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp.Web;
|
||||
using SixLabors.ImageSharp.Web.Commands;
|
||||
using SixLabors.ImageSharp.Web.Processors;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.ImageProcessors
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows the cropping of images.
|
||||
/// </summary>
|
||||
public class CropWebProcessor : IImageWebProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// The command constant for the crop coordinates.
|
||||
/// </summary>
|
||||
public const string Coordinates = "cc";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<string> Commands { get; } = new[]
|
||||
{
|
||||
Coordinates
|
||||
};
|
||||
|
||||
/// <inheritdoc/>
|
||||
public FormattedImage Process(FormattedImage image, ILogger logger, IDictionary<string, string> commands, CommandParser parser, CultureInfo culture)
|
||||
{
|
||||
RectangleF? coordinates = GetCoordinates(commands, parser, culture);
|
||||
if (coordinates != null)
|
||||
{
|
||||
// Convert the coordinates to a pixel based rectangle
|
||||
int sourceWidth = image.Image.Width;
|
||||
int sourceHeight = image.Image.Height;
|
||||
int x = (int)MathF.Round(coordinates.Value.X * sourceWidth);
|
||||
int y = (int)MathF.Round(coordinates.Value.Y * sourceHeight);
|
||||
int width = (int)MathF.Round(coordinates.Value.Width * sourceWidth);
|
||||
int height = (int)MathF.Round(coordinates.Value.Height * sourceHeight);
|
||||
|
||||
var cropRectangle = new Rectangle(x, y, width, height);
|
||||
|
||||
image.Image.Mutate(x => x.Crop(cropRectangle));
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static RectangleF? GetCoordinates(IDictionary<string, string> commands, CommandParser parser, CultureInfo culture)
|
||||
{
|
||||
float[] coordinates = parser.ParseValue<float[]>(commands.GetValueOrDefault(Coordinates), culture);
|
||||
|
||||
if (coordinates.Length != 4)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// The right and bottom values are actually the distance from those sides, so convert them into real coordinates
|
||||
return RectangleF.FromLTRB(coordinates[0], coordinates[1], 1 - coordinates[2], 1 - coordinates[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,6 +180,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_inMemoryModelFactory.ModelsChanged -= InMemoryModelFactoryModelsChanged;
|
||||
_locker.Dispose();
|
||||
}
|
||||
|
||||
|
||||
756
src/Umbraco.Web.UI.Client/package-lock.json
generated
756
src/Umbraco.Web.UI.Client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.resources.imageUrlGeneratorResource
|
||||
* @function
|
||||
@@ -11,14 +11,14 @@
|
||||
|
||||
function imageUrlGeneratorResource($http, umbRequestHelper) {
|
||||
|
||||
function getCropUrl(mediaPath, width, height, imageCropMode, animationProcessMode) {
|
||||
function getCropUrl(mediaPath, width, height, imageCropMode) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"imageUrlGeneratorApiBaseUrl",
|
||||
"GetCropUrl",
|
||||
{ mediaPath, width, height, imageCropMode, animationProcessMode })),
|
||||
{ mediaPath, width, height, imageCropMode })),
|
||||
'Failed to get crop URL');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.mediaHelper
|
||||
* @description A helper object used for dealing with media items
|
||||
@@ -408,16 +408,20 @@ function mediaHelper(umbRequestHelper, $http, $log) {
|
||||
* @param {string} imagePath Raw image path
|
||||
* @param {object} options Object describing image generation parameters:
|
||||
* {
|
||||
* animationProcessMode: <string>
|
||||
* cacheBusterValue: <string>
|
||||
* width: <int>
|
||||
* height: <int>
|
||||
* focalPoint: {
|
||||
* left: <int>
|
||||
* top: <int>
|
||||
* },
|
||||
* height: <int>
|
||||
* mode: <string>
|
||||
* upscale: <boolean>
|
||||
* width: <int>
|
||||
* cacheBusterValue: <string>
|
||||
* crop: {
|
||||
* x1: <int>
|
||||
* x2: <int>
|
||||
* y1: <int>
|
||||
* y2: <int>
|
||||
* },
|
||||
* }
|
||||
*/
|
||||
getProcessedImageUrl: function (imagePath, options) {
|
||||
@@ -433,18 +437,16 @@ function mediaHelper(umbRequestHelper, $http, $log) {
|
||||
"GetProcessedImageUrl",
|
||||
{
|
||||
imagePath,
|
||||
animationProcessMode: options.animationProcessMode,
|
||||
cacheBusterValue: options.cacheBusterValue,
|
||||
width: options.width,
|
||||
height: options.height,
|
||||
focalPointLeft: options.focalPoint ? options.focalPoint.left : null,
|
||||
focalPointTop: options.focalPoint ? options.focalPoint.top : null,
|
||||
height: options.height,
|
||||
mode: options.mode,
|
||||
upscale: options.upscale || false,
|
||||
width: options.width,
|
||||
cacheBusterValue: options.cacheBusterValue,
|
||||
cropX1: options.crop ? options.crop.x1 : null,
|
||||
cropX2: options.crop ? options.crop.x2 : null,
|
||||
cropY1: options.crop ? options.crop.y1 : null,
|
||||
cropY2: options.crop ? options.crop.y : null
|
||||
cropY2: options.crop ? options.crop.y2 : null
|
||||
})),
|
||||
"Failed to retrieve processed image URL for image: " + imagePath);
|
||||
}
|
||||
|
||||
@@ -306,8 +306,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
if (imgUrl) {
|
||||
mediaHelper.getProcessedImageUrl(imgUrl,
|
||||
{
|
||||
height: newSize.height,
|
||||
width: newSize.width
|
||||
width: newSize.width,
|
||||
height: newSize.height
|
||||
})
|
||||
.then(function (resizedImgUrl) {
|
||||
editor.dom.setAttrib(imageDomElement, 'data-mce-src', resizedImgUrl);
|
||||
@@ -1526,15 +1526,13 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
args.editor.on('ObjectResized', function (e) {
|
||||
var srcAttr = $(e.target).attr("src");
|
||||
var path = srcAttr.split("?")[0];
|
||||
mediaHelper.getProcessedImageUrl(path,
|
||||
{
|
||||
height: e.height,
|
||||
moded: "max",
|
||||
width: e.width
|
||||
})
|
||||
.then(function (resizedPath) {
|
||||
$(e.target).attr("data-mce-src", resizedPath);
|
||||
});
|
||||
mediaHelper.getProcessedImageUrl(path, {
|
||||
width: e.width,
|
||||
height: e.height,
|
||||
mode: "max"
|
||||
}).then(function (resizedPath) {
|
||||
$(e.target).attr("data-mce-src", resizedPath);
|
||||
});
|
||||
|
||||
syncContent();
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
}
|
||||
|
||||
.umb-expansion-panel__header {
|
||||
box-sizing: border-box;
|
||||
padding: 10px 20px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
var path = umbRequestHelper.convertVirtualToAbsolutePath(vm.blockConfigModel.thumbnail);
|
||||
if (path.toLowerCase().endsWith(".svg") === false) {
|
||||
path += "?upscale=false&width=400";
|
||||
path += "?width=400";
|
||||
}
|
||||
vm.styleBackgroundImage = 'url(\''+path+'\')';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(function () {
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
@@ -55,7 +55,7 @@
|
||||
if (thumbnail) {
|
||||
if (mediaHelper.detectIfImageByExtension(property.value)) {
|
||||
//get default big thumbnail from image processor
|
||||
var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first";
|
||||
var thumbnailUrl = property.value + "?width=500&rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss");
|
||||
return thumbnailUrl;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -70,28 +70,25 @@ angular.module("umbraco")
|
||||
if ($scope.control.value.coordinates) {
|
||||
// New way, crop by percent must come before width/height.
|
||||
var coords = $scope.control.value.coordinates;
|
||||
url += `?crop=${coords.x1},${coords.y1},${coords.x2},${coords.y2}&cropmode=percentage`;
|
||||
url += `?cc=${coords.x1},${coords.y1},${coords.x2},${coords.y2}`;
|
||||
} else {
|
||||
// Here in order not to break existing content where focalPoint were used.
|
||||
// For some reason width/height have to come first when mode=crop.
|
||||
if ($scope.control.value.focalPoint) {
|
||||
url += `?center=${$scope.control.value.focalPoint.top},${$scope.control.value.focalPoint.left}`;
|
||||
url += '&mode=crop';
|
||||
url += `?rxy=${$scope.control.value.focalPoint.left},${$scope.control.value.focalPoint.top}`;
|
||||
} else {
|
||||
// Prevent black padding and no crop when focal point not set / changed from default
|
||||
url += '?center=0.5,0.5&mode=crop';
|
||||
url += '?rxy=0.5,0.5';
|
||||
}
|
||||
}
|
||||
|
||||
url += '&width=' + $scope.control.editor.config.size.width;
|
||||
url += '&height=' + $scope.control.editor.config.size.height;
|
||||
url += '&animationprocessmode=first';
|
||||
}
|
||||
|
||||
// set default size if no crop present (moved from the view)
|
||||
if (url.includes('?') === false)
|
||||
{
|
||||
url += '?width=800&upscale=false&animationprocessmode=false'
|
||||
url += '?width=800'
|
||||
}
|
||||
|
||||
return url;
|
||||
|
||||
@@ -234,7 +234,7 @@ angular.module('umbraco')
|
||||
if (property.value && property.value.src) {
|
||||
|
||||
if (thumbnail === true) {
|
||||
return property.value.src + "?width=500&mode=max&animationprocessmode=first";
|
||||
return property.value.src + "?width=500";
|
||||
}
|
||||
else {
|
||||
return property.value.src;
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
<PropertyGroup>
|
||||
<BellePath>$(ProjectDir)wwwroot/umbraco</BellePath>
|
||||
<JsonSchemaPath>$(ProjectDir)umbraco/config/appsettings-schema.json</JsonSchemaPath>
|
||||
|
||||
</PropertyGroup>
|
||||
<Target Name="CheckPreconditions" BeforeTargets="Build">
|
||||
<Message Text="-CheckPreconditions-" Importance="high" />
|
||||
@@ -100,6 +101,11 @@
|
||||
<Message Text="Generate the appsettings json schema." Importance="High" Condition="!Exists('$(JsonSchemaPath)') and '$(UmbracoBuild)' == ''" />
|
||||
|
||||
<CallTarget Targets="JsonSchemaBuild" Condition="!Exists('$(JsonSchemaPath)') and '$(UmbracoBuild)' == ''" />
|
||||
|
||||
|
||||
<CallTarget Targets="AppsettingsBuild" Condition="!Exists('appsettings.json')" />
|
||||
<CallTarget Targets="AppsettingsDevBuild" Condition="!Exists('appsettings.Development.json')" />
|
||||
|
||||
</Target>
|
||||
<Target Name="BelleBuild">
|
||||
<!-- <Exec WorkingDirectory="$(ProjectDir)/../../src/Umbraco.Web.UI.Client/" Command="powershell -ExecutionPolicy RemoteSigned -Command '&{ npm install ; npm run build }'" />-->
|
||||
@@ -107,6 +113,15 @@
|
||||
<Target Name="JsonSchemaBuild">
|
||||
<!-- <Exec WorkingDirectory="$(ProjectDir)/../../" Command="powershell -ExecutionPolicy RemoteSigned -Command '&dotnet run --project $pwd/src/JsonSchema/JsonSchema.csproj -c Release -- --outputFile $pwd/src/Umbraco.Web.UI.NetCore/$(JsonSchemaPath)'" />-->
|
||||
</Target>
|
||||
<Target Name="AppsettingsBuild">
|
||||
<Message Text="Generating appsettings.json because it doesnt exist" Importance="High" />
|
||||
<Copy SourceFiles="$(Projectdir)/appsettings.template.json" DestinationFiles="$(ProjectDir)/appsettings.json" />
|
||||
</Target>
|
||||
<Target Name="AppsettingsDevBuild">
|
||||
<Message Text="Generating appsettings.Development.json because it doesnt exist" Importance="High" />
|
||||
<Copy SourceFiles="$(ProjectDir)appsettings.Development.template.json" DestinationFiles="$(ProjectDir)appsettings.Development.json" />
|
||||
</Target>
|
||||
|
||||
|
||||
<!-- clean Belle when cleaning and rebuilding, but only in Visual Studio -->
|
||||
<Target Name="CleanPreconditions" AfterTargets="Clean" Condition="'$(UmbracoBuild)' == ''">
|
||||
|
||||
Reference in New Issue
Block a user