V10: fix build warnings in Web.BackOffice (#12479)

* Run code cleanup

* Start manual run

* Finish dotnet format + manual cleanup

* Fix up after merge

* Fix substrings changed to [..]

Co-authored-by: Nikolaj Geisle <niko737@edu.ucl.dk>
Co-authored-by: Zeegaan <nge@umbraco.dk>
This commit is contained in:
Nikolaj Geisle
2022-06-20 08:37:17 +02:00
committed by GitHub
parent 7688c61621
commit e762fa91bc
234 changed files with 28037 additions and 27527 deletions

View File

@@ -1,152 +1,157 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
namespace Umbraco.Cms.Web.BackOffice.Extensions
namespace Umbraco.Cms.Web.BackOffice.Extensions;
internal static class ControllerContextExtensions
{
internal static class ControllerContextExtensions
/// <summary>
/// Invokes the authorization filters for the controller action.
/// </summary>
/// <returns>Whether the user is authenticated or not.</returns>
internal static async Task<bool> InvokeAuthorizationFiltersForRequest(this ControllerContext controllerContext, ActionContext actionContext)
{
/// <summary>
/// Invokes the authorization filters for the controller action.
/// </summary>
/// <returns>Whether the user is authenticated or not.</returns>
internal static async Task<bool> InvokeAuthorizationFiltersForRequest(this ControllerContext controllerContext, ActionContext actionContext)
ControllerActionDescriptor actionDescriptor = controllerContext.ActionDescriptor;
var metadataCollection =
new EndpointMetadataCollection(actionDescriptor.EndpointMetadata.Union(new[] { actionDescriptor }));
IReadOnlyList<IAuthorizeData> authorizeData = metadataCollection.GetOrderedMetadata<IAuthorizeData>();
IAuthorizationPolicyProvider policyProvider = controllerContext.HttpContext.RequestServices
.GetRequiredService<IAuthorizationPolicyProvider>();
AuthorizationPolicy? policy = await AuthorizationPolicy.CombineAsync(policyProvider, authorizeData);
if (policy is not null)
{
IPolicyEvaluator policyEvaluator =
controllerContext.HttpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();
AuthenticateResult authenticateResult =
await policyEvaluator.AuthenticateAsync(policy, controllerContext.HttpContext);
var actionDescriptor = controllerContext.ActionDescriptor;
var metadataCollection = new EndpointMetadataCollection(actionDescriptor.EndpointMetadata.Union(new []{actionDescriptor}));
var authorizeData = metadataCollection.GetOrderedMetadata<IAuthorizeData>();
var policyProvider = controllerContext.HttpContext.RequestServices.GetRequiredService<IAuthorizationPolicyProvider>();
var policy = await AuthorizationPolicy.CombineAsync(policyProvider, authorizeData);
if (policy is not null)
if (!authenticateResult.Succeeded)
{
var policyEvaluator = controllerContext.HttpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();
var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, controllerContext.HttpContext);
if (!authenticateResult.Succeeded)
{
return false;
}
// TODO this is super hacky, but we rely on the FeatureAuthorizeHandler can still handle endpoints
// (The way before .NET 5). The .NET 5 way would need to use han http context, for the "inner" request
// with the nested controller
var resource = new Endpoint(null,metadataCollection, null);
var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, controllerContext.HttpContext, resource);
if (!authorizeResult.Succeeded)
{
return false;
}
return false;
}
var filters = actionDescriptor.FilterDescriptors;
var filterGrouping = new FilterGrouping(filters, controllerContext.HttpContext.RequestServices);
// because the continuation gets built from the inside out we need to reverse the filter list
// so that least specific filters (Global) get run first and the most specific filters (Action) get run last.
var authorizationFilters = filterGrouping.AuthorizationFilters.Reverse().ToList();
var asyncAuthorizationFilters = filterGrouping.AsyncAuthorizationFilters.Reverse().ToList();
if (authorizationFilters.Count == 0 && asyncAuthorizationFilters.Count == 0)
return true;
// if the authorization filter returns a result, it means it failed to authorize
var authorizationFilterContext = new AuthorizationFilterContext(actionContext, filters.Select(x=>x.Filter).ToArray());
return await ExecuteAuthorizationFiltersAsync(authorizationFilterContext, authorizationFilters, asyncAuthorizationFilters);
// TODO this is super hacky, but we rely on the FeatureAuthorizeHandler can still handle endpoints
// (The way before .NET 5). The .NET 5 way would need to use han http context, for the "inner" request
// with the nested controller
var resource = new Endpoint(null, metadataCollection, null);
PolicyAuthorizationResult authorizeResult =
await policyEvaluator.AuthorizeAsync(policy, authenticateResult, controllerContext.HttpContext, resource);
if (!authorizeResult.Succeeded)
{
return false;
}
}
/// <summary>
/// Executes a chain of filters.
/// </summary>
/// <remarks>
/// Recursively calls in to itself as its continuation for the next filter in the chain.
/// </remarks>
private static async Task<bool> ExecuteAuthorizationFiltersAsync(
AuthorizationFilterContext authorizationFilterContext,
IList<IAuthorizationFilter> authorizationFilters,
IList<IAsyncAuthorizationFilter> asyncAuthorizationFilters)
IList<FilterDescriptor> filters = actionDescriptor.FilterDescriptors;
var filterGrouping = new FilterGrouping(filters, controllerContext.HttpContext.RequestServices);
// because the continuation gets built from the inside out we need to reverse the filter list
// so that least specific filters (Global) get run first and the most specific filters (Action) get run last.
var authorizationFilters = filterGrouping.AuthorizationFilters.Reverse().ToList();
var asyncAuthorizationFilters = filterGrouping.AsyncAuthorizationFilters.Reverse().ToList();
if (authorizationFilters.Count == 0 && asyncAuthorizationFilters.Count == 0)
{
foreach (var authorizationFilter in authorizationFilters)
{
authorizationFilter.OnAuthorization(authorizationFilterContext);
if (!(authorizationFilterContext.Result is null))
{
return false;
}
}
foreach (var asyncAuthorizationFilter in asyncAuthorizationFilters)
{
await asyncAuthorizationFilter.OnAuthorizationAsync(authorizationFilterContext);
if (!(authorizationFilterContext.Result is null))
{
return false;
}
}
return true;
}
/// <summary>
/// Quickly split filters into different types
/// </summary>
private class FilterGrouping
// if the authorization filter returns a result, it means it failed to authorize
var authorizationFilterContext =
new AuthorizationFilterContext(actionContext, filters.Select(x => x.Filter).ToArray());
return await ExecuteAuthorizationFiltersAsync(authorizationFilterContext, authorizationFilters, asyncAuthorizationFilters);
}
/// <summary>
/// Executes a chain of filters.
/// </summary>
/// <remarks>
/// Recursively calls in to itself as its continuation for the next filter in the chain.
/// </remarks>
private static async Task<bool> ExecuteAuthorizationFiltersAsync(
AuthorizationFilterContext authorizationFilterContext,
IList<IAuthorizationFilter> authorizationFilters,
IList<IAsyncAuthorizationFilter> asyncAuthorizationFilters)
{
foreach (IAuthorizationFilter authorizationFilter in authorizationFilters)
{
private readonly List<IActionFilter> _actionFilters = new List<IActionFilter>();
private readonly List<IAsyncActionFilter> _asyncActionFilters = new List<IAsyncActionFilter>();
private readonly List<IAuthorizationFilter> _authorizationFilters = new List<IAuthorizationFilter>();
private readonly List<IAsyncAuthorizationFilter> _asyncAuthorizationFilters = new List<IAsyncAuthorizationFilter>();
private readonly List<IExceptionFilter> _exceptionFilters = new List<IExceptionFilter>();
private readonly List<IAsyncExceptionFilter> _asyncExceptionFilters = new List<IAsyncExceptionFilter>();
public FilterGrouping(IEnumerable<FilterDescriptor> filters, IServiceProvider serviceProvider)
authorizationFilter.OnAuthorization(authorizationFilterContext);
if (!(authorizationFilterContext.Result is null))
{
if (filters == null) throw new ArgumentNullException("filters");
return false;
}
}
foreach (FilterDescriptor f in filters)
{
var filter = f.Filter;
Categorize(filter, _actionFilters, serviceProvider);
Categorize(filter, _authorizationFilters, serviceProvider);
Categorize(filter, _exceptionFilters, serviceProvider);
Categorize(filter, _asyncActionFilters, serviceProvider);
Categorize(filter, _asyncAuthorizationFilters, serviceProvider);
}
foreach (IAsyncAuthorizationFilter asyncAuthorizationFilter in asyncAuthorizationFilters)
{
await asyncAuthorizationFilter.OnAuthorizationAsync(authorizationFilterContext);
if (!(authorizationFilterContext.Result is null))
{
return false;
}
}
return true;
}
/// <summary>
/// Quickly split filters into different types
/// </summary>
private class FilterGrouping
{
private readonly List<IActionFilter> _actionFilters = new();
private readonly List<IAsyncActionFilter> _asyncActionFilters = new();
private readonly List<IAsyncAuthorizationFilter> _asyncAuthorizationFilters = new();
private readonly List<IAsyncExceptionFilter> _asyncExceptionFilters = new();
private readonly List<IAuthorizationFilter> _authorizationFilters = new();
private readonly List<IExceptionFilter> _exceptionFilters = new();
public FilterGrouping(IEnumerable<FilterDescriptor> filters, IServiceProvider serviceProvider)
{
if (filters == null)
{
throw new ArgumentNullException("filters");
}
public IEnumerable<IActionFilter> ActionFilters => _actionFilters;
public IEnumerable<IAsyncActionFilter> AsyncActionFilters => _asyncActionFilters;
public IEnumerable<IAuthorizationFilter> AuthorizationFilters => _authorizationFilters;
public IEnumerable<IAsyncAuthorizationFilter> AsyncAuthorizationFilters => _asyncAuthorizationFilters;
public IEnumerable<IExceptionFilter> ExceptionFilters => _exceptionFilters;
public IEnumerable<IAsyncExceptionFilter> AsyncExceptionFilters => _asyncExceptionFilters;
private static void Categorize<T>(IFilterMetadata filter, List<T> list, IServiceProvider serviceProvider) where T : class
foreach (FilterDescriptor f in filters)
{
if(filter is TypeFilterAttribute typeFilterAttribute)
{
filter = typeFilterAttribute.CreateInstance(serviceProvider);
}
IFilterMetadata filter = f.Filter;
Categorize(filter, _actionFilters, serviceProvider);
Categorize(filter, _authorizationFilters, serviceProvider);
Categorize(filter, _exceptionFilters, serviceProvider);
Categorize(filter, _asyncActionFilters, serviceProvider);
Categorize(filter, _asyncAuthorizationFilters, serviceProvider);
}
}
T? match = filter as T;
if (match != null)
{
list.Add(match);
}
public IEnumerable<IActionFilter> ActionFilters => _actionFilters;
public IEnumerable<IAsyncActionFilter> AsyncActionFilters => _asyncActionFilters;
public IEnumerable<IAuthorizationFilter> AuthorizationFilters => _authorizationFilters;
public IEnumerable<IAsyncAuthorizationFilter> AsyncAuthorizationFilters => _asyncAuthorizationFilters;
public IEnumerable<IExceptionFilter> ExceptionFilters => _exceptionFilters;
public IEnumerable<IAsyncExceptionFilter> AsyncExceptionFilters => _asyncExceptionFilters;
private static void Categorize<T>(IFilterMetadata filter, List<T> list, IServiceProvider serviceProvider)
where T : class
{
if (filter is TypeFilterAttribute typeFilterAttribute)
{
filter = typeFilterAttribute.CreateInstance(serviceProvider);
}
if (filter is T match)
{
list.Add(match);
}
}
}

View File

@@ -1,7 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using Newtonsoft.Json;
@@ -11,131 +8,138 @@ using Umbraco.Cms.Infrastructure.WebAssets;
using Umbraco.Cms.Web.BackOffice.Controllers;
using Umbraco.Cms.Web.BackOffice.Security;
namespace Umbraco.Extensions
{
public static class HtmlHelperBackOfficeExtensions
{
/// <summary>
/// Outputs a script tag containing the bare minimum (non secure) server vars for use with the angular app
/// </summary>
/// <param name="html"></param>
/// <param name="linkGenerator"></param>
/// <param name="features"></param>
/// <param name="globalSettings"></param>
/// <param name="umbracoVersion"></param>
/// <param name="contentSettings"></param>
/// <param name="treeCollection"></param>
/// <param name="httpContextAccessor"></param>
/// <param name="hostingEnvironment"></param>
/// <param name="settings"></param>
/// <param name="securitySettings"></param>
/// <param name="runtimeMinifier"></param>
/// <returns></returns>
/// <remarks>
/// These are the bare minimal server variables that are required for the application to start without being authenticated,
/// we will load the rest of the server vars after the user is authenticated.
/// </remarks>
public static async Task<IHtmlContent> BareMinimumServerVariablesScriptAsync(this IHtmlHelper html, BackOfficeServerVariables backOfficeServerVariables)
{
var minVars = await backOfficeServerVariables.BareMinimumServerVariablesAsync();
namespace Umbraco.Extensions;
var str = @"<script type=""text/javascript"">
public static class HtmlHelperBackOfficeExtensions
{
/// <summary>
/// Outputs a script tag containing the bare minimum (non secure) server vars for use with the angular app
/// </summary>
/// <param name="html"></param>
/// <param name="linkGenerator"></param>
/// <param name="features"></param>
/// <param name="globalSettings"></param>
/// <param name="umbracoVersion"></param>
/// <param name="contentSettings"></param>
/// <param name="treeCollection"></param>
/// <param name="httpContextAccessor"></param>
/// <param name="hostingEnvironment"></param>
/// <param name="settings"></param>
/// <param name="securitySettings"></param>
/// <param name="runtimeMinifier"></param>
/// <returns></returns>
/// <remarks>
/// These are the bare minimal server variables that are required for the application to start without being
/// authenticated,
/// we will load the rest of the server vars after the user is authenticated.
/// </remarks>
public static async Task<IHtmlContent> BareMinimumServerVariablesScriptAsync(this IHtmlHelper html,
BackOfficeServerVariables backOfficeServerVariables)
{
Dictionary<string, object> minVars = await backOfficeServerVariables.BareMinimumServerVariablesAsync();
var str = @"<script type=""text/javascript"">
var Umbraco = {};
Umbraco.Sys = {};
Umbraco.Sys.ServerVariables = " + JsonConvert.SerializeObject(minVars) + @";
</script>";
return html.Raw(str);
}
return html.Raw(str);
}
/// <summary>
/// Used to render the script that will pass in the angular "externalLoginInfo" service/value on page load
/// </summary>
/// <param name="html"></param>
/// <param name="externalLogins"></param>
/// <returns></returns>
public static async Task<IHtmlContent> AngularValueExternalLoginInfoScriptAsync(this IHtmlHelper html,
IBackOfficeExternalLoginProviders externalLogins,
BackOfficeExternalLoginProviderErrors externalLoginErrors)
{
var providers = await externalLogins.GetBackOfficeProvidersAsync();
/// <summary>
/// Used to render the script that will pass in the angular "externalLoginInfo" service/value on page load
/// </summary>
/// <param name="html"></param>
/// <param name="externalLogins"></param>
/// <returns></returns>
public static async Task<IHtmlContent> AngularValueExternalLoginInfoScriptAsync(this IHtmlHelper html,
IBackOfficeExternalLoginProviders externalLogins,
BackOfficeExternalLoginProviderErrors externalLoginErrors)
{
IEnumerable<BackOfficeExternaLoginProviderScheme>
providers = await externalLogins.GetBackOfficeProvidersAsync();
var loginProviders = providers
.Select(p => new
{
authType = p.ExternalLoginProvider.AuthenticationType,
caption = p.AuthenticationScheme.DisplayName,
properties = p.ExternalLoginProvider.Options
})
.ToArray();
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine(@"var errors = [];");
if (externalLoginErrors != null)
var loginProviders = providers
.Select(p => new
{
if (externalLoginErrors.Errors is not null)
authType = p.ExternalLoginProvider.AuthenticationType,
caption = p.AuthenticationScheme.DisplayName,
properties = p.ExternalLoginProvider.Options
})
.ToArray();
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine(@"var errors = [];");
if (externalLoginErrors != null)
{
if (externalLoginErrors.Errors is not null)
{
foreach (var error in externalLoginErrors.Errors)
{
foreach (var error in externalLoginErrors.Errors)
{
sb.AppendFormat(@"errors.push(""{0}"");", error.ToSingleLine()).AppendLine();
}
sb.AppendFormat(@"errors.push(""{0}"");", error.ToSingleLine()).AppendLine();
}
}
sb.AppendLine(@"app.value(""externalLoginInfo"", {");
if (externalLoginErrors?.AuthenticationType != null)
sb.AppendLine($@"errorProvider: '{externalLoginErrors.AuthenticationType}',");
sb.AppendLine(@"errors: errors,");
sb.Append(@"providers: ");
sb.AppendLine(JsonConvert.SerializeObject(loginProviders));
sb.AppendLine(@"});");
return html.Raw(sb.ToString());
}
/// <summary>
/// Used to render the script that will pass in the angular "resetPasswordCodeInfo" service/value on page load
/// </summary>
/// <param name="html"></param>
/// <param name="val"></param>
/// <returns></returns>
public static IHtmlContent AngularValueResetPasswordCodeInfoScript(this IHtmlHelper html, object val)
sb.AppendLine(@"app.value(""externalLoginInfo"", {");
if (externalLoginErrors?.AuthenticationType != null)
{
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine(@"var errors = [];");
sb.AppendLine($@"errorProvider: '{externalLoginErrors.AuthenticationType}',");
}
if (val is IEnumerable<string> errors)
sb.AppendLine(@"errors: errors,");
sb.Append(@"providers: ");
sb.AppendLine(JsonConvert.SerializeObject(loginProviders));
sb.AppendLine(@"});");
return html.Raw(sb.ToString());
}
/// <summary>
/// Used to render the script that will pass in the angular "resetPasswordCodeInfo" service/value on page load
/// </summary>
/// <param name="html"></param>
/// <param name="val"></param>
/// <returns></returns>
public static IHtmlContent AngularValueResetPasswordCodeInfoScript(this IHtmlHelper html, object val)
{
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine(@"var errors = [];");
if (val is IEnumerable<string> errors)
{
foreach (var error in errors)
{
foreach (var error in errors)
{
sb.AppendFormat(@"errors.push(""{0}"");", error).AppendLine();
}
sb.AppendFormat(@"errors.push(""{0}"");", error).AppendLine();
}
sb.AppendLine(@"app.value(""resetPasswordCodeInfo"", {");
sb.AppendLine(@"errors: errors,");
sb.Append(@"resetCodeModel: ");
sb.AppendLine(val?.ToString() ?? "null");
sb.AppendLine(@"});");
return html.Raw(sb.ToString());
}
public static async Task<IHtmlContent> AngularValueTinyMceAssetsAsync(this IHtmlHelper html, IRuntimeMinifier runtimeMinifier)
{
var files = await runtimeMinifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoTinyMceJsBundleName);
sb.AppendLine(@"app.value(""resetPasswordCodeInfo"", {");
sb.AppendLine(@"errors: errors,");
sb.Append(@"resetCodeModel: ");
sb.AppendLine(val?.ToString() ?? "null");
sb.AppendLine(@"});");
var sb = new StringBuilder();
return html.Raw(sb.ToString());
}
sb.AppendLine(@"app.value(""tinyMceAssets"",");
sb.AppendLine(JsonConvert.SerializeObject(files));
sb.AppendLine(@");");
public static async Task<IHtmlContent> AngularValueTinyMceAssetsAsync(this IHtmlHelper html,
IRuntimeMinifier runtimeMinifier)
{
IEnumerable<string> files =
await runtimeMinifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoTinyMceJsBundleName);
var sb = new StringBuilder();
sb.AppendLine(@"app.value(""tinyMceAssets"",");
sb.AppendLine(JsonConvert.SerializeObject(files));
sb.AppendLine(@");");
return html.Raw(sb.ToString());
}
return html.Raw(sb.ToString());
}
}

View File

@@ -1,17 +1,13 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core.Security;
namespace Umbraco.Extensions
namespace Umbraco.Extensions;
public static class HttpContextExtensions
{
public static class HttpContextExtensions
{
public static void SetExternalLoginProviderErrors(this HttpContext httpContext, BackOfficeExternalLoginProviderErrors errors)
=> httpContext.Items[nameof(BackOfficeExternalLoginProviderErrors)] = errors;
public static void SetExternalLoginProviderErrors(this HttpContext httpContext, BackOfficeExternalLoginProviderErrors errors)
=> httpContext.Items[nameof(BackOfficeExternalLoginProviderErrors)] = errors;
public static BackOfficeExternalLoginProviderErrors? GetExternalLoginProviderErrors(this HttpContext httpContext)
=> httpContext.Items[nameof(BackOfficeExternalLoginProviderErrors)] as BackOfficeExternalLoginProviderErrors;
}
public static BackOfficeExternalLoginProviderErrors? GetExternalLoginProviderErrors(this HttpContext httpContext)
=> httpContext.Items[nameof(BackOfficeExternalLoginProviderErrors)] as BackOfficeExternalLoginProviderErrors;
}

View File

@@ -2,41 +2,40 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.Security;
namespace Umbraco.Extensions
namespace Umbraco.Extensions;
/// <summary>
/// Extension methods for <see cref="IdentityBuilder" />
/// </summary>
public static class IdentityBuilderExtensions
{
/// <summary>
/// Extension methods for <see cref="IdentityBuilder"/>
/// Adds a <see cref="UserManager{TUser}" /> implementation for <seealso cref="BackOfficeIdentityUser" />
/// </summary>
public static class IdentityBuilderExtensions
/// <typeparam name="TInterface">The usermanager interface</typeparam>
/// <typeparam name="TUserManager">The usermanager type</typeparam>
/// <param name="identityBuilder">The <see cref="IdentityBuilder" /></param>
/// <returns>The current <see cref="IdentityBuilder" /> instance.</returns>
public static IdentityBuilder AddUserManager<TInterface, TUserManager>(this IdentityBuilder identityBuilder)
where TUserManager : UserManager<BackOfficeIdentityUser>, TInterface
{
/// <summary>
/// Adds a <see cref="UserManager{TUser}"/> implementation for <seealso cref="BackOfficeIdentityUser"/>
/// </summary>
/// <typeparam name="TInterface">The usermanager interface</typeparam>
/// <typeparam name="TUserManager">The usermanager type</typeparam>
/// <param name="identityBuilder">The <see cref="IdentityBuilder"/></param>
/// <returns>The current <see cref="IdentityBuilder"/> instance.</returns>
public static IdentityBuilder AddUserManager<TInterface, TUserManager>(this IdentityBuilder identityBuilder)
where TUserManager : UserManager<BackOfficeIdentityUser>, TInterface
{
identityBuilder.AddUserManager<TUserManager>();
identityBuilder.Services.AddScoped(typeof(TInterface), typeof(TUserManager));
return identityBuilder;
}
identityBuilder.AddUserManager<TUserManager>();
identityBuilder.Services.AddScoped(typeof(TInterface), typeof(TUserManager));
return identityBuilder;
}
/// <summary>
/// Adds a <see cref="SignInManager{TUser}"/> implementation for <seealso cref="BackOfficeIdentityUser"/>
/// </summary>
/// <typeparam name="TInterface">The sign in manager interface</typeparam>
/// <typeparam name="TSignInManager">The sign in manager type</typeparam>
/// <param name="identityBuilder">The <see cref="IdentityBuilder"/></param>
/// <returns>The current <see cref="IdentityBuilder"/> instance.</returns>
public static IdentityBuilder AddSignInManager<TInterface, TSignInManager>(this IdentityBuilder identityBuilder)
where TSignInManager : SignInManager<BackOfficeIdentityUser>, TInterface
{
identityBuilder.AddSignInManager<TSignInManager>();
identityBuilder.Services.AddScoped(typeof(TInterface), typeof(TSignInManager));
return identityBuilder;
}
/// <summary>
/// Adds a <see cref="SignInManager{TUser}" /> implementation for <seealso cref="BackOfficeIdentityUser" />
/// </summary>
/// <typeparam name="TInterface">The sign in manager interface</typeparam>
/// <typeparam name="TSignInManager">The sign in manager type</typeparam>
/// <param name="identityBuilder">The <see cref="IdentityBuilder" /></param>
/// <returns>The current <see cref="IdentityBuilder" /> instance.</returns>
public static IdentityBuilder AddSignInManager<TInterface, TSignInManager>(this IdentityBuilder identityBuilder)
where TSignInManager : SignInManager<BackOfficeIdentityUser>, TInterface
{
identityBuilder.AddSignInManager<TSignInManager>();
identityBuilder.Services.AddScoped(typeof(TInterface), typeof(TSignInManager));
return identityBuilder;
}
}

View File

@@ -1,25 +1,23 @@
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing;
using Umbraco.Cms.Core;
using Umbraco.Cms.Web.BackOffice.Install;
namespace Umbraco.Extensions
namespace Umbraco.Extensions;
public static class BackofficeLinkGeneratorExtensions
{
public static class BackofficeLinkGeneratorExtensions
{
/// <summary>
/// Returns the URL for the installer
/// </summary>
public static string? GetInstallerUrl(this LinkGenerator linkGenerator)
=> linkGenerator.GetPathByAction(nameof(InstallController.Index), ControllerExtensions.GetControllerName<InstallController>(), new { area = Cms.Core.Constants.Web.Mvc.InstallArea });
/// <summary>
/// Returns the URL for the installer
/// </summary>
public static string? GetInstallerUrl(this LinkGenerator linkGenerator)
=> linkGenerator.GetPathByAction(nameof(InstallController.Index), ControllerExtensions.GetControllerName<InstallController>(), new { area = Constants.Web.Mvc.InstallArea });
/// <summary>
/// Returns the URL for the installer api
/// </summary>
public static string? GetInstallerApiUrl(this LinkGenerator linkGenerator)
=> linkGenerator.GetPathByAction(
nameof(InstallApiController.GetSetup),
ControllerExtensions.GetControllerName<InstallApiController>(),
new { area = Cms.Core.Constants.Web.Mvc.InstallArea })?.TrimEnd(nameof(InstallApiController.GetSetup));
}
/// <summary>
/// Returns the URL for the installer api
/// </summary>
public static string? GetInstallerApiUrl(this LinkGenerator linkGenerator)
=> linkGenerator.GetPathByAction(
nameof(InstallApiController.GetSetup),
ControllerExtensions.GetControllerName<InstallApiController>(),
new { area = Constants.Web.Mvc.InstallArea })?.TrimEnd(nameof(InstallApiController.GetSetup));
}

View File

@@ -1,168 +1,169 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Umbraco.Cms.Web.BackOffice.PropertyEditors.Validation;
using Umbraco.Extensions;
namespace Umbraco.Extensions
namespace Umbraco.Extensions;
public static class ModelStateExtensions
{
public static class ModelStateExtensions
/// <summary>
/// Adds the <see cref="ValidationResult" /> to the model state with the appropriate keys for property errors
/// </summary>
/// <param name="modelState"></param>
/// <param name="result"></param>
/// <param name="propertyAlias"></param>
/// <param name="culture"></param>
/// <param name="segment"></param>
internal static void AddPropertyValidationError(this ModelStateDictionary modelState,
ValidationResult result,
string propertyAlias,
string culture = "",
string segment = "") =>
modelState.AddValidationError(
result,
"_Properties",
propertyAlias,
//if the culture is null, we'll add the term 'invariant' as part of the key
culture.IsNullOrWhiteSpace() ? "invariant" : culture,
// if the segment is null, we'll add the term 'null' as part of the key
segment.IsNullOrWhiteSpace() ? "null" : segment);
/// <summary>
/// Adds an <see cref="ContentPropertyValidationResult" /> error to model state for a property so we can use it on the
/// client side.
/// </summary>
/// <param name="modelState"></param>
/// <param name="result"></param>
/// <param name="propertyAlias"></param>
/// <param name="culture">The culture for the property, if the property is invariant than this is empty</param>
internal static void AddPropertyError(this ModelStateDictionary modelState,
ValidationResult result, string propertyAlias, string culture = "", string segment = "") =>
modelState.AddPropertyValidationError(new ContentPropertyValidationResult(result, culture, segment),
propertyAlias, culture, segment);
/// <summary>
/// Adds a generic culture error for use in displaying the culture validation error in the save/publish/etc... dialogs
/// </summary>
/// <param name="modelState"></param>
/// <param name="culture"></param>
/// <param name="segment"></param>
/// <param name="errMsg"></param>
internal static void AddVariantValidationError(this ModelStateDictionary modelState,
string? culture, string? segment, string errMsg)
{
/// <summary>
/// Adds the <see cref="ValidationResult"/> to the model state with the appropriate keys for property errors
/// </summary>
/// <param name="modelState"></param>
/// <param name="result"></param>
/// <param name="propertyAlias"></param>
/// <param name="culture"></param>
/// <param name="segment"></param>
internal static void AddPropertyValidationError(this ModelStateDictionary modelState,
ValidationResult result,
string propertyAlias,
string culture = "",
string segment = "") =>
modelState.AddValidationError(
result,
"_Properties",
propertyAlias,
//if the culture is null, we'll add the term 'invariant' as part of the key
culture.IsNullOrWhiteSpace() ? "invariant" : culture,
// if the segment is null, we'll add the term 'null' as part of the key
segment.IsNullOrWhiteSpace() ? "null" : segment);
/// <summary>
/// Adds an <see cref="ContentPropertyValidationResult"/> error to model state for a property so we can use it on the client side.
/// </summary>
/// <param name="modelState"></param>
/// <param name="result"></param>
/// <param name="propertyAlias"></param>
/// <param name="culture">The culture for the property, if the property is invariant than this is empty</param>
internal static void AddPropertyError(this ModelStateDictionary modelState,
ValidationResult result, string propertyAlias, string culture = "", string segment = "") =>
modelState.AddPropertyValidationError(new ContentPropertyValidationResult(result, culture, segment), propertyAlias, culture, segment);
/// <summary>
/// Adds a generic culture error for use in displaying the culture validation error in the save/publish/etc... dialogs
/// </summary>
/// <param name="modelState"></param>
/// <param name="culture"></param>
/// <param name="segment"></param>
/// <param name="errMsg"></param>
internal static void AddVariantValidationError(this ModelStateDictionary modelState,
string? culture, string? segment, string errMsg)
var key = "_content_variant_" + (culture.IsNullOrWhiteSpace() ? "invariant" : culture) + "_" +
(segment.IsNullOrWhiteSpace() ? "null" : segment) + "_";
if (modelState.ContainsKey(key))
{
var key = "_content_variant_" + (culture.IsNullOrWhiteSpace() ? "invariant" : culture) + "_" + (segment.IsNullOrWhiteSpace() ? "null" : segment) + "_";
if (modelState.ContainsKey(key))
{
return;
}
modelState.AddModelError(key, errMsg);
return;
}
/// <summary>
/// Returns a list of cultures that have property validation errors
/// </summary>
/// <param name="modelState"></param>
/// <param name="localizationService"></param>
/// <param name="cultureForInvariantErrors">The culture to affiliate invariant errors with</param>
/// <returns>
/// A list of cultures that have property validation errors. The default culture will be returned for any invariant property errors.
/// </returns>
internal static IReadOnlyList<(string? culture, string? segment)>? GetVariantsWithPropertyErrors(this ModelStateDictionary modelState,
string? cultureForInvariantErrors)
{
//Add any variant specific errors here
var variantErrors = modelState.Keys
.Where(key => key.StartsWith("_Properties.")) //only choose _Properties errors
.Select(x => x.Split('.')) //split into parts
.Where(x => x.Length >= 4 && !x[2].IsNullOrWhiteSpace() && !x[3].IsNullOrWhiteSpace())
.Select(x => (culture: x[2], segment: x[3]))
//if the culture is marked "invariant" than return the default language, this is because we can only edit invariant properties on the default language
//so errors for those must show up under the default lang.
//if the segment is marked "null" then return an actual null
.Select(x =>
{
var culture = x.culture == "invariant" ? cultureForInvariantErrors : x.culture;
var segment = x.segment == "null" ? null : x.segment;
return (culture, segment);
})
.Distinct()
.ToList();
modelState.AddModelError(key, errMsg);
}
return variantErrors;
/// <summary>
/// Returns a list of cultures that have property validation errors
/// </summary>
/// <param name="modelState"></param>
/// <param name="localizationService"></param>
/// <param name="cultureForInvariantErrors">The culture to affiliate invariant errors with</param>
/// <returns>
/// A list of cultures that have property validation errors. The default culture will be returned for any invariant
/// property errors.
/// </returns>
internal static IReadOnlyList<(string? culture, string? segment)>? GetVariantsWithPropertyErrors(
this ModelStateDictionary modelState,
string? cultureForInvariantErrors)
{
//Add any variant specific errors here
var variantErrors = modelState.Keys
.Where(key => key.StartsWith("_Properties.")) //only choose _Properties errors
.Select(x => x.Split('.')) //split into parts
.Where(x => x.Length >= 4 && !x[2].IsNullOrWhiteSpace() && !x[3].IsNullOrWhiteSpace())
.Select(x => (culture: x[2], segment: x[3]))
//if the culture is marked "invariant" than return the default language, this is because we can only edit invariant properties on the default language
//so errors for those must show up under the default lang.
//if the segment is marked "null" then return an actual null
.Select(x =>
{
var culture = x.culture == "invariant" ? cultureForInvariantErrors : x.culture;
var segment = x.segment == "null" ? null : x.segment;
return (culture, segment);
})
.Distinct()
.ToList();
return variantErrors;
}
/// <summary>
/// Returns a list of cultures that have any validation errors
/// </summary>
/// <param name="modelState"></param>
/// <param name="localizationService"></param>
/// <param name="cultureForInvariantErrors">The culture to affiliate invariant errors with</param>
/// <returns>
/// A list of cultures that have validation errors. The default culture will be returned for any invariant errors.
/// </returns>
internal static IReadOnlyList<(string? culture, string? segment)>? GetVariantsWithErrors(
this ModelStateDictionary modelState, string? cultureForInvariantErrors)
{
IReadOnlyList<(string? culture, string? segment)>? propertyVariantErrors =
modelState.GetVariantsWithPropertyErrors(cultureForInvariantErrors);
//now check the other special variant errors that are
IEnumerable<(string? culture, string? segment)>? genericVariantErrors = modelState.Keys
.Where(x => x.StartsWith("_content_variant_") && x.EndsWith("_"))
.Select(x => x.TrimStart("_content_variant_").TrimEnd("_"))
.Select(x =>
{
// Format "<culture>_<segment>"
var cs = x.Split(new[] { '_' });
return (culture: cs[0], segment: cs[1]);
})
.Where(x => !x.culture.IsNullOrWhiteSpace())
//if it's marked "invariant" than return the default language, this is because we can only edit invariant properties on the default language
//so errors for those must show up under the default lang.
//if the segment is marked "null" then return an actual null
.Select(x =>
{
var culture = x.culture == "invariant" ? cultureForInvariantErrors : x.culture;
var segment = x.segment == "null" ? null : x.segment;
return (culture, segment);
})
.Distinct();
return propertyVariantErrors?.Union(genericVariantErrors).Distinct().ToList();
}
/// <summary>
/// Adds the error to model state correctly for a property so we can use it on the client side.
/// </summary>
/// <param name="modelState"></param>
/// <param name="result"></param>
/// <param name="parts">
/// Each model state validation error has a name and in most cases this name is made up of parts which are delimited by
/// a '.'
/// </param>
internal static void AddValidationError(this ModelStateDictionary modelState,
ValidationResult result, params string[] parts)
{
// if there are assigned member names, we combine the member name with the owner name
// so that we can try to match it up to a real field. otherwise, we assume that the
// validation message is for the overall owner.
// Owner = the component being validated, like a content property but could be just an HTML field on another editor
var withNames = false;
var delimitedParts = string.Join(".", parts);
foreach (var memberName in result.MemberNames)
{
modelState.TryAddModelError($"{delimitedParts}.{memberName}", result.ErrorMessage ?? string.Empty);
withNames = true;
}
/// <summary>
/// Returns a list of cultures that have any validation errors
/// </summary>
/// <param name="modelState"></param>
/// <param name="localizationService"></param>
/// <param name="cultureForInvariantErrors">The culture to affiliate invariant errors with</param>
/// <returns>
/// A list of cultures that have validation errors. The default culture will be returned for any invariant errors.
/// </returns>
internal static IReadOnlyList<(string? culture, string? segment)>? GetVariantsWithErrors(this ModelStateDictionary modelState, string? cultureForInvariantErrors)
if (!withNames)
{
IReadOnlyList<(string? culture, string? segment)>? propertyVariantErrors = modelState.GetVariantsWithPropertyErrors(cultureForInvariantErrors);
//now check the other special variant errors that are
IEnumerable<(string? culture, string? segment)>? genericVariantErrors = modelState.Keys
.Where(x => x.StartsWith("_content_variant_") && x.EndsWith("_"))
.Select(x => x.TrimStart("_content_variant_").TrimEnd("_"))
.Select(x =>
{
// Format "<culture>_<segment>"
var cs = x.Split(new[] { '_' });
return (culture: cs[0], segment: cs[1]);
})
.Where(x => !x.culture.IsNullOrWhiteSpace())
//if it's marked "invariant" than return the default language, this is because we can only edit invariant properties on the default language
//so errors for those must show up under the default lang.
//if the segment is marked "null" then return an actual null
.Select(x =>
{
var culture = x.culture == "invariant" ? cultureForInvariantErrors : x.culture;
var segment = x.segment == "null" ? null : x.segment;
return (culture, segment);
})
.Distinct();
return propertyVariantErrors?.Union(genericVariantErrors).Distinct().ToList();
}
/// <summary>
/// Adds the error to model state correctly for a property so we can use it on the client side.
/// </summary>
/// <param name="modelState"></param>
/// <param name="result"></param>
/// <param name="parts">
/// Each model state validation error has a name and in most cases this name is made up of parts which are delimited by a '.'
/// </param>
internal static void AddValidationError(this ModelStateDictionary modelState,
ValidationResult result, params string[] parts)
{
// if there are assigned member names, we combine the member name with the owner name
// so that we can try to match it up to a real field. otherwise, we assume that the
// validation message is for the overall owner.
// Owner = the component being validated, like a content property but could be just an HTML field on another editor
var withNames = false;
var delimitedParts = string.Join(".", parts);
foreach (var memberName in result.MemberNames)
{
modelState.TryAddModelError($"{delimitedParts}.{memberName}", result.ErrorMessage ?? string.Empty);
withNames = true;
}
if (!withNames)
{
modelState.TryAddModelError($"{delimitedParts}", result.ToString());
}
modelState.TryAddModelError($"{delimitedParts}", result.ToString());
}
}
}

View File

@@ -1,110 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Manifest;
using Umbraco.Cms.Core.WebAssets;
using Umbraco.Cms.Infrastructure.WebAssets;
namespace Umbraco.Extensions
namespace Umbraco.Extensions;
public static class RuntimeMinifierExtensions
{
public static class RuntimeMinifierExtensions
/// <summary>
/// Returns the JavaScript to load the back office's assets
/// </summary>
/// <returns></returns>
public static async Task<string> GetScriptForLoadingBackOfficeAsync(
this IRuntimeMinifier minifier,
GlobalSettings globalSettings,
IHostingEnvironment hostingEnvironment,
IManifestParser manifestParser)
{
/// <summary>
/// Returns the JavaScript to load the back office's assets
/// </summary>
/// <returns></returns>
public static async Task<string> GetScriptForLoadingBackOfficeAsync(
this IRuntimeMinifier minifier,
GlobalSettings globalSettings,
IHostingEnvironment hostingEnvironment,
IManifestParser manifestParser)
var files = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
foreach (var file in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoCoreJsBundleName))
{
var files = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
foreach(var file in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoCoreJsBundleName))
{
files.Add(file);
}
foreach (var file in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoExtensionsJsBundleName))
{
files.Add(file);
}
// process the independent bundles
if (manifestParser.CombinedManifest.Scripts.TryGetValue(BundleOptions.Independent, out IReadOnlyList<ManifestAssets>? independentManifestAssetsList))
{
foreach (ManifestAssets manifestAssets in independentManifestAssetsList)
{
var bundleName = BackOfficeWebAssets.GetIndependentPackageBundleName(manifestAssets, AssetType.Javascript);
foreach(var asset in await minifier.GetJsAssetPathsAsync(bundleName))
{
files.Add(asset);
}
}
}
// process the "None" bundles, meaning we'll just render the script as-is
foreach (var asset in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoNonOptimizedPackageJsBundleName))
{
files.Add(asset);
}
var result = BackOfficeJavaScriptInitializer.GetJavascriptInitialization(
files,
"umbraco",
globalSettings,
hostingEnvironment);
result += await GetStylesheetInitializationAsync(minifier, manifestParser);
return result;
files.Add(file);
}
/// <summary>
/// Gets the back office css bundle paths and formats a JS call to lazy load them
/// </summary>
private static async Task<string> GetStylesheetInitializationAsync(
IRuntimeMinifier minifier,
IManifestParser manifestParser)
foreach (var file in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoExtensionsJsBundleName))
{
var files = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
foreach(var file in await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets.UmbracoCssBundleName))
{
files.Add(file);
}
// process the independent bundles
if (manifestParser.CombinedManifest.Stylesheets.TryGetValue(BundleOptions.Independent, out IReadOnlyList<ManifestAssets>? independentManifestAssetsList))
{
foreach (ManifestAssets manifestAssets in independentManifestAssetsList)
{
var bundleName = BackOfficeWebAssets.GetIndependentPackageBundleName(manifestAssets, AssetType.Css);
foreach (var asset in await minifier.GetCssAssetPathsAsync(bundleName))
{
files.Add(asset);
}
}
}
// process the "None" bundles, meaning we'll just render the script as-is
foreach (var asset in await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets.UmbracoNonOptimizedPackageCssBundleName))
{
files.Add(asset);
}
var sb = new StringBuilder();
foreach (string file in files)
{
sb.AppendFormat("{0}LazyLoad.css('{1}');", Environment.NewLine, file);
}
return sb.ToString();
files.Add(file);
}
// process the independent bundles
if (manifestParser.CombinedManifest.Scripts.TryGetValue(BundleOptions.Independent,
out IReadOnlyList<ManifestAssets>? independentManifestAssetsList))
{
foreach (ManifestAssets manifestAssets in independentManifestAssetsList)
{
var bundleName =
BackOfficeWebAssets.GetIndependentPackageBundleName(manifestAssets, AssetType.Javascript);
foreach (var asset in await minifier.GetJsAssetPathsAsync(bundleName))
{
files.Add(asset);
}
}
}
// process the "None" bundles, meaning we'll just render the script as-is
foreach (var asset in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets
.UmbracoNonOptimizedPackageJsBundleName))
{
files.Add(asset);
}
var result = BackOfficeJavaScriptInitializer.GetJavascriptInitialization(
files,
"umbraco",
globalSettings,
hostingEnvironment);
result += await GetStylesheetInitializationAsync(minifier, manifestParser);
return result;
}
/// <summary>
/// Gets the back office css bundle paths and formats a JS call to lazy load them
/// </summary>
private static async Task<string> GetStylesheetInitializationAsync(
IRuntimeMinifier minifier,
IManifestParser manifestParser)
{
var files = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
foreach (var file in await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets.UmbracoCssBundleName))
{
files.Add(file);
}
// process the independent bundles
if (manifestParser.CombinedManifest.Stylesheets.TryGetValue(BundleOptions.Independent,
out IReadOnlyList<ManifestAssets>? independentManifestAssetsList))
{
foreach (ManifestAssets manifestAssets in independentManifestAssetsList)
{
var bundleName = BackOfficeWebAssets.GetIndependentPackageBundleName(manifestAssets, AssetType.Css);
foreach (var asset in await minifier.GetCssAssetPathsAsync(bundleName))
{
files.Add(asset);
}
}
}
// process the "None" bundles, meaning we'll just render the script as-is
foreach (var asset in await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets
.UmbracoNonOptimizedPackageCssBundleName))
{
files.Add(asset);
}
var sb = new StringBuilder();
foreach (var file in files)
{
sb.AppendFormat("{0}LazyLoad.css('{1}');", Environment.NewLine, file);
}
return sb.ToString();
}
}

View File

@@ -1,4 +1,3 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@@ -8,51 +7,50 @@ using Umbraco.Cms.Web.BackOffice.Middleware;
using Umbraco.Cms.Web.BackOffice.Routing;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace Umbraco.Extensions;
namespace Umbraco.Extensions
/// <summary>
/// <see cref="IUmbracoEndpointBuilderContext" /> extensions for Umbraco
/// </summary>
public static partial class UmbracoApplicationBuilderExtensions
{
/// <summary>
/// <see cref="IUmbracoEndpointBuilderContext"/> extensions for Umbraco
/// Adds all required middleware to run the back office
/// </summary>
public static partial class UmbracoApplicationBuilderExtensions
/// <param name="builder"></param>
/// <returns></returns>
public static IUmbracoApplicationBuilderContext UseBackOffice(this IUmbracoApplicationBuilderContext builder)
{
/// <summary>
/// Adds all required middleware to run the back office
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static IUmbracoApplicationBuilderContext UseBackOffice(this IUmbracoApplicationBuilderContext builder)
{
KeepAliveSettings keepAliveSettings = builder.ApplicationServices.GetRequiredService<IOptions<KeepAliveSettings>>().Value;
IHostingEnvironment hostingEnvironment = builder.ApplicationServices.GetRequiredService<IHostingEnvironment>();
builder.AppBuilder.Map(
hostingEnvironment.ToAbsolute(keepAliveSettings.KeepAlivePingUrl),
a => a.UseMiddleware<KeepAliveMiddleware>());
KeepAliveSettings keepAliveSettings =
builder.ApplicationServices.GetRequiredService<IOptions<KeepAliveSettings>>().Value;
IHostingEnvironment hostingEnvironment = builder.ApplicationServices.GetRequiredService<IHostingEnvironment>();
builder.AppBuilder.Map(
hostingEnvironment.ToAbsolute(keepAliveSettings.KeepAlivePingUrl),
a => a.UseMiddleware<KeepAliveMiddleware>());
builder.AppBuilder.UseMiddleware<BackOfficeExternalLoginProviderErrorMiddleware>();
return builder;
builder.AppBuilder.UseMiddleware<BackOfficeExternalLoginProviderErrorMiddleware>();
return builder;
}
public static IUmbracoEndpointBuilderContext UseBackOfficeEndpoints(this IUmbracoEndpointBuilderContext app)
{
// NOTE: This method will have been called after UseRouting, UseAuthentication, UseAuthorization
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
public static IUmbracoEndpointBuilderContext UseBackOfficeEndpoints(this IUmbracoEndpointBuilderContext app)
if (!app.RuntimeState.UmbracoCanBoot())
{
// NOTE: This method will have been called after UseRouting, UseAuthentication, UseAuthorization
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (!app.RuntimeState.UmbracoCanBoot())
{
return app;
}
BackOfficeAreaRoutes backOfficeRoutes = app.ApplicationServices.GetRequiredService<BackOfficeAreaRoutes>();
backOfficeRoutes.CreateRoutes(app.EndpointRouteBuilder);
app.UseUmbracoRuntimeMinificationEndpoints();
app.UseUmbracoPreviewEndpoints();
return app;
}
BackOfficeAreaRoutes backOfficeRoutes = app.ApplicationServices.GetRequiredService<BackOfficeAreaRoutes>();
backOfficeRoutes.CreateRoutes(app.EndpointRouteBuilder);
app.UseUmbracoRuntimeMinificationEndpoints();
app.UseUmbracoPreviewEndpoints();
return app;
}
}

View File

@@ -3,27 +3,26 @@ using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Web.BackOffice.Install;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace Umbraco.Extensions
namespace Umbraco.Extensions;
/// <summary>
/// <see cref="IApplicationBuilder" /> extensions for Umbraco installer
/// </summary>
public static partial class UmbracoApplicationBuilderExtensions
{
/// <summary>
/// <see cref="IApplicationBuilder"/> extensions for Umbraco installer
/// Enables the Umbraco installer
/// </summary>
public static partial class UmbracoApplicationBuilderExtensions
public static IUmbracoEndpointBuilderContext UseInstallerEndpoints(this IUmbracoEndpointBuilderContext app)
{
/// <summary>
/// Enables the Umbraco installer
/// </summary>
public static IUmbracoEndpointBuilderContext UseInstallerEndpoints(this IUmbracoEndpointBuilderContext app)
if (!app.RuntimeState.UmbracoCanBoot())
{
if (!app.RuntimeState.UmbracoCanBoot())
{
return app;
}
InstallAreaRoutes installerRoutes = app.ApplicationServices.GetRequiredService<InstallAreaRoutes>();
installerRoutes.CreateRoutes(app.EndpointRouteBuilder);
return app;
}
InstallAreaRoutes installerRoutes = app.ApplicationServices.GetRequiredService<InstallAreaRoutes>();
installerRoutes.CreateRoutes(app.EndpointRouteBuilder);
return app;
}
}

View File

@@ -1,21 +1,19 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Web.BackOffice.Routing;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace Umbraco.Extensions
{
/// <summary>
/// <see cref="IUmbracoEndpointBuilderContext"/> extensions for Umbraco
/// </summary>
public static partial class UmbracoApplicationBuilderExtensions
{
public static IUmbracoEndpointBuilderContext UseUmbracoPreviewEndpoints(this IUmbracoEndpointBuilderContext app)
{
PreviewRoutes previewRoutes = app.ApplicationServices.GetRequiredService<PreviewRoutes>();
previewRoutes.CreateRoutes(app.EndpointRouteBuilder);
namespace Umbraco.Extensions;
return app;
}
/// <summary>
/// <see cref="IUmbracoEndpointBuilderContext" /> extensions for Umbraco
/// </summary>
public static partial class UmbracoApplicationBuilderExtensions
{
public static IUmbracoEndpointBuilderContext UseUmbracoPreviewEndpoints(this IUmbracoEndpointBuilderContext app)
{
PreviewRoutes previewRoutes = app.ApplicationServices.GetRequiredService<PreviewRoutes>();
previewRoutes.CreateRoutes(app.EndpointRouteBuilder);
return app;
}
}

View File

@@ -3,20 +3,19 @@ using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Web.BackOffice.Mapping;
namespace Umbraco.Extensions
namespace Umbraco.Extensions;
public static class WebMappingProfiles
{
public static class WebMappingProfiles
public static IUmbracoBuilder AddWebMappingProfiles(this IUmbracoBuilder builder)
{
public static IUmbracoBuilder AddWebMappingProfiles(this IUmbracoBuilder builder)
{
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>()
.Add<ContentMapDefinition>()
.Add<MediaMapDefinition>()
.Add<MemberMapDefinition>();
builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>()
.Add<ContentMapDefinition>()
.Add<MediaMapDefinition>()
.Add<MemberMapDefinition>();
builder.Services.AddTransient<CommonTreeNodeMapper>();
builder.Services.AddTransient<CommonTreeNodeMapper>();
return builder;
}
return builder;
}
}