Merge remote-tracking branch 'origin/netcore/netcore' into netcore/feature/remove-httpresponseexception

# Conflicts:
#	src/Umbraco.Web.BackOffice/Controllers/EntityController.cs
#	src/Umbraco.Web.Common/Install/InstallApiController.cs
This commit is contained in:
Bjarke Berg
2021-01-05 14:15:56 +01:00
205 changed files with 2294 additions and 2693 deletions

View File

@@ -6,6 +6,8 @@
at %APPDATA%\NuGet\NuGet.config
-->
<packageSources>
<clear />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
<add key="UmbracoCoreMyGet" value="https://www.myget.org/F/umbracocore/api/v3/index.json" />
<add key="ExamineAzurePipelines" value="https://shazwazza.pkgs.visualstudio.com/Examine/_packaging/Examine-Beta/nuget/v3/index.json" />
<add key="SmidgeAppVeyor" value="https://ci.appveyor.com/nuget/smidge" />

View File

@@ -46,7 +46,7 @@ namespace Umbraco.Web.Cache
{
if (refresherGuid == Guid.Empty || instances.Length == 0 || getNumericId == null) return;
_serverMessenger.PerformRefresh(
_serverMessenger.QueueRefresh(
GetRefresherById(refresherGuid),
getNumericId,
instances);
@@ -61,7 +61,7 @@ namespace Umbraco.Web.Cache
{
if (refresherGuid == Guid.Empty || id == default(int)) return;
_serverMessenger.PerformRefresh(
_serverMessenger.QueueRefresh(
GetRefresherById(refresherGuid),
id);
}
@@ -75,7 +75,7 @@ namespace Umbraco.Web.Cache
{
if (refresherGuid == Guid.Empty || id == Guid.Empty) return;
_serverMessenger.PerformRefresh(
_serverMessenger.QueueRefresh(
GetRefresherById(refresherGuid),
id);
}
@@ -86,7 +86,7 @@ namespace Umbraco.Web.Cache
{
if (refresherGuid == Guid.Empty || payload == null) return;
_serverMessenger.PerformRefresh(
_serverMessenger.QueueRefresh(
GetRefresherById(refresherGuid),
payload);
}
@@ -97,7 +97,7 @@ namespace Umbraco.Web.Cache
{
if (refresherGuid == Guid.Empty || payloads == null) return;
_serverMessenger.PerformRefresh(
_serverMessenger.QueueRefresh(
GetRefresherById(refresherGuid),
payloads.ToArray());
}
@@ -125,7 +125,7 @@ namespace Umbraco.Web.Cache
{
if (refresherGuid == Guid.Empty) return;
_serverMessenger.PerformRefreshAll(
_serverMessenger.QueueRefreshAll(
GetRefresherById(refresherGuid));
}
@@ -138,7 +138,7 @@ namespace Umbraco.Web.Cache
{
if (refresherGuid == Guid.Empty || id == default(int)) return;
_serverMessenger.PerformRemove(
_serverMessenger.QueueRemove(
GetRefresherById(refresherGuid),
id);
}
@@ -155,7 +155,7 @@ namespace Umbraco.Web.Cache
/// </remarks>
public void Remove<T>(Guid refresherGuid, Func<T, int> getNumericId, params T[] instances)
{
_serverMessenger.PerformRemove(
_serverMessenger.QueueRemove(
GetRefresherById(refresherGuid),
getNumericId,
instances);
@@ -166,7 +166,13 @@ namespace Umbraco.Web.Cache
// helper method to get an ICacheRefresher by its unique identifier
private ICacheRefresher GetRefresherById(Guid refresherGuid)
{
return _cacheRefreshers[refresherGuid];
ICacheRefresher refresher = _cacheRefreshers[refresherGuid];
if (refresher == null)
{
throw new InvalidOperationException($"No cache refresher found with id {refresherGuid}");
}
return refresher;
}
}
}

View File

@@ -1,22 +0,0 @@
using Umbraco.Core.Composing;
namespace Umbraco.Web.Cache
{
public class DistributedCacheBinderComponent : IComponent
{
private readonly IDistributedCacheBinder _binder;
public DistributedCacheBinderComponent(IDistributedCacheBinder distributedCacheBinder)
{
_binder = distributedCacheBinder;
}
public void Initialize()
{
_binder.BindEvents();
}
public void Terminate()
{ }
}
}

View File

@@ -1,7 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core.DependencyInjection;
namespace Umbraco.Core.Composing
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -70,7 +70,6 @@ namespace Umbraco.Core.Composing
foreach (var composer in composers)
{
var componentType = composer.GetType();
composer.Compose(_builder);
}
}

View File

@@ -1,5 +1,4 @@
using System;
using Umbraco.Core;
using System;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;
using Umbraco.Web.PublishedCache;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core.DependencyInjection;
namespace Umbraco.Core.Composing
{

View File

@@ -1,123 +0,0 @@
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;
using Umbraco.Core.HealthCheck;
using Umbraco.Core.Manifest;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.Actions;
using Umbraco.Web.ContentApps;
using Umbraco.Web.Dashboards;
using Umbraco.Web.Editors;
using Umbraco.Web.Routing;
using Umbraco.Web.Sections;
using Umbraco.Web.Tour;
namespace Umbraco.Core
{
public static partial class CompositionExtensions
{
#region Collection Builders
/// <summary>
/// Gets the actions collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static ActionCollectionBuilder Actions(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ActionCollectionBuilder>();
/// <summary>
/// Gets the content apps collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static ContentAppFactoryCollectionBuilder ContentApps(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ContentAppFactoryCollectionBuilder>();
/// <summary>
/// Gets the content finders collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static ContentFinderCollectionBuilder ContentFinders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ContentFinderCollectionBuilder>();
/// <summary>
/// Gets the editor validators collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static EditorValidatorCollectionBuilder EditorValidators(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<EditorValidatorCollectionBuilder>();
/// <summary>
/// Gets the health checks collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static HealthCheckCollectionBuilder HealthChecks(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<HealthCheckCollectionBuilder>();
/// <summary>
/// Gets the TourFilters collection builder.
/// </summary>
public static TourFilterCollectionBuilder TourFilters(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<TourFilterCollectionBuilder>();
/// <summary>
/// Gets the URL providers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static UrlProviderCollectionBuilder UrlProviders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<UrlProviderCollectionBuilder>();
/// <summary>
/// Gets the media url providers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static MediaUrlProviderCollectionBuilder MediaUrlProviders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<MediaUrlProviderCollectionBuilder>();
/// <summary>
/// Gets the backoffice sections/applications collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static SectionCollectionBuilder Sections(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<SectionCollectionBuilder>();
/// <summary>
/// Gets the components collection builder.
/// </summary>
public static ComponentCollectionBuilder Components(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ComponentCollectionBuilder>();
/// <summary>
/// Gets the backoffice dashboards collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static DashboardCollectionBuilder Dashboards(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<DashboardCollectionBuilder>()
.Add<ContentDashboard>()
.Add<ExamineDashboard>()
.Add<FormsDashboard>()
.Add<HealthCheckDashboard>()
.Add<ManifestDashboard>()
.Add<MediaDashboard>()
.Add<MembersDashboard>()
.Add<ProfilerDashboard>()
.Add<PublishedStatusDashboard>()
.Add<RedirectUrlDashboard>()
.Add<SettingsDashboard>();
/// <summary>
/// Gets the content finders collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static MediaUrlGeneratorCollectionBuilder MediaUrlGenerators(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<MediaUrlGeneratorCollectionBuilder>();
#endregion
}
}

View File

@@ -1,9 +1,10 @@
using System;
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Umbraco.Core;
using Umbraco.Core.Composing;
namespace Umbraco.Core
namespace Umbraco.Core.DependencyInjection
{
public static class ServiceCollectionExtensions
{
@@ -21,7 +22,7 @@ namespace Umbraco.Core
where TImplementing : class, TService1, TService2
{
services.AddUnique<TService1, TImplementing>();
services.AddUnique<TService2>(factory => (TImplementing) factory.GetRequiredService<TService1>());
services.AddUnique<TService2>(factory => (TImplementing)factory.GetRequiredService<TService1>());
}
public static void AddUnique<TImplementing>(this IServiceCollection services)
@@ -48,9 +49,9 @@ namespace Umbraco.Core
/// </summary>
public static void AddUnique<TService>(this IServiceCollection services, TService instance)
where TService : class
=> services.Replace(ServiceDescriptor.Singleton<TService>(instance));
=> services.Replace(ServiceDescriptor.Singleton(instance));
public static IServiceCollection AddLazySupport(this IServiceCollection services)
internal static IServiceCollection AddLazySupport(this IServiceCollection services)
{
services.Replace(ServiceDescriptor.Transient(typeof(Lazy<>), typeof(LazyResolve<>)));
return services;

View File

@@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core.Composing;
namespace Umbraco.Core
namespace Umbraco.Core.DependencyInjection
{
/// <summary>
/// Provides extension methods to the <see cref="IFactory"/> class.

View File

@@ -0,0 +1,275 @@
using System.Security.Cryptography;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Dashboards;
using Umbraco.Core.HealthCheck;
using Umbraco.Core.Manifest;
using Umbraco.Core.PackageActions;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.Validators;
using Umbraco.Core.Strings;
using Umbraco.Core.Trees;
using Umbraco.Web.Actions;
using Umbraco.Web.ContentApps;
using Umbraco.Web.Dashboards;
using Umbraco.Web.Editors;
using Umbraco.Web.HealthCheck;
using Umbraco.Web.HealthCheck.NotificationMethods;
using Umbraco.Web.Media.EmbedProviders;
using Umbraco.Web.Routing;
using Umbraco.Web.Sections;
using Umbraco.Web.Tour;
namespace Umbraco.Core.DependencyInjection
{
/// <summary>
/// Extension methods for <see cref="IUmbracoBuilder"/>
/// </summary>
public static partial class UmbracoBuilderExtensions
{
/// <summary>
/// Adds all core collection builders
/// </summary>
internal static void AddAllCoreCollectionBuilders(this IUmbracoBuilder builder)
{
builder.CacheRefreshers().Add(() => builder.TypeLoader.GetCacheRefreshers());
builder.DataEditors().Add(() => builder.TypeLoader.GetDataEditors());
builder.Actions().Add(() => builder.TypeLoader.GetTypes<IAction>());
// register known content apps
builder.ContentApps()
.Append<ListViewContentAppFactory>()
.Append<ContentEditorContentAppFactory>()
.Append<ContentInfoContentAppFactory>()
.Append<ContentTypeDesignContentAppFactory>()
.Append<ContentTypeListViewContentAppFactory>()
.Append<ContentTypePermissionsContentAppFactory>()
.Append<ContentTypeTemplatesContentAppFactory>();
// all built-in finders in the correct order,
// devs can then modify this list on application startup
builder.ContentFinders()
.Append<ContentFinderByPageIdQuery>()
.Append<ContentFinderByUrl>()
.Append<ContentFinderByIdPath>()
/*.Append<ContentFinderByUrlAndTemplate>() // disabled, this is an odd finder */
.Append<ContentFinderByUrlAlias>()
.Append<ContentFinderByRedirectUrl>();
builder.EditorValidators().Add(() => builder.TypeLoader.GetTypes<IEditorValidator>());
builder.HealthChecks().Add(() => builder.TypeLoader.GetTypes<Core.HealthCheck.HealthCheck>());
builder.HealthCheckNotificationMethods().Add(() => builder.TypeLoader.GetTypes<IHealthCheckNotificationMethod>());
builder.TourFilters();
builder.UrlProviders()
.Append<AliasUrlProvider>()
.Append<DefaultUrlProvider>();
builder.MediaUrlProviders()
.Append<DefaultMediaUrlProvider>();
// register back office sections in the order we want them rendered
builder.Sections()
.Append<ContentSection>()
.Append<MediaSection>()
.Append<SettingsSection>()
.Append<PackagesSection>()
.Append<UsersSection>()
.Append<MembersSection>()
.Append<FormsSection>()
.Append<TranslationSection>();
builder.Components();
// register core CMS dashboards and 3rd party types - will be ordered by weight attribute & merged with package.manifest dashboards
builder.Dashboards()
.Add<ContentDashboard>()
.Add<ExamineDashboard>()
.Add<FormsDashboard>()
.Add<HealthCheckDashboard>()
.Add<ManifestDashboard>()
.Add<MediaDashboard>()
.Add<MembersDashboard>()
.Add<ProfilerDashboard>()
.Add<PublishedStatusDashboard>()
.Add<RedirectUrlDashboard>()
.Add<SettingsDashboard>()
.Add(builder.TypeLoader.GetTypes<IDashboard>());
builder.PackageActions().Add(() => builder.TypeLoader.GetPackageActions());
builder.DataValueReferenceFactories();
builder.PropertyValueConverters().Append(builder.TypeLoader.GetTypes<IPropertyValueConverter>());
builder.UrlSegmentProviders().Append<DefaultUrlSegmentProvider>();
builder.ManifestValueValidators()
.Add<RequiredValidator>()
.Add<RegexValidator>()
.Add<DelimitedValueValidator>()
.Add<EmailValidator>()
.Add<IntegerValidator>()
.Add<DecimalValidator>();
builder.ManifestFilters();
builder.MediaUrlGenerators();
// register OEmbed providers - no type scanning - all explicit opt-in of adding types, IEmbedProvider is not IDiscoverable
builder.OEmbedProviders()
.Append<YouTube>()
.Append<Twitter>()
.Append<Vimeo>()
.Append<DailyMotion>()
.Append<Flickr>()
.Append<Slideshare>()
.Append<Kickstarter>()
.Append<GettyImages>()
.Append<Ted>()
.Append<Soundcloud>()
.Append<Issuu>()
.Append<Hulu>()
.Append<Giphy>();
builder.SearchableTrees().Add(() => builder.TypeLoader.GetTypes<ISearchableTree>());
}
/// <summary>
/// Gets the actions collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static ActionCollectionBuilder Actions(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ActionCollectionBuilder>();
/// <summary>
/// Gets the content apps collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static ContentAppFactoryCollectionBuilder ContentApps(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ContentAppFactoryCollectionBuilder>();
/// <summary>
/// Gets the content finders collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static ContentFinderCollectionBuilder ContentFinders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ContentFinderCollectionBuilder>();
/// <summary>
/// Gets the editor validators collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static EditorValidatorCollectionBuilder EditorValidators(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<EditorValidatorCollectionBuilder>();
/// <summary>
/// Gets the health checks collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static HealthCheckCollectionBuilder HealthChecks(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<HealthCheckCollectionBuilder>();
public static HealthCheckNotificationMethodCollectionBuilder HealthCheckNotificationMethods(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<HealthCheckNotificationMethodCollectionBuilder>();
/// <summary>
/// Gets the TourFilters collection builder.
/// </summary>
public static TourFilterCollectionBuilder TourFilters(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<TourFilterCollectionBuilder>();
/// <summary>
/// Gets the URL providers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static UrlProviderCollectionBuilder UrlProviders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<UrlProviderCollectionBuilder>();
/// <summary>
/// Gets the media url providers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static MediaUrlProviderCollectionBuilder MediaUrlProviders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<MediaUrlProviderCollectionBuilder>();
/// <summary>
/// Gets the backoffice sections/applications collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static SectionCollectionBuilder Sections(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<SectionCollectionBuilder>();
/// <summary>
/// Gets the components collection builder.
/// </summary>
public static ComponentCollectionBuilder Components(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ComponentCollectionBuilder>();
/// <summary>
/// Gets the backoffice dashboards collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static DashboardCollectionBuilder Dashboards(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<DashboardCollectionBuilder>();
/// <summary>
/// Gets the cache refreshers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static CacheRefresherCollectionBuilder CacheRefreshers(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<CacheRefresherCollectionBuilder>();
/// <summary>
/// Gets the package actions collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
internal static PackageActionCollectionBuilder PackageActions(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<PackageActionCollectionBuilder>();
/// <summary>
/// Gets the data editor collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static DataEditorCollectionBuilder DataEditors(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<DataEditorCollectionBuilder>();
/// <summary>
/// Gets the data value reference factory collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static DataValueReferenceFactoryCollectionBuilder DataValueReferenceFactories(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<DataValueReferenceFactoryCollectionBuilder>();
/// <summary>
/// Gets the property value converters collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static PropertyValueConverterCollectionBuilder PropertyValueConverters(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<PropertyValueConverterCollectionBuilder>();
/// <summary>
/// Gets the url segment providers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static UrlSegmentProviderCollectionBuilder UrlSegmentProviders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<UrlSegmentProviderCollectionBuilder>();
/// <summary>
/// Gets the validators collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
internal static ManifestValueValidatorCollectionBuilder ManifestValueValidators(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ManifestValueValidatorCollectionBuilder>();
/// <summary>
/// Gets the manifest filter collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static ManifestFilterCollectionBuilder ManifestFilters(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ManifestFilterCollectionBuilder>();
/// <summary>
/// Gets the content finders collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static MediaUrlGeneratorCollectionBuilder MediaUrlGenerators(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<MediaUrlGeneratorCollectionBuilder>();
/// <summary>
/// Gets the backoffice OEmbed Providers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static EmbedProvidersCollectionBuilder OEmbedProviders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<EmbedProvidersCollectionBuilder>();
/// <summary>
/// Gets the back office searchable tree collection builder
/// </summary>
public static SearchableTreeCollectionBuilder SearchableTrees(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<SearchableTreeCollectionBuilder>();
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Umbraco.Core.Composing;
namespace Umbraco.Core.DependencyInjection
{
/// <summary>
/// Extension methods for <see cref="IUmbracoBuilder"/>
/// </summary>
public static partial class UmbracoBuilderExtensions
{
/// <summary>
/// Adds Umbraco composers for plugins
/// </summary>
public static IUmbracoBuilder AddComposers(this IUmbracoBuilder builder)
{
// TODO: Should have a better name
IEnumerable<Type> composerTypes = builder.TypeLoader.GetTypes<IComposer>();
IEnumerable<Attribute> enableDisable = builder.TypeLoader.GetAssemblyAttributes(typeof(EnableComposerAttribute), typeof(DisableComposerAttribute));
new Composers(builder, composerTypes, enableDisable, builder.BuilderLoggerFactory.CreateLogger<Composers>()).Compose();
return builder;
}
}
}

View File

@@ -0,0 +1,52 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Configuration.Models.Validation;
namespace Umbraco.Core.DependencyInjection
{
/// <summary>
/// Extension methods for <see cref="IUmbracoBuilder"/>
/// </summary>
public static partial class UmbracoBuilderExtensions
{
/// <summary>
/// Add Umbraco configuration services and options
/// </summary>
public static IUmbracoBuilder AddConfiguration(this IUmbracoBuilder builder)
{
// Register configuration validators.
builder.Services.AddSingleton<IValidateOptions<ContentSettings>, ContentSettingsValidator>();
builder.Services.AddSingleton<IValidateOptions<GlobalSettings>, GlobalSettingsValidator>();
builder.Services.AddSingleton<IValidateOptions<HealthChecksSettings>, HealthChecksSettingsValidator>();
builder.Services.AddSingleton<IValidateOptions<RequestHandlerSettings>, RequestHandlerSettingsValidator>();
// Register configuration sections.
builder.Services.Configure<ActiveDirectorySettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigActiveDirectory));
builder.Services.Configure<ConnectionStrings>(builder.Config.GetSection("ConnectionStrings"), o => o.BindNonPublicProperties = true);
builder.Services.Configure<ContentSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigContent));
builder.Services.Configure<CoreDebugSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigCoreDebug));
builder.Services.Configure<ExceptionFilterSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigExceptionFilter));
builder.Services.Configure<GlobalSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigGlobal));
builder.Services.Configure<HealthChecksSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigHealthChecks));
builder.Services.Configure<HostingSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigHosting));
builder.Services.Configure<ImagingSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigImaging));
builder.Services.Configure<IndexCreatorSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigExamine));
builder.Services.Configure<KeepAliveSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigKeepAlive));
builder.Services.Configure<LoggingSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigLogging));
builder.Services.Configure<MemberPasswordConfigurationSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigMemberPassword));
builder.Services.Configure<ModelsBuilderSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigModelsBuilder), o => o.BindNonPublicProperties = true);
builder.Services.Configure<NuCacheSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigNuCache));
builder.Services.Configure<RequestHandlerSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigRequestHandler));
builder.Services.Configure<RuntimeSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigRuntime));
builder.Services.Configure<SecuritySettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigSecurity));
builder.Services.Configure<TourSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigTours));
builder.Services.Configure<TypeFinderSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigTypeFinder));
builder.Services.Configure<UserPasswordConfigurationSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigUserPassword));
builder.Services.Configure<WebRoutingSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigWebRouting));
builder.Services.Configure<UmbracoPluginSettings>(builder.Config.GetSection(Core.Constants.Configuration.ConfigPlugins));
return builder;
}
}
}

View File

@@ -2,6 +2,7 @@
// See LICENSE for more details.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Umbraco.Core.Events;
namespace Umbraco.Core.DependencyInjection
@@ -23,7 +24,15 @@ namespace Umbraco.Core.DependencyInjection
where TNotification : INotification
{
// Register the handler as transient. This ensures that anything can be injected into it.
builder.Services.AddTransient(typeof(INotificationHandler<TNotification>), typeof(TNotificationHandler));
var descriptor = new ServiceDescriptor(typeof(INotificationHandler<TNotification>), typeof(TNotificationHandler), ServiceLifetime.Transient);
// TODO: Waiting on feedback here https://github.com/umbraco/Umbraco-CMS/pull/9556/files#r548365396 about whether
// we perform this duplicate check or not.
if (!builder.Services.Contains(descriptor))
{
builder.Services.Add(descriptor);
}
return builder;
}
}

View File

@@ -3,28 +3,65 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Diagnostics;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Mail;
using Umbraco.Core.Manifest;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Runtime;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
using Umbraco.Web;
using Umbraco.Web.Cache;
using Umbraco.Web.Editors;
using Umbraco.Web.Features;
using Umbraco.Web.Install;
using Umbraco.Web.Models.PublishedContent;
using Umbraco.Web.Routing;
using Umbraco.Web.Services;
using Umbraco.Web.Templates;
namespace Umbraco.Core.DependencyInjection
{
public class UmbracoBuilder : IUmbracoBuilder
{
public IServiceCollection Services { get; }
public IConfiguration Config { get; }
public TypeLoader TypeLoader { get; }
public ILoggerFactory BuilderLoggerFactory { get; }
private readonly Dictionary<Type, ICollectionBuilder> _builders = new Dictionary<Type, ICollectionBuilder>();
public IServiceCollection Services { get; }
public IConfiguration Config { get; }
public TypeLoader TypeLoader { get; }
public ILoggerFactory BuilderLoggerFactory { get; }
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoBuilder"/> class.
/// </summary>
public UmbracoBuilder(IServiceCollection services, IConfiguration config, TypeLoader typeLoader)
: this(services, config, typeLoader, NullLoggerFactory.Instance)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoBuilder"/> class.
/// </summary>
public UmbracoBuilder(IServiceCollection services, IConfiguration config, TypeLoader typeLoader, ILoggerFactory loggerFactory)
{
Services = services;
@@ -43,10 +80,12 @@ namespace Umbraco.Core.DependencyInjection
public TBuilder WithCollectionBuilder<TBuilder>()
where TBuilder : ICollectionBuilder, new()
{
var typeOfBuilder = typeof(TBuilder);
Type typeOfBuilder = typeof(TBuilder);
if (_builders.TryGetValue(typeOfBuilder, out var o))
if (_builders.TryGetValue(typeOfBuilder, out ICollectionBuilder o))
{
return (TBuilder)o;
}
var builder = new TBuilder();
_builders[typeOfBuilder] = builder;
@@ -55,8 +94,10 @@ namespace Umbraco.Core.DependencyInjection
public void Build()
{
foreach (var builder in _builders.Values)
foreach (ICollectionBuilder builder in _builders.Values)
{
builder.RegisterWith(Services);
}
_builders.Clear();
}
@@ -66,6 +107,115 @@ namespace Umbraco.Core.DependencyInjection
// Register as singleton to allow injection everywhere.
Services.AddSingleton<ServiceFactory>(p => p.GetService);
Services.AddSingleton<IEventAggregator, EventAggregator>();
Services.AddLazySupport();
// Adds no-op registrations as many core services require these dependencies but these
// dependencies cannot be fulfilled in the Core project
Services.AddUnique<IMarchal, NoopMarchal>();
Services.AddUnique<IProfiler, NoopProfiler>();
Services.AddUnique<IApplicationShutdownRegistry, NoopApplicationShutdownRegistry>();
Services.AddUnique<IUmbracoApplicationLifetimeManager, NoopUmbracoApplicationLifetimeManager>();
Services.AddUnique<IMainDom, MainDom>();
Services.AddUnique<IMainDomLock, MainDomSemaphoreLock>();
Services.AddUnique<IIOHelper>(factory =>
{
IHostingEnvironment hostingEnvironment = factory.GetRequiredService<IHostingEnvironment>();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return new IOHelperLinux(hostingEnvironment);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return new IOHelperOSX(hostingEnvironment);
}
return new IOHelperWindows(hostingEnvironment);
});
Services.AddUnique(factory => factory.GetRequiredService<AppCaches>().RuntimeCache);
Services.AddUnique(factory => factory.GetRequiredService<AppCaches>().RequestCache);
Services.AddUnique<IProfilingLogger, ProfilingLogger>();
Services.AddUnique<IUmbracoVersion, UmbracoVersion>();
this.AddAllCoreCollectionBuilders();
this.AddNotificationHandler<UmbracoApplicationStarting, EssentialDirectoryCreator>();
Services.AddSingleton<ManifestWatcher>();
this.AddNotificationHandler<UmbracoApplicationStarting, AppPluginsManifestWatcherNotificationHandler>();
Services.AddUnique<InstallStatusTracker>();
// by default, register a noop factory
Services.AddUnique<IPublishedModelFactory, NoopPublishedModelFactory>();
Services.AddUnique<ICultureDictionaryFactory, DefaultCultureDictionaryFactory>();
Services.AddSingleton(f => f.GetRequiredService<ICultureDictionaryFactory>().CreateDictionary());
Services.AddUnique<UriUtility>();
Services.AddUnique<IDashboardService, DashboardService>();
// will be injected in controllers when needed to invoke rest endpoints on Our
Services.AddUnique<IInstallationService, InstallationService>();
Services.AddUnique<IUpgradeService, UpgradeService>();
// Grid config is not a real config file as we know them
Services.AddUnique<IGridConfig, GridConfig>();
Services.AddUnique<IPublishedUrlProvider, UrlProvider>();
Services.AddUnique<ISiteDomainHelper, SiteDomainHelper>();
Services.AddUnique<HtmlLocalLinkParser>();
Services.AddUnique<HtmlImageSourceParser>();
Services.AddUnique<HtmlUrlParser>();
// register properties fallback
Services.AddUnique<IPublishedValueFallback, PublishedValueFallback>();
Services.AddUnique<UmbracoFeatures>();
// register published router
Services.AddUnique<IPublishedRouter, PublishedRouter>();
Services.AddUnique<IEventMessagesFactory, DefaultEventMessagesFactory>();
Services.AddUnique<IEventMessagesAccessor, HybridEventMessagesAccessor>();
Services.AddUnique<ITreeService, TreeService>();
Services.AddUnique<ISectionService, SectionService>();
Services.AddUnique<ISmsSender, NotImplementedSmsSender>();
Services.AddUnique<IEmailSender, NotImplementedEmailSender>();
// register distributed cache
Services.AddUnique(f => new DistributedCache(f.GetRequiredService<IServerMessenger>(), f.GetRequiredService<CacheRefresherCollection>()));
// register the http context and umbraco context accessors
// we *should* use the HttpContextUmbracoContextAccessor, however there are cases when
// we have no http context, eg when booting Umbraco or in background threads, so instead
// let's use an hybrid accessor that can fall back to a ThreadStatic context.
Services.AddUnique<IUmbracoContextAccessor, HybridUmbracoContextAccessor>();
Services.AddUnique<LegacyPasswordSecurity>();
Services.AddUnique<UserEditorAuthorizationHelper>();
Services.AddUnique<ContentPermissions>();
Services.AddUnique<MediaPermissions>();
Services.AddUnique<PropertyEditorCollection>();
Services.AddUnique<ParameterEditorCollection>();
// register a server registrar, by default it's the db registrar
Services.AddUnique<IServerRoleAccessor>(f =>
{
GlobalSettings globalSettings = f.GetRequiredService<IOptions<GlobalSettings>>().Value;
var singleServer = globalSettings.DisableElectionForSingleServer;
return singleServer
? (IServerRoleAccessor)new SingleServerRoleAccessor()
: new ElectedServerRoleAccessor(f.GetRequiredService<IServerRegistrationService>());
});
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace Umbraco.Core.Diagnostics
{
internal class NoopMarchal : IMarchal
{
public IntPtr GetExceptionPointers() => IntPtr.Zero;
}
}

View File

@@ -5,6 +5,7 @@ using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.HealthCheck;
using Umbraco.Core.Mail;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Infrastructure.HealthCheck;

View File

@@ -1,25 +1,20 @@
using System;
namespace Umbraco.Net
namespace Umbraco.Core.Hosting
{
// TODO: This shouldn't be in this namespace?
public interface IUmbracoApplicationLifetime
{
/// <summary>
/// A value indicating whether the application is restarting after the current request.
/// </summary>
bool IsRestarting { get; }
/// <summary>
/// Terminates the current application. The application restarts the next time a request is received for it.
/// </summary>
void Restart();
// TODO: Should be killed and replaced with UmbracoApplicationStarting notifications
event EventHandler ApplicationInit;
}
public interface IUmbracoApplicationLifetimeManager
{
void InvokeApplicationInit();
}
}

View File

@@ -0,0 +1,8 @@
namespace Umbraco.Core.Hosting
{
// TODO: Should be killed and replaced with UmbracoApplicationStarting notifications
public interface IUmbracoApplicationLifetimeManager
{
void InvokeApplicationInit();
}
}

View File

@@ -0,0 +1,8 @@
namespace Umbraco.Core.Hosting
{
internal class NoopApplicationShutdownRegistry : IApplicationShutdownRegistry
{
public void RegisterObject(IRegisteredObject registeredObject) { }
public void UnregisterObject(IRegisteredObject registeredObject) { }
}
}

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Core.Hosting
{
internal class NoopUmbracoApplicationLifetimeManager : IUmbracoApplicationLifetimeManager
{
public void InvokeApplicationInit() { }
}
}

View File

@@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging;
using Umbraco.Core.Hosting;
using Umbraco.Core.Configuration.Models;
using Microsoft.Extensions.Options;
using Umbraco.Core.DependencyInjection;
namespace Umbraco.Core.IO
{

View File

@@ -2,9 +2,9 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Umbraco.Core.Hosting;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Net;
using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps

View File

@@ -1,8 +1,8 @@
using System;
using System;
namespace Umbraco.Core.Logging
{
public class VoidProfiler : IProfiler
public class NoopProfiler : IProfiler
{
private readonly VoidDisposable _disposable = new VoidDisposable();

View File

@@ -1,7 +1,7 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using Umbraco.Core.Models;
namespace Umbraco.Core
namespace Umbraco.Core.Mail
{
/// <summary>
/// Simple abstraction to send an email message

View File

@@ -1,6 +1,6 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
namespace Umbraco.Core
namespace Umbraco.Core.Mail
{
/// <summary>
/// Service to send an SMS

View File

@@ -0,0 +1,12 @@
using System;
using System.Threading.Tasks;
using Umbraco.Core.Models;
namespace Umbraco.Core.Mail
{
internal class NotImplementedEmailSender : IEmailSender
{
public Task SendAsync(EmailMessage message)
=> throw new NotImplementedException("To send an Email ensure IEmailSender is implemented with a custom implementation");
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System;
using System.Threading.Tasks;
namespace Umbraco.Core
namespace Umbraco.Core.Mail
{
/// <summary>
/// An <see cref="ISmsSender"/> that throws <see cref="NotImplementedException"/>

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.Logging;
using Umbraco.Net;
using Umbraco.Core.Hosting;
namespace Umbraco.Core.Manifest
{

View File

@@ -1,11 +1,9 @@
using System.Linq;
using System.Linq;
using Umbraco.Core.Composing;
using Umbraco.Core.Manifest;
namespace Umbraco.Core.PropertyEditors
{
public class PropertyEditorCollection : BuilderCollectionBase<IDataEditor>
{
public PropertyEditorCollection(DataEditorCollection dataEditors, IManifestParser manifestParser)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Strings;
@@ -8,7 +8,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
/// Value converter for the RTE so that it always returns IHtmlString so that Html.Raw doesn't have to be used.
/// </summary>
[DefaultPropertyValueConverter]
public class TinyMceValueConverter : PropertyValueConverterBase
public class SimpleTinyMceValueConverter : PropertyValueConverterBase
{
public override bool IsConverter(IPublishedPropertyType propertyType)
=> propertyType.EditorAlias == Constants.PropertyEditors.Aliases.TinyMce;

View File

@@ -1,45 +0,0 @@
using System;
using System.Linq;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.PropertyEditors.ValueConverters
{
[DefaultPropertyValueConverter]
public class TextStringValueConverter : PropertyValueConverterBase
{
private static readonly string[] PropertyTypeAliases =
{
Constants.PropertyEditors.Aliases.TextBox,
Constants.PropertyEditors.Aliases.TextArea
};
public override bool IsConverter(IPublishedPropertyType propertyType)
=> PropertyTypeAliases.Contains(propertyType.EditorAlias);
public override Type GetPropertyValueType(IPublishedPropertyType propertyType)
=> typeof (string);
public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)
=> PropertyCacheLevel.Element;
public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview)
{
// in xml a string is: string
// in the database a string is: string
// default value is: null
return source;
}
public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
{
// source should come from ConvertSource and be a string (or null) already
return inter ?? string.Empty;
}
public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
{
// source should come from ConvertSource and be a string (or null) already
return inter;
}
}
}

View File

@@ -2,12 +2,11 @@ using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.Manifest;
namespace Umbraco.Infrastructure.Runtime
namespace Umbraco.Core.Runtime
{
/// <summary>
/// Starts monitoring AppPlugins directory during debug runs, to restart site when a plugin manifest changes.

View File

@@ -1,13 +1,12 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
namespace Umbraco.Infrastructure.Runtime
namespace Umbraco.Core.Runtime
{
public class EssentialDirectoryCreator : INotificationHandler<UmbracoApplicationStarting>
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Runtime
_logger = logger;
}
//WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread
// WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread
public Task ListenAsync() => _signal.WaitOneAsync();
public Task<bool> AcquireLockAsync(int millisecondsTimeout)
@@ -44,7 +44,7 @@ namespace Umbraco.Core.Runtime
// if more than 1 instance reach that point, one will get the lock
// and the other one will timeout, which is accepted
//This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset.
// This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset.
try
{
_lockRelease = _systemLock.Lock(millisecondsTimeout);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Sync;
@@ -11,9 +11,8 @@ namespace Umbraco.Core.Services
/// Touches a server to mark it as active; deactivate stale servers.
/// </summary>
/// <param name="serverAddress">The server URL.</param>
/// <param name="serverIdentity">The server unique identity.</param>
/// <param name="staleTimeout">The time after which a server is considered stale.</param>
void TouchServer(string serverAddress, string serverIdentity, TimeSpan staleTimeout);
void TouchServer(string serverAddress, TimeSpan staleTimeout);
/// <summary>
/// Deactivates a server.
@@ -38,11 +37,6 @@ namespace Umbraco.Core.Services
/// from the database.</remarks>
IEnumerable<IServerRegistration> GetActiveServers(bool refresh = false);
/// <summary>
/// Gets the current server identity.
/// </summary>
string CurrentServerIdentity { get; }
/// <summary>
/// Gets the role of the current server.
/// </summary>

View File

@@ -1,8 +1,8 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Repositories;
namespace Umbraco.Core.Services.Implement
namespace Umbraco.Core.Services
{
public class InstallationService : IInstallationService
{

View File

@@ -1,10 +1,9 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using Semver;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Services;
namespace Umbraco.Core
namespace Umbraco.Core.Services
{
public class UpgradeService : IUpgradeService
{

View File

@@ -1,10 +1,10 @@
using System;
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Sync
{
/// <summary>
/// Holds a list of callbacks associated with implementations of <see cref="IBatchedDatabaseServerMessenger"/>.
/// Holds a list of callbacks associated with implementations of <see cref="IServerMessenger"/>.
/// </summary>
public class DatabaseServerMessengerCallbacks
{

View File

@@ -1,43 +0,0 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Services;
namespace Umbraco.Core.Sync
{
/// <summary>
/// A registrar that stores registered server nodes in the database.
/// </summary>
/// <remarks>
/// This is the default registrar which determines a server's role by using a master election process.
/// The master election process doesn't occur until just after startup so this election process doesn't really affect the primary startup phase.
/// </remarks>
public sealed class DatabaseServerRegistrar : IServerRegistrar
{
private readonly Lazy<IServerRegistrationService> _registrationService;
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseServerRegistrar"/> class.
/// </summary>
/// <param name="registrationService">The registration service.</param>
/// <param name="options">Some options.</param>
public DatabaseServerRegistrar(Lazy<IServerRegistrationService> registrationService)
{
_registrationService = registrationService ?? throw new ArgumentNullException(nameof(registrationService));
}
/// <summary>
/// Gets the registered servers.
/// </summary>
public IEnumerable<IServerAddress> Registrations => _registrationService.Value.GetActiveServers();
/// <summary>
/// Gets the role of the current server in the application environment.
/// </summary>
public ServerRole GetCurrentServerRole()
{
var service = _registrationService.Value;
return service.GetCurrentServerRole();
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using Umbraco.Core.Services;
namespace Umbraco.Core.Sync
{
/// <summary>
/// Gets the current server's <see cref="ServerRole"/> based on active servers registered with <see cref="IServerRegistrationService"/>
/// </summary>
/// <remarks>
/// This is the default service which determines a server's role by using a master election process.
/// The master election process doesn't occur until just after startup so this election process doesn't really affect the primary startup phase.
/// </remarks>
public sealed class ElectedServerRoleAccessor : IServerRoleAccessor
{
private readonly IServerRegistrationService _registrationService;
/// <summary>
/// Initializes a new instance of the <see cref="ElectedServerRoleAccessor"/> class.
/// </summary>
/// <param name="registrationService">The registration service.</param>
/// <param name="options">Some options.</param>
public ElectedServerRoleAccessor(IServerRegistrationService registrationService) => _registrationService = registrationService ?? throw new ArgumentNullException(nameof(registrationService));
/// <summary>
/// Gets the role of the current server in the application environment.
/// </summary>
public ServerRole CurrentServerRole => _registrationService.GetCurrentServerRole();
}
}

View File

@@ -1,12 +0,0 @@
namespace Umbraco.Core.Sync
{
/// <summary>
/// An <see cref="IServerMessenger"/> implementation that works by storing messages in the database.
/// </summary>
public interface IBatchedDatabaseServerMessenger : IDatabaseServerMessenger
{
void FlushBatch();
DatabaseServerMessengerCallbacks Callbacks { get; }
void Startup();
}
}

View File

@@ -1,7 +0,0 @@
namespace Umbraco.Core.Sync
{
public interface IDatabaseServerMessenger: IServerMessenger
{
void Sync();
}
}

View File

@@ -1,21 +1,30 @@
using System;
using System.Collections.Generic;
using System;
using Umbraco.Core.Cache;
namespace Umbraco.Core.Sync
{
/// <summary>
/// Broadcasts distributed cache notifications to all servers of a load balanced environment.
/// Transmits distributed cache notifications for all servers of a load balanced environment.
/// </summary>
/// <remarks>Also ensures that the notification is processed on the local environment.</remarks>
public interface IServerMessenger
{
/// <summary>
/// Called to synchronize a server with queued notifications
/// </summary>
void Sync();
/// <summary>
/// Called to send/commit the queued messages created with the Perform methods
/// </summary>
void SendMessages();
/// <summary>
/// Notifies the distributed cache, for a specified <see cref="ICacheRefresher"/>.
/// </summary>
/// <param name="refresher">The ICacheRefresher.</param>
/// <param name="payload">The notification content.</param>
void PerformRefresh<TPayload>(ICacheRefresher refresher, TPayload[] payload);
void QueueRefresh<TPayload>(ICacheRefresher refresher, TPayload[] payload);
/// <summary>
/// Notifies the distributed cache of specified item invalidation, for a specified <see cref="ICacheRefresher"/>.
@@ -24,7 +33,7 @@ namespace Umbraco.Core.Sync
/// <param name="refresher">The ICacheRefresher.</param>
/// <param name="getNumericId">A function returning the unique identifier of items.</param>
/// <param name="instances">The invalidated items.</param>
void PerformRefresh<T>(ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances);
void QueueRefresh<T>(ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances);
/// <summary>
/// Notifies the distributed cache of specified item invalidation, for a specified <see cref="ICacheRefresher"/>.
@@ -33,7 +42,7 @@ namespace Umbraco.Core.Sync
/// <param name="refresher">The ICacheRefresher.</param>
/// <param name="getGuidId">A function returning the unique identifier of items.</param>
/// <param name="instances">The invalidated items.</param>
void PerformRefresh<T>(ICacheRefresher refresher, Func<T, Guid> getGuidId, params T[] instances);
void QueueRefresh<T>(ICacheRefresher refresher, Func<T, Guid> getGuidId, params T[] instances);
/// <summary>
/// Notifies all servers of specified items removal, for a specified <see cref="ICacheRefresher"/>.
@@ -42,33 +51,33 @@ namespace Umbraco.Core.Sync
/// <param name="refresher">The ICacheRefresher.</param>
/// <param name="getNumericId">A function returning the unique identifier of items.</param>
/// <param name="instances">The removed items.</param>
void PerformRemove<T>(ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances);
void QueueRemove<T>(ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances);
/// <summary>
/// Notifies all servers of specified items removal, for a specified <see cref="ICacheRefresher"/>.
/// </summary>
/// <param name="refresher">The ICacheRefresher.</param>
/// <param name="numericIds">The unique identifiers of the removed items.</param>
void PerformRemove(ICacheRefresher refresher, params int[] numericIds);
void QueueRemove(ICacheRefresher refresher, params int[] numericIds);
/// <summary>
/// Notifies all servers of specified items invalidation, for a specified <see cref="ICacheRefresher"/>.
/// </summary>
/// <param name="refresher">The ICacheRefresher.</param>
/// <param name="numericIds">The unique identifiers of the invalidated items.</param>
void PerformRefresh(ICacheRefresher refresher, params int[] numericIds);
void QueueRefresh(ICacheRefresher refresher, params int[] numericIds);
/// <summary>
/// Notifies all servers of specified items invalidation, for a specified <see cref="ICacheRefresher"/>.
/// </summary>
/// <param name="refresher">The ICacheRefresher.</param>
/// <param name="guidIds">The unique identifiers of the invalidated items.</param>
void PerformRefresh(ICacheRefresher refresher, params Guid[] guidIds);
void QueueRefresh(ICacheRefresher refresher, params Guid[] guidIds);
/// <summary>
/// Notifies all servers of a global invalidation for a specified <see cref="ICacheRefresher"/>.
/// </summary>
/// <param name="refresher">The ICacheRefresher.</param>
void PerformRefreshAll(ICacheRefresher refresher);
void QueueRefreshAll(ICacheRefresher refresher);
}
}

View File

@@ -1,21 +0,0 @@
using System.Collections.Generic;
namespace Umbraco.Core.Sync
{
/// <summary>
/// Provides server registrations to the distributed cache.
/// </summary>
public interface IServerRegistrar
{
/// <summary>
/// Gets the server registrations.
/// </summary>
IEnumerable<IServerAddress> Registrations { get; }
/// <summary>
/// Gets the role of the current server in the application environment.
/// </summary>
ServerRole GetCurrentServerRole();
}
}

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace Umbraco.Core.Sync
{
/// <summary>
/// Gets the current server's <see cref="ServerRole"/>
/// </summary>
public interface IServerRoleAccessor
{
/// <summary>
/// Gets the role of the current server in the application environment.
/// </summary>
ServerRole CurrentServerRole { get; }
}
}

View File

@@ -1,44 +0,0 @@
using System;
using System.Collections.Generic;
using Umbraco.Web;
namespace Umbraco.Core.Sync
{
/// <summary>
/// Can be used when Umbraco is definitely not operating in a Load Balanced scenario to micro-optimize some startup performance
/// </summary>
/// <remarks>
/// The micro optimization is specifically to avoid a DB query just after the app starts up to determine the <see cref="ServerRole"/>
/// which by default is done with master election by a database query. The master election process doesn't occur until just after startup
/// so this micro optimization doesn't really affect the primary startup phase.
/// </remarks>
public class SingleServerRegistrar : IServerRegistrar
{
private readonly IRequestAccessor _requestAccessor;
private readonly Lazy<IServerAddress[]> _registrations;
public IEnumerable<IServerAddress> Registrations => _registrations.Value;
public SingleServerRegistrar(IRequestAccessor requestAccessor)
{
_requestAccessor = requestAccessor;
_registrations = new Lazy<IServerAddress[]>(() => new IServerAddress[] { new ServerAddressImpl(_requestAccessor.GetApplicationUrl().ToString()) });
}
public ServerRole GetCurrentServerRole()
{
return ServerRole.Single;
}
private class ServerAddressImpl : IServerAddress
{
public ServerAddressImpl(string serverAddress)
{
ServerAddress = serverAddress;
}
public string ServerAddress { get; }
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using Umbraco.Web;
namespace Umbraco.Core.Sync
{
/// <summary>
/// Can be used when Umbraco is definitely not operating in a Load Balanced scenario to micro-optimize some startup performance
/// </summary>
/// <remarks>
/// The micro optimization is specifically to avoid a DB query just after the app starts up to determine the <see cref="ServerRole"/>
/// which by default is done with master election by a database query. The master election process doesn't occur until just after startup
/// so this micro optimization doesn't really affect the primary startup phase.
/// </remarks>
public class SingleServerRoleAccessor : IServerRoleAccessor
{
public ServerRole CurrentServerRole => ServerRole.Single;
}
}

View File

@@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Umbraco.Core.Composing;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Trees
namespace Umbraco.Core.Trees
{
public interface ISearchableTree : IDiscoverable
{

View File

@@ -1,6 +1,4 @@
using Umbraco.Web.Trees;
namespace Umbraco.Web.Search
namespace Umbraco.Core.Trees
{
public class SearchableApplicationTree
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
@@ -6,7 +6,7 @@ using Umbraco.Core.Composing;
using Umbraco.Web.Services;
using Umbraco.Web.Trees;
namespace Umbraco.Web.Search
namespace Umbraco.Core.Trees
{
public class SearchableTreeCollection : BuilderCollectionBase<ISearchableTree>
{

View File

@@ -1,8 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core.Composing;
using Umbraco.Web.Trees;
namespace Umbraco.Web.Search
namespace Umbraco.Core.Trees
{
public class SearchableTreeCollectionBuilder : LazyCollectionBuilderBase<SearchableTreeCollectionBuilder, SearchableTreeCollection, ISearchableTree>
{

View File

@@ -19,6 +19,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.8" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.8" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.7.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />

View File

@@ -1,5 +1,4 @@
using System.Runtime.InteropServices;
using Umbraco.Core;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;

View File

@@ -0,0 +1,96 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Umbraco.Core.Events;
using Umbraco.Core.Persistence;
using Umbraco.Core.Sync;
using Umbraco.Web;
using Umbraco.Web.Cache;
using Umbraco.Web.Routing;
namespace Umbraco.Infrastructure.Cache
{
/// <summary>
/// Ensures that distributed cache events are setup and the <see cref="IServerMessenger"/> is initialized
/// </summary>
public sealed class DatabaseServerMessengerNotificationHandler : INotificationHandler<UmbracoApplicationStarting>
{
private readonly IServerMessenger _messenger;
private readonly IRequestAccessor _requestAccessor;
private readonly IUmbracoDatabaseFactory _databaseFactory;
private readonly IDistributedCacheBinder _distributedCacheBinder;
private readonly ILogger<DatabaseServerMessengerNotificationHandler> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseServerMessengerNotificationHandler"/> class.
/// </summary>
public DatabaseServerMessengerNotificationHandler(
IServerMessenger serverMessenger,
IRequestAccessor requestAccessor,
IUmbracoDatabaseFactory databaseFactory,
IDistributedCacheBinder distributedCacheBinder,
ILogger<DatabaseServerMessengerNotificationHandler> logger)
{
_requestAccessor = requestAccessor;
_databaseFactory = databaseFactory;
_distributedCacheBinder = distributedCacheBinder;
_logger = logger;
_messenger = serverMessenger;
}
/// <inheritdoc/>
public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken)
{
// The scheduled tasks - TouchServerTask and InstructionProcessTask - run as .NET Core hosted services.
// The former (as well as other hosted services that run outside of an HTTP request context) depends on the application URL
// being available (via IRequestAccessor), which can only be retrieved within an HTTP request (unless it's explicitly configured).
// Hence we hook up a one-off task on an HTTP request to ensure this is retrieved, which caches the value and makes it available
// for the hosted services to use when the HTTP request is not available.
_requestAccessor.RouteAttempt += EnsureApplicationUrlOnce;
_requestAccessor.EndRequest += EndRequest;
Startup();
return Task.CompletedTask;
}
private void Startup()
{
if (_databaseFactory.CanConnect == false)
{
_logger.LogWarning("Cannot connect to the database, distributed calls will not be enabled for this server.");
}
else
{
_distributedCacheBinder.BindEvents();
// Sync on startup, this will run through the messenger's initialization sequence
_messenger?.Sync();
}
}
// TODO: I don't really know or think that the Application Url plays a role anymore with the DB dist cache,
// this might be really old stuff. I 'think' all this is doing is ensuring that the IRequestAccessor.GetApplicationUrl
// is definitely called during the first request. If that is still required, that logic doesn't belong here. That logic
// should be part of it's own service/middleware. There's also TODO notes within IRequestAccessor.GetApplicationUrl directly
// mentioning that the property doesn't belong on that service either. This should be investigated and resolved in a separate task.
private void EnsureApplicationUrlOnce(object sender, RoutableAttemptEventArgs e)
{
if (e.Outcome == EnsureRoutableOutcome.IsRoutable || e.Outcome == EnsureRoutableOutcome.NotDocumentRequest)
{
_requestAccessor.RouteAttempt -= EnsureApplicationUrlOnce;
EnsureApplicationUrl();
}
}
// By retrieving the application URL within the context of a request (as we are here in responding
// to the IRequestAccessor's RouteAttempt event), we'll get it from the HTTP context and save it for
// future requests that may not be within an HTTP request (e.g. from hosted services).
private void EnsureApplicationUrl() => _requestAccessor.GetApplicationUrl();
/// <summary>
/// Clear the batch on end request
/// </summary>
private void EndRequest(object sender, UmbracoRequestEventArgs e) => _messenger?.SendMessages();
}
}

View File

@@ -1,20 +0,0 @@
using Umbraco.Core;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;
namespace Umbraco.Web.Cache
{
/// <summary>
/// Installs listeners on service events in order to refresh our caches.
/// </summary>
[ComposeBefore(typeof(ICoreComposer))] // runs before every other IUmbracoCoreComponent!
public sealed class DistributedCacheBinderComposer : ComponentComposer<DistributedCacheBinderComponent>, ICoreComposer
{
public override void Compose(IUmbracoBuilder builder)
{
base.Compose(builder);
builder.Services.AddUnique<IDistributedCacheBinder, DistributedCacheBinder>();
}
}
}

View File

@@ -1,121 +0,0 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;
using Umbraco.Core.Services.Changes;
using Umbraco.Core.Sync;
using Umbraco.Web.Cache;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
using Umbraco.Web.Search;
namespace Umbraco.Web.Compose
{
/// <summary>
/// Ensures that servers are automatically registered in the database, when using the database server registrar.
/// </summary>
/// <remarks>
/// <para>At the moment servers are automatically registered upon first request and then on every
/// request but not more than once per (configurable) period. This really is "for information & debug" purposes so
/// we can look at the table and see what servers are registered - but the info is not used anywhere.</para>
/// <para>Should we actually want to use this, we would need a better and more deterministic way of figuring
/// out the "server address" ie the address to which server-to-server requests should be sent - because it
/// probably is not the "current request address" - especially in multi-domains configurations.</para>
/// </remarks>
// during Initialize / Startup, we end up checking Examine, which needs to be initialized beforehand
// TODO: should not be a strong dependency on "examine" but on an "indexing component"
[ComposeAfter(typeof(ExamineComposer))]
public sealed class DatabaseServerRegistrarAndMessengerComposer : ComponentComposer<DatabaseServerRegistrarAndMessengerComponent>, ICoreComposer
{
public static DatabaseServerMessengerCallbacks GetCallbacks(IServiceProvider factory)
{
return new DatabaseServerMessengerCallbacks
{
//These callbacks will be executed if the server has not been synced
// (i.e. it is a new server or the lastsynced.txt file has been removed)
InitializingCallbacks = new Action[]
{
//rebuild the xml cache file if the server is not synced
() =>
{
var publishedSnapshotService = factory.GetRequiredService<IPublishedSnapshotService>();
// rebuild the published snapshot caches entirely, if the server is not synced
// this is equivalent to DistributedCache RefreshAll... but local only
// (we really should have a way to reuse RefreshAll... locally)
// note: refresh all content & media caches does refresh content types too
publishedSnapshotService.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) });
publishedSnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _);
publishedSnapshotService.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _);
},
//rebuild indexes if the server is not synced
// NOTE: This will rebuild ALL indexes including the members, if developers want to target specific
// indexes then they can adjust this logic themselves.
() =>
{
var indexRebuilder = factory.GetRequiredService<BackgroundIndexRebuilder>();
indexRebuilder.RebuildIndexes(false, 5000);
}
}
};
}
public override void Compose(IUmbracoBuilder builder)
{
base.Compose(builder);
builder.SetDatabaseServerMessengerCallbacks(GetCallbacks);
builder.SetServerMessenger<BatchedDatabaseServerMessenger>();
}
}
public sealed class DatabaseServerRegistrarAndMessengerComponent : IComponent
{
private readonly IBatchedDatabaseServerMessenger _messenger;
private readonly IRequestAccessor _requestAccessor;
public DatabaseServerRegistrarAndMessengerComponent(
IServerMessenger serverMessenger,
IRequestAccessor requestAccessor)
{
_requestAccessor = requestAccessor;
_messenger = serverMessenger as IBatchedDatabaseServerMessenger;
}
public void Initialize()
{
// The scheduled tasks - TouchServerTask and InstructionProcessTask - run as .NET Core hosted services.
// The former (as well as other hosted services that run outside of an HTTP request context) depends on the application URL
// being available (via IRequestAccessor), which can only be retrieved within an HTTP request (unless it's explicitly configured).
// Hence we hook up a one-off task on an HTTP request to ensure this is retrieved, which caches the value and makes it available
// for the hosted services to use when the HTTP request is not available.
_requestAccessor.RouteAttempt += EnsureApplicationUrlOnce;
// Must come last, as it references some _variables
_messenger?.Startup();
}
public void Terminate()
{ }
private void EnsureApplicationUrlOnce(object sender, RoutableAttemptEventArgs e)
{
if (e.Outcome == EnsureRoutableOutcome.IsRoutable || e.Outcome == EnsureRoutableOutcome.NotDocumentRequest)
{
_requestAccessor.RouteAttempt -= EnsureApplicationUrlOnce;
EnsureApplicationUrl();
}
}
private void EnsureApplicationUrl()
{
// By retrieving the application URL within the context of a request (as we are here in responding
// to the IRequestAccessor's RouteAttempt event), we'll get it from the HTTP context and save it for
// future requests that may not be within an HTTP request (e.g. from hosted services).
_requestAccessor.GetApplicationUrl();
}
}
}

View File

@@ -1,5 +1,4 @@
using Umbraco.Core;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;
namespace Umbraco.Web.Compose

View File

@@ -1,37 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;
using Umbraco.Web.Install;
using Umbraco.Web.Install.InstallSteps;
using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Composing.CompositionExtensions
{
public static class Installer
{
public static IUmbracoBuilder ComposeInstaller(this IUmbracoBuilder builder)
{
// register the installer steps
builder.Services.AddScoped<InstallSetupStep,NewInstallStep>();
builder.Services.AddScoped<InstallSetupStep,UpgradeStep>();
builder.Services.AddScoped<InstallSetupStep,FilePermissionsStep>();
builder.Services.AddScoped<InstallSetupStep,DatabaseConfigureStep>();
builder.Services.AddScoped<InstallSetupStep,DatabaseInstallStep>();
builder.Services.AddScoped<InstallSetupStep,DatabaseUpgradeStep>();
// TODO: Add these back once we have a compatible Starter kit
// composition.Services.AddScoped<InstallSetupStep,StarterKitDownloadStep>();
// composition.Services.AddScoped<InstallSetupStep,StarterKitInstallStep>();
// composition.Services.AddScoped<InstallSetupStep,StarterKitCleanupStep>();
builder.Services.AddScoped<InstallSetupStep,CompleteInstallStep>();
builder.Services.AddTransient<InstallStepCollection>();
builder.Services.AddUnique<InstallHelper>();
return builder;
}
}
}

View File

@@ -1,335 +0,0 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Dictionary;
using Umbraco.Core.IO;
using Umbraco.Core.Logging.Viewer;
using Umbraco.Core.Manifest;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PackageActions;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
using Umbraco.Web.Media.EmbedProviders;
using Umbraco.Web.Search;
namespace Umbraco.Core
{
/// <summary>
/// Provides extension methods to the <see cref="Composition"/> class.
/// </summary>
public static partial class CompositionExtensions
{
#region Collection Builders
/// <summary>
/// Gets the cache refreshers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static CacheRefresherCollectionBuilder CacheRefreshers(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<CacheRefresherCollectionBuilder>();
/// <summary>
/// Gets the mappers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static MapperCollectionBuilder Mappers(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<MapperCollectionBuilder>();
/// <summary>
/// Gets the package actions collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
internal static PackageActionCollectionBuilder PackageActions(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<PackageActionCollectionBuilder>();
/// <summary>
/// Gets the data editor collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static DataEditorCollectionBuilder DataEditors(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<DataEditorCollectionBuilder>();
/// <summary>
/// Gets the data value reference factory collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static DataValueReferenceFactoryCollectionBuilder DataValueReferenceFactories(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<DataValueReferenceFactoryCollectionBuilder>();
/// <summary>
/// Gets the property value converters collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static PropertyValueConverterCollectionBuilder PropertyValueConverters(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<PropertyValueConverterCollectionBuilder>();
/// <summary>
/// Gets the url segment providers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static UrlSegmentProviderCollectionBuilder UrlSegmentProviders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<UrlSegmentProviderCollectionBuilder>();
/// <summary>
/// Gets the validators collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
internal static ManifestValueValidatorCollectionBuilder ManifestValueValidators(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ManifestValueValidatorCollectionBuilder>();
/// <summary>
/// Gets the manifest filter collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static ManifestFilterCollectionBuilder ManifestFilters(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<ManifestFilterCollectionBuilder>();
/// <summary>
/// Gets the backoffice OEmbed Providers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static EmbedProvidersCollectionBuilder OEmbedProviders(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<EmbedProvidersCollectionBuilder>();
/// <summary>
/// Gets the back office searchable tree collection builder
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static SearchableTreeCollectionBuilder SearchableTrees(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<SearchableTreeCollectionBuilder>();
#endregion
#region Uniques
/// <summary>
/// Sets the culture dictionary factory.
/// </summary>
/// <typeparam name="T">The type of the factory.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetCultureDictionaryFactory<T>(this IUmbracoBuilder builder)
where T : class, ICultureDictionaryFactory
{
builder.Services.AddUnique<ICultureDictionaryFactory, T>();
}
/// <summary>
/// Sets the culture dictionary factory.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a culture dictionary factory.</param>
public static void SetCultureDictionaryFactory(this IUmbracoBuilder builder, Func<IServiceProvider, ICultureDictionaryFactory> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the culture dictionary factory.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A factory.</param>
public static void SetCultureDictionaryFactory(this IUmbracoBuilder builder, ICultureDictionaryFactory factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the published content model factory.
/// </summary>
/// <typeparam name="T">The type of the factory.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetPublishedContentModelFactory<T>(this IUmbracoBuilder builder)
where T : class, IPublishedModelFactory
{
builder.Services.AddUnique<IPublishedModelFactory, T>();
}
/// <summary>
/// Sets the published content model factory.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a published content model factory.</param>
public static void SetPublishedContentModelFactory(this IUmbracoBuilder builder, Func<IServiceProvider, IPublishedModelFactory> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the published content model factory.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A published content model factory.</param>
public static void SetPublishedContentModelFactory(this IUmbracoBuilder builder, IPublishedModelFactory factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the server registrar.
/// </summary>
/// <typeparam name="T">The type of the server registrar.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetServerRegistrar<T>(this IUmbracoBuilder builder)
where T : class, IServerRegistrar
{
builder.Services.AddUnique<IServerRegistrar, T>();
}
/// <summary>
/// Sets the server registrar.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a server registrar.</param>
public static void SetServerRegistrar(this IUmbracoBuilder builder, Func<IServiceProvider, IServerRegistrar> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the server registrar.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="registrar">A server registrar.</param>
public static void SetServerRegistrar(this IUmbracoBuilder builder, IServerRegistrar registrar)
{
builder.Services.AddUnique(registrar);
}
/// <summary>
/// Sets the server messenger.
/// </summary>
/// <typeparam name="T">The type of the server registrar.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetServerMessenger<T>(this IUmbracoBuilder builder)
where T : class, IServerMessenger
{
builder.Services.AddUnique<IServerMessenger, T>();
}
/// <summary>
/// Sets the server messenger.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a server messenger.</param>
public static void SetServerMessenger(this IUmbracoBuilder builder, Func<IServiceProvider, IServerMessenger> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the server messenger.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="registrar">A server messenger.</param>
public static void SetServerMessenger(this IUmbracoBuilder builder, IServerMessenger registrar)
{
builder.Services.AddUnique(registrar);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating the options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerCallbacks(this IUmbracoBuilder builder, Func<IServiceProvider, DatabaseServerMessengerCallbacks> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="options">Options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this IUmbracoBuilder builder, DatabaseServerMessengerCallbacks options)
{
builder.Services.AddUnique(options);
}
/// <summary>
/// Sets the short string helper.
/// </summary>
/// <typeparam name="T">The type of the short string helper.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetShortStringHelper<T>(this IUmbracoBuilder builder)
where T : class, IShortStringHelper
{
builder.Services.AddUnique<IShortStringHelper, T>();
}
/// <summary>
/// Sets the short string helper.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a short string helper.</param>
public static void SetShortStringHelper(this IUmbracoBuilder builder, Func<IServiceProvider, IShortStringHelper> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the short string helper.
/// </summary>
/// <param name="builder">A builder.</param>
/// <param name="helper">A short string helper.</param>
public static void SetShortStringHelper(this IUmbracoBuilder builder, IShortStringHelper helper)
{
builder.Services.AddUnique(helper);
}
/// <summary>
/// Sets the underlying media filesystem.
/// </summary>
/// <param name="builder">A builder.</param>
/// <param name="filesystemFactory">A filesystem factory.</param>
/// <remarks>
/// Using this helper will ensure that your IFileSystem implementation is wrapped by the ShadowWrapper
/// </remarks>
public static void SetMediaFileSystem(this IUmbracoBuilder builder, Func<IServiceProvider, IFileSystem> filesystemFactory)
=> builder.Services.AddUnique<IMediaFileSystem>(factory =>
{
var fileSystems = factory.GetRequiredService<IO.FileSystems>();
return fileSystems.GetFileSystem<MediaFileSystem>(filesystemFactory(factory));
});
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <typeparam name="T">The type of the log viewer.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetLogViewer<T>(this IUmbracoBuilder builder)
where T : class, ILogViewer
{
builder.Services.AddUnique<ILogViewer, T>();
}
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a log viewer.</param>
public static void SetLogViewer(this IUmbracoBuilder builder, Func<IServiceProvider, ILogViewer> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <param name="builder">A builder.</param>
/// <param name="helper">A log viewer.</param>
public static void SetLogViewer(this IUmbracoBuilder builder, ILogViewer viewer)
{
builder.Services.AddUnique(viewer);
}
#endregion
}
}

View File

@@ -0,0 +1,25 @@
using Umbraco.Core.Cache;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Manifest;
using Umbraco.Core.PackageActions;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
using Umbraco.Core.Trees;
using Umbraco.Web.Media.EmbedProviders;
namespace Umbraco.Infrastructure.DependencyInjection
{
/// <summary>
/// Provides extension methods to the <see cref="IUmbracoBuilder"/> class.
/// </summary>
public static partial class UmbracoBuilderExtensions
{
/// <summary>
/// Gets the mappers collection builder.
/// </summary>
/// <param name="builder">The builder.</param>
public static MapperCollectionBuilder Mappers(this IUmbracoBuilder builder)
=> builder.WithCollectionBuilder<MapperCollectionBuilder>();
}
}

View File

@@ -0,0 +1,208 @@
using System.Runtime.InteropServices;
using Examine;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Hosting;
using Umbraco.Core.Install;
using Umbraco.Core.Logging.Serilog.Enrichers;
using Umbraco.Core.Mail;
using Umbraco.Core.Manifest;
using Umbraco.Core.Media;
using Umbraco.Core.Migrations;
using Umbraco.Core.Migrations.Install;
using Umbraco.Core.Migrations.PostMigrations;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Core.Runtime;
using Umbraco.Core.Scoping;
using Umbraco.Core.Serialization;
using Umbraco.Core.Strings;
using Umbraco.Core.Templates;
using Umbraco.Examine;
using Umbraco.Infrastructure.Examine;
using Umbraco.Infrastructure.Logging.Serilog.Enrichers;
using Umbraco.Infrastructure.Media;
using Umbraco.Infrastructure.Runtime;
using Umbraco.Web;
using Umbraco.Web.HealthCheck;
using Umbraco.Web.HealthCheck.NotificationMethods;
using Umbraco.Web.Install;
using Umbraco.Web.Media;
using Umbraco.Web.Migrations.PostMigrations;
using Umbraco.Web.Models.PublishedContent;
using Umbraco.Web.PropertyEditors;
using Umbraco.Web.PropertyEditors.ValueConverters;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
using Umbraco.Web.Search;
using Umbraco.Web.Trees;
namespace Umbraco.Infrastructure.DependencyInjection
{
public static partial class UmbracoBuilderExtensions
{
/// <summary>
/// Adds all core Umbraco services required to run which may be replaced later in the pipeline
/// </summary>
public static IUmbracoBuilder AddCoreInitialServices(this IUmbracoBuilder builder)
{
builder
.AddMainDom()
.AddLogging();
builder.Services.AddUnique<IUmbracoDatabaseFactory, UmbracoDatabaseFactory>();
builder.Services.AddUnique(factory => factory.GetRequiredService<IUmbracoDatabaseFactory>().CreateDatabase());
builder.Services.AddUnique(factory => factory.GetRequiredService<IUmbracoDatabaseFactory>().SqlContext);
builder.Services.AddUnique<IRuntimeState, RuntimeState>();
builder.Services.AddUnique<IRuntime, CoreRuntime>();
// composers
builder
.AddRepositories()
.AddServices()
.AddCoreMappingProfiles()
.AddFileSystems();
// register persistence mappers - required by database factory so needs to be done here
// means the only place the collection can be modified is in a runtime - afterwards it
// has been frozen and it is too late
builder.Mappers().AddCoreMappers();
// register the scope provider
builder.Services.AddUnique<ScopeProvider>(); // implements both IScopeProvider and IScopeAccessor
builder.Services.AddUnique<IScopeProvider>(f => f.GetRequiredService<ScopeProvider>());
builder.Services.AddUnique<IScopeAccessor>(f => f.GetRequiredService<ScopeProvider>());
builder.Services.AddUnique<IJsonSerializer, JsonNetSerializer>();
builder.Services.AddUnique<IConfigurationEditorJsonSerializer, ConfigurationEditorJsonSerializer>();
builder.Services.AddUnique<IMenuItemCollectionFactory, MenuItemCollectionFactory>();
// register database builder
// *not* a singleton, don't want to keep it around
builder.Services.AddTransient<DatabaseBuilder>();
// register manifest parser, will be injected in collection builders where needed
builder.Services.AddUnique<IManifestParser, ManifestParser>();
// register the manifest filter collection builder (collection is empty by default)
builder.ManifestFilters();
builder.MediaUrlGenerators()
.Add<FileUploadPropertyEditor>()
.Add<ImageCropperPropertyEditor>();
builder.Services.AddUnique<IPublishedContentTypeFactory, PublishedContentTypeFactory>();
builder.Services.AddUnique<IShortStringHelper>(factory
=> new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetRequiredService<IOptions<RequestHandlerSettings>>().Value)));
builder.Services.AddUnique<IMigrationBuilder>(factory => new MigrationBuilder(factory));
builder.Services.AddUnique<IPublishedSnapshotRebuilder, PublishedSnapshotRebuilder>();
// register the published snapshot accessor - the "current" published snapshot is in the umbraco context
builder.Services.AddUnique<IPublishedSnapshotAccessor, UmbracoContextPublishedSnapshotAccessor>();
builder.Services.AddUnique<IVariationContextAccessor, HybridVariationContextAccessor>();
// Config manipulator
builder.Services.AddUnique<IConfigManipulator, JsonConfigManipulator>();
builder.Services.AddUnique<RichTextEditorPastedImages>();
builder.Services.AddUnique<BlockEditorConverter>();
// both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be
// discovered when CoreBootManager configures the converters. We will remove the basic one defined
// in core so that the more enhanced version is active.
builder.PropertyValueConverters()
.Remove<SimpleTinyMceValueConverter>();
builder.Services.AddUnique<IImageUrlGenerator, ImageSharpImageUrlGenerator>();
builder.Services.AddUnique<IPublishedSnapshotRebuilder, PublishedSnapshotRebuilder>();
// register *all* checks, except those marked [HideFromTypeFinder] of course
builder.Services.AddUnique<IMarkdownToHtmlConverter, MarkdownToHtmlConverter>();
builder.Services.AddUnique<IContentLastChanceFinder, ContentFinderByConfigured404>();
builder.Services.AddScoped<UmbracoTreeSearcher>();
// replace
builder.Services.AddUnique<IEmailSender, EmailSender>();
builder.Services.AddUnique<IExamineManager, ExamineManager>();
builder.Services.AddScoped<ITagQuery, TagQuery>();
builder.Services.AddUnique<IUmbracoTreeSearcherFields, UmbracoTreeSearcherFields>();
builder.Services.AddScoped<IPublishedContentQuery>(factory =>
{
var umbCtx = factory.GetRequiredService<IUmbracoContextAccessor>();
return new PublishedContentQuery(umbCtx.UmbracoContext.PublishedSnapshot, factory.GetRequiredService<IVariationContextAccessor>(), factory.GetRequiredService<IExamineManager>());
});
// register accessors for cultures
builder.Services.AddUnique<IDefaultCultureAccessor, DefaultCultureAccessor>();
builder.Services.AddSingleton<IFilePermissionHelper, FilePermissionHelper>();
builder.Services.AddUnique<IUmbracoComponentRenderer, UmbracoComponentRenderer>();
// Register noop versions for examine to be overridden by examine
builder.Services.AddUnique<IUmbracoIndexesCreator, NoopUmbracoIndexesCreator>();
builder.Services.AddUnique<IBackOfficeExamineSearcher, NoopBackOfficeExamineSearcher>();
builder.Services.AddUnique<UploadAutoFillProperties>();
builder.Services.AddUnique<ICronTabParser, NCronTabParser>();
builder.Services.AddUnique<IImageDimensionExtractor, ImageDimensionExtractor>();
builder.Services.AddUnique<PackageDataInstallation>();
builder.AddInstaller();
return builder;
}
/// <summary>
/// Adds logging requirements for Umbraco
/// </summary>
private static IUmbracoBuilder AddLogging(this IUmbracoBuilder builder)
{
builder.Services.AddUnique<ThreadAbortExceptionEnricher>();
builder.Services.AddUnique<HttpSessionIdEnricher>();
builder.Services.AddUnique<HttpRequestNumberEnricher>();
builder.Services.AddUnique<HttpRequestIdEnricher>();
return builder;
}
private static IUmbracoBuilder AddMainDom(this IUmbracoBuilder builder)
{
builder.Services.AddUnique<IMainDomLock>(factory =>
{
var globalSettings = factory.GetRequiredService<IOptions<GlobalSettings>>().Value;
var connectionStrings = factory.GetRequiredService<IOptions<ConnectionStrings>>().Value;
var hostingEnvironment = factory.GetRequiredService<IHostingEnvironment>();
var dbCreator = factory.GetRequiredService<IDbProviderFactoryCreator>();
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var loggerFactory = factory.GetRequiredService<ILoggerFactory>();
return globalSettings.MainDomLock.Equals("SqlMainDomLock") || isWindows == false
? (IMainDomLock)new SqlMainDomLock(loggerFactory.CreateLogger<SqlMainDomLock>(), loggerFactory, globalSettings, connectionStrings, dbCreator, hostingEnvironment)
: new MainDomSemaphoreLock(loggerFactory.CreateLogger<MainDomSemaphoreLock>(), hostingEnvironment);
});
return builder;
}
}
}

View File

@@ -0,0 +1,131 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Events;
using Umbraco.Core.Services.Changes;
using Umbraco.Core.Sync;
using Umbraco.Infrastructure.Cache;
using Umbraco.Web.Cache;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Search;
namespace Umbraco.Infrastructure.DependencyInjection
{
/// <summary>
/// Provides extension methods to the <see cref="IUmbracoBuilder"/> class.
/// </summary>
public static partial class UmbracoBuilderExtensions
{
/// <summary>
/// Adds distributed cache support
/// </summary>
public static IUmbracoBuilder AddDistributedCache(this IUmbracoBuilder builder)
{
builder.SetDatabaseServerMessengerCallbacks(GetCallbacks);
builder.SetServerMessenger<BatchedDatabaseServerMessenger>();
builder.AddNotificationHandler<UmbracoApplicationStarting, DatabaseServerMessengerNotificationHandler>();
builder.Services.AddUnique<IDistributedCacheBinder, DistributedCacheBinder>();
return builder;
}
/// <summary>
/// Sets the server registrar.
/// </summary>
/// <typeparam name="T">The type of the server registrar.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetServerRegistrar<T>(this IUmbracoBuilder builder)
where T : class, IServerRoleAccessor
=> builder.Services.AddUnique<IServerRoleAccessor, T>();
/// <summary>
/// Sets the server registrar.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a server registrar.</param>
public static void SetServerRegistrar(this IUmbracoBuilder builder, Func<IServiceProvider, IServerRoleAccessor> factory)
=> builder.Services.AddUnique(factory);
/// <summary>
/// Sets the server registrar.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="registrar">A server registrar.</param>
public static void SetServerRegistrar(this IUmbracoBuilder builder, IServerRoleAccessor registrar)
=> builder.Services.AddUnique(registrar);
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating the options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerCallbacks(this IUmbracoBuilder builder, Func<IServiceProvider, DatabaseServerMessengerCallbacks> factory)
=> builder.Services.AddUnique(factory);
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="options">Options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this IUmbracoBuilder builder, DatabaseServerMessengerCallbacks options)
=> builder.Services.AddUnique(options);
/// <summary>
/// Sets the server messenger.
/// </summary>
/// <typeparam name="T">The type of the server registrar.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetServerMessenger<T>(this IUmbracoBuilder builder)
where T : class, IServerMessenger
=> builder.Services.AddUnique<IServerMessenger, T>();
/// <summary>
/// Sets the server messenger.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a server messenger.</param>
public static void SetServerMessenger(this IUmbracoBuilder builder, Func<IServiceProvider, IServerMessenger> factory)
=> builder.Services.AddUnique(factory);
/// <summary>
/// Sets the server messenger.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="registrar">A server messenger.</param>
public static void SetServerMessenger(this IUmbracoBuilder builder, IServerMessenger registrar)
=> builder.Services.AddUnique(registrar);
private static DatabaseServerMessengerCallbacks GetCallbacks(IServiceProvider factory) => new DatabaseServerMessengerCallbacks
{
// These callbacks will be executed if the server has not been synced
// (i.e. it is a new server or the lastsynced.txt file has been removed)
InitializingCallbacks = new Action[]
{
// rebuild the xml cache file if the server is not synced
() =>
{
IPublishedSnapshotService publishedSnapshotService = factory.GetRequiredService<IPublishedSnapshotService>();
// rebuild the published snapshot caches entirely, if the server is not synced
// this is equivalent to DistributedCache RefreshAll... but local only
// (we really should have a way to reuse RefreshAll... locally)
// note: refresh all content & media caches does refresh content types too
publishedSnapshotService.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) });
publishedSnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _);
publishedSnapshotService.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _);
},
// rebuild indexes if the server is not synced
// NOTE: This will rebuild ALL indexes including the members, if developers want to target specific
// indexes then they can adjust this logic themselves.
() =>
{
var indexRebuilder = factory.GetRequiredService<BackgroundIndexRebuilder>();
indexRebuilder.RebuildIndexes(false, 5000);
}
}
};
}
}

View File

@@ -1,16 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.IO.MediaPathSchemes;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Infrastructure.DependencyInjection
{
internal static class FileSystems
public static partial class UmbracoBuilderExtensions
{
/*
* HOW TO REPLACE THE MEDIA UNDERLYING FILESYSTEM
@@ -34,16 +33,16 @@ namespace Umbraco.Core.Composing.CompositionExtensions
*
*/
public static IUmbracoBuilder ComposeFileSystems(this IUmbracoBuilder builder)
internal static IUmbracoBuilder AddFileSystems(this IUmbracoBuilder builder)
{
// register FileSystems, which manages all filesystems
// it needs to be registered (not only the interface) because it provides additional
// functionality eg for scoping, and is injected in the scope provider - whereas the
// interface is really for end-users to get access to filesystems.
builder.Services.AddUnique(factory => factory.CreateInstance<Core.IO.FileSystems>(factory));
builder.Services.AddUnique(factory => factory.CreateInstance<FileSystems>(factory));
// register IFileSystems, which gives access too all filesystems
builder.Services.AddUnique<IFileSystems>(factory => factory.GetRequiredService<Core.IO.FileSystems>());
builder.Services.AddUnique<IFileSystems>(factory => factory.GetRequiredService<FileSystems>());
// register the scheme for media paths
builder.Services.AddUnique<IMediaPathScheme, UniqueMediaPathScheme>();

View File

@@ -0,0 +1,36 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core.DependencyInjection;
using Umbraco.Web.Install;
using Umbraco.Web.Install.InstallSteps;
using Umbraco.Web.Install.Models;
namespace Umbraco.Infrastructure.DependencyInjection
{
public static partial class UmbracoBuilderExtensions
{
/// <summary>
/// Adds the services for the Umbraco installer
/// </summary>
internal static IUmbracoBuilder AddInstaller(this IUmbracoBuilder builder)
{
// register the installer steps
builder.Services.AddScoped<InstallSetupStep, NewInstallStep>();
builder.Services.AddScoped<InstallSetupStep, UpgradeStep>();
builder.Services.AddScoped<InstallSetupStep, FilePermissionsStep>();
builder.Services.AddScoped<InstallSetupStep, DatabaseConfigureStep>();
builder.Services.AddScoped<InstallSetupStep, DatabaseInstallStep>();
builder.Services.AddScoped<InstallSetupStep, DatabaseUpgradeStep>();
// TODO: Add these back once we have a compatible Starter kit
// composition.Services.AddScoped<InstallSetupStep,StarterKitDownloadStep>();
// composition.Services.AddScoped<InstallSetupStep,StarterKitInstallStep>();
// composition.Services.AddScoped<InstallSetupStep,StarterKitCleanupStep>();
builder.Services.AddScoped<InstallSetupStep, CompleteInstallStep>();
builder.Services.AddTransient<InstallStepCollection>();
builder.Services.AddUnique<InstallHelper>();
return builder;
}
}
}

View File

@@ -4,17 +4,14 @@ using Umbraco.Core.Mapping;
using Umbraco.Core.Security;
using Umbraco.Web.Models.Mapping;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Infrastructure.DependencyInjection
{
public static class CoreMappingProfiles
public static partial class UmbracoBuilderExtensions
{
/// <summary>
/// Registers the core Umbraco mapper definitions
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static IUmbracoBuilder ComposeCoreMappingProfiles(this IUmbracoBuilder builder)
public static IUmbracoBuilder AddCoreMappingProfiles(this IUmbracoBuilder builder)
{
builder.Services.AddUnique<UmbracoMapper>();
@@ -34,8 +31,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
.Add<TemplateMapDefinition>()
.Add<UserMapDefinition>()
.Add<LanguageMapDefinition>()
.Add<IdentityMapDefinition>()
;
.Add<IdentityMapDefinition>();
builder.Services.AddTransient<CommonMapper>();
builder.Services.AddTransient<MemberTabsAndPropertiesMapper>();

View File

@@ -1,15 +1,18 @@
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.Repositories.Implement;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Infrastructure.DependencyInjection
{
/// <summary>
/// Composes repositories.
/// </summary>
internal static class Repositories
public static partial class UmbracoBuilderExtensions
{
public static IUmbracoBuilder ComposeRepositories(this IUmbracoBuilder builder)
/// <summary>
/// Adds the Umbraco repositories
/// </summary>
internal static IUmbracoBuilder AddRepositories(this IUmbracoBuilder builder)
{
// repositories
builder.Services.AddUnique<IAuditRepository, AuditRepository>();

View File

@@ -1,9 +1,10 @@
using System;
using System;
using System.IO;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
@@ -15,16 +16,15 @@ using Umbraco.Core.Routing;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Infrastructure.DependencyInjection
{
internal static class Services
public static partial class UmbracoBuilderExtensions
{
public static IUmbracoBuilder ComposeServices(this IUmbracoBuilder builder)
/// <summary>
/// Adds Umbraco services
/// </summary>
internal static IUmbracoBuilder AddServices(this IUmbracoBuilder builder)
{
// register a transient messages factory, which will be replaced by the web
// boot manager when running in a web context
builder.Services.AddUnique<IEventMessagesFactory, TransientEventMessagesFactory>();
// register the service context
builder.Services.AddUnique<ServiceContext>();
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
builder.Services.AddUnique<IExternalLoginService, ExternalLoginService>();
builder.Services.AddUnique<IRedirectUrlService, RedirectUrlService>();
builder.Services.AddUnique<IConsentService, ConsentService>();
builder.Services.AddTransient<LocalizedTextServiceFileSources>(SourcesFactory);
builder.Services.AddTransient(SourcesFactory);
builder.Services.AddUnique<ILocalizedTextService>(factory => new LocalizedTextService(
factory.GetRequiredService<Lazy<LocalizedTextServiceFileSources>>(),
factory.GetRequiredService<ILogger<LocalizedTextService>>()));
@@ -82,9 +82,6 @@ namespace Umbraco.Core.Composing.CompositionExtensions
/// <summary>
/// Creates an instance of PackagesRepository for either the ICreatedPackagesRepository or the IInstalledPackagesRepository
/// </summary>
/// <param name="factory"></param>
/// <param name="packageRepoFileName"></param>
/// <returns></returns>
private static PackagesRepository CreatePackageRepository(IServiceProvider factory, string packageRepoFileName)
=> new PackagesRepository(
factory.GetRequiredService<IContentService>(),
@@ -106,9 +103,9 @@ namespace Umbraco.Core.Composing.CompositionExtensions
{
var hostingEnvironment = container.GetRequiredService<IHostingEnvironment>();
var globalSettings = container.GetRequiredService<IOptions<GlobalSettings>>().Value;
var mainLangFolder = new DirectoryInfo(hostingEnvironment.MapPathContentRoot(WebPath.Combine(globalSettings.UmbracoPath , "config","lang")));
var mainLangFolder = new DirectoryInfo(hostingEnvironment.MapPathContentRoot(WebPath.Combine(globalSettings.UmbracoPath, "config", "lang")));
var appPlugins = new DirectoryInfo(hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins));
var configLangFolder = new DirectoryInfo(hostingEnvironment.MapPathContentRoot(WebPath.Combine(Constants.SystemDirectories.Config ,"lang")));
var configLangFolder = new DirectoryInfo(hostingEnvironment.MapPathContentRoot(WebPath.Combine(Constants.SystemDirectories.Config, "lang")));
var pluginLangFolders = appPlugins.Exists == false
? Enumerable.Empty<LocalizedTextServiceSupplementaryFileSource>()
@@ -117,7 +114,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
.SelectMany(x => x.GetFiles("*.xml", SearchOption.TopDirectoryOnly))
.Select(x => new LocalizedTextServiceSupplementaryFileSource(x, false));
//user defined langs that overwrite the default, these should not be used by plugin creators
// user defined langs that overwrite the default, these should not be used by plugin creators
var userLangFolders = configLangFolder.Exists == false
? Enumerable.Empty<LocalizedTextServiceSupplementaryFileSource>()
: configLangFolder

View File

@@ -0,0 +1,157 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Dictionary;
using Umbraco.Core.IO;
using Umbraco.Core.Logging.Viewer;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
namespace Umbraco.Infrastructure.DependencyInjection
{
/// <summary>
/// Provides extension methods to the <see cref="IUmbracoBuilder"/> class.
/// </summary>
public static partial class UmbracoBuilderExtensions
{
/// <summary>
/// Sets the culture dictionary factory.
/// </summary>
/// <typeparam name="T">The type of the factory.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetCultureDictionaryFactory<T>(this IUmbracoBuilder builder)
where T : class, ICultureDictionaryFactory
{
builder.Services.AddUnique<ICultureDictionaryFactory, T>();
}
/// <summary>
/// Sets the culture dictionary factory.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a culture dictionary factory.</param>
public static void SetCultureDictionaryFactory(this IUmbracoBuilder builder, Func<IServiceProvider, ICultureDictionaryFactory> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the culture dictionary factory.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A factory.</param>
public static void SetCultureDictionaryFactory(this IUmbracoBuilder builder, ICultureDictionaryFactory factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the published content model factory.
/// </summary>
/// <typeparam name="T">The type of the factory.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetPublishedContentModelFactory<T>(this IUmbracoBuilder builder)
where T : class, IPublishedModelFactory
{
builder.Services.AddUnique<IPublishedModelFactory, T>();
}
/// <summary>
/// Sets the published content model factory.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a published content model factory.</param>
public static void SetPublishedContentModelFactory(this IUmbracoBuilder builder, Func<IServiceProvider, IPublishedModelFactory> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the published content model factory.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A published content model factory.</param>
public static void SetPublishedContentModelFactory(this IUmbracoBuilder builder, IPublishedModelFactory factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the short string helper.
/// </summary>
/// <typeparam name="T">The type of the short string helper.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetShortStringHelper<T>(this IUmbracoBuilder builder)
where T : class, IShortStringHelper
{
builder.Services.AddUnique<IShortStringHelper, T>();
}
/// <summary>
/// Sets the short string helper.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a short string helper.</param>
public static void SetShortStringHelper(this IUmbracoBuilder builder, Func<IServiceProvider, IShortStringHelper> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the short string helper.
/// </summary>
/// <param name="builder">A builder.</param>
/// <param name="helper">A short string helper.</param>
public static void SetShortStringHelper(this IUmbracoBuilder builder, IShortStringHelper helper)
{
builder.Services.AddUnique(helper);
}
/// <summary>
/// Sets the underlying media filesystem.
/// </summary>
/// <param name="builder">A builder.</param>
/// <param name="filesystemFactory">A filesystem factory.</param>
/// <remarks>
/// Using this helper will ensure that your IFileSystem implementation is wrapped by the ShadowWrapper
/// </remarks>
public static void SetMediaFileSystem(this IUmbracoBuilder builder, Func<IServiceProvider, IFileSystem> filesystemFactory)
=> builder.Services.AddUnique<IMediaFileSystem>(factory =>
{
var fileSystems = factory.GetRequiredService<FileSystems>();
return fileSystems.GetFileSystem<MediaFileSystem>(filesystemFactory(factory));
});
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <typeparam name="T">The type of the log viewer.</typeparam>
/// <param name="builder">The builder.</param>
public static void SetLogViewer<T>(this IUmbracoBuilder builder)
where T : class, ILogViewer
{
builder.Services.AddUnique<ILogViewer, T>();
}
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="factory">A function creating a log viewer.</param>
public static void SetLogViewer(this IUmbracoBuilder builder, Func<IServiceProvider, ILogViewer> factory)
{
builder.Services.AddUnique(factory);
}
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <param name="builder">A builder.</param>
/// <param name="helper">A log viewer.</param>
public static void SetLogViewer(this IUmbracoBuilder builder, ILogViewer viewer)
{
builder.Services.AddUnique(viewer);
}
}
}

View File

@@ -7,6 +7,7 @@ using MimeKit;
using MimeKit.Text;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Events;
using Umbraco.Core.Mail;
using Umbraco.Core.Models;
using SmtpClient = MailKit.Net.Smtp.SmtpClient;

View File

@@ -30,7 +30,7 @@ namespace Umbraco.Infrastructure.HostedServices
private readonly HealthCheckCollection _healthChecks;
private readonly HealthCheckNotificationMethodCollection _notifications;
private readonly IRuntimeState _runtimeState;
private readonly IServerRegistrar _serverRegistrar;
private readonly IServerRoleAccessor _serverRegistrar;
private readonly IMainDom _mainDom;
private readonly IScopeProvider _scopeProvider;
private readonly ILogger<HealthCheckNotifier> _logger;
@@ -54,7 +54,7 @@ namespace Umbraco.Infrastructure.HostedServices
HealthCheckCollection healthChecks,
HealthCheckNotificationMethodCollection notifications,
IRuntimeState runtimeState,
IServerRegistrar serverRegistrar,
IServerRoleAccessor serverRegistrar,
IMainDom mainDom,
IScopeProvider scopeProvider,
ILogger<HealthCheckNotifier> logger,
@@ -87,7 +87,7 @@ namespace Umbraco.Infrastructure.HostedServices
return;
}
switch (_serverRegistrar.GetCurrentServerRole())
switch (_serverRegistrar.CurrentServerRole)
{
case ServerRole.Replica:
_logger.LogDebug("Does not run on replica servers.");

View File

@@ -24,7 +24,7 @@ namespace Umbraco.Infrastructure.HostedServices
private readonly KeepAliveSettings _keepAliveSettings;
private readonly ILogger<KeepAlive> _logger;
private readonly IProfilingLogger _profilingLogger;
private readonly IServerRegistrar _serverRegistrar;
private readonly IServerRoleAccessor _serverRegistrar;
private readonly IHttpClientFactory _httpClientFactory;
/// <summary>
@@ -43,7 +43,7 @@ namespace Umbraco.Infrastructure.HostedServices
IOptions<KeepAliveSettings> keepAliveSettings,
ILogger<KeepAlive> logger,
IProfilingLogger profilingLogger,
IServerRegistrar serverRegistrar,
IServerRoleAccessor serverRegistrar,
IHttpClientFactory httpClientFactory)
: base(TimeSpan.FromMinutes(5), DefaultDelay)
{
@@ -64,7 +64,7 @@ namespace Umbraco.Infrastructure.HostedServices
}
// Don't run on replicas nor unknown role servers
switch (_serverRegistrar.GetCurrentServerRole())
switch (_serverRegistrar.CurrentServerRole)
{
case ServerRole.Replica:
_logger.LogDebug("Does not run on replica servers.");

View File

@@ -23,7 +23,7 @@ namespace Umbraco.Infrastructure.HostedServices
public class LogScrubber : RecurringHostedServiceBase
{
private readonly IMainDom _mainDom;
private readonly IServerRegistrar _serverRegistrar;
private readonly IServerRoleAccessor _serverRegistrar;
private readonly IAuditService _auditService;
private readonly LoggingSettings _settings;
private readonly IProfilingLogger _profilingLogger;
@@ -42,7 +42,7 @@ namespace Umbraco.Infrastructure.HostedServices
/// <param name="profilingLogger">The profiling logger.</param>
public LogScrubber(
IMainDom mainDom,
IServerRegistrar serverRegistrar,
IServerRoleAccessor serverRegistrar,
IAuditService auditService,
IOptions<LoggingSettings> settings,
IScopeProvider scopeProvider,
@@ -61,7 +61,7 @@ namespace Umbraco.Infrastructure.HostedServices
internal override Task PerformExecuteAsync(object state)
{
switch (_serverRegistrar.GetCurrentServerRole())
switch (_serverRegistrar.CurrentServerRole)
{
case ServerRole.Replica:
_logger.LogDebug("Does not run on replica servers.");

View File

@@ -27,7 +27,7 @@ namespace Umbraco.Infrastructure.HostedServices
private readonly IRuntimeState _runtimeState;
private readonly IServerMessenger _serverMessenger;
private readonly IBackOfficeSecurityFactory _backofficeSecurityFactory;
private readonly IServerRegistrar _serverRegistrar;
private readonly IServerRoleAccessor _serverRegistrar;
private readonly IUmbracoContextFactory _umbracoContextFactory;
/// <summary>
@@ -44,7 +44,7 @@ namespace Umbraco.Infrastructure.HostedServices
public ScheduledPublishing(
IRuntimeState runtimeState,
IMainDom mainDom,
IServerRegistrar serverRegistrar,
IServerRoleAccessor serverRegistrar,
IContentService contentService,
IUmbracoContextFactory umbracoContextFactory,
ILogger<ScheduledPublishing> logger,
@@ -69,7 +69,7 @@ namespace Umbraco.Infrastructure.HostedServices
return Task.CompletedTask;
}
switch (_serverRegistrar.GetCurrentServerRole())
switch (_serverRegistrar.CurrentServerRole)
{
case ServerRole.Replica:
_logger.LogDebug("Does not run on replica servers.");
@@ -123,9 +123,9 @@ namespace Umbraco.Infrastructure.HostedServices
finally
{
// If running on a temp context, we have to flush the messenger
if (contextReference.IsRoot && _serverMessenger is IBatchedDatabaseServerMessenger m)
if (contextReference.IsRoot)
{
m.FlushBatch();
_serverMessenger.SendMessages();
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Umbraco.Infrastructure.HostedServices.ServerRegistration
public class InstructionProcessTask : RecurringHostedServiceBase
{
private readonly IRuntimeState _runtimeState;
private readonly IDatabaseServerMessenger _messenger;
private readonly IServerMessenger _messenger;
private readonly ILogger<InstructionProcessTask> _logger;
/// <summary>
@@ -31,7 +31,7 @@ namespace Umbraco.Infrastructure.HostedServices.ServerRegistration
: base(globalSettings.Value.DatabaseServerMessenger.TimeBetweenSyncOperations, TimeSpan.FromMinutes(1))
{
_runtimeState = runtimeState;
_messenger = messenger as IDatabaseServerMessenger ?? throw new ArgumentNullException(nameof(messenger));
_messenger = messenger;
_logger = logger;
}

View File

@@ -57,9 +57,7 @@ namespace Umbraco.Infrastructure.HostedServices.ServerRegistration
try
{
// TouchServer uses a proper unit of work etc underneath so even in a
// background task it is safe to call it without dealing with any scope.
_serverRegistrationService.TouchServer(serverAddress, _serverRegistrationService.CurrentServerIdentity, _globalSettings.DatabaseServerRegistrar.StaleServerTimeout);
_serverRegistrationService.TouchServer(serverAddress, _globalSettings.DatabaseServerRegistrar.StaleServerTimeout);
}
catch (Exception ex)
{

View File

@@ -6,9 +6,9 @@ using Umbraco.Core.Services;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Security;
using Umbraco.Net;
using Umbraco.Web.Install.Models;
using Umbraco.Web.Security;
using Umbraco.Core.Hosting;
namespace Umbraco.Web.Install.InstallSteps
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Reflection;
using System.Threading;
using Microsoft.Extensions.Options;
@@ -55,6 +55,7 @@ namespace Umbraco.Infrastructure.Logging.Serilog.Enrichers
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ThreadAbortExceptionInfo", message));
}
else
{
try
{
var dumped = MiniDump.Dump(_marchal, _hostingEnvironment, withException: true);
@@ -69,6 +70,7 @@ namespace Umbraco.Infrastructure.Logging.Serilog.Enrichers
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ThreadAbortExceptionInfo", message));
}
}
}
private static bool IsTimeoutThreadAbortException(Exception exception)
{

View File

@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using Umbraco.Core;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging.Serilog.Enrichers;
using Umbraco.Infrastructure.Logging.Serilog.Enrichers;
namespace Umbraco.Infrastructure.Logging.Serilog
{
public class SerilogComposer : ICoreComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.Services.AddUnique<ThreadAbortExceptionEnricher>();
builder.Services.AddUnique<HttpSessionIdEnricher>();
builder.Services.AddUnique<HttpRequestNumberEnricher>();
builder.Services.AddUnique<HttpRequestIdEnricher>();
}
}
}

View File

@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using Serilog;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;
using Umbraco.Infrastructure.DependencyInjection;
namespace Umbraco.Core.Logging.Viewer
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Drawing;
using System.IO;
using Umbraco.Core;
@@ -18,7 +18,7 @@ namespace Umbraco.Web.Media
/// use potentially large amounts of memory.</remarks>
public ImageSize GetDimensions(Stream stream)
{
//Try to load with exif
// Try to load with exif
try
{
if (ExifImageDimensionExtractor.TryGetDimensions(stream, out var width, out var height))
@@ -28,12 +28,13 @@ namespace Umbraco.Web.Media
}
catch
{
//We will just swallow, just means we can't read exif data, we don't want to log an error either
// We will just swallow, just means we can't read exif data, we don't want to log an error either
}
//we have no choice but to try to read in via GDI
// we have no choice but to try to read in via GDI
try
{
// TODO: We should be using ImageSharp for this
using (var image = Image.FromStream(stream))
{
var fileWidth = image.Width;
@@ -43,7 +44,7 @@ namespace Umbraco.Web.Media
}
catch (Exception)
{
//We will just swallow, just means we can't read via GDI, we don't want to log an error either
// We will just swallow, just means we can't read via GDI, we don't want to log an error either
}
return new ImageSize(Constants.Conventions.Media.DefaultSize, Constants.Conventions.Media.DefaultSize);

View File

@@ -1,5 +1,6 @@
using System;
using Umbraco.Core.Composing;
using Umbraco.Core.DependencyInjection;
namespace Umbraco.Core.Migrations
{

View File

@@ -1,12 +0,0 @@
namespace Umbraco.Core.Migrations.PostMigrations
{
/// <summary>
/// Implements <see cref="IPublishedSnapshotRebuilder"/> in Umbraco.Core (doing nothing).
/// </summary>
public class NoopPublishedSnapshotRebuilder : IPublishedSnapshotRebuilder
{
/// <inheritdoc />
public void Rebuild()
{ }
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -17,16 +17,11 @@ namespace Umbraco.Core.Packaging
private readonly IPackageActionRunner _packageActionRunner;
private readonly DirectoryInfo _applicationRootFolder;
/// <summary>
/// Constructor
/// Initializes a new instance of the <see cref="PackageInstallation"/> class.
/// </summary>
/// <param name="packageDataInstallation"></param>
/// <param name="packageFileInstallation"></param>
/// <param name="parser"></param>
/// <param name="packageActionRunner"></param>
/// <param name="hostingEnvironment"></param>
public PackageInstallation(PackageDataInstallation packageDataInstallation, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, IPackageActionRunner packageActionRunner,
IHostingEnvironment hostingEnvironment)
public PackageInstallation(PackageDataInstallation packageDataInstallation, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, IPackageActionRunner packageActionRunner, IHostingEnvironment hostingEnvironment)
{
_packageExtraction = new PackageExtraction();
_packageFileInstallation = packageFileInstallation ?? throw new ArgumentNullException(nameof(packageFileInstallation));

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Text;
using HtmlAgilityPack;
using Umbraco.Core;
@@ -16,7 +16,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
/// used dynamically.
/// </summary>
[DefaultPropertyValueConverter]
public class RteMacroRenderingValueConverter : TinyMceValueConverter
public class RteMacroRenderingValueConverter : SimpleTinyMceValueConverter
{
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
private readonly IMacroRenderer _macroRenderer;

View File

@@ -1,392 +0,0 @@
using System;
using Examine;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing.CompositionExtensions;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Dashboards;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.Install;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
using Umbraco.Core.Media;
using Umbraco.Core.Migrations;
using Umbraco.Core.Migrations.Install;
using Umbraco.Core.Migrations.PostMigrations;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.Validators;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Core.Scoping;
using Umbraco.Core.Security;
using Umbraco.Core.Serialization;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
using Umbraco.Core.Templates;
using Umbraco.Examine;
using Umbraco.Infrastructure.Examine;
using Umbraco.Infrastructure.Media;
using Umbraco.Web;
using Umbraco.Web.Actions;
using Umbraco.Web.Cache;
using Umbraco.Web.ContentApps;
using Umbraco.Web.Editors;
using Umbraco.Web.Features;
using Umbraco.Web.HealthCheck;
using Umbraco.Web.HealthCheck.NotificationMethods;
using Umbraco.Web.Install;
using Umbraco.Web.Media;
using Umbraco.Web.Media.EmbedProviders;
using Umbraco.Web.Migrations.PostMigrations;
using Umbraco.Web.Models.PublishedContent;
using Umbraco.Web.PropertyEditors;
using Umbraco.Web.PropertyEditors.ValueConverters;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
using Umbraco.Web.Search;
using Umbraco.Web.Sections;
using Umbraco.Web.Services;
using Umbraco.Web.Templates;
using Umbraco.Web.Trees;
using TextStringValueConverter = Umbraco.Core.PropertyEditors.ValueConverters.TextStringValueConverter;
namespace Umbraco.Infrastructure.Runtime
{
public static class CoreInitialServices
{
public static IUmbracoBuilder AddCoreInitialServices(this IUmbracoBuilder builder)
{
builder.AddNotificationHandler<UmbracoApplicationStarting, EssentialDirectoryCreator>();
builder.Services.AddSingleton<ManifestWatcher>();
builder.AddNotificationHandler<UmbracoApplicationStarting, AppPluginsManifestWatcherNotificationHandler>();
// composers
builder
.ComposeRepositories()
.ComposeServices()
.ComposeCoreMappingProfiles()
.ComposeFileSystems();
// register persistence mappers - required by database factory so needs to be done here
// means the only place the collection can be modified is in a runtime - afterwards it
// has been frozen and it is too late
builder.Mappers().AddCoreMappers();
// register the scope provider
builder.Services.AddUnique<ScopeProvider>(); // implements both IScopeProvider and IScopeAccessor
builder.Services.AddUnique<IScopeProvider>(f => f.GetRequiredService<ScopeProvider>());
builder.Services.AddUnique<IScopeAccessor>(f => f.GetRequiredService<ScopeProvider>());
builder.Services.AddUnique<IJsonSerializer, JsonNetSerializer>();
builder.Services.AddUnique<IConfigurationEditorJsonSerializer, ConfigurationEditorJsonSerializer>();
builder.Services.AddUnique<IMenuItemCollectionFactory, MenuItemCollectionFactory>();
builder.Services.AddUnique<InstallStatusTracker>();
// register database builder
// *not* a singleton, don't want to keep it around
builder.Services.AddTransient<DatabaseBuilder>();
// register manifest parser, will be injected in collection builders where needed
builder.Services.AddUnique<IManifestParser, ManifestParser>();
// register our predefined validators
builder.ManifestValueValidators()
.Add<RequiredValidator>()
.Add<RegexValidator>()
.Add<DelimitedValueValidator>()
.Add<EmailValidator>()
.Add<IntegerValidator>()
.Add<DecimalValidator>();
// register the manifest filter collection builder (collection is empty by default)
builder.ManifestFilters();
// properties and parameters derive from data editors
builder.DataEditors()
.Add(() => builder.TypeLoader.GetDataEditors());
builder.MediaUrlGenerators()
.Add<FileUploadPropertyEditor>()
.Add<ImageCropperPropertyEditor>();
builder.Services.AddUnique<PropertyEditorCollection>();
builder.Services.AddUnique<ParameterEditorCollection>();
// Used to determine if a datatype/editor should be storing/tracking
// references to media item/s
builder.DataValueReferenceFactories();
// register a server registrar, by default it's the db registrar
builder.Services.AddUnique<IServerRegistrar>(f =>
{
var globalSettings = f.GetRequiredService<IOptions<GlobalSettings>>().Value;
// TODO: we still register the full IServerMessenger because
// even on 1 single server we can have 2 concurrent app domains
var singleServer = globalSettings.DisableElectionForSingleServer;
return singleServer
? (IServerRegistrar) new SingleServerRegistrar(f.GetRequiredService<IRequestAccessor>())
: new DatabaseServerRegistrar(
new Lazy<IServerRegistrationService>(f.GetRequiredService<IServerRegistrationService>));
});
// by default we'll use the database server messenger with default options (no callbacks),
// this will be overridden by the db thing in the corresponding components in the web
// project
builder.Services.AddUnique<IServerMessenger>(factory
=> new DatabaseServerMessenger(
factory.GetRequiredService<IMainDom>(),
factory.GetRequiredService<IScopeProvider>(),
factory.GetRequiredService<IUmbracoDatabaseFactory>(),
factory.GetRequiredService<IProfilingLogger>(),
factory.GetRequiredService<ILogger<DatabaseServerMessenger>>(),
factory.GetRequiredService<IServerRegistrar>(),
true,
new DatabaseServerMessengerCallbacks(),
factory.GetRequiredService<IHostingEnvironment>(),
factory.GetRequiredService<CacheRefresherCollection>(),
factory.GetRequiredService<IOptions<GlobalSettings>>()
));
builder.CacheRefreshers()
.Add(() => builder.TypeLoader.GetCacheRefreshers());
builder.PackageActions()
.Add(() => builder.TypeLoader.GetPackageActions());
builder.PropertyValueConverters()
.Append(builder.TypeLoader.GetTypes<IPropertyValueConverter>());
builder.Services.AddUnique<IPublishedContentTypeFactory, PublishedContentTypeFactory>();
builder.Services.AddUnique<IShortStringHelper>(factory
=> new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetRequiredService<IOptions<RequestHandlerSettings>>().Value)));
builder.UrlSegmentProviders()
.Append<DefaultUrlSegmentProvider>();
builder.Services.AddUnique<IMigrationBuilder>(factory => new MigrationBuilder(factory));
// by default, register a noop factory
builder.Services.AddUnique<IPublishedModelFactory, NoopPublishedModelFactory>();
// by default
builder.Services.AddUnique<IPublishedSnapshotRebuilder, PublishedSnapshotRebuilder>();
builder.SetCultureDictionaryFactory<DefaultCultureDictionaryFactory>();
builder.Services.AddSingleton(f => f.GetRequiredService<ICultureDictionaryFactory>().CreateDictionary());
builder.Services.AddUnique<UriUtility>();
// register the published snapshot accessor - the "current" published snapshot is in the umbraco context
builder.Services.AddUnique<IPublishedSnapshotAccessor, UmbracoContextPublishedSnapshotAccessor>();
builder.Services.AddUnique<IVariationContextAccessor, HybridVariationContextAccessor>();
builder.Services.AddUnique<IDashboardService, DashboardService>();
// register core CMS dashboards and 3rd party types - will be ordered by weight attribute & merged with package.manifest dashboards
builder.Dashboards()
.Add(builder.TypeLoader.GetTypes<IDashboard>());
// will be injected in controllers when needed to invoke rest endpoints on Our
builder.Services.AddUnique<IInstallationService, InstallationService>();
builder.Services.AddUnique<IUpgradeService, UpgradeService>();
// Grid config is not a real config file as we know them
builder.Services.AddUnique<IGridConfig, GridConfig>();
// Config manipulator
builder.Services.AddUnique<IConfigManipulator, JsonConfigManipulator>();
// register the umbraco context factory
// composition.Services.AddUnique<IUmbracoContextFactory, UmbracoContextFactory>();
builder.Services.AddUnique<IPublishedUrlProvider, UrlProvider>();
builder.Services.AddUnique<HtmlLocalLinkParser>();
builder.Services.AddUnique<HtmlImageSourceParser>();
builder.Services.AddUnique<HtmlUrlParser>();
builder.Services.AddUnique<RichTextEditorPastedImages>();
builder.Services.AddUnique<BlockEditorConverter>();
// both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be
// discovered when CoreBootManager configures the converters. We HAVE to remove one of them
// here because there cannot be two converters for one property editor - and we want the full
// RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter.
// (the limited one, defined in Core, is there for tests) - same for others
builder.PropertyValueConverters()
.Remove<TinyMceValueConverter>()
.Remove<TextStringValueConverter>()
.Remove<MarkdownEditorValueConverter>();
builder.UrlProviders()
.Append<AliasUrlProvider>()
.Append<DefaultUrlProvider>();
builder.MediaUrlProviders()
.Append<DefaultMediaUrlProvider>();
builder.Services.AddUnique<ISiteDomainHelper, SiteDomainHelper>();
// register properties fallback
builder.Services.AddUnique<IPublishedValueFallback, PublishedValueFallback>();
builder.Services.AddUnique<IImageUrlGenerator, ImageSharpImageUrlGenerator>();
builder.Services.AddUnique<UmbracoFeatures>();
builder.Actions()
.Add(() => builder.TypeLoader.GetTypes<IAction>());
builder.EditorValidators()
.Add(() => builder.TypeLoader.GetTypes<IEditorValidator>());
builder.TourFilters();
// replace with web implementation
builder.Services.AddUnique<IPublishedSnapshotRebuilder, PublishedSnapshotRebuilder>();
// register OEmbed providers - no type scanning - all explicit opt-in of adding types
// note: IEmbedProvider is not IDiscoverable - think about it if going for type scanning
builder.OEmbedProviders()
.Append<YouTube>()
.Append<Twitter>()
.Append<Vimeo>()
.Append<DailyMotion>()
.Append<Flickr>()
.Append<Slideshare>()
.Append<Kickstarter>()
.Append<GettyImages>()
.Append<Ted>()
.Append<Soundcloud>()
.Append<Issuu>()
.Append<Hulu>()
.Append<Giphy>();
// register back office sections in the order we want them rendered
builder.Sections()
.Append<ContentSection>()
.Append<MediaSection>()
.Append<SettingsSection>()
.Append<PackagesSection>()
.Append<UsersSection>()
.Append<MembersSection>()
.Append<FormsSection>()
.Append<TranslationSection>();
// register known content apps
builder.ContentApps()
.Append<ListViewContentAppFactory>()
.Append<ContentEditorContentAppFactory>()
.Append<ContentInfoContentAppFactory>()
.Append<ContentTypeDesignContentAppFactory>()
.Append<ContentTypeListViewContentAppFactory>()
.Append<ContentTypePermissionsContentAppFactory>()
.Append<ContentTypeTemplatesContentAppFactory>();
// register published router
builder.Services.AddUnique<IPublishedRouter, PublishedRouter>();
// register *all* checks, except those marked [HideFromTypeFinder] of course
builder.Services.AddUnique<IMarkdownToHtmlConverter, MarkdownToHtmlConverter>();
builder.HealthChecks()
.Add(() => builder.TypeLoader.GetTypes<Core.HealthCheck.HealthCheck>());
builder.WithCollectionBuilder<HealthCheckNotificationMethodCollectionBuilder>()
.Add(() => builder.TypeLoader.GetTypes<IHealthCheckNotificationMethod>());
builder.Services.AddUnique<IContentLastChanceFinder, ContentFinderByConfigured404>();
builder.ContentFinders()
// all built-in finders in the correct order,
// devs can then modify this list on application startup
.Append<ContentFinderByPageIdQuery>()
.Append<ContentFinderByUrl>()
.Append<ContentFinderByIdPath>()
//.Append<ContentFinderByUrlAndTemplate>() // disabled, this is an odd finder
.Append<ContentFinderByUrlAlias>()
.Append<ContentFinderByRedirectUrl>();
builder.Services.AddScoped<UmbracoTreeSearcher>();
builder.SearchableTrees()
.Add(() => builder.TypeLoader.GetTypes<ISearchableTree>());
// replace some services
builder.Services.AddUnique<IEventMessagesFactory, DefaultEventMessagesFactory>();
builder.Services.AddUnique<IEventMessagesAccessor, HybridEventMessagesAccessor>();
builder.Services.AddUnique<ITreeService, TreeService>();
builder.Services.AddUnique<ISectionService, SectionService>();
builder.Services.AddUnique<IEmailSender, EmailSender>();
builder.Services.AddUnique<ISmsSender, NotImplementedSmsSender>();
builder.Services.AddUnique<IExamineManager, ExamineManager>();
// register distributed cache
builder.Services.AddUnique(f => new DistributedCache(f.GetRequiredService<IServerMessenger>(), f.GetRequiredService<CacheRefresherCollection>()));
builder.Services.AddScoped<ITagQuery, TagQuery>();
builder.Services.AddUnique<HtmlLocalLinkParser>();
builder.Services.AddUnique<HtmlUrlParser>();
builder.Services.AddUnique<HtmlImageSourceParser>();
builder.Services.AddUnique<RichTextEditorPastedImages>();
builder.Services.AddUnique<IUmbracoTreeSearcherFields, UmbracoTreeSearcherFields>();
builder.Services.AddScoped<IPublishedContentQuery>(factory =>
{
var umbCtx = factory.GetRequiredService<IUmbracoContextAccessor>();
return new PublishedContentQuery(umbCtx.UmbracoContext.PublishedSnapshot, factory.GetRequiredService<IVariationContextAccessor>(), factory.GetRequiredService<IExamineManager>());
});
builder.Services.AddUnique<IPublishedUrlProvider, UrlProvider>();
// register the http context and umbraco context accessors
// we *should* use the HttpContextUmbracoContextAccessor, however there are cases when
// we have no http context, eg when booting Umbraco or in background threads, so instead
// let's use an hybrid accessor that can fall back to a ThreadStatic context.
builder.Services.AddUnique<IUmbracoContextAccessor, HybridUmbracoContextAccessor>();
// register accessors for cultures
builder.Services.AddUnique<IDefaultCultureAccessor, DefaultCultureAccessor>();
builder.Services.AddSingleton<IFilePermissionHelper, FilePermissionHelper>();
builder.Services.AddUnique<IUmbracoComponentRenderer, UmbracoComponentRenderer>();
// Register noop versions for examine to be overridden by examine
builder.Services.AddUnique<IUmbracoIndexesCreator, NoopUmbracoIndexesCreator>();
builder.Services.AddUnique<IBackOfficeExamineSearcher, NoopBackOfficeExamineSearcher>();
builder.Services.AddUnique<UploadAutoFillProperties>();
builder.Services.AddUnique<ICronTabParser, NCronTabParser>();
builder.Services.AddUnique(factory => new LegacyPasswordSecurity());
builder.Services.AddUnique<UserEditorAuthorizationHelper>();
builder.Services.AddUnique<ContentPermissions>();
builder.Services.AddUnique<MediaPermissions>();
builder.Services.AddUnique<IImageDimensionExtractor, ImageDimensionExtractor>();
builder.Services.AddUnique<PackageDataInstallation>();
return builder;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Data;
using Umbraco.Core.Events;
using Umbraco.Core.Persistence;

View File

@@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;

View File

@@ -8,11 +8,11 @@ using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Core.Trees;
using Umbraco.Examine;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Mapping;
using Umbraco.Web.Routing;
using Umbraco.Web.Trees;
namespace Umbraco.Web.Search
{

View File

@@ -14,6 +14,7 @@ using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Scoping;
using Umbraco.Core.Mail;
namespace Umbraco.Core.Services.Implement
{

View File

@@ -1,8 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Umbraco.Core.Composing;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.Models;
@@ -26,11 +25,12 @@ namespace Umbraco.Core.Services.Implement
/// <summary>
/// Initializes a new instance of the <see cref="ServerRegistrationService"/> class.
/// </summary>
/// <param name="scopeProvider">A UnitOfWork provider.</param>
/// <param name="loggerFactory">A logger factory</param>
/// <param name="eventMessagesFactory"></param>
public ServerRegistrationService(IScopeProvider scopeProvider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory,
IServerRegistrationRepository serverRegistrationRepository, IHostingEnvironment hostingEnvironment)
public ServerRegistrationService(
IScopeProvider scopeProvider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IServerRegistrationRepository serverRegistrationRepository,
IHostingEnvironment hostingEnvironment)
: base(scopeProvider, loggerFactory, eventMessagesFactory)
{
_serverRegistrationRepository = serverRegistrationRepository;
@@ -41,10 +41,10 @@ namespace Umbraco.Core.Services.Implement
/// Touches a server to mark it as active; deactivate stale servers.
/// </summary>
/// <param name="serverAddress">The server URL.</param>
/// <param name="serverIdentity">The server unique identity.</param>
/// <param name="staleTimeout">The time after which a server is considered stale.</param>
public void TouchServer(string serverAddress, string serverIdentity, TimeSpan staleTimeout)
public void TouchServer(string serverAddress, TimeSpan staleTimeout)
{
var serverIdentity = GetCurrentServerIdentity();
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.Servers);
@@ -144,19 +144,16 @@ namespace Umbraco.Core.Services.Implement
}
}
/// <summary>
/// Gets the local server identity.
/// </summary>
public string CurrentServerIdentity => NetworkHelper.MachineName // eg DOMAIN\SERVER
+ "/" + _hostingEnvironment.ApplicationId; // eg /LM/S3SVC/11/ROOT;
/// <summary>
/// Gets the role of the current server.
/// </summary>
/// <returns>The role of the current server.</returns>
public ServerRole GetCurrentServerRole()
{
return _currentServerRole;
}
public ServerRole GetCurrentServerRole() => _currentServerRole;
/// <summary>
/// Gets the local server identity.
/// </summary>
private string GetCurrentServerIdentity() => NetworkHelper.MachineName // eg DOMAIN\SERVER
+ "/" + _hostingEnvironment.ApplicationId; // eg /LM/S3SVC/11/ROOT;
}
}

View File

@@ -1,104 +1,83 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Scoping;
using Umbraco.Core.Sync;
using Umbraco.Web.Routing;
using Umbraco.Web;
namespace Umbraco.Web
namespace Umbraco.Core.Sync
{
/// <summary>
/// An <see cref="IServerMessenger"/> implementation that works by storing messages in the database.
/// </summary>
/// <remarks>
/// This binds to appropriate umbraco events in order to trigger the Boot(), Sync() & FlushBatch() calls
/// </remarks>
public class BatchedDatabaseServerMessenger : DatabaseServerMessenger, IBatchedDatabaseServerMessenger
public class BatchedDatabaseServerMessenger : DatabaseServerMessenger
{
private readonly IUmbracoDatabaseFactory _databaseFactory;
private readonly IRequestCache _requestCache;
private readonly IRequestAccessor _requestAccessor;
/// <summary>
/// Initializes a new instance of the <see cref="BatchedDatabaseServerMessenger"/> class.
/// </summary>
public BatchedDatabaseServerMessenger(
IMainDom mainDom,
IUmbracoDatabaseFactory databaseFactory,
IScopeProvider scopeProvider,
IProfilingLogger proflog,
ILogger<BatchedDatabaseServerMessenger> logger,
IServerRegistrar serverRegistrar,
IServerRoleAccessor serverRegistrar,
DatabaseServerMessengerCallbacks callbacks,
IHostingEnvironment hostingEnvironment,
CacheRefresherCollection cacheRefreshers,
IRequestCache requestCache,
IRequestAccessor requestAccessor,
IOptions<GlobalSettings> globalSettings)
: base(mainDom, scopeProvider, databaseFactory, proflog, logger, serverRegistrar, true, callbacks, hostingEnvironment, cacheRefreshers, globalSettings)
: base(mainDom, scopeProvider, proflog, logger, serverRegistrar, true, callbacks, hostingEnvironment, cacheRefreshers, globalSettings)
{
_databaseFactory = databaseFactory;
_requestCache = requestCache;
_requestAccessor = requestAccessor;
}
// invoked by DatabaseServerRegistrarAndMessengerComponent
public void Startup()
{
_requestAccessor.EndRequest += UmbracoModule_EndRequest;
if (_databaseFactory.CanConnect == false)
{
Logger.LogWarning("Cannot connect to the database, distributed calls will not be enabled for this server.");
}
else
{
Boot();
}
}
private void UmbracoModule_EndRequest(object sender, UmbracoRequestEventArgs e)
{
// will clear the batch - will remain in HttpContext though - that's ok
FlushBatch();
}
/// <inheritdoc/>
protected override void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable<object> ids = null, string json = null)
{
var idsA = ids?.ToArray();
Type arrayType;
if (GetArrayType(idsA, out arrayType) == false)
if (GetArrayType(idsA, out Type arrayType) == false)
{
throw new ArgumentException("All items must be of the same type, either int or Guid.", nameof(ids));
}
BatchMessage(refresher, messageType, idsA, arrayType, json);
}
public void FlushBatch()
/// <inheritdoc/>
public override void SendMessages()
{
var batch = GetBatch(false);
if (batch == null) return;
ICollection<RefreshInstructionEnvelope> batch = GetBatch(false);
if (batch == null)
{
return;
}
var instructions = batch.SelectMany(x => x.Instructions).ToArray();
RefreshInstruction[] instructions = batch.SelectMany(x => x.Instructions).ToArray();
batch.Clear();
//Write the instructions but only create JSON blobs with a max instruction count equal to MaxProcessingInstructionCount
using (var scope = ScopeProvider.CreateScope())
// Write the instructions but only create JSON blobs with a max instruction count equal to MaxProcessingInstructionCount
using (IScope scope = ScopeProvider.CreateScope())
{
foreach (var instructionsBatch in instructions.InGroupsOf(GlobalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount))
foreach (IEnumerable<RefreshInstruction> instructionsBatch in instructions.InGroupsOf(GlobalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount))
{
WriteInstructions(scope, instructionsBatch);
}
scope.Complete();
}
}
private void WriteInstructions(IScope scope, IEnumerable<RefreshInstruction> instructions)
@@ -113,11 +92,14 @@ namespace Umbraco.Web
scope.Database.Insert(dto);
}
protected ICollection<RefreshInstructionEnvelope> GetBatch(bool create)
private ICollection<RefreshInstructionEnvelope> GetBatch(bool create)
{
var key = nameof(BatchedDatabaseServerMessenger);
if (!_requestCache.IsAvailable) return null;
if (!_requestCache.IsAvailable)
{
return null;
}
// no thread-safety here because it'll run in only 1 thread (request) at a time
var batch = (ICollection<RefreshInstructionEnvelope>)_requestCache.Get(key);
@@ -130,26 +112,27 @@ namespace Umbraco.Web
return batch;
}
protected void BatchMessage(
private void BatchMessage(
ICacheRefresher refresher,
MessageType messageType,
IEnumerable<object> ids = null,
Type idType = null,
string json = null)
{
var batch = GetBatch(true);
var instructions = RefreshInstruction.GetInstructions(refresher, messageType, ids, idType, json);
ICollection<RefreshInstructionEnvelope> batch = GetBatch(true);
IEnumerable<RefreshInstruction> instructions = RefreshInstruction.GetInstructions(refresher, messageType, ids, idType, json);
// batch if we can, else write to DB immediately
if (batch == null)
{
//only write the json blob with a maximum count of the MaxProcessingInstructionCount
using (var scope = ScopeProvider.CreateScope())
// only write the json blob with a maximum count of the MaxProcessingInstructionCount
using (IScope scope = ScopeProvider.CreateScope())
{
foreach (var maxBatch in instructions.InGroupsOf(GlobalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount))
foreach (IEnumerable<RefreshInstruction> maxBatch in instructions.InGroupsOf(GlobalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount))
{
WriteInstructions(scope, maxBatch);
}
scope.Complete();
}
}

View File

@@ -23,19 +23,21 @@ namespace Umbraco.Core.Sync
/// <summary>
/// An <see cref="IServerMessenger"/> that works by storing messages in the database.
/// </summary>
//
// this messenger writes ALL instructions to the database,
// but only processes instructions coming from remote servers,
// thus ensuring that instructions run only once
//
public class DatabaseServerMessenger : ServerMessengerBase, IDatabaseServerMessenger
public abstract class DatabaseServerMessenger : ServerMessengerBase
{
// TODO: This class needs to be split into a service/repo for DB access
/*
* this messenger writes ALL instructions to the database,
* but only processes instructions coming from remote servers,
* thus ensuring that instructions run only once
*/
private readonly IMainDom _mainDom;
private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory;
private readonly ManualResetEvent _syncIdle;
private readonly object _locko = new object();
private readonly IProfilingLogger _profilingLogger;
private readonly IServerRegistrar _serverRegistrar;
private readonly IServerRoleAccessor _serverRegistrar;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly CacheRefresherCollection _cacheRefreshers;
@@ -43,22 +45,28 @@ namespace Umbraco.Core.Sync
private int _lastId = -1;
private DateTime _lastSync;
private DateTime _lastPruned;
private bool _initialized;
private readonly Lazy<bool> _initialized;
private bool _syncing;
private bool _released;
public DatabaseServerMessengerCallbacks Callbacks { get; }
public GlobalSettings GlobalSettings { get; }
public DatabaseServerMessenger(
IMainDom mainDom, IScopeProvider scopeProvider, IUmbracoDatabaseFactory umbracoDatabaseFactory, IProfilingLogger proflog, ILogger<DatabaseServerMessenger> logger, IServerRegistrar serverRegistrar,
bool distributedEnabled, DatabaseServerMessengerCallbacks callbacks, IHostingEnvironment hostingEnvironment, CacheRefresherCollection cacheRefreshers, IOptions<GlobalSettings> globalSettings)
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseServerMessenger"/> class.
/// </summary>
protected DatabaseServerMessenger(
IMainDom mainDom,
IScopeProvider scopeProvider,
IProfilingLogger proflog,
ILogger<DatabaseServerMessenger> logger,
IServerRoleAccessor serverRegistrar,
bool distributedEnabled,
DatabaseServerMessengerCallbacks callbacks,
IHostingEnvironment hostingEnvironment,
CacheRefresherCollection cacheRefreshers,
IOptions<GlobalSettings> globalSettings)
: base(distributedEnabled)
{
ScopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider));
_mainDom = mainDom;
_umbracoDatabaseFactory = umbracoDatabaseFactory;
_profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog));
_serverRegistrar = serverRegistrar;
_hostingEnvironment = hostingEnvironment;
@@ -76,24 +84,28 @@ namespace Umbraco.Core.Sync
+ " [P" + Process.GetCurrentProcess().Id // eg 1234
+ "/D" + AppDomain.CurrentDomain.Id // eg 22
+ "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique
_initialized = new Lazy<bool>(EnsureInitialized);
}
public DatabaseServerMessengerCallbacks Callbacks { get; }
public GlobalSettings GlobalSettings { get; }
protected ILogger<DatabaseServerMessenger> Logger { get; }
protected IScopeProvider ScopeProvider { get; }
protected Sql<ISqlContext> Sql() => _umbracoDatabaseFactory.SqlContext.Sql();
protected Sql<ISqlContext> Sql() => ScopeProvider.SqlContext.Sql();
private string DistCacheFilePath => _distCacheFilePath.Value;
#region Messenger
protected override bool RequiresDistributed(ICacheRefresher refresher, MessageType dispatchType)
{
// we don't care if there's servers listed or not,
// if distributed call is enabled we will make the call
return _initialized && DistributedEnabled;
}
protected override bool RequiresDistributed(ICacheRefresher refresher, MessageType dispatchType)
=> _initialized.Value && DistributedEnabled;
protected override void DeliverRemote(
ICacheRefresher refresher,
@@ -104,7 +116,9 @@ namespace Umbraco.Core.Sync
var idsA = ids?.ToArray();
if (GetArrayType(idsA, out var idType) == false)
{
throw new ArgumentException("All items must be of the same type, either int or Guid.", nameof(ids));
}
var instructions = RefreshInstruction.GetInstructions(refresher, messageType, idsA, idType, json);
@@ -130,17 +144,12 @@ namespace Umbraco.Core.Sync
/// <summary>
/// Boots the messenger.
/// </summary>
/// <remarks>
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
/// Callers MUST ensure thread-safety.
/// </remarks>
protected void Boot()
private bool EnsureInitialized()
{
// weight:10, must release *before* the published snapshot service, because once released
// the service will *not* be able to properly handle our notifications anymore
const int weight = 10;
var registered = _mainDom.Register(
() =>
{
@@ -154,7 +163,7 @@ namespace Umbraco.Core.Sync
// properly releasing MainDom - a timeout here means that one refresher
// is taking too much time processing, however when it's done we will
// not update lastId and stop everything
var idle =_syncIdle.WaitOne(5000);
var idle = _syncIdle.WaitOne(5000);
if (idle == false)
{
Logger.LogWarning("The wait lock timed out, application is shutting down. The current instruction batch will be re-processed.");
@@ -163,15 +172,16 @@ namespace Umbraco.Core.Sync
weight);
if (registered == false)
return;
{
return false;
}
ReadLastSynced(); // get _lastId
using (var scope = ScopeProvider.CreateScope())
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
EnsureInstructions(scope.Database); // reset _lastId if instructions are missing
Initialize(scope.Database); // boot
scope.Complete();
return Initialize(scope.Database); // boot
}
}
@@ -182,14 +192,19 @@ namespace Umbraco.Core.Sync
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
/// Callers MUST ensure thread-safety.
/// </remarks>
private void Initialize(IUmbracoDatabase database)
private bool Initialize(IUmbracoDatabase database)
{
lock (_locko)
{
if (_released) return;
if (_released)
{
return false;
}
var coldboot = false;
if (_lastId < 0) // never synced before
// never synced before
if (_lastId < 0)
{
// we haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new
// server and it will need to rebuild it's own caches, eg Lucene or the xml cache file.
@@ -201,12 +216,12 @@ namespace Umbraco.Core.Sync
}
else
{
//check for how many instructions there are to process, each row contains a count of the number of instructions contained in each
//row so we will sum these numbers to get the actual count.
var count = database.ExecuteScalar<int>("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _lastId});
// check for how many instructions there are to process, each row contains a count of the number of instructions contained in each
// row so we will sum these numbers to get the actual count.
var count = database.ExecuteScalar<int>("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new { lastId = _lastId });
if (count > GlobalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount)
{
//too many instructions, proceed to cold boot
// too many instructions, proceed to cold boot
Logger.LogWarning(
"The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})."
+ " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id"
@@ -224,38 +239,55 @@ namespace Umbraco.Core.Sync
// when doing it before, some instructions might run twice - not an issue
var maxId = database.ExecuteScalar<int>("SELECT MAX(id) FROM umbracoCacheInstruction");
//if there is a max currently, or if we've never synced
// if there is a max currently, or if we've never synced
if (maxId > 0 || _lastId < 0)
{
SaveLastSynced(maxId);
}
// execute initializing callbacks
if (Callbacks.InitializingCallbacks != null)
{
foreach (var callback in Callbacks.InitializingCallbacks)
{
callback();
}
}
}
_initialized = true;
return true;
}
}
/// <summary>
/// Synchronize the server (throttled).
/// </summary>
public void Sync()
public override void Sync()
{
if (!_initialized.Value)
{
return;
}
lock (_locko)
{
if (_syncing)
{
return;
}
//Don't continue if we are released
// Don't continue if we are released
if (_released)
{
return;
}
if ((DateTime.UtcNow - _lastSync) <= GlobalSettings.DatabaseServerMessenger.TimeBetweenSyncOperations)
{
return;
}
//Set our flag and the lock to be in it's original state (i.e. it can be awaited)
// Set our flag and the lock to be in it's original state (i.e. it can be awaited)
_syncing = true;
_syncIdle.Reset();
_lastSync = DateTime.UtcNow;
@@ -268,7 +300,7 @@ namespace Umbraco.Core.Sync
{
ProcessDatabaseInstructions(scope.Database);
//Check for pruning throttling
// Check for pruning throttling
if (_released || (DateTime.UtcNow - _lastPruned) <= GlobalSettings.DatabaseServerMessenger.TimeBetweenPruneOperations)
{
scope.Complete();
@@ -277,7 +309,7 @@ namespace Umbraco.Core.Sync
_lastPruned = _lastSync;
switch (_serverRegistrar.GetCurrentServerRole())
switch (_serverRegistrar.CurrentServerRole)
{
case ServerRole.Single:
case ServerRole.Master:
@@ -292,7 +324,7 @@ namespace Umbraco.Core.Sync
{
lock (_locko)
{
//We must reset our flag and signal any waiting locks
// We must reset our flag and signal any waiting locks
_syncing = false;
}
@@ -306,9 +338,6 @@ namespace Umbraco.Core.Sync
/// <remarks>
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
/// </remarks>
/// <returns>
/// Returns the number of processed instructions
/// </returns>
private void ProcessDatabaseInstructions(IUmbracoDatabase database)
{
// NOTE
@@ -324,7 +353,7 @@ namespace Umbraco.Core.Sync
.Where<CacheInstructionDto>(dto => dto.Id > _lastId)
.OrderBy<CacheInstructionDto>(dto => dto.Id);
//only retrieve the top 100 (just in case there's tons)
// only retrieve the top 100 (just in case there's tons)
// even though MaxProcessingInstructionCount is by default 1000 we still don't want to process that many
// rows in one request thread since each row can contain a ton of instructions (until 7.5.5 in which case
// a row can only contain MaxProcessingInstructionCount)
@@ -337,15 +366,15 @@ namespace Umbraco.Core.Sync
var lastId = 0;
//tracks which ones have already been processed to avoid duplicates
// tracks which ones have already been processed to avoid duplicates
var processed = new HashSet<RefreshInstruction>();
//It would have been nice to do this in a Query instead of Fetch using a data reader to save
// It would have been nice to do this in a Query instead of Fetch using a data reader to save
// some memory however we cannot do that because inside of this loop the cache refreshers are also
// performing some lookups which cannot be done with an active reader open
foreach (var dto in database.Fetch<CacheInstructionDto>(topSql))
{
//If this flag gets set it means we're shutting down! In this case, we need to exit asap and cannot
// If this flag gets set it means we're shutting down! In this case, we need to exit asap and cannot
// continue processing anything otherwise we'll hold up the app domain shutdown
if (_released)
{
@@ -377,10 +406,10 @@ namespace Umbraco.Core.Sync
var instructionBatch = GetAllInstructions(jsonA);
//process as per-normal
// process as per-normal
var success = ProcessDatabaseInstructions(instructionBatch, dto, processed, ref lastId);
//if they couldn't be all processed (i.e. we're shutting down) then exit
// if they couldn't be all processed (i.e. we're shutting down) then exit
if (success == false)
{
Logger.LogInformation("The current batch of instructions was not processed, app is shutting down");
@@ -509,7 +538,8 @@ namespace Umbraco.Core.Sync
/// </remarks>
private void ReadLastSynced()
{
if (File.Exists(DistCacheFilePath) == false) return;
if (File.Exists(DistCacheFilePath) == false)
return;
var content = File.ReadAllText(DistCacheFilePath);
if (int.TryParse(content, out var last))

Some files were not shown because too many files have changed in this diff Show More