// Copyright (c) Umbraco. // See LICENSE for more details. 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.Cms.Core.Cache; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Diagnostics; using Umbraco.Cms.Core.Dictionary; using Umbraco.Cms.Core.Editors; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Features; using Umbraco.Cms.Core.Handlers; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Install; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Mail; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Packaging; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.PublishedCache.Internal; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.ContentTypeEditing; using Umbraco.Cms.Core.DynamicRoot; using Umbraco.Cms.Core.Services.FileSystem; using Umbraco.Cms.Core.Services.Querying.RecycleBin; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Telemetry; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Core.Web; using Umbraco.Extensions; namespace Umbraco.Cms.Core.DependencyInjection { public class UmbracoBuilder : IUmbracoBuilder { private readonly Dictionary _builders = new Dictionary(); public IServiceCollection Services { get; } public IConfiguration Config { get; } public TypeLoader TypeLoader { get; } /// public ILoggerFactory BuilderLoggerFactory { get; } /// public IHostingEnvironment? BuilderHostingEnvironment { get; } public IProfiler Profiler { get; } public AppCaches AppCaches { get; } /// /// Initializes a new instance of the class primarily for testing. /// public UmbracoBuilder(IServiceCollection services, IConfiguration config, TypeLoader typeLoader) : this(services, config, typeLoader, NullLoggerFactory.Instance, new NoopProfiler(), AppCaches.Disabled, null) { } /// /// Initializes a new instance of the class. /// public UmbracoBuilder( IServiceCollection services, IConfiguration config, TypeLoader typeLoader, ILoggerFactory loggerFactory, IProfiler profiler, AppCaches appCaches, IHostingEnvironment? hostingEnvironment) { Services = services; Config = config; BuilderLoggerFactory = loggerFactory; BuilderHostingEnvironment = hostingEnvironment; Profiler = profiler; AppCaches = appCaches; TypeLoader = typeLoader; AddCoreServices(); } /// /// Gets a collection builder (and registers the collection). /// /// The type of the collection builder. /// The collection builder. public TBuilder WithCollectionBuilder() where TBuilder : ICollectionBuilder { Type typeOfBuilder = typeof(TBuilder); if (_builders.TryGetValue(typeOfBuilder, out ICollectionBuilder? o)) { return (TBuilder)o; } TBuilder builder; if (typeof(TBuilder).GetConstructor(Type.EmptyTypes) != null) { builder = Activator.CreateInstance(); } else if (typeof(TBuilder).GetConstructor(new[] { typeof(IUmbracoBuilder) }) != null) { // Handle those collection builders which need a reference to umbraco builder i.e. DistributedLockingCollectionBuilder. builder = (TBuilder)Activator.CreateInstance(typeof(TBuilder), this)!; } else { throw new InvalidOperationException("A CollectionBuilder must have either a parameterless constructor or a constructor whose only parameter is of type IUmbracoBuilder"); } _builders[typeOfBuilder] = builder; return builder; } public void Build() { foreach (ICollectionBuilder builder in _builders.Values) { builder.RegisterWith(Services); } _builders.Clear(); } private void AddCoreServices() { Services.AddSingleton(AppCaches); Services.AddSingleton(Profiler); // Register as singleton to allow injection everywhere. Services.AddSingleton(p => p.GetService!); Services.AddSingleton(); 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(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(factory => { IHostingEnvironment hostingEnvironment = factory.GetRequiredService(); 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().RuntimeCache); Services.AddUnique(factory => factory.GetRequiredService().RequestCache); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); this.AddAllCoreCollectionBuilders(); this.AddNotificationHandler(); Services.AddSingleton(); Services.AddSingleton(); Services.AddUnique(); Services.AddSingleton(f => f.GetRequiredService().CreateDictionary()); Services.AddSingleton(); Services.AddUnique(); Services.AddSingleton(); // will be injected in controllers when needed to invoke rest endpoints on Our Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); // register properties fallback Services.AddUnique(); Services.AddSingleton(); // register published router Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); // register distributed cache Services.AddUnique(f => new DistributedCache(f.GetRequiredService(), f.GetRequiredService())); Services.AddUnique(); // 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(); Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); // register a server registrar, by default it's the db registrar Services.AddUnique(f => { GlobalSettings globalSettings = f.GetRequiredService>().Value; var singleServer = globalSettings.DisableElectionForSingleServer; return singleServer ? new SingleServerRoleAccessor() : new ElectedServerRoleAccessor(f.GetRequiredService()); }); // For Umbraco to work it must have the default IPublishedModelFactory // which may be replaced by models builder but the default is required to make plain old IPublishedContent // instances. Services.AddSingleton(factory => factory.CreateDefaultPublishedModelFactory()); Services .AddNotificationHandler() .AddNotificationHandler(); Services.AddSingleton(); // register a basic/noop published snapshot service to be replaced Services.AddSingleton(); // Register ValueEditorCache used for validation Services.AddSingleton(); // Register telemetry service used to gather data about installed packages Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(factory => new ExternalLoginService( factory.GetRequiredService(), factory.GetRequiredService(), factory.GetRequiredService(), factory.GetRequiredService() )); Services.AddUnique(); Services.AddUnique(factory => factory.GetRequiredService()); Services.AddUnique(factory => new LocalizedTextService( factory.GetRequiredService>(), factory.GetRequiredService>())); Services.AddUnique(); Services.AddSingleton(); Services.AddSingleton(); Services.AddUnique(); // Register a noop IHtmlSanitizer & IMarkdownSanitizer to be replaced Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(provider => new CultureImpactFactory(provider.GetRequiredService>())); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); // Register filestream security analyzers Services.AddUnique(); Services.AddUnique(); // Register Webhook services Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); // Data type configuration cache Services.AddUnique(); Services.AddNotificationHandler(); // Two factor providers Services.AddUnique(); Services.AddUnique(); // Add Query services Services.AddUnique(); Services.AddUnique(); } } }