Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/booting-netcore

# Conflicts:
#	src/Umbraco.Core/IO/IOHelper.cs
#	src/Umbraco.Infrastructure/Runtime/WebRuntime.cs
#	src/Umbraco.Tests.Common/TestHelperBase.cs
#	src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
#	src/Umbraco.Tests/Runtimes/StandaloneTests.cs
#	src/Umbraco.Web.BackOffice/AspNetCore/UmbracoBackOfficeServiceCollectionExtensions.cs
#	src/Umbraco.Web.UI.NetCore/Program.cs
#	src/Umbraco.Web.UI.NetCore/Startup.cs
#	src/Umbraco.Web/UmbracoApplication.cs
This commit is contained in:
Shannon
2020-03-23 15:50:01 +11:00
198 changed files with 2127 additions and 920 deletions

View File

@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.FileProviders;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.IO;
using Umbraco.Core.Serialization;
namespace Umbraco.Core.Configuration
{
public class JsonConfigManipulator : IConfigManipulator
{
private readonly IConfiguration _configuration;
public JsonConfigManipulator(IConfiguration configuration)
{
_configuration = configuration;
}
public string UmbracoConnectionPath { get; } = $"ConnectionStrings:{ Constants.System.UmbracoConnectionName}";
public void RemoveConnectionString()
{
var provider = GetJsonConfigurationProvider(UmbracoConnectionPath);
var json = GetJson(provider);
RemoveJsonKey(json, UmbracoConnectionPath);
SaveJson(provider, json);
}
public void SaveConnectionString(string connectionString, string providerName)
{
var provider = GetJsonConfigurationProvider();
var json = GetJson(provider);
var item = GetConnectionItem(connectionString, providerName);
json.Merge(item, new JsonMergeSettings());
SaveJson(provider, json);
}
private JToken GetConnectionItem(string connectionString, string providerName)
{
JTokenWriter writer = new JTokenWriter();
writer.WriteStartObject();
writer.WritePropertyName("ConnectionStrings");
writer.WriteStartObject();
writer.WritePropertyName(Constants.System.UmbracoConnectionName);
writer.WriteValue(connectionString);
writer.WriteEndObject();
writer.WriteEndObject();
return writer.Token;
}
private static void RemoveJsonKey(JObject json, string key)
{
JToken token = json;
foreach (var propertyName in key.Split(new[] { ':' }))
{
token = CaseSelectPropertyValues(token, propertyName);
}
token?.Parent?.Remove();
}
private static void SaveJson(JsonConfigurationProvider provider, JObject json)
{
if (provider.Source.FileProvider is PhysicalFileProvider physicalFileProvider)
{
var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path);
using (var sw = new StreamWriter(jsonFilePath, false))
using (var jsonTextWriter = new JsonTextWriter(sw)
{
Formatting = Formatting.Indented,
})
{
json?.WriteTo(jsonTextWriter);
}
}
}
private static JObject GetJson(JsonConfigurationProvider provider)
{
if (provider.Source.FileProvider is PhysicalFileProvider physicalFileProvider)
{
var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path);
var serializer = new JsonSerializer();
using (var sr = new StreamReader(jsonFilePath))
using (var jsonTextReader = new JsonTextReader(sr))
{
return serializer.Deserialize<JObject>(jsonTextReader);
}
}
return null;
}
private JsonConfigurationProvider GetJsonConfigurationProvider(string requiredKey = null)
{
if (_configuration is IConfigurationRoot configurationRoot)
{
foreach (var provider in configurationRoot.Providers)
{
if(provider is JsonConfigurationProvider jsonConfigurationProvider)
{
if (requiredKey is null || provider.TryGet(requiredKey, out _))
{
return jsonConfigurationProvider;
}
}
}
}
throw new InvalidOperationException("Could not find a writable json config source");
}
/// <summary>
/// Returns the property value when case insensative
/// </summary>
/// <remarks>
/// This method is required because keys are case insensative in IConfiguration.
/// JObject[..] do not support case insensative and JObject.Property(...) do not return a new JObject.
/// </remarks>
private static JToken CaseSelectPropertyValues(JToken token, string name)
{
if (token is JObject obj)
{
foreach (var property in obj.Properties())
{
if (name is null)
return property.Value;
if (string.Equals(property.Name, name, StringComparison.OrdinalIgnoreCase))
return property.Value;
}
}
return null;
}
}
}

View File

@@ -20,7 +20,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods
private readonly IGlobalSettings _globalSettings;
private readonly IContentSettings _contentSettings;
public EmailNotificationMethod(ILocalizedTextService textService, IRuntimeState runtimeState, ILogger logger, IGlobalSettings globalSettings, IHealthChecks healthChecks, IContentSettings contentSettings) : base(healthChecks)
public EmailNotificationMethod(ILocalizedTextService textService, IRuntimeState runtimeState, ILogger logger, IGlobalSettings globalSettings, IHealthChecksSettings healthChecksSettings, IContentSettings contentSettings) : base(healthChecksSettings)
{
var recipientEmail = Settings["recipientEmail"]?.Value;
if (string.IsNullOrWhiteSpace(recipientEmail))

View File

@@ -8,7 +8,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods
{
public abstract class NotificationMethodBase : IHealthCheckNotificationMethod
{
protected NotificationMethodBase(IHealthChecks healthCheckConfig)
protected NotificationMethodBase(IHealthChecksSettings healthCheckSettingsConfig)
{
var type = GetType();
var attribute = type.GetCustomAttribute<HealthCheckNotificationMethodAttribute>();
@@ -18,7 +18,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods
return;
}
var notificationMethods = healthCheckConfig.NotificationSettings.NotificationMethods;
var notificationMethods = healthCheckSettingsConfig.NotificationSettings.NotificationMethods;
var notificationMethod = notificationMethods[attribute.Alias];
if (notificationMethod == null)
{

View File

@@ -1,71 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Security;
using Umbraco.Web.Install.Models;
namespace Umbraco.Web.Install.InstallSteps
{
[InstallSetupStep(InstallationType.NewInstall,
"ConfigureMachineKey", "machinekey", 2,
"Updating some security settings...",
PerformsAppRestart = true)]
public class ConfigureMachineKey : InstallSetupStep<bool?>
{
private readonly IIOHelper _ioHelper;
private readonly IMachineKeyConfig _machineKeyConfig;
public ConfigureMachineKey(IIOHelper ioHelper, IMachineKeyConfig machineKeyConfig)
{
_ioHelper = ioHelper;
_machineKeyConfig = machineKeyConfig;
}
public override string View => HasMachineKey() == false ? base.View : "";
/// <summary>
/// Don't display the view or execute if a machine key already exists
/// </summary>
/// <returns></returns>
private bool HasMachineKey()
{
return _machineKeyConfig.HasMachineKey;
}
/// <summary>
/// The step execution method
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public override Task<InstallSetupResult> ExecuteAsync(bool? model)
{
if (model.HasValue && model.Value == false) return Task.FromResult<InstallSetupResult>(null);
//install the machine key
var fileName = _ioHelper.MapPath($"{_ioHelper.Root}/web.config");
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
// we only want to get the element that is under the root, (there may be more under <location> tags we don't want them)
var systemWeb = xml.Root.Element("system.web");
// Update appSetting if it exists, or else create a new appSetting for the given key and value
var machineKey = systemWeb.Descendants("machineKey").FirstOrDefault();
if (machineKey != null) return Task.FromResult<InstallSetupResult>(null);
var generator = new MachineKeyGenerator();
var generatedSection = generator.GenerateConfigurationBlock();
systemWeb.Add(XElement.Parse(generatedSection));
xml.Save(fileName, SaveOptions.DisableFormatting);
return Task.FromResult<InstallSetupResult>(null);
}
public override bool RequiresExecution(bool? model)
{
return HasMachineKey() == false;
}
}
}

View File

@@ -19,14 +19,16 @@ namespace Umbraco.Web.Install.InstallSteps
private readonly ILogger _logger;
private readonly IIOHelper _ioHelper;
private readonly IConnectionStrings _connectionStrings;
private readonly IConfigManipulator _configManipulator;
public DatabaseInstallStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime, ILogger logger, IIOHelper ioHelper, IConnectionStrings connectionStrings)
public DatabaseInstallStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime, ILogger logger, IIOHelper ioHelper, IConnectionStrings connectionStrings, IConfigManipulator configManipulator)
{
_databaseBuilder = databaseBuilder;
_runtime = runtime;
_logger = logger;
_ioHelper = ioHelper;
_connectionStrings = connectionStrings;
_configManipulator = configManipulator;
}
public override Task<InstallSetupResult> ExecuteAsync(object model)
@@ -43,7 +45,7 @@ namespace Umbraco.Web.Install.InstallSteps
if (result.RequiresUpgrade == false)
{
HandleConnectionStrings(_logger, _ioHelper, _connectionStrings);
HandleConnectionStrings(_logger, _ioHelper, _connectionStrings, _configManipulator);
return Task.FromResult<InstallSetupResult>(null);
}
@@ -54,7 +56,7 @@ namespace Umbraco.Web.Install.InstallSteps
}));
}
internal static void HandleConnectionStrings(ILogger logger, IIOHelper ioHelper, IConnectionStrings connectionStrings)
internal static void HandleConnectionStrings(ILogger logger, IIOHelper ioHelper, IConnectionStrings connectionStrings, IConfigManipulator configManipulator)
{
@@ -65,7 +67,7 @@ namespace Umbraco.Web.Install.InstallSteps
// Remove legacy umbracoDbDsn configuration setting if it exists and connectionstring also exists
if (databaseSettings != null)
{
connectionStrings.RemoveConnectionString(Constants.System.UmbracoConnectionName);
configManipulator.RemoveConnectionString();
}
else
{

View File

@@ -23,8 +23,17 @@ namespace Umbraco.Web.Install.InstallSteps
private readonly IGlobalSettings _globalSettings;
private readonly IConnectionStrings _connectionStrings;
private readonly IIOHelper _ioHelper;
private readonly IConfigManipulator _configManipulator;
public DatabaseUpgradeStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime, ILogger logger, IUmbracoVersion umbracoVersion, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, IIOHelper ioHelper)
public DatabaseUpgradeStep(
DatabaseBuilder databaseBuilder,
IRuntimeState runtime,
ILogger logger,
IUmbracoVersion umbracoVersion,
IGlobalSettings globalSettings,
IConnectionStrings connectionStrings,
IIOHelper ioHelper,
IConfigManipulator configManipulator)
{
_databaseBuilder = databaseBuilder;
_runtime = runtime;
@@ -33,6 +42,7 @@ namespace Umbraco.Web.Install.InstallSteps
_globalSettings = globalSettings;
_connectionStrings = connectionStrings ?? throw new ArgumentNullException(nameof(connectionStrings));
_ioHelper = ioHelper;
_configManipulator = configManipulator;
}
public override Task<InstallSetupResult> ExecuteAsync(object model)
@@ -55,7 +65,7 @@ namespace Umbraco.Web.Install.InstallSteps
throw new InstallException("The database failed to upgrade. ERROR: " + result.Message);
}
DatabaseInstallStep.HandleConnectionStrings(_logger, _ioHelper, _connectionStrings);
DatabaseInstallStep.HandleConnectionStrings(_logger, _ioHelper, _connectionStrings, _configManipulator);
}
return Task.FromResult<InstallSetupResult>(null);

View File

@@ -18,7 +18,7 @@ namespace Umbraco.Core.Logging.Serilog
///</summary>
public class SerilogLogger : ILogger, IDisposable
{
private readonly ICoreDebug _coreDebug;
private readonly ICoreDebugSettings _coreDebugSettings;
private readonly IIOHelper _ioHelper;
private readonly IMarchal _marchal;
@@ -26,9 +26,9 @@ namespace Umbraco.Core.Logging.Serilog
/// Initialize a new instance of the <see cref="SerilogLogger"/> class with a configuration file.
/// </summary>
/// <param name="logConfigFile"></param>
public SerilogLogger(ICoreDebug coreDebug, IIOHelper ioHelper, IMarchal marchal, FileInfo logConfigFile)
public SerilogLogger(ICoreDebugSettings coreDebugSettings, IIOHelper ioHelper, IMarchal marchal, FileInfo logConfigFile)
{
_coreDebug = coreDebug;
_coreDebugSettings = coreDebugSettings;
_ioHelper = ioHelper;
_marchal = marchal;
@@ -37,9 +37,9 @@ namespace Umbraco.Core.Logging.Serilog
.CreateLogger();
}
public SerilogLogger(ICoreDebug coreDebug, IIOHelper ioHelper, IMarchal marchal, LoggerConfiguration logConfig)
public SerilogLogger(ICoreDebugSettings coreDebugSettings, IIOHelper ioHelper, IMarchal marchal, LoggerConfiguration logConfig)
{
_coreDebug = coreDebug;
_coreDebugSettings = coreDebugSettings;
_ioHelper = ioHelper;
_marchal = marchal;
@@ -51,7 +51,7 @@ namespace Umbraco.Core.Logging.Serilog
/// Creates a logger with some pre-defined configuration and remainder from config file
/// </summary>
/// <remarks>Used by UmbracoApplicationBase to get its logger.</remarks>
public static SerilogLogger CreateWithDefaultConfiguration(IHostingEnvironment hostingEnvironment, ISessionIdResolver sessionIdResolver, Func<IRequestCache> requestCacheGetter, ICoreDebug coreDebug, IIOHelper ioHelper, IMarchal marchal)
public static SerilogLogger CreateWithDefaultConfiguration(IHostingEnvironment hostingEnvironment, ISessionIdResolver sessionIdResolver, Func<IRequestCache> requestCacheGetter, ICoreDebugSettings coreDebugSettings, IIOHelper ioHelper, IMarchal marchal)
{
var loggerConfig = new LoggerConfiguration();
loggerConfig
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Logging.Serilog
.ReadFromConfigFile()
.ReadFromUserConfigFile();
return new SerilogLogger(coreDebug, ioHelper, marchal, loggerConfig);
return new SerilogLogger(coreDebugSettings, ioHelper, marchal, loggerConfig);
}
/// <summary>
@@ -179,7 +179,7 @@ namespace Umbraco.Core.Logging.Serilog
messageTemplate += "\r\nThe thread has been aborted, because the request has timed out.";
// dump if configured, or if stacktrace contains Monitor.ReliableEnter
dump = _coreDebug.DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception);
dump = _coreDebugSettings.DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception);
// dump if it is ok to dump (might have a cap on number of dump...)
dump &= MiniDump.OkToDump(_ioHelper);

View File

@@ -220,11 +220,5 @@ namespace Umbraco.Core.Manifest
return manifest;
}
// purely for tests
public IEnumerable<GridEditor> ParseGridEditors(string text)
{
return _jsonSerializer.Deserialize<IEnumerable<GridEditor>>(text);
}
}
}

View File

@@ -28,7 +28,7 @@ namespace Umbraco.Core.Migrations.Install
private readonly IIOHelper _ioHelper;
private readonly IUmbracoVersion _umbracoVersion;
private readonly IDbProviderFactoryCreator _dbProviderFactoryCreator;
private readonly IConnectionStrings _connectionStrings;
private readonly IConfigManipulator _configManipulator;
private DatabaseSchemaResult _databaseSchemaValidationResult;
@@ -46,7 +46,7 @@ namespace Umbraco.Core.Migrations.Install
IIOHelper ioHelper,
IUmbracoVersion umbracoVersion,
IDbProviderFactoryCreator dbProviderFactoryCreator,
IConnectionStrings connectionStrings)
IConfigManipulator configManipulator)
{
_scopeProvider = scopeProvider;
_globalSettings = globalSettings;
@@ -58,7 +58,7 @@ namespace Umbraco.Core.Migrations.Install
_ioHelper = ioHelper;
_umbracoVersion = umbracoVersion;
_dbProviderFactoryCreator = dbProviderFactoryCreator;
_connectionStrings = connectionStrings;
_configManipulator = configManipulator;
}
#region Status
@@ -146,7 +146,7 @@ namespace Umbraco.Core.Migrations.Install
private void ConfigureEmbeddedDatabaseConnection(IUmbracoDatabaseFactory factory, IIOHelper ioHelper)
{
_connectionStrings.SaveConnectionString(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe);
_configManipulator.SaveConnectionString(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe);
var path = Path.Combine(ioHelper.GetRootDirectorySafe(), "App_Data", "Umbraco.sdf");
if (File.Exists(path) == false)
@@ -169,7 +169,7 @@ namespace Umbraco.Core.Migrations.Install
{
const string providerName = Constants.DbProviderNames.SqlServer;
_connectionStrings.SaveConnectionString(connectionString, providerName);
_configManipulator.SaveConnectionString(connectionString, providerName);
_databaseFactory.Configure(connectionString, providerName);
}
@@ -185,7 +185,7 @@ namespace Umbraco.Core.Migrations.Install
{
var connectionString = GetDatabaseConnectionString(server, databaseName, user, password, databaseProvider, out var providerName);
_connectionStrings.SaveConnectionString(connectionString, providerName);
_configManipulator.SaveConnectionString(connectionString, providerName);
_databaseFactory.Configure(connectionString, providerName);
}
@@ -216,7 +216,7 @@ namespace Umbraco.Core.Migrations.Install
public void ConfigureIntegratedSecurityDatabaseConnection(string server, string databaseName)
{
var connectionString = GetIntegratedSecurityDatabaseConnectionString(server, databaseName);
_connectionStrings.SaveConnectionString(connectionString, Constants.DbProviderNames.SqlServer);
_configManipulator.SaveConnectionString(connectionString, Constants.DbProviderNames.SqlServer);
_databaseFactory.Configure(connectionString, Constants.DbProviderNames.SqlServer);
}

View File

@@ -191,7 +191,7 @@ namespace Umbraco.Web.Models.Mapping
target.Icon = source.Icon;
target.IconFilePath = target.IconIsClass
? string.Empty
: $"{_globalSettings.Path.EnsureEndsWith("/")}images/umbraco/{source.Icon}";
: $"{_ioHelper.BackOfficePath.EnsureEndsWith("/")}images/umbraco/{source.Icon}";
target.Trashed = source.Trashed;
target.Id = source.Id;
@@ -497,7 +497,7 @@ namespace Umbraco.Web.Models.Mapping
target.Icon = source.Icon;
target.IconFilePath = target.IconIsClass
? string.Empty
: $"{_globalSettings.Path.EnsureEndsWith("/")}images/umbraco/{source.Icon}";
: $"{_ioHelper.BackOfficePath.EnsureEndsWith("/")}images/umbraco/{source.Icon}";
target.Id = source.Id;
target.IsContainer = source.IsContainer;
target.IsElement = source.IsElement;
@@ -540,7 +540,7 @@ namespace Umbraco.Web.Models.Mapping
target.Icon = source.Icon;
target.IconFilePath = target.IconIsClass
? string.Empty
: $"{_globalSettings.Path.EnsureEndsWith("/")}images/umbraco/{source.Icon}";
: $"{_ioHelper.BackOfficePath.EnsureEndsWith("/")}images/umbraco/{source.Icon}";
target.Id = source.Id;
target.IsContainer = source.IsContainer;
target.IsElement = source.IsElement;

View File

@@ -27,8 +27,9 @@ namespace Umbraco.Core.Persistence
// TODO: this class needs not be disposable!
internal class UmbracoDatabaseFactory : DisposableObjectSlim, IUmbracoDatabaseFactory
{
private readonly Configs _configs;
private readonly IDbProviderFactoryCreator _dbProviderFactoryCreator;
private readonly IGlobalSettings _globalSettings;
private readonly IConnectionStrings _connectionStrings;
private readonly Lazy<IMapperCollection> _mappers;
private readonly ILogger _logger;
@@ -71,27 +72,28 @@ namespace Umbraco.Core.Persistence
/// Initializes a new instance of the <see cref="UmbracoDatabaseFactory"/>.
/// </summary>
/// <remarks>Used by core runtime.</remarks>
public UmbracoDatabaseFactory(ILogger logger, Lazy<IMapperCollection> mappers, Configs configs, IDbProviderFactoryCreator dbProviderFactoryCreator)
: this(Constants.System.UmbracoConnectionName, logger, mappers, configs, dbProviderFactoryCreator)
public UmbracoDatabaseFactory(ILogger logger, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, Lazy<IMapperCollection> mappers,IDbProviderFactoryCreator dbProviderFactoryCreator)
: this(Constants.System.UmbracoConnectionName, globalSettings, connectionStrings, logger, mappers, dbProviderFactoryCreator)
{
_configs = configs;
}
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoDatabaseFactory"/>.
/// </summary>
/// <remarks>Used by the other ctor and in tests.</remarks>
public UmbracoDatabaseFactory(string connectionStringName, ILogger logger, Lazy<IMapperCollection> mappers, Configs configs, IDbProviderFactoryCreator dbProviderFactoryCreator)
public UmbracoDatabaseFactory(string connectionStringName, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, ILogger logger, Lazy<IMapperCollection> mappers, IDbProviderFactoryCreator dbProviderFactoryCreator)
{
if (connectionStringName == null) throw new ArgumentNullException(nameof(connectionStringName));
if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(connectionStringName));
_globalSettings = globalSettings;
_connectionStrings = connectionStrings;
_mappers = mappers ?? throw new ArgumentNullException(nameof(mappers));
_configs = configs;
_dbProviderFactoryCreator = dbProviderFactoryCreator ?? throw new ArgumentNullException(nameof(dbProviderFactoryCreator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
var settings = configs.ConnectionStrings()[connectionStringName];
var settings = connectionStrings[connectionStringName];
if (settings == null)
{
@@ -161,7 +163,7 @@ namespace Umbraco.Core.Persistence
{
// replace NPoco database type by a more efficient one
var setting = _configs.Global().DatabaseFactoryServerVersion;
var setting = _globalSettings.DatabaseFactoryServerVersion;
var fromSettings = false;
if (setting.IsNullOrWhiteSpace() || !setting.StartsWith("SqlServer.")

View File

@@ -1,12 +1,15 @@
using System;
using System.IO;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Composing.CompositionExtensions;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Dashboards;
using Umbraco.Core.Hosting;
using Umbraco.Core.Dictionary;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
using Umbraco.Core.Migrations;
@@ -169,6 +172,10 @@ namespace Umbraco.Core.Runtime
// will be injected in controllers when needed to invoke rest endpoints on Our
composition.RegisterUnique<IInstallationService, InstallationService>();
composition.RegisterUnique<IUpgradeService, UpgradeService>();
// Grid config is not a real config file as we know them
composition.RegisterUnique<IGridConfig, GridConfig>();
}
}
}

View File

@@ -27,6 +27,8 @@ namespace Umbraco.Core.Runtime
private IFactory _factory;
private RuntimeState _state;
private readonly IUmbracoBootPermissionChecker _umbracoBootPermissionChecker;
private readonly IGlobalSettings _globalSettings;
private readonly IConnectionStrings _connectionStrings;
public CoreRuntime(
@@ -56,6 +58,10 @@ namespace Umbraco.Core.Runtime
MainDom = mainDom;
TypeFinder = typeFinder;
_globalSettings = Configs.Global();
_connectionStrings = configs.ConnectionStrings();
// runtime state
// beware! must use '() => _factory.GetInstance<T>()' and NOT '_factory.GetInstance<T>'
// as the second one captures the current value (null) and therefore fails
@@ -367,9 +373,9 @@ namespace Umbraco.Core.Runtime
// is overridden by the web runtime
return new AppCaches(
new DeepCloneAppCache(new ObjectCacheAppCache(TypeFinder)),
new DeepCloneAppCache(new ObjectCacheAppCache()),
NoAppCache.Instance,
new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache(TypeFinder))));
new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache())));
}
// by default, returns null, meaning that Umbraco should auto-detect the application root path.
@@ -382,7 +388,7 @@ namespace Umbraco.Core.Runtime
/// </summary>
/// <remarks>This is strictly internal, for tests only.</remarks>
protected internal virtual IUmbracoDatabaseFactory GetDatabaseFactory()
=> new UmbracoDatabaseFactory(Logger, new Lazy<IMapperCollection>(() => _factory.GetInstance<IMapperCollection>()), Configs, DbProviderFactoryCreator);
=> new UmbracoDatabaseFactory(Logger, _globalSettings, _connectionStrings, new Lazy<IMapperCollection>(() => _factory.GetInstance<IMapperCollection>()), DbProviderFactoryCreator);
#endregion

View File

@@ -28,16 +28,18 @@ namespace Umbraco.Core.Runtime
private bool _hasError;
private object _locker = new object();
public SqlMainDomLock(ILogger logger, Configs configs, IDbProviderFactoryCreator dbProviderFactoryCreator)
public SqlMainDomLock(ILogger logger, IGlobalSettings globalSettings, IConnectionStrings connectionStrings, IDbProviderFactoryCreator dbProviderFactoryCreator)
{
// unique id for our appdomain, this is more unique than the appdomain id which is just an INT counter to its safer
_lockId = Guid.NewGuid().ToString();
_logger = logger;
_dbFactory = new UmbracoDatabaseFactory(
Constants.System.UmbracoConnectionName,
globalSettings,
connectionStrings,
_logger,
new Lazy<IMapperCollection>(() => new MapperCollection(Enumerable.Empty<BaseMapper>())),
configs, dbProviderFactoryCreator
dbProviderFactoryCreator
);
}

View File

@@ -0,0 +1,90 @@
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Runtime;
namespace Umbraco.Web.Runtime
{
/// <summary>
/// Represents the Web Umbraco runtime.
/// </summary>
/// <remarks>On top of CoreRuntime, handles all of the web-related runtime aspects of Umbraco.</remarks>
public class WebRuntime : CoreRuntime
{
private readonly IRequestCache _requestCache;
/// <summary>
/// Initializes a new instance of the <see cref="WebRuntime"/> class.
/// </summary>
public WebRuntime(
Configs configs,
IUmbracoVersion umbracoVersion,
IIOHelper ioHelper,
ILogger logger,
IProfiler profiler,
IHostingEnvironment hostingEnvironment,
IBackOfficeInfo backOfficeInfo,
IDbProviderFactoryCreator dbProviderFactoryCreator,
IMainDom mainDom,
ITypeFinder typeFinder,
IRequestCache requestCache,
IUmbracoBootPermissionChecker umbracoBootPermissionChecker):
base(configs, umbracoVersion, ioHelper, logger, profiler ,umbracoBootPermissionChecker, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom, typeFinder)
{
_requestCache = requestCache;
}
/// <inheritdoc/>
public override IFactory Boot(IRegister register)
{
var profilingLogger = new ProfilingLogger(Logger, Profiler);
var umbracoVersion = new UmbracoVersion();
using (var timer = profilingLogger.TraceDuration<CoreRuntime>(
$"Booting Umbraco {umbracoVersion.SemanticVersion.ToSemanticString()}.",
"Booted.",
"Boot failed."))
{
Logger.Info<CoreRuntime>("Booting site '{HostingSiteName}', app '{HostingApplicationId}', path '{HostingPhysicalPath}', server '{MachineName}'.",
HostingEnvironment.SiteName,
HostingEnvironment.ApplicationId,
HostingEnvironment.ApplicationPhysicalPath,
NetworkHelper.MachineName);
Logger.Debug<CoreRuntime>("Runtime: {Runtime}", GetType().FullName);
var factory = base.Boot(register);
// now (and only now) is the time to switch over to perWebRequest scopes.
// up until that point we may not have a request, and scoped services would
// fail to resolve - but we run Initialize within a factory scope - and then,
// here, we switch the factory to bind scopes to requests
factory.EnablePerWebRequestScope();
return factory;
}
}
#region Getters
protected override AppCaches GetAppCaches() => new AppCaches(
// we need to have the dep clone runtime cache provider to ensure
// all entities are cached properly (cloned in and cloned out)
new DeepCloneAppCache(new ObjectCacheAppCache()),
// we need request based cache when running in web-based context
_requestCache,
new IsolatedCaches(type =>
// we need to have the dep clone runtime cache provider to ensure
// all entities are cached properly (cloned in and cloned out)
new DeepCloneAppCache(new ObjectCacheAppCache())));
#endregion
}
}

View File

@@ -15,18 +15,18 @@ namespace Umbraco.Web.Scheduling
private readonly HealthCheckCollection _healthChecks;
private readonly HealthCheckNotificationMethodCollection _notifications;
private readonly IProfilingLogger _logger;
private readonly IHealthChecks _healthChecksConfig;
private readonly IHealthChecksSettings _healthChecksSettingsConfig;
public HealthCheckNotifier(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications,
IRuntimeState runtimeState, IProfilingLogger logger, IHealthChecks healthChecksConfig)
IRuntimeState runtimeState, IProfilingLogger logger, IHealthChecksSettings healthChecksSettingsConfig)
: base(runner, delayMilliseconds, periodMilliseconds)
{
_healthChecks = healthChecks;
_notifications = notifications;
_runtimeState = runtimeState;
_logger = logger;
_healthChecksConfig = healthChecksConfig;
_healthChecksSettingsConfig = healthChecksSettingsConfig;
}
public override async Task<bool> PerformRunAsync(CancellationToken token)
@@ -53,7 +53,7 @@ namespace Umbraco.Web.Scheduling
using (_logger.DebugDuration<HealthCheckNotifier>("Health checks executing", "Health checks complete"))
{
var healthCheckConfig = _healthChecksConfig;
var healthCheckConfig = _healthChecksSettingsConfig;
// Don't notify for any checks that are disabled, nor for any disabled
// just for notifications

View File

@@ -34,7 +34,7 @@ namespace Umbraco.Web.Scheduling
private readonly HealthCheckCollection _healthChecks;
private readonly HealthCheckNotificationMethodCollection _notifications;
private readonly IUmbracoContextFactory _umbracoContextFactory;
private readonly IHealthChecks _healthChecksConfig;
private readonly IHealthChecksSettings _healthChecksSettingsConfig;
private readonly IIOHelper _ioHelper;
private readonly IServerMessenger _serverMessenger;
private readonly IRequestAccessor _requestAccessor;
@@ -56,7 +56,7 @@ namespace Umbraco.Web.Scheduling
IContentService contentService, IAuditService auditService,
HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications,
IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger,
IHostingEnvironment hostingEnvironment, IHealthChecks healthChecksConfig,
IHostingEnvironment hostingEnvironment, IHealthChecksSettings healthChecksSettingsConfig,
IIOHelper ioHelper, IServerMessenger serverMessenger, IRequestAccessor requestAccessor,
ILoggingSettings loggingSettings, IKeepAliveSettings keepAliveSettings)
{
@@ -70,7 +70,7 @@ namespace Umbraco.Web.Scheduling
_healthChecks = healthChecks;
_notifications = notifications;
_healthChecksConfig = healthChecksConfig ?? throw new ArgumentNullException(nameof(healthChecksConfig));
_healthChecksSettingsConfig = healthChecksSettingsConfig ?? throw new ArgumentNullException(nameof(healthChecksSettingsConfig));
_ioHelper = ioHelper;
_serverMessenger = serverMessenger;
_requestAccessor = requestAccessor;
@@ -126,7 +126,7 @@ namespace Umbraco.Web.Scheduling
tasks.Add(RegisterLogScrubber(_loggingSettings));
tasks.Add(RegisterTempFileCleanup());
var healthCheckConfig = _healthChecksConfig;
var healthCheckConfig = _healthChecksSettingsConfig;
if (healthCheckConfig.NotificationSettings.Enabled)
tasks.Add(RegisterHealthCheckNotifier(healthCheckConfig, _healthChecks, _notifications, _logger));
@@ -152,28 +152,28 @@ namespace Umbraco.Web.Scheduling
return task;
}
private IBackgroundTask RegisterHealthCheckNotifier(IHealthChecks healthCheckConfig,
private IBackgroundTask RegisterHealthCheckNotifier(IHealthChecksSettings healthCheckSettingsConfig,
HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications,
IProfilingLogger logger)
{
// If first run time not set, start with just small delay after application start
int delayInMilliseconds;
if (string.IsNullOrEmpty(healthCheckConfig.NotificationSettings.FirstRunTime))
if (string.IsNullOrEmpty(healthCheckSettingsConfig.NotificationSettings.FirstRunTime))
{
delayInMilliseconds = DefaultDelayMilliseconds;
}
else
{
// Otherwise start at scheduled time
delayInMilliseconds = DateTime.Now.PeriodicMinutesFrom(healthCheckConfig.NotificationSettings.FirstRunTime) * 60 * 1000;
delayInMilliseconds = DateTime.Now.PeriodicMinutesFrom(healthCheckSettingsConfig.NotificationSettings.FirstRunTime) * 60 * 1000;
if (delayInMilliseconds < DefaultDelayMilliseconds)
{
delayInMilliseconds = DefaultDelayMilliseconds;
}
}
var periodInMilliseconds = healthCheckConfig.NotificationSettings.PeriodInHours * 60 * 60 * 1000;
var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger, _healthChecksConfig);
var periodInMilliseconds = healthCheckSettingsConfig.NotificationSettings.PeriodInHours * 60 * 60 * 1000;
var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger, _healthChecksSettingsConfig);
_healthCheckRunner.TryAdd(task);
return task;
}

View File

@@ -17,7 +17,7 @@ namespace Umbraco.Core.Scoping
internal class Scope : IScope
{
private readonly ScopeProvider _scopeProvider;
private readonly ICoreDebug _coreDebug;
private readonly ICoreDebugSettings _coreDebugSettings;
private readonly IMediaFileSystem _mediaFileSystem;
private readonly ILogger _logger;
private readonly ITypeFinder _typeFinder;
@@ -39,7 +39,7 @@ namespace Umbraco.Core.Scoping
// initializes a new scope
private Scope(ScopeProvider scopeProvider,
ICoreDebug coreDebug,
ICoreDebugSettings coreDebugSettings,
IMediaFileSystem mediaFileSystem,
ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, Scope parent, IScopeContext scopeContext, bool detachable,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
@@ -50,7 +50,7 @@ namespace Umbraco.Core.Scoping
bool autoComplete = false)
{
_scopeProvider = scopeProvider;
_coreDebug = coreDebug;
_coreDebugSettings = coreDebugSettings;
_mediaFileSystem = mediaFileSystem;
_logger = logger;
_typeFinder = typeFinder;
@@ -118,7 +118,7 @@ namespace Umbraco.Core.Scoping
// initializes a new scope
public Scope(ScopeProvider scopeProvider,
ICoreDebug coreDebug,
ICoreDebugSettings coreDebugSettings,
IMediaFileSystem mediaFileSystem,
ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, bool detachable, IScopeContext scopeContext,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
@@ -127,12 +127,12 @@ namespace Umbraco.Core.Scoping
bool? scopeFileSystems = null,
bool callContext = false,
bool autoComplete = false)
: this(scopeProvider, coreDebug, mediaFileSystem, logger, typeFinder, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
: this(scopeProvider, coreDebugSettings, mediaFileSystem, logger, typeFinder, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
{ }
// initializes a new scope in a nested scopes chain, with its parent
public Scope(ScopeProvider scopeProvider,
ICoreDebug coreDebug,
ICoreDebugSettings coreDebugSettings,
IMediaFileSystem mediaFileSystem,
ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, Scope parent,
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
@@ -141,7 +141,7 @@ namespace Umbraco.Core.Scoping
bool? scopeFileSystems = null,
bool callContext = false,
bool autoComplete = false)
: this(scopeProvider, coreDebug, mediaFileSystem, logger, typeFinder, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
: this(scopeProvider, coreDebugSettings, mediaFileSystem, logger, typeFinder, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
{ }
public Guid InstanceId { get; } = Guid.NewGuid();
@@ -188,7 +188,7 @@ namespace Umbraco.Core.Scoping
if (ParentScope != null) return ParentScope.IsolatedCaches;
return _isolatedCaches ?? (_isolatedCaches
= new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache(_typeFinder))));
= new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache())));
}
}
@@ -494,9 +494,9 @@ namespace Umbraco.Core.Scoping
private static bool? _logUncompletedScopes;
// caching config
// true if Umbraco.CoreDebug.LogUncompletedScope appSetting is set to "true"
// true if Umbraco.CoreDebugSettings.LogUncompletedScope appSetting is set to "true"
private bool LogUncompletedScopes => (_logUncompletedScopes
?? (_logUncompletedScopes = _coreDebug.LogUncompletedScopes)).Value;
?? (_logUncompletedScopes = _coreDebugSettings.LogUncompletedScopes)).Value;
/// <inheritdoc />
public void ReadLock(params int[] lockIds) => Database.SqlContext.SqlSyntax.ReadLock(Database, lockIds);

View File

@@ -26,14 +26,14 @@ namespace Umbraco.Core.Scoping
private readonly ITypeFinder _typeFinder;
private readonly IRequestCache _requestCache;
private readonly FileSystems _fileSystems;
private readonly ICoreDebug _coreDebug;
private readonly ICoreDebugSettings _coreDebugSettings;
private readonly IMediaFileSystem _mediaFileSystem;
public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, ICoreDebug coreDebug, IMediaFileSystem mediaFileSystem, ILogger logger, ITypeFinder typeFinder, IRequestCache requestCache)
public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, ICoreDebugSettings coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger logger, ITypeFinder typeFinder, IRequestCache requestCache)
{
DatabaseFactory = databaseFactory;
_fileSystems = fileSystems;
_coreDebug = coreDebug;
_coreDebugSettings = coreDebugSettings;
_mediaFileSystem = mediaFileSystem;
_logger = logger;
_typeFinder = typeFinder;
@@ -255,7 +255,7 @@ namespace Umbraco.Core.Scoping
IEventDispatcher eventDispatcher = null,
bool? scopeFileSystems = null)
{
return new Scope(this, _coreDebug, _mediaFileSystem, _logger, _typeFinder, _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems);
return new Scope(this, _coreDebugSettings, _mediaFileSystem, _logger, _typeFinder, _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems);
}
/// <inheritdoc />
@@ -311,13 +311,13 @@ namespace Umbraco.Core.Scoping
{
var ambientContext = AmbientContext;
var newContext = ambientContext == null ? new ScopeContext() : null;
var scope = new Scope(this, _coreDebug, _mediaFileSystem, _logger, _typeFinder, _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _logger, _typeFinder, _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
// assign only if scope creation did not throw!
SetAmbient(scope, newContext ?? ambientContext);
return scope;
}
var nested = new Scope(this, _coreDebug, _mediaFileSystem, _logger, _typeFinder, _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _logger, _typeFinder, _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
SetAmbient(nested, AmbientContext);
return nested;
}

View File

@@ -112,7 +112,7 @@ namespace Umbraco.Web.Models.Trees
return Current.IOHelper.ResolveUrl("~" + Icon.TrimStart('~'));
//legacy icon path
return string.Format("{0}images/umbraco/{1}", Current.Configs.Global().Path.EnsureEndsWith("/"), Icon);
return string.Format("{0}images/umbraco/{1}", Current.IOHelper.BackOfficePath.EnsureEndsWith("/"), Icon);
}
}

View File

@@ -14,6 +14,8 @@
<PackageReference Include="Markdown" Version="2.2.1" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.2" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
<PackageReference Include="MiniProfiler.Shared" Version="4.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />