Merge branch 'v10/dev' into v11/dev
# Conflicts: # src/Umbraco.Web.UI.Client/src/common/directives/components/umbavatar.directive.js # src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
This commit is contained in:
@@ -74,6 +74,7 @@ public sealed class RichTextEditorPastedImages
|
|||||||
// we have already processed to avoid dupes
|
// we have already processed to avoid dupes
|
||||||
var uploadedImages = new Dictionary<string, GuidUdi>();
|
var uploadedImages = new Dictionary<string, GuidUdi>();
|
||||||
|
|
||||||
|
|
||||||
foreach (HtmlNode? img in tmpImages)
|
foreach (HtmlNode? img in tmpImages)
|
||||||
{
|
{
|
||||||
// The data attribute contains the path to the tmp img to persist as a media item
|
// The data attribute contains the path to the tmp img to persist as a media item
|
||||||
@@ -84,6 +85,11 @@ public sealed class RichTextEditorPastedImages
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsValidPath(tmpImgPath) == false)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var absoluteTempImagePath = _hostingEnvironment.MapPathContentRoot(tmpImgPath);
|
var absoluteTempImagePath = _hostingEnvironment.MapPathContentRoot(tmpImgPath);
|
||||||
var fileName = Path.GetFileName(absoluteTempImagePath);
|
var fileName = Path.GetFileName(absoluteTempImagePath);
|
||||||
var safeFileName = fileName.ToSafeFileName(_shortStringHelper);
|
var safeFileName = fileName.ToSafeFileName(_shortStringHelper);
|
||||||
@@ -184,4 +190,6 @@ public sealed class RichTextEditorPastedImages
|
|||||||
|
|
||||||
return htmlDoc.DocumentNode.OuterHtml;
|
return htmlDoc.DocumentNode.OuterHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsValidPath(string imagePath) => imagePath.StartsWith(Constants.SystemDirectories.TempImageUploads);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ public static partial class UmbracoBuilderExtensions
|
|||||||
builder.Services.AddSingleton<IUmbracoVirtualPageRoute, UmbracoVirtualPageRoute>();
|
builder.Services.AddSingleton<IUmbracoVirtualPageRoute, UmbracoVirtualPageRoute>();
|
||||||
builder.Services.AddSingleton<IUmbracoRouteValuesFactory, UmbracoRouteValuesFactory>();
|
builder.Services.AddSingleton<IUmbracoRouteValuesFactory, UmbracoRouteValuesFactory>();
|
||||||
builder.Services.AddSingleton<IRoutableDocumentFilter, RoutableDocumentFilter>();
|
builder.Services.AddSingleton<IRoutableDocumentFilter, RoutableDocumentFilter>();
|
||||||
|
builder.Services.AddSingleton<MatcherPolicy, SurfaceControllerMatcherPolicy>();
|
||||||
|
|
||||||
builder.Services.AddSingleton<FrontEndRoutes>();
|
builder.Services.AddSingleton<FrontEndRoutes>();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.AspNetCore.Routing.Matching;
|
||||||
|
using Umbraco.Cms.Web.Website.Controllers;
|
||||||
|
using Umbraco.Extensions;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Web.Website.Routing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures the surface controller requests takes priority over other things like virtual routes.
|
||||||
|
/// Also ensures that requests to a surface controller on a virtual route will return 405, like HttpMethodMatcherPolicy ensures for non-virtual route requests.
|
||||||
|
/// </summary>
|
||||||
|
internal class SurfaceControllerMatcherPolicy : MatcherPolicy, IEndpointSelectorPolicy
|
||||||
|
{
|
||||||
|
private const string Http405EndpointDisplayName = "405 HTTP Method Not Supported";
|
||||||
|
|
||||||
|
public override int Order { get; } // default order should be okay. Count be everything positive to not conflict with MS policies
|
||||||
|
|
||||||
|
public bool AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints)
|
||||||
|
{
|
||||||
|
// In theory all endpoints can have the query string data for a surface controller
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(nameof(httpContext));
|
||||||
|
ArgumentNullException.ThrowIfNull(nameof(candidates));
|
||||||
|
|
||||||
|
if (candidates.Count < 2)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
int? surfaceControllerIndex = GetSurfaceControllerCandidateIndex(candidates);
|
||||||
|
if (surfaceControllerIndex.HasValue)
|
||||||
|
{
|
||||||
|
HashSet<string> allowedHttpMethods = GetAllowedHttpMethods(candidates[surfaceControllerIndex.Value]);
|
||||||
|
|
||||||
|
if (allowedHttpMethods.Any()
|
||||||
|
&& allowedHttpMethods.Contains(httpContext.Request.Method) is false)
|
||||||
|
{
|
||||||
|
// We need to handle this as a 405 like the HttpMethodMatcherPolicy would do.
|
||||||
|
httpContext.SetEndpoint(CreateRejectEndpoint(allowedHttpMethods));
|
||||||
|
httpContext.Request.RouteValues = null!;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise we invalidate all other endpoints than the surface controller that matched.
|
||||||
|
InvalidateAllCandidatesExceptIndex(candidates, surfaceControllerIndex.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static HashSet<string> GetAllowedHttpMethods(CandidateState candidate)
|
||||||
|
{
|
||||||
|
var surfaceControllerAllowedHttpMethods = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
IHttpMethodMetadata? httpMethodMetadata = candidate.Endpoint?.Metadata.GetMetadata<IHttpMethodMetadata>();
|
||||||
|
if (httpMethodMetadata is not null)
|
||||||
|
{
|
||||||
|
foreach (var httpMethod in httpMethodMetadata.HttpMethods)
|
||||||
|
{
|
||||||
|
surfaceControllerAllowedHttpMethods.Add(httpMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return surfaceControllerAllowedHttpMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int? GetSurfaceControllerCandidateIndex(CandidateSet candidates)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < candidates.Count; i++)
|
||||||
|
{
|
||||||
|
if (candidates.IsValidCandidate(i))
|
||||||
|
{
|
||||||
|
CandidateState candidate = candidates[i];
|
||||||
|
ControllerActionDescriptor? controllerActionDescriptor =
|
||||||
|
candidate.Endpoint?.Metadata.GetMetadata<ControllerActionDescriptor>();
|
||||||
|
|
||||||
|
if (controllerActionDescriptor?.ControllerTypeInfo.IsType<SurfaceController>() == true)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InvalidateAllCandidatesExceptIndex(CandidateSet candidates, int index)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < candidates.Count; i++)
|
||||||
|
{
|
||||||
|
if (i != index)
|
||||||
|
{
|
||||||
|
candidates.SetValidity(i, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Endpoint CreateRejectEndpoint(ISet<string> allowedHttpMethods) =>
|
||||||
|
new Endpoint(
|
||||||
|
(context) =>
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 405;
|
||||||
|
|
||||||
|
context.Response.Headers.Allow = string.Join(", ", allowedHttpMethods);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
},
|
||||||
|
EndpointMetadataCollection.Empty,
|
||||||
|
Http405EndpointDisplayName);
|
||||||
|
}
|
||||||
@@ -26,6 +26,8 @@
|
|||||||
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
||||||
<RazorCompileOnPublish>false</RazorCompileOnPublish>
|
<RazorCompileOnPublish>false</RazorCompileOnPublish>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="..\PACKAGE_PROJECT_NAME_FROM_TEMPLATE\build\PACKAGE_PROJECT_NAME_FROM_TEMPLATE.targets" Condition="'$(PackageProjectName)' != ''" />
|
||||||
|
|
||||||
<Import Project="..\PACKAGE_PROJECT_NAME_FROM_TEMPLATE\buildTransitive\PACKAGE_PROJECT_NAME_FROM_TEMPLATE.targets" Condition="'$(PackageProjectName)' != ''" />
|
<Import Project="..\PACKAGE_PROJECT_NAME_FROM_TEMPLATE\buildTransitive\PACKAGE_PROJECT_NAME_FROM_TEMPLATE.targets" Condition="'$(PackageProjectName)' != ''" />
|
||||||
<ItemGroup Condition="'$(PackageProjectName)' != ''">
|
<ItemGroup Condition="'$(PackageProjectName)' != ''">
|
||||||
|
|||||||
Reference in New Issue
Block a user