Merge remote-tracking branch 'origin/v13/dev' into v14/dev

# Conflicts:
#	build/azure-pipelines.yml
#	src/Umbraco.Cms.Api.Delivery/Controllers/DeliveryApiControllerBase.cs
#	src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
#	src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs
#	src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs
#	src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
#	tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/PackageDataInstallationTests.cs
#	tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/LocalizationServiceTests.cs
This commit is contained in:
Bjarke Berg
2023-08-28 11:46:22 +02:00
126 changed files with 2396 additions and 831 deletions

View File

@@ -1,30 +1,47 @@
namespace Umbraco.Cms.Web.Common.ApplicationBuilder;
/// <summary>
/// The context object used during
/// The context object used when building the Umbraco application.
/// </summary>
/// <seealso cref="Umbraco.Cms.Web.Common.ApplicationBuilder.IUmbracoApplicationBuilderServices" />
public interface IUmbracoApplicationBuilderContext : IUmbracoApplicationBuilderServices
{
/// <summary>
/// Called to include the core umbraco middleware.
/// Called to include the core Umbraco middlewares.
/// </summary>
void UseUmbracoCoreMiddleware();
/// <summary>
/// Manually runs the <see cref="IUmbracoPipelineFilter" /> pre pipeline filters
/// Manually runs the <see cref="IUmbracoPipelineFilter" /> pre pipeline filters.
/// </summary>
void RunPrePipeline();
/// <summary>
/// Manually runs the <see cref="IUmbracoPipelineFilter " /> post pipeline filters
/// Manually runs the <see cref="IUmbracoPipelineFilter" /> pre routing filters.
/// </summary>
void RunPreRouting()
{
// TODO: Remove default implementation in Umbraco 13
}
/// <summary>
/// Manually runs the <see cref="IUmbracoPipelineFilter" /> post routing filters.
/// </summary>
void RunPostRouting()
{
// TODO: Remove default implementation in Umbraco 13
}
/// <summary>
/// Manually runs the <see cref="IUmbracoPipelineFilter" /> post pipeline filters.
/// </summary>
void RunPostPipeline();
/// <summary>
/// Called to include all of the default umbraco required middleware.
/// Called to include all of the default Umbraco required middleware.
/// </summary>
/// <remarks>
/// If using this method, there is no need to use <see cref="UseUmbracoCoreMiddleware" />
/// If using this method, there is no need to use <see cref="UseUmbracoCoreMiddleware" />.
/// </remarks>
void RegisterDefaultRequiredMiddleware();
}

View File

@@ -3,37 +3,57 @@ using Microsoft.AspNetCore.Builder;
namespace Umbraco.Cms.Web.Common.ApplicationBuilder;
/// <summary>
/// Used to modify the <see cref="IApplicationBuilder" /> pipeline before and after Umbraco registers it's core
/// middlewares.
/// Used to modify the <see cref="IApplicationBuilder" /> pipeline before and after Umbraco registers its middlewares.
/// </summary>
/// <remarks>
/// Mainly used for package developers.
/// Mainly used for package developers.
/// </remarks>
public interface IUmbracoPipelineFilter
{
/// <summary>
/// The name of the filter
/// The name of the filter.
/// </summary>
/// <value>
/// The name.
/// </value>
/// <remarks>
/// This can be used by developers to see what is registered and if anything should be re-ordered, removed, etc...
/// This can be used by developers to see what is registered and if anything should be re-ordered, removed, etc...
/// </remarks>
string Name { get; }
/// <summary>
/// Executes before Umbraco middlewares are registered
/// Executes before any default Umbraco middlewares are registered.
/// </summary>
/// <param name="app"></param>
/// <param name="app">The application.</param>
void OnPrePipeline(IApplicationBuilder app);
/// <summary>
/// Executes after core Umbraco middlewares are registered and before any Endpoints are declared
/// Executes after static files middlewares are registered and just before the routing middleware is registered.
/// </summary>
/// <param name="app"></param>
/// <param name="app">The application.</param>
void OnPreRouting(IApplicationBuilder app)
{
// TODO: Remove default implementation in Umbraco 13
}
/// <summary>
/// Executes after the routing middleware is registered and just before the authentication and authorization middlewares are registered. This can be used to add CORS policies.
/// </summary>
/// <param name="app">The application.</param>
void OnPostRouting(IApplicationBuilder app)
{
// TODO: Remove default implementation in Umbraco 13
}
/// <summary>
/// Executes after core Umbraco middlewares are registered and before any endpoints are declared.
/// </summary>
/// <param name="app">The application.</param>
void OnPostPipeline(IApplicationBuilder app);
/// <summary>
/// Executes after <see cref="OnPostPipeline(IApplicationBuilder)" /> just before any Umbraco endpoints are declared.
/// Executes after the middlewares are registered and just before any Umbraco endpoints are declared.
/// </summary>
/// <param name="app"></param>
/// <param name="app">The application.</param>
void OnEndpoints(IApplicationBuilder app);
}

View File

@@ -84,7 +84,10 @@ public class UmbracoApplicationBuilder : IUmbracoApplicationBuilder, IUmbracoEnd
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-5.0
// where we need to have UseAuthentication and UseAuthorization proceeding this call but before
// endpoints are defined.
RunPreRouting();
AppBuilder.UseRouting();
RunPostRouting();
AppBuilder.UseAuthentication();
AppBuilder.UseAuthorization();
@@ -116,6 +119,22 @@ public class UmbracoApplicationBuilder : IUmbracoApplicationBuilder, IUmbracoEnd
}
}
public void RunPreRouting()
{
foreach (IUmbracoPipelineFilter filter in _umbracoPipelineStartupOptions.Value.PipelineFilters)
{
filter.OnPreRouting(AppBuilder);
}
}
public void RunPostRouting()
{
foreach (IUmbracoPipelineFilter filter in _umbracoPipelineStartupOptions.Value.PipelineFilters)
{
filter.OnPostRouting(AppBuilder);
}
}
public void RunPostPipeline()
{
foreach (IUmbracoPipelineFilter filter in _umbracoPipelineStartupOptions.Value.PipelineFilters)

View File

@@ -2,43 +2,113 @@ using Microsoft.AspNetCore.Builder;
namespace Umbraco.Cms.Web.Common.ApplicationBuilder;
/// <summary>
/// Used to modify the <see cref="IApplicationBuilder" /> pipeline before and after Umbraco registers it's core
/// middlewares.
/// </summary>
/// <remarks>
/// Mainly used for package developers.
/// </remarks>
/// <inheritdoc />
public class UmbracoPipelineFilter : IUmbracoPipelineFilter
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoPipelineFilter" /> class.
/// </summary>
/// <param name="name">The name.</param>
public UmbracoPipelineFilter(string name)
: this(name, null, null, null)
{
}
: this(name, null, null, null, null, null)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoPipelineFilter" /> class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="prePipeline">The pre pipeline callback.</param>
/// <param name="postPipeline">The post pipeline callback.</param>
/// <param name="endpointCallback">The endpoint callback.</param>
[Obsolete("Use the constructor with named parameters or set the callback properties instead. This constructor will be removed in Umbraco 13.")]
public UmbracoPipelineFilter(
string name,
Action<IApplicationBuilder>? prePipeline,
Action<IApplicationBuilder>? postPipeline,
Action<IApplicationBuilder>? endpointCallback)
: this(name, prePipeline, null, null, postPipeline, endpointCallback)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoPipelineFilter" /> class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="prePipeline">The pre pipeline callback.</param>
/// <param name="preRouting">The pre routing callback.</param>
/// <param name="postRouting">The post routing callback.</param>
/// <param name="postPipeline">The post pipeline callback.</param>
/// <param name="endpoints">The endpoints callback.</param>
public UmbracoPipelineFilter(
string name,
Action<IApplicationBuilder>? prePipeline = null,
Action<IApplicationBuilder>? preRouting = null,
Action<IApplicationBuilder>? postRouting = null,
Action<IApplicationBuilder>? postPipeline = null,
Action<IApplicationBuilder>? endpoints = null)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
PrePipeline = prePipeline;
PreRouting = preRouting;
PostRouting = postRouting;
PostPipeline = postPipeline;
Endpoints = endpointCallback;
Endpoints = endpoints;
}
/// <summary>
/// Gets or sets the pre pipeline callback.
/// </summary>
/// <value>
/// The pre pipeline callback.
/// </value>
public Action<IApplicationBuilder>? PrePipeline { get; set; }
/// <summary>
/// Gets or sets the pre routing.
/// </summary>
/// <value>
/// The pre routing.
/// </value>
public Action<IApplicationBuilder>? PreRouting { get; set; }
/// <summary>
/// Gets or sets the post routing callback.
/// </summary>
/// <value>
/// The post routing callback.
/// </value>
public Action<IApplicationBuilder>? PostRouting { get; set; }
/// <summary>
/// Gets or sets the post pipeline callback.
/// </summary>
/// <value>
/// The post pipeline callback.
/// </value>
public Action<IApplicationBuilder>? PostPipeline { get; set; }
/// <summary>
/// Gets or sets the endpoints callback.
/// </summary>
/// <value>
/// The endpoints callback.
/// </value>
public Action<IApplicationBuilder>? Endpoints { get; set; }
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
public void OnPrePipeline(IApplicationBuilder app) => PrePipeline?.Invoke(app);
/// <inheritdoc />
public void OnPreRouting(IApplicationBuilder app) => PreRouting?.Invoke(app);
/// <inheritdoc />
public void OnPostRouting(IApplicationBuilder app) => PostRouting?.Invoke(app);
/// <inheritdoc />
public void OnPostPipeline(IApplicationBuilder app) => PostPipeline?.Invoke(app);
/// <inheritdoc />
public void OnEndpoints(IApplicationBuilder app) => Endpoints?.Invoke(app);
}

View File

@@ -144,8 +144,6 @@ public static partial class UmbracoBuilderExtensions
sp,
sp.GetRequiredService<IApplicationDiscriminator>()));
builder.Services.AddHostedService(factory => factory.GetRequiredService<IRuntime>());
builder.Services.AddSingleton<DatabaseSchemaCreatorFactory>();
builder.Services.TryAddEnumerable(ServiceDescriptor
.Singleton<IDatabaseProviderMetadata, CustomConnectionStringDatabaseProviderMetadata>());

View File

@@ -10,6 +10,8 @@ using Serilog.Context;
using StackExchange.Profiling;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Exceptions;
using Umbraco.Cms.Core.Extensions;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Logging.Serilog.Enrichers;
@@ -32,7 +34,21 @@ public static class ApplicationBuilderExtensions
/// Configures and use services required for using Umbraco
/// </summary>
public static IUmbracoApplicationBuilder UseUmbraco(this IApplicationBuilder app)
=> new UmbracoApplicationBuilder(app);
{
// Ensure Umbraco is booted and StaticServiceProvider.Instance is set before continuing
IRuntimeState runtimeState = app.ApplicationServices.GetRequiredService<IRuntimeState>();
if (runtimeState.Level == RuntimeLevel.Unknown)
{
throw new BootFailedException("The runtime level is unknown, please make sure Umbraco is booted by adding `await app.BootUmbracoAsync();` just after `WebApplication app = builder.Build();` in your Program.cs file.");
}
if (StaticServiceProvider.Instance is null)
{
throw new BootFailedException("StaticServiceProvider.Instance is not set, please make sure ConfigureUmbracoDefaults() is added in your Program.cs file.");
}
return new UmbracoApplicationBuilder(app);
}
/// <summary>
/// Returns true if Umbraco <see cref="IRuntimeState" /> is greater than <see cref="RuntimeLevel.BootFailed" />

View File

@@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
namespace Umbraco.Extensions;
/// <summary>
/// Extension methods for <see cref="WebApplicationBuilder" />.
/// </summary>
public static class WebApplicationBuilderExtensions
{
/// <summary>
/// Creates an <see cref="IUmbracoBuilder" /> and registers basic Umbraco services.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns>
/// The Umbraco builder.
/// </returns>
public static IUmbracoBuilder CreateUmbracoBuilder(this WebApplicationBuilder builder)
{
// Configure Umbraco defaults, but ignore decorated host builder and
// don't add runtime as hosted service (this is replaced by the explicit BootUmbracoAsync)
builder.Host.ConfigureUmbracoDefaults(false);
// Do not enable static web assets on production environments,
// because the files are already copied to the publish output folder.
if (builder.Configuration.GetRuntimeMode() != RuntimeMode.Production)
{
builder.WebHost.UseStaticWebAssets();
}
return builder.Services.AddUmbraco(builder.Environment, builder.Configuration);
}
}

View File

@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Extensions;
/// <summary>
/// Extension methods for <see cref="WebApplication" />.
/// </summary>
public static class WebApplicationExtensions
{
/// <summary>
/// Starts the <see cref="IRuntime" /> to ensure Umbraco is ready for middleware to be added.
/// </summary>
/// <param name="app">The application.</param>
/// <returns>
/// A <see cref="Task" /> representing the asynchronous operation.
/// </returns>
public static async Task BootUmbracoAsync(this WebApplication app)
{
// Set static IServiceProvider before booting
StaticServiceProvider.Instance = app.Services;
// Ensure the Umbraco runtime is started before middleware is added and stopped when performing a graceful shutdown
IRuntime umbracoRuntime = app.Services.GetRequiredService<IRuntime>();
CancellationTokenRegistration cancellationTokenRegistration = app.Lifetime.ApplicationStopping.Register((_, token) => umbracoRuntime.StopAsync(token), null);
await umbracoRuntime.StartAsync(cancellationTokenRegistration.Token);
}
}

View File

@@ -1,6 +1,8 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Hosting;
// ReSharper disable once CheckNamespace
@@ -15,6 +17,9 @@ public static class HostBuilderExtensions
/// Configures an existing <see cref="IHostBuilder" /> with defaults for an Umbraco application.
/// </summary>
public static IHostBuilder ConfigureUmbracoDefaults(this IHostBuilder builder)
=> builder.ConfigureUmbracoDefaults(true);
internal static IHostBuilder ConfigureUmbracoDefaults(this IHostBuilder builder, bool addRuntimeHostedService)
{
#if DEBUG
builder.ConfigureAppConfiguration(config
@@ -26,10 +31,16 @@ public static class HostBuilderExtensions
#endif
builder.ConfigureLogging(x => x.ClearProviders());
if (addRuntimeHostedService)
{
// Add the Umbraco IRuntime as hosted service
builder.ConfigureServices(services => services.AddHostedService(factory => factory.GetRequiredService<IRuntime>()));
}
return new UmbracoHostBuilderDecorator(builder, OnHostBuilt);
}
// Runs before any IHostedService starts (including generic web host).
// Runs before any IHostedService starts (including generic web host)
private static void OnHostBuilt(IHost host) =>
StaticServiceProvider.Instance = host.Services;
}