Merge remote-tracking branch 'origin/netcore/netcore' into netcore/bugfix/azure-devops-tests
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -189,3 +189,9 @@ cypress.env.json
|
||||
/src/Umbraco.Web.UI.NetCore/App_Data/TEMP/*
|
||||
/src/Umbraco.Web.UI.NetCore/App_Data/Smidge/Cache/*
|
||||
/src/Umbraco.Web.UI.NetCore/umbraco/logs
|
||||
|
||||
src/Umbraco.Tests.Integration/umbraco/logs/
|
||||
|
||||
src/Umbraco.Tests.Integration/Views/
|
||||
|
||||
src/Umbraco.Tests/TEMP/
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Umbraco.Core.Cache
|
||||
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
IEnumerable<KeyValuePair<object, object>> entries;
|
||||
try
|
||||
{
|
||||
EnterReadLock();
|
||||
@@ -65,7 +65,7 @@ namespace Umbraco.Core.Cache
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
var plen = prefix.Length;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
IEnumerable<KeyValuePair<object, object>> entries;
|
||||
try
|
||||
{
|
||||
EnterReadLock();
|
||||
@@ -249,7 +249,7 @@ namespace Umbraco.Core.Cache
|
||||
// manipulate the underlying cache entries
|
||||
// these *must* be called from within the appropriate locks
|
||||
// and use the full prefixed cache keys
|
||||
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
|
||||
protected abstract IEnumerable<KeyValuePair<object, object>> GetDictionaryEntries();
|
||||
protected abstract void RemoveEntry(string key);
|
||||
protected abstract object GetEntry(string key);
|
||||
|
||||
|
||||
@@ -115,13 +115,13 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
#region Entries
|
||||
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
protected override IEnumerable<KeyValuePair<object, object>> GetDictionaryEntries()
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
|
||||
if (!TryGetContextItems(out var items)) return Enumerable.Empty<DictionaryEntry>();
|
||||
if (!TryGetContextItems(out var items)) return Enumerable.Empty<KeyValuePair<object, object>>();
|
||||
|
||||
return items.Cast<DictionaryEntry>()
|
||||
return items.Cast<KeyValuePair<object, object>>()
|
||||
.Where(x => x.Key is string s && s.StartsWith(prefix));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
@@ -17,19 +16,23 @@ namespace Umbraco.Core.Cache
|
||||
/// </remarks>
|
||||
public class HttpRequestAppCache : FastDictionaryAppCacheBase, IRequestCache
|
||||
{
|
||||
private static object _syncRoot = new object(); // Using this for locking as the SyncRoot property is not available to us
|
||||
// on the provided collection provided from .NET Core's HttpContext.Items dictionary,
|
||||
// as it doesn't implement ICollection where SyncRoot is defined.
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class with a context, for unit tests!
|
||||
/// </summary>
|
||||
public HttpRequestAppCache(Func<IDictionary> requestItems) : base()
|
||||
public HttpRequestAppCache(Func<IDictionary<object, object>> requestItems) : base()
|
||||
{
|
||||
ContextItems = requestItems;
|
||||
}
|
||||
|
||||
private Func<IDictionary> ContextItems { get; }
|
||||
private Func<IDictionary<object, object>> ContextItems { get; }
|
||||
|
||||
public bool IsAvailable => TryGetContextItems(out _);
|
||||
|
||||
private bool TryGetContextItems(out IDictionary items)
|
||||
private bool TryGetContextItems(out IDictionary<object, object> items)
|
||||
{
|
||||
items = ContextItems?.Invoke();
|
||||
return items != null;
|
||||
@@ -115,13 +118,13 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
#region Entries
|
||||
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
protected override IEnumerable<KeyValuePair<object, object>> GetDictionaryEntries()
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
|
||||
if (!TryGetContextItems(out var items)) return Enumerable.Empty<DictionaryEntry>();
|
||||
if (!TryGetContextItems(out var items)) return Enumerable.Empty<KeyValuePair<object, object>>();
|
||||
|
||||
return items.Cast<DictionaryEntry>()
|
||||
return items.Cast<KeyValuePair<object, object>>()
|
||||
.Where(x => x.Key is string s && s.StartsWith(prefix));
|
||||
}
|
||||
|
||||
@@ -154,7 +157,7 @@ namespace Umbraco.Core.Cache
|
||||
// ContextItems - which is locked, so this should be safe
|
||||
|
||||
var entered = false;
|
||||
Monitor.Enter(items.SyncRoot, ref entered);
|
||||
Monitor.Enter(_syncRoot, ref entered);
|
||||
items[ContextItemsLockKey] = entered;
|
||||
}
|
||||
|
||||
@@ -166,7 +169,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
var entered = (bool?)items[ContextItemsLockKey] ?? false;
|
||||
if (entered)
|
||||
Monitor.Exit(items.SyncRoot);
|
||||
Monitor.Exit(_syncRoot);
|
||||
items.Remove(ContextItemsLockKey);
|
||||
}
|
||||
|
||||
@@ -179,7 +182,7 @@ namespace Umbraco.Core.Cache
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (DictionaryEntry item in items)
|
||||
foreach (var item in items)
|
||||
{
|
||||
yield return new KeyValuePair<string, object>(item.Key.ToString(), item.Value);
|
||||
}
|
||||
|
||||
@@ -64,10 +64,7 @@ namespace Umbraco.Core.Composing
|
||||
#endregion
|
||||
|
||||
#region IRegister
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Concrete => _register.Concrete;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient)
|
||||
=> _register.Register(serviceType, lifetime);
|
||||
@@ -85,33 +82,6 @@ namespace Umbraco.Core.Composing
|
||||
public void Register(Type serviceType, object instance)
|
||||
=> _register.Register(serviceType, instance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> _register.RegisterFor<TService, TTarget>(lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> _register.RegisterFor<TService, TTarget>(implementingType, lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> _register.RegisterFor<TService, TTarget>(factory, lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(TService instance)
|
||||
where TService : class
|
||||
=> _register.RegisterFor<TService, TTarget>(instance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterAuto(Type serviceBaseType)
|
||||
=> _register.RegisterAuto(serviceBaseType);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ConfigureForWeb()
|
||||
=> _register.ConfigureForWeb();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFactory CreateFactory()
|
||||
@@ -127,13 +97,7 @@ namespace Umbraco.Core.Composing
|
||||
builder.RegisterWith(_register);
|
||||
_builders.Clear(); // no point keep them around
|
||||
|
||||
IFactory factory = null;
|
||||
|
||||
// ReSharper disable once AccessToModifiedClosure -- on purpose
|
||||
_register.Register(_ => factory, Lifetime.Singleton);
|
||||
factory = _register.CreateFactory();
|
||||
|
||||
return factory;
|
||||
return _register.CreateFactory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -186,38 +150,6 @@ namespace Umbraco.Core.Composing
|
||||
public void RegisterUnique(Type serviceType, object instance)
|
||||
=> _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, instance);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service for a target, as its own implementation.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUniqueFor<TService, TTarget>()
|
||||
where TService : class
|
||||
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(Lifetime.Singleton);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service for a target, with an implementing type.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUniqueFor<TService, TTarget>(Type implementingType)
|
||||
where TService : class
|
||||
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(implementingType, Lifetime.Singleton);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service for a target, with an implementation factory.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUniqueFor<TService, TTarget>(Func<IFactory, TService> factory)
|
||||
where TService : class
|
||||
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(factory, Lifetime.Singleton);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service for a target, with an implementing instance.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUniqueFor<TService, TTarget>(TService instance)
|
||||
where TService : class
|
||||
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(instance);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Collection Builders
|
||||
|
||||
@@ -21,15 +21,6 @@ namespace Umbraco.Core.Composing
|
||||
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
|
||||
object GetInstance(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a targeted instance of a service.
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">The type of the service.</typeparam>
|
||||
/// <typeparam name="TTarget">The type of the target.</typeparam>
|
||||
/// <returns>The instance of the specified type for the specified target.</returns>
|
||||
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
|
||||
TService GetInstanceFor<TService, TTarget>();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get an instance of a service.
|
||||
/// </summary>
|
||||
@@ -52,18 +43,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <typeparam name="TService">The type of the service.</typeparam>
|
||||
IEnumerable<TService> GetAllInstances<TService>()
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Releases an instance.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <remarks>
|
||||
/// See https://stackoverflow.com/questions/14072208 and http://kozmic.net/2010/08/27/must-i-release-everything-when-using-windsor/,
|
||||
/// you only need to release instances you specifically resolved, and even then, if done right, that might never be needed. For
|
||||
/// instance, LightInject does not require this and does not support it - should work with scopes.
|
||||
/// </remarks>
|
||||
void Release(object instance);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Begins a scope.
|
||||
/// </summary>
|
||||
@@ -72,13 +52,5 @@ namespace Umbraco.Core.Composing
|
||||
/// <para>Scopes can be nested. Each instance is disposed individually.</para>
|
||||
/// </remarks>
|
||||
IDisposable BeginScope();
|
||||
|
||||
/// <summary>
|
||||
/// Enables per-request scope.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Ties scopes to web requests.</para>
|
||||
/// </remarks>
|
||||
void EnablePerWebRequestScope();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,6 @@ namespace Umbraco.Core.Composing
|
||||
/// </summary>
|
||||
public interface IRegister
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the concrete container.
|
||||
/// </summary>
|
||||
object Concrete { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service as its own implementation.
|
||||
/// </summary>
|
||||
@@ -33,69 +28,8 @@ namespace Umbraco.Core.Composing
|
||||
/// </summary>
|
||||
void Register(Type serviceType, object instance);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service for a target, as its own implementation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There can only be one implementation or instanced registered for a service and target;
|
||||
/// what happens if many are registered is not specified.
|
||||
/// </remarks>
|
||||
void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service for a target, with an implementation type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There can only be one implementation or instanced registered for a service and target;
|
||||
/// what happens if many are registered is not specified.
|
||||
/// </remarks>
|
||||
void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service for a target, with an implementation factory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There can only be one implementation or instanced registered for a service and target;
|
||||
/// what happens if many are registered is not specified.
|
||||
/// </remarks>
|
||||
void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service for a target, with an implementing instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There can only be one implementation or instanced registered for a service and target;
|
||||
/// what happens if many are registered is not specified.
|
||||
/// </remarks>
|
||||
void RegisterFor<TService, TTarget>(TService instance)
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a base type for auto-registration.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Auto-registration means that anytime the container is asked to create an instance
|
||||
/// of a type deriving from <paramref name="serviceBaseType"/>, it will first register that
|
||||
/// type automatically.</para>
|
||||
/// <para>This can be used for instance for views or controllers. Then, one just needs to
|
||||
/// register a common base class or interface, and the container knows how to create instances.</para>
|
||||
/// </remarks>
|
||||
void RegisterAuto(Type serviceBaseType);
|
||||
|
||||
#region Control
|
||||
|
||||
/// <summary>
|
||||
/// Configures the container for web support.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Enables support for MVC, WebAPI, but *not* per-request scope. This is used early in the boot
|
||||
/// process, where anything "scoped" should not be linked to a web request.</para>
|
||||
/// </remarks>
|
||||
void ConfigureForWeb(); // TODO: Unsure if we need this anymore
|
||||
|
||||
/// <summary>
|
||||
/// Creates the factory.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for targeted service factories.
|
||||
/// </summary>
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
public abstract class TargetedServiceFactory<TService>
|
||||
{
|
||||
private readonly IFactory _factory;
|
||||
|
||||
protected TargetedServiceFactory(IFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public TService For<TTarget>() => _factory.GetInstanceFor<TService, TTarget>();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
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.HealthCheck;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Sections;
|
||||
using Umbraco.Web.Tour;
|
||||
|
||||
@@ -19,13 +19,6 @@ namespace Umbraco.Core
|
||||
public static void RegisterUnique<TService, TImplementing>(this Composition composition)
|
||||
=> composition.RegisterUnique(typeof(TService), typeof(TImplementing));
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service with an implementation type, for a target.
|
||||
/// </summary>
|
||||
public static void RegisterUniqueFor<TService, TTarget, TImplementing>(this Composition composition)
|
||||
where TService : class
|
||||
=> composition.RegisterUniqueFor<TService, TTarget>(typeof(TImplementing));
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service with an implementing instance.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
{
|
||||
public abstract class AbstractConfigCheck : HealthCheck
|
||||
{
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly ConfigurationService _configurationService;
|
||||
|
||||
protected ILocalizedTextService TextService { get; }
|
||||
protected ILoggerFactory LoggerFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the config file path.
|
||||
/// </summary>
|
||||
public abstract string FilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets XPath statement to the config element to check.
|
||||
/// </summary>
|
||||
public abstract string XPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values to compare against.
|
||||
/// </summary>
|
||||
public abstract IEnumerable<AcceptableConfiguration> Values { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value
|
||||
/// </summary>
|
||||
public string CurrentValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provided value
|
||||
/// </summary>
|
||||
public string ProvidedValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the comparison type for checking the value.
|
||||
/// </summary>
|
||||
public abstract ValueComparisonType ValueComparisonType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flag indicating if the check is considered successful if the config value is missing (defaults to false - an error - if missing)
|
||||
/// </summary>
|
||||
public virtual bool ValidIfConfigMissing
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
protected AbstractConfigCheck(ILocalizedTextService textService, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
TextService = textService;
|
||||
LoggerFactory = loggerFactory;
|
||||
_configurationService = new ConfigurationService(AbsoluteFilePath, XPath, textService, loggerFactory.CreateLogger<ConfigurationService>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the file.
|
||||
/// </summary>
|
||||
private string FileName => Path.GetFileName(FilePath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute file path.
|
||||
/// </summary>
|
||||
private string AbsoluteFilePath => _hostingEnvironment.MapPathContentRoot(FilePath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message for when the check has succeeded.
|
||||
/// </summary>
|
||||
public virtual string CheckSuccessMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return TextService.Localize("healthcheck/checkSuccessMessage",
|
||||
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message for when the check has failed.
|
||||
/// </summary>
|
||||
public virtual string CheckErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return ValueComparisonType == ValueComparisonType.ShouldEqual
|
||||
? TextService.Localize("healthcheck/checkErrorMessageDifferentExpectedValue",
|
||||
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath })
|
||||
: TextService.Localize("healthcheck/checkErrorMessageUnexpectedValue",
|
||||
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rectify success message.
|
||||
/// </summary>
|
||||
public virtual string RectifySuccessMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
var recommendedValue = Values.FirstOrDefault(v => v.IsRecommended);
|
||||
var rectifiedValue = recommendedValue != null
|
||||
? recommendedValue.Value
|
||||
: ProvidedValue;
|
||||
return TextService.Localize("healthcheck/rectifySuccessMessage",
|
||||
new[]
|
||||
{
|
||||
CurrentValue,
|
||||
rectifiedValue,
|
||||
XPath,
|
||||
AbsoluteFilePath
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this check can be rectified automatically.
|
||||
/// </summary>
|
||||
public virtual bool CanRectify => ValueComparisonType == ValueComparisonType.ShouldEqual;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this check can be rectified automatically if a value is provided.
|
||||
/// </summary>
|
||||
public virtual bool CanRectifyWithValue => ValueComparisonType == ValueComparisonType.ShouldNotEqual;
|
||||
|
||||
public override IEnumerable<HealthCheckStatus> GetStatus()
|
||||
{
|
||||
var successMessage = string.Format(CheckSuccessMessage, FileName, XPath, Values);
|
||||
|
||||
var configValue = _configurationService.GetConfigurationValue();
|
||||
if (configValue.Success == false)
|
||||
{
|
||||
if (ValidIfConfigMissing)
|
||||
{
|
||||
return new[] { new HealthCheckStatus(successMessage) { ResultType = StatusResultType.Success } };
|
||||
}
|
||||
|
||||
var errorMessage = configValue.Result;
|
||||
return new[] { new HealthCheckStatus(errorMessage) { ResultType = StatusResultType.Error } };
|
||||
}
|
||||
|
||||
CurrentValue = configValue.Result;
|
||||
|
||||
// need to update the successMessage with the CurrentValue
|
||||
successMessage = string.Format(CheckSuccessMessage, FileName, XPath, Values, CurrentValue);
|
||||
|
||||
var valueFound = Values.Any(value => string.Equals(CurrentValue, value.Value, StringComparison.InvariantCultureIgnoreCase));
|
||||
if (ValueComparisonType == ValueComparisonType.ShouldEqual && valueFound || ValueComparisonType == ValueComparisonType.ShouldNotEqual && valueFound == false)
|
||||
{
|
||||
return new[] { new HealthCheckStatus(successMessage) { ResultType = StatusResultType.Success } };
|
||||
}
|
||||
|
||||
// Declare the action for rectifying the config value
|
||||
var rectifyAction = new HealthCheckAction("rectify", Id)
|
||||
{
|
||||
Name = TextService.Localize("healthcheck/rectifyButton"),
|
||||
ValueRequired = CanRectifyWithValue,
|
||||
};
|
||||
|
||||
var resultMessage = string.Format(CheckErrorMessage, FileName, XPath, Values, CurrentValue);
|
||||
return new[]
|
||||
{
|
||||
new HealthCheckStatus(resultMessage)
|
||||
{
|
||||
ResultType = StatusResultType.Error,
|
||||
Actions = CanRectify || CanRectifyWithValue ? new[] { rectifyAction } : new HealthCheckAction[0]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rectifies this check.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual HealthCheckStatus Rectify()
|
||||
{
|
||||
if (ValueComparisonType == ValueComparisonType.ShouldNotEqual)
|
||||
throw new InvalidOperationException(TextService.Localize("healthcheck/cannotRectifyShouldNotEqual"));
|
||||
|
||||
var recommendedValue = Values.First(v => v.IsRecommended).Value;
|
||||
return UpdateConfigurationValue(recommendedValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rectifies this check with a provided value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value provided</param>
|
||||
/// <returns></returns>
|
||||
public virtual HealthCheckStatus Rectify(string value)
|
||||
{
|
||||
if (ValueComparisonType == ValueComparisonType.ShouldEqual)
|
||||
throw new InvalidOperationException(TextService.Localize("healthcheck/cannotRectifyShouldEqualWithValue"));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
throw new InvalidOperationException(TextService.Localize("healthcheck/valueToRectifyNotProvided"));
|
||||
|
||||
// Need to track provided value in order to correctly put together the rectify message
|
||||
ProvidedValue = value;
|
||||
|
||||
return UpdateConfigurationValue(value);
|
||||
}
|
||||
|
||||
private HealthCheckStatus UpdateConfigurationValue(string value)
|
||||
{
|
||||
var updateConfigFile = _configurationService.UpdateConfigFile(value);
|
||||
|
||||
if (updateConfigFile.Success == false)
|
||||
{
|
||||
var message = updateConfigFile.Result;
|
||||
return new HealthCheckStatus(message) { ResultType = StatusResultType.Error };
|
||||
}
|
||||
|
||||
var resultMessage = string.Format(RectifySuccessMessage, FileName, XPath, Values);
|
||||
return new HealthCheckStatus(resultMessage) { ResultType = StatusResultType.Success };
|
||||
}
|
||||
|
||||
public override HealthCheckStatus ExecuteAction(HealthCheckAction action)
|
||||
{
|
||||
return string.IsNullOrEmpty(action.ProvidedValue)
|
||||
? Rectify()
|
||||
: Rectify(action.ProvidedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.HealthCheck;
|
||||
using Umbraco.Core.HealthCheck.Checks;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
{
|
||||
[HealthCheck("61214FF3-FC57-4B31-B5CF-1D095C977D6D", "Debug Compilation Mode",
|
||||
Description = "Leaving debug compilation mode enabled can severely slow down a website and take up more memory on the server.",
|
||||
Group = "Live Environment")]
|
||||
public class CompilationDebugCheck : AbstractConfigCheck
|
||||
public class CompilationDebugCheck : AbstractSettingsCheck
|
||||
{
|
||||
public CompilationDebugCheck(ILocalizedTextService textService, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory)
|
||||
: base(textService, hostingEnvironment, loggerFactory)
|
||||
{ }
|
||||
private readonly IOptionsMonitor<HostingSettings> _hostingSettings;
|
||||
|
||||
public override string FilePath => "~/Web.config";
|
||||
public CompilationDebugCheck(ILocalizedTextService textService, ILoggerFactory loggerFactory, IOptionsMonitor<HostingSettings> hostingSettings)
|
||||
: base(textService, loggerFactory)
|
||||
{
|
||||
_hostingSettings = hostingSettings;
|
||||
}
|
||||
|
||||
public override string XPath => "/configuration/system.web/compilation/@debug";
|
||||
public override string ItemPath => Constants.Configuration.ConfigHostingDebug;
|
||||
|
||||
public override ValueComparisonType ValueComparisonType => ValueComparisonType.ShouldEqual;
|
||||
|
||||
public override bool ValidIfConfigMissing => true;
|
||||
|
||||
public override IEnumerable<AcceptableConfiguration> Values => new List<AcceptableConfiguration>
|
||||
{
|
||||
new AcceptableConfiguration { IsRecommended = true, Value = bool.FalseString.ToLower() }
|
||||
new AcceptableConfiguration
|
||||
{
|
||||
IsRecommended = true,
|
||||
Value = bool.FalseString.ToLower()
|
||||
}
|
||||
};
|
||||
|
||||
public override string CurrentValue => _hostingSettings.CurrentValue.Debug.ToString();
|
||||
|
||||
public override string CheckSuccessMessage => TextService.Localize("healthcheck/compilationDebugCheckSuccessMessage");
|
||||
|
||||
public override string CheckErrorMessage => TextService.Localize("healthcheck/compilationDebugCheckErrorMessage");
|
||||
|
||||
@@ -1,109 +1,61 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.HealthCheck;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
{
|
||||
// TODO: Add config transform for when config with specified XPath is not found
|
||||
|
||||
public class ConfigurationService
|
||||
public class ConfigurationService : IConfigurationService
|
||||
{
|
||||
private readonly string _configFilePath;
|
||||
private readonly string _xPath;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly ILogger<ConfigurationService> _logger;
|
||||
private readonly IConfigManipulator _configManipulator;
|
||||
|
||||
/// <param name="configFilePath">The absolute file location of the configuration file</param>
|
||||
/// <param name="xPath">The XPath to select the value</param>
|
||||
/// <param name="textService"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="configManipulator"></param>
|
||||
/// <returns></returns>
|
||||
public ConfigurationService(string configFilePath, string xPath, ILocalizedTextService textService, ILogger<ConfigurationService> logger)
|
||||
public ConfigurationService(ILocalizedTextService textService, ILogger<ConfigurationService> logger, IConfigManipulator configManipulator)
|
||||
{
|
||||
_configFilePath = configFilePath;
|
||||
_xPath = xPath;
|
||||
if (textService == null) HandleNullParameter(nameof(textService));
|
||||
if (configManipulator == null) HandleNullParameter(nameof(configManipulator));
|
||||
if (logger == null) HandleNullParameter(nameof(logger));
|
||||
|
||||
_configManipulator = configManipulator;
|
||||
_textService = textService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value from a given configuration file with the given XPath
|
||||
/// </summary>
|
||||
public ConfigurationServiceResult GetConfigurationValue()
|
||||
private void HandleNullParameter(string parameter)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_configFilePath) == false)
|
||||
return new ConfigurationServiceResult
|
||||
{
|
||||
Success = false,
|
||||
Result = _textService.Localize("healthcheck/configurationServiceFileNotFound", new[] { _configFilePath })
|
||||
};
|
||||
|
||||
var xmlDocument = new XmlDocument();
|
||||
xmlDocument.Load(_configFilePath);
|
||||
|
||||
var xmlNode = xmlDocument.SelectSingleNode(_xPath);
|
||||
if (xmlNode == null)
|
||||
return new ConfigurationServiceResult
|
||||
{
|
||||
Success = false,
|
||||
Result = _textService.Localize("healthcheck/configurationServiceNodeNotFound", new[] { _xPath, _configFilePath })
|
||||
};
|
||||
|
||||
return new ConfigurationServiceResult
|
||||
{
|
||||
Success = true,
|
||||
Result = string.Format(xmlNode.Value ?? xmlNode.InnerText)
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error trying to get configuration value");
|
||||
return new ConfigurationServiceResult
|
||||
{
|
||||
Success = false,
|
||||
Result = _textService.Localize("healthcheck/configurationServiceError", new[] { ex.Message })
|
||||
};
|
||||
}
|
||||
_logger.LogError("Error trying to get configuration value", parameter);
|
||||
throw new ArgumentNullException(parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a value in a given configuration file with the given XPath
|
||||
/// Updates a value in a given configuration file with the given path
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="itemPath"></param>
|
||||
/// <returns></returns>
|
||||
public ConfigurationServiceResult UpdateConfigFile(string value)
|
||||
public ConfigurationServiceResult UpdateConfigFile(string value, string itemPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_configFilePath) == false)
|
||||
if (itemPath == null)
|
||||
{
|
||||
return new ConfigurationServiceResult
|
||||
{
|
||||
Success = false,
|
||||
Result = _textService.Localize("healthcheck/configurationServiceFileNotFound", new[] { _configFilePath })
|
||||
Result = _textService.Localize("healthcheck/configurationServiceNodeNotFound", new[] { itemPath, value })
|
||||
};
|
||||
}
|
||||
|
||||
var xmlDocument = new XmlDocument { PreserveWhitespace = true };
|
||||
xmlDocument.Load(_configFilePath);
|
||||
|
||||
var node = xmlDocument.SelectSingleNode(_xPath);
|
||||
if (node == null)
|
||||
return new ConfigurationServiceResult
|
||||
{
|
||||
Success = false,
|
||||
Result = _textService.Localize("healthcheck/configurationServiceNodeNotFound", new[] { _xPath, _configFilePath })
|
||||
};
|
||||
|
||||
if (node.NodeType == XmlNodeType.Element)
|
||||
node.InnerText = value;
|
||||
else
|
||||
node.Value = value;
|
||||
|
||||
xmlDocument.Save(_configFilePath);
|
||||
return new ConfigurationServiceResult { Success = true };
|
||||
_configManipulator.SaveConfigValue(itemPath, value);
|
||||
return new ConfigurationServiceResult
|
||||
{
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Configuration
|
||||
{
|
||||
[HealthCheck("D0F7599E-9B2A-4D9E-9883-81C7EDC5616F", "Macro errors",
|
||||
Description = "Checks to make sure macro errors are not set to throw a YSOD (yellow screen of death), which would prevent certain or all pages from loading completely.",
|
||||
Description =
|
||||
"Checks to make sure macro errors are not set to throw a YSOD (yellow screen of death), which would prevent certain or all pages from loading completely.",
|
||||
Group = "Configuration")]
|
||||
public class MacroErrorsCheck : AbstractConfigCheck
|
||||
public class MacroErrorsCheck : AbstractSettingsCheck
|
||||
{
|
||||
public MacroErrorsCheck(ILocalizedTextService textService, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory)
|
||||
: base(textService, hostingEnvironment, loggerFactory)
|
||||
{ }
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IOptionsMonitor<ContentSettings> _contentSettings;
|
||||
|
||||
public override string FilePath => "~/Config/umbracoSettings.config";
|
||||
|
||||
public override string XPath => "/settings/content/MacroErrors";
|
||||
public MacroErrorsCheck(ILocalizedTextService textService, ILoggerFactory loggerFactory,
|
||||
IOptionsMonitor<ContentSettings> contentSettings)
|
||||
: base(textService, loggerFactory)
|
||||
{
|
||||
_textService = textService;
|
||||
_loggerFactory = loggerFactory;
|
||||
_contentSettings = contentSettings;
|
||||
}
|
||||
|
||||
public override ValueComparisonType ValueComparisonType => ValueComparisonType.ShouldEqual;
|
||||
|
||||
public override string ItemPath => Constants.Configuration.ConfigContentMacroErrors;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values to compare against.
|
||||
/// </summary>
|
||||
public override IEnumerable<AcceptableConfiguration> Values
|
||||
{
|
||||
get
|
||||
@@ -44,24 +57,35 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
}
|
||||
}
|
||||
|
||||
public override string CurrentValue => _contentSettings.CurrentValue.MacroErrors.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message for when the check has succeeded.
|
||||
/// </summary>
|
||||
public override string CheckSuccessMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return TextService.Localize("healthcheck/macroErrorModeCheckSuccessMessage",
|
||||
return _textService.Localize("healthcheck/macroErrorModeCheckSuccessMessage",
|
||||
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message for when the check has failed.
|
||||
/// </summary>
|
||||
public override string CheckErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return TextService.Localize("healthcheck/macroErrorModeCheckErrorMessage",
|
||||
return _textService.Localize("healthcheck/macroErrorModeCheckErrorMessage",
|
||||
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rectify success message.
|
||||
/// </summary>
|
||||
public override string RectifySuccessMessage
|
||||
{
|
||||
get
|
||||
|
||||
@@ -2,32 +2,40 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Configuration
|
||||
{
|
||||
[Obsolete("This is not currently in the appsettings.JSON and so can either be removed, or rewritten in .NET Core fashion")]
|
||||
[HealthCheck("046A066C-4FB2-4937-B931-069964E16C66", "Try Skip IIS Custom Errors",
|
||||
Description = "Starting with IIS 7.5, this must be set to true for Umbraco 404 pages to show. Otherwise, IIS will takeover and render its built-in error page.",
|
||||
Group = "Configuration")]
|
||||
public class TrySkipIisCustomErrorsCheck : AbstractConfigCheck
|
||||
public class TrySkipIisCustomErrorsCheck : AbstractSettingsCheck
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly Version _iisVersion;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public TrySkipIisCustomErrorsCheck(ILocalizedTextService textService, ILoggerFactory loggerFactory,
|
||||
IHostingEnvironment hostingEnvironment)
|
||||
: base(textService, hostingEnvironment, loggerFactory)
|
||||
public TrySkipIisCustomErrorsCheck(ILocalizedTextService textService, ILoggerFactory loggerFactory, IOptions<GlobalSettings> globalSettings)
|
||||
: base(textService, loggerFactory)
|
||||
{
|
||||
_iisVersion = hostingEnvironment.IISVersion;
|
||||
_textService = textService;
|
||||
_loggerFactory = loggerFactory;
|
||||
//TODO: detect if hosted in IIS, and then IIS version if we want to go this route
|
||||
_iisVersion = new Version("7.5");
|
||||
_globalSettings = globalSettings.Value;
|
||||
}
|
||||
|
||||
public override string FilePath => "~/Config/umbracoSettings.config";
|
||||
|
||||
public override string XPath => "/settings/web.routing/@trySkipIisCustomErrors";
|
||||
public override string ItemPath => "TBC";
|
||||
|
||||
public override ValueComparisonType ValueComparisonType => ValueComparisonType.ShouldEqual;
|
||||
|
||||
public override string CurrentValue => null;
|
||||
|
||||
public override IEnumerable<AcceptableConfiguration> Values
|
||||
{
|
||||
get
|
||||
@@ -36,7 +44,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
var recommendedValue = _iisVersion >= new Version("7.5")
|
||||
? bool.TrueString.ToLower()
|
||||
: bool.FalseString.ToLower();
|
||||
return new List<AcceptableConfiguration> { new AcceptableConfiguration { IsRecommended = true, Value = recommendedValue } };
|
||||
return new List<AcceptableConfiguration> { new AcceptableConfiguration { IsRecommended = true, Value = recommendedValue } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +52,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
{
|
||||
get
|
||||
{
|
||||
return TextService.Localize("healthcheck/trySkipIisCustomErrorsCheckSuccessMessage",
|
||||
return _textService.Localize("healthcheck/trySkipIisCustomErrorsCheckSuccessMessage",
|
||||
new[] { Values.First(v => v.IsRecommended).Value, _iisVersion.ToString() });
|
||||
}
|
||||
}
|
||||
@@ -53,7 +61,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
{
|
||||
get
|
||||
{
|
||||
return TextService.Localize("healthcheck/trySkipIisCustomErrorsCheckErrorMessage",
|
||||
return _textService.Localize("healthcheck/trySkipIisCustomErrorsCheckErrorMessage",
|
||||
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, _iisVersion.ToString() });
|
||||
}
|
||||
}
|
||||
@@ -62,8 +70,10 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
{
|
||||
get
|
||||
{
|
||||
return TextService.Localize("healthcheck/trySkipIisCustomErrorsCheckRectifySuccessMessage",
|
||||
new[] { Values.First(v => v.IsRecommended).Value, _iisVersion.ToString() });
|
||||
return _textService.Localize("healthcheck/trySkipIisCustomErrorsCheckRectifySuccessMessage",
|
||||
new[] { "Not implemented" });
|
||||
|
||||
//new[] { Values.First(v => v.IsRecommended).Value, _iisVersion.ToString() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration.HealthChecks;
|
||||
using Umbraco.Core.HealthCheck;
|
||||
using Umbraco.Core.HealthCheck.Checks;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Models
|
||||
{
|
||||
|
||||
@@ -12,6 +12,6 @@ namespace Umbraco.Core.Configuration.Models
|
||||
|
||||
public uint CachedNameLength { get; set; } = 8;
|
||||
|
||||
public string CacheFolder { get; set; } = Path.Combine("..", "Umbraco", "MediaCache");
|
||||
public string CacheFolder { get; set; } = Path.Combine("..", "umbraco", "mediacache");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ namespace Umbraco.Core.Configuration.Models
|
||||
/// <summary>
|
||||
/// Represents the models builder configuration.
|
||||
/// </summary>
|
||||
public class ModelsBuilderSettings
|
||||
public class ModelsBuilderSettings
|
||||
{
|
||||
// TODO: This should not go into App_Data - that folder isn't really a real thing anymore
|
||||
public static string DefaultModelsDirectory => "~/App_Data/Models";
|
||||
public static string DefaultModelsDirectory => "~/umbraco/models";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the whole models experience is enabled.
|
||||
|
||||
@@ -11,9 +11,23 @@
|
||||
/// ":" is used as marker for nested objects in json. E.g. "Umbraco:CMS:" = {"Umbraco":{"CMS":{....}}
|
||||
/// </remarks>
|
||||
public const string ConfigPrefix = "Umbraco:CMS:";
|
||||
public const string ConfigContentPrefix = ConfigPrefix + "Content:";
|
||||
public const string ConfigContentNotificationsPrefix = ConfigContentPrefix + "Notifications:";
|
||||
public const string ConfigCorePrefix = ConfigPrefix + "Core:";
|
||||
public const string ConfigCustomErrorsPrefix = ConfigPrefix + "CustomErrors:";
|
||||
public const string ConfigGlobalPrefix = ConfigPrefix + "Global:";
|
||||
public const string ConfigHostingPrefix = ConfigPrefix + "Hosting:";
|
||||
public const string ConfigModelsBuilderPrefix = ConfigPrefix + "ModelsBuilder:";
|
||||
public const string ConfigSecurityPrefix = ConfigPrefix + "Security:";
|
||||
|
||||
public const string ConfigContentNotificationsEmail = ConfigContentNotificationsPrefix + "Email";
|
||||
public const string ConfigContentMacroErrors = ConfigContentPrefix + "MacroErrors";
|
||||
public const string ConfigGlobalUseHttps = ConfigGlobalPrefix + "UseHttps";
|
||||
public const string ConfigHostingDebug = ConfigHostingPrefix + "Debug";
|
||||
public const string ConfigCustomErrorsMode = ConfigCustomErrorsPrefix + "Mode";
|
||||
public const string ConfigActiveDirectory = ConfigPrefix + "ActiveDirectory";
|
||||
public const string ConfigContent = ConfigPrefix + "Content";
|
||||
public const string ConfigCoreDebug = ConfigPrefix + "Core:Debug";
|
||||
public const string ConfigCoreDebug = ConfigCorePrefix + "Debug";
|
||||
public const string ConfigExceptionFilter = ConfigPrefix + "ExceptionFilter";
|
||||
public const string ConfigGlobal = ConfigPrefix + "Global";
|
||||
public const string ConfigHealthChecks = ConfigPrefix + "HealthChecks";
|
||||
@@ -27,14 +41,13 @@
|
||||
public const string ConfigNuCache = ConfigPrefix + "NuCache";
|
||||
public const string ConfigRequestHandler = ConfigPrefix + "RequestHandler";
|
||||
public const string ConfigRuntime = ConfigPrefix + "Runtime";
|
||||
public const string ConfigRuntimeMinification = ConfigPrefix + "RuntimeMinification";
|
||||
public const string ConfigRuntimeMinificationVersion = ConfigRuntimeMinification + ":Version";
|
||||
public const string ConfigSecurity = ConfigPrefix + "Security";
|
||||
public const string ConfigTours = ConfigPrefix + "Tours";
|
||||
public const string ConfigTypeFinder = ConfigPrefix + "TypeFinder";
|
||||
public const string ConfigWebRouting = ConfigPrefix + "WebRouting";
|
||||
public const string ConfigUserPassword = ConfigPrefix + "Security:UserPassword";
|
||||
|
||||
public const string ConfigRuntimeMinification = ConfigPrefix + "RuntimeMinification";
|
||||
public const string ConfigRuntimeMinificationVersion = ConfigRuntimeMinification + ":Version";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Net.Mail;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class SendEmailEventArgs : EventArgs
|
||||
{
|
||||
public MailMessage Message { get; private set; }
|
||||
public EmailMessage Message { get; }
|
||||
|
||||
public SendEmailEventArgs(MailMessage message)
|
||||
public SendEmailEventArgs(EmailMessage message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public class AcceptableConfiguration
|
||||
{
|
||||
160
src/Umbraco.Core/HealthCheck/Checks/AbstractSettingsCheck.cs
Normal file
160
src/Umbraco.Core/HealthCheck/Checks/AbstractSettingsCheck.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.HealthCheck.Checks
|
||||
{
|
||||
public abstract class AbstractSettingsCheck : HealthCheck
|
||||
{
|
||||
protected ILocalizedTextService TextService { get; }
|
||||
protected ILoggerFactory LoggerFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets key within the JSON to check, in the colon-delimited format
|
||||
/// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1
|
||||
/// </summary>
|
||||
public abstract string ItemPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values to compare against.
|
||||
/// </summary>
|
||||
public abstract IEnumerable<AcceptableConfiguration> Values { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value of the config setting
|
||||
/// </summary>
|
||||
public abstract string CurrentValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provided value
|
||||
/// </summary>
|
||||
public string ProvidedValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the comparison type for checking the value.
|
||||
/// </summary>
|
||||
public abstract ValueComparisonType ValueComparisonType { get; }
|
||||
|
||||
protected AbstractSettingsCheck(ILocalizedTextService textService, ILoggerFactory loggerFactory)
|
||||
{
|
||||
TextService = textService;
|
||||
LoggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message for when the check has succeeded.
|
||||
/// </summary>
|
||||
public virtual string CheckSuccessMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return TextService.Localize("healthcheck/checkSuccessMessage", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, ItemPath });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message for when the check has failed.
|
||||
/// </summary>
|
||||
public virtual string CheckErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return ValueComparisonType == ValueComparisonType.ShouldEqual
|
||||
? TextService.Localize("healthcheck/checkErrorMessageDifferentExpectedValue",
|
||||
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, ItemPath })
|
||||
: TextService.Localize("healthcheck/checkErrorMessageUnexpectedValue",
|
||||
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, ItemPath });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rectify success message.
|
||||
/// </summary>
|
||||
public virtual string RectifySuccessMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
AcceptableConfiguration recommendedValue = Values.FirstOrDefault(v => v.IsRecommended);
|
||||
string rectifiedValue = recommendedValue != null ? recommendedValue.Value : ProvidedValue;
|
||||
return TextService.Localize("healthcheck/rectifySuccessMessage",
|
||||
new[]
|
||||
{
|
||||
CurrentValue,
|
||||
rectifiedValue,
|
||||
ItemPath
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this check can be rectified automatically.
|
||||
/// </summary>
|
||||
public virtual bool CanRectify => ValueComparisonType == ValueComparisonType.ShouldEqual;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this check can be rectified automatically if a value is provided.
|
||||
/// </summary>
|
||||
public virtual bool CanRectifyWithValue => ValueComparisonType == ValueComparisonType.ShouldNotEqual;
|
||||
|
||||
public override IEnumerable<HealthCheckStatus> GetStatus()
|
||||
{
|
||||
// update the successMessage with the CurrentValue
|
||||
var successMessage = string.Format(CheckSuccessMessage, ItemPath, Values, CurrentValue);
|
||||
bool valueFound = Values.Any(value => string.Equals(CurrentValue, value.Value, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (ValueComparisonType == ValueComparisonType.ShouldEqual
|
||||
&& valueFound || ValueComparisonType == ValueComparisonType.ShouldNotEqual
|
||||
&& valueFound == false)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new HealthCheckStatus(successMessage)
|
||||
{
|
||||
ResultType = StatusResultType.Success
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Declare the action for rectifying the config value
|
||||
var rectifyAction = new HealthCheckAction("rectify", Id)
|
||||
{
|
||||
Name = TextService.Localize("healthcheck/rectifyButton"),
|
||||
ValueRequired = CanRectifyWithValue
|
||||
};
|
||||
|
||||
string resultMessage = string.Format(CheckErrorMessage, ItemPath, Values, CurrentValue);
|
||||
return new[]
|
||||
{
|
||||
new HealthCheckStatus(resultMessage)
|
||||
{
|
||||
ResultType = StatusResultType.Error,
|
||||
Actions = CanRectify || CanRectifyWithValue ? new[] { rectifyAction } : new HealthCheckAction[0]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rectifies this check.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual HealthCheckStatus Rectify(HealthCheckAction action)
|
||||
{
|
||||
if (ValueComparisonType == ValueComparisonType.ShouldNotEqual)
|
||||
{
|
||||
throw new InvalidOperationException(TextService.Localize("healthcheck/cannotRectifyShouldNotEqual"));
|
||||
}
|
||||
|
||||
//TODO: show message instead of actually fixing config
|
||||
string recommendedValue = Values.First(v => v.IsRecommended).Value;
|
||||
string resultMessage = string.Format(RectifySuccessMessage, ItemPath, Values);
|
||||
return new HealthCheckStatus(resultMessage) { ResultType = StatusResultType.Success };
|
||||
}
|
||||
|
||||
public override HealthCheckStatus ExecuteAction(HealthCheckAction action)
|
||||
{
|
||||
return Rectify(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Configuration
|
||||
{
|
||||
[HealthCheck("3E2F7B14-4B41-452B-9A30-E67FBC8E1206", "Notification Email Settings",
|
||||
Description = "If notifications are used, the 'from' email address should be specified and changed from the default value.",
|
||||
Group = "Configuration")]
|
||||
public class NotificationEmailCheck : AbstractConfigCheck
|
||||
public class NotificationEmailCheck : AbstractSettingsCheck
|
||||
{
|
||||
private readonly IOptionsMonitor<ContentSettings> _contentSettings;
|
||||
private const string DefaultFromEmail = "your@email.here";
|
||||
|
||||
public NotificationEmailCheck(ILocalizedTextService textService, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory)
|
||||
: base(textService, hostingEnvironment, loggerFactory)
|
||||
{ }
|
||||
public NotificationEmailCheck(ILocalizedTextService textService, ILoggerFactory loggerFactory, IOptionsMonitor<ContentSettings> contentSettings)
|
||||
: base(textService, loggerFactory)
|
||||
{
|
||||
_contentSettings = contentSettings;
|
||||
}
|
||||
|
||||
public override string FilePath => "~/Config/umbracoSettings.config";
|
||||
|
||||
public override string XPath => "/settings/content/notifications/email";
|
||||
public override string ItemPath => Constants.Configuration.ConfigContentNotificationsEmail;
|
||||
|
||||
public override ValueComparisonType ValueComparisonType => ValueComparisonType.ShouldNotEqual;
|
||||
|
||||
@@ -28,8 +29,11 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
new AcceptableConfiguration { IsRecommended = false, Value = DefaultFromEmail }
|
||||
};
|
||||
|
||||
public override string CheckSuccessMessage => TextService.Localize("healthcheck/notificationEmailsCheckSuccessMessage", new [] { CurrentValue } );
|
||||
public override string CurrentValue => _contentSettings.CurrentValue.Notifications.Email;
|
||||
|
||||
public override string CheckSuccessMessage => TextService.Localize("healthcheck/notificationEmailsCheckSuccessMessage", new[] { CurrentValue });
|
||||
|
||||
public override string CheckErrorMessage => TextService.Localize("healthcheck/notificationEmailsCheckErrorMessage", new[] { DefaultFromEmail });
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using System.Text;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Data
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Data
|
||||
{
|
||||
[HealthCheck(
|
||||
"73DD0C1C-E0CA-4C31-9564-1DCA509788AF",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
namespace Umbraco.Core.HealthCheck.Checks
|
||||
{
|
||||
public class DisabledHealthCheck
|
||||
{
|
||||
@@ -1,24 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.HealthCheck.Checks.LiveEnvironment
|
||||
{
|
||||
[HealthCheck("4090C0A1-2C52-4124-92DD-F028FD066A64", "Custom Errors",
|
||||
Description = "Leaving custom errors off will display a complete stack trace to your visitors if an exception occurs.",
|
||||
Group = "Live Environment")]
|
||||
public class CustomErrorsCheck : AbstractConfigCheck
|
||||
public class CustomErrorsCheck : AbstractSettingsCheck
|
||||
{
|
||||
public CustomErrorsCheck(ILocalizedTextService textService, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory)
|
||||
: base(textService, hostingEnvironment, loggerFactory)
|
||||
public CustomErrorsCheck(ILocalizedTextService textService, ILoggerFactory loggerFactory)
|
||||
: base(textService, loggerFactory)
|
||||
{ }
|
||||
|
||||
public override string FilePath => "~/Web.config";
|
||||
|
||||
public override string XPath => "/configuration/system.web/customErrors/@mode";
|
||||
public override string ItemPath => Constants.Configuration.ConfigCustomErrorsMode;
|
||||
|
||||
public override ValueComparisonType ValueComparisonType => ValueComparisonType.ShouldEqual;
|
||||
|
||||
@@ -28,6 +24,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
new AcceptableConfiguration { IsRecommended = false, Value = "On" }
|
||||
};
|
||||
|
||||
public override string CurrentValue { get; }
|
||||
|
||||
public override string CheckSuccessMessage
|
||||
{
|
||||
get
|
||||
@@ -1,24 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.HealthCheck.Checks.LiveEnvironment
|
||||
{
|
||||
[HealthCheck("9BED6EF4-A7F3-457A-8935-B64E9AA8BAB3", "Trace Mode",
|
||||
Description = "Leaving trace mode enabled can make valuable information about your system available to hackers.",
|
||||
Group = "Live Environment")]
|
||||
public class TraceCheck : AbstractConfigCheck
|
||||
public class TraceCheck : AbstractSettingsCheck
|
||||
{
|
||||
|
||||
public TraceCheck(ILocalizedTextService textService, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory)
|
||||
: base(textService, hostingEnvironment, loggerFactory)
|
||||
public TraceCheck(ILocalizedTextService textService, ILoggerFactory loggerFactory)
|
||||
: base(textService, loggerFactory)
|
||||
{ }
|
||||
|
||||
public override string FilePath => "~/Web.config";
|
||||
|
||||
public override string XPath => "/configuration/system.web/trace/@enabled";
|
||||
public override string ItemPath => "/configuration/system.web/trace/@enabled";
|
||||
|
||||
public override ValueComparisonType ValueComparisonType => ValueComparisonType.ShouldEqual;
|
||||
|
||||
@@ -27,10 +22,13 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
new AcceptableConfiguration { IsRecommended = true, Value = bool.FalseString.ToLower() }
|
||||
};
|
||||
|
||||
public override string CurrentValue { get; }
|
||||
|
||||
public override string CheckSuccessMessage => TextService.Localize("healthcheck/traceModeCheckSuccessMessage");
|
||||
|
||||
public override string CheckErrorMessage => TextService.Localize("healthcheck/traceModeCheckErrorMessage");
|
||||
|
||||
public override string RectifySuccessMessage => TextService.Localize("healthcheck/traceModeCheckRectifySuccessMessage");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,27 +2,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Install;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Permissions
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Permissions
|
||||
{
|
||||
internal enum PermissionCheckRequirement
|
||||
{
|
||||
Required,
|
||||
Optional
|
||||
}
|
||||
|
||||
internal enum PermissionCheckFor
|
||||
{
|
||||
Folder,
|
||||
File
|
||||
}
|
||||
|
||||
[HealthCheck(
|
||||
"53DBA282-4A79-4B67-B958-B29EC40FCC23",
|
||||
"Folder & File Permissions",
|
||||
@@ -31,14 +17,14 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions
|
||||
public class FolderAndFilePermissionsCheck : HealthCheck
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private readonly IFilePermissionHelper _filePermissionHelper;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
|
||||
public FolderAndFilePermissionsCheck(ILocalizedTextService textService, IOptions<GlobalSettings> globalSettings, IFilePermissionHelper filePermissionHelper, IIOHelper ioHelper)
|
||||
public FolderAndFilePermissionsCheck(ILocalizedTextService textService, IOptionsMonitor<GlobalSettings> globalSettings, IFilePermissionHelper filePermissionHelper, IIOHelper ioHelper)
|
||||
{
|
||||
_textService = textService;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_globalSettings = globalSettings;
|
||||
_filePermissionHelper = filePermissionHelper;
|
||||
_ioHelper = ioHelper;
|
||||
}
|
||||
@@ -74,10 +60,10 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions
|
||||
{ Constants.SystemDirectories.Preview, PermissionCheckRequirement.Required },
|
||||
{ Constants.SystemDirectories.AppPlugins, PermissionCheckRequirement.Required },
|
||||
{ Constants.SystemDirectories.Config, PermissionCheckRequirement.Optional },
|
||||
{ _globalSettings.UmbracoCssPath, PermissionCheckRequirement.Optional },
|
||||
{ _globalSettings.UmbracoMediaPath, PermissionCheckRequirement.Optional },
|
||||
{ _globalSettings.UmbracoScriptsPath, PermissionCheckRequirement.Optional },
|
||||
{ _globalSettings.UmbracoPath, PermissionCheckRequirement.Optional },
|
||||
{ _globalSettings.CurrentValue.UmbracoCssPath, PermissionCheckRequirement.Optional },
|
||||
{ _globalSettings.CurrentValue.UmbracoMediaPath, PermissionCheckRequirement.Optional },
|
||||
{ _globalSettings.CurrentValue.UmbracoScriptsPath, PermissionCheckRequirement.Optional },
|
||||
{ _globalSettings.CurrentValue.UmbracoPath, PermissionCheckRequirement.Optional },
|
||||
{ Constants.SystemDirectories.MvcViews, PermissionCheckRequirement.Optional }
|
||||
};
|
||||
|
||||
@@ -97,7 +83,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions
|
||||
|
||||
//now check the special folders
|
||||
var requiredPathCheckResult2 = _filePermissionHelper.EnsureDirectories(
|
||||
GetPathsToCheck(pathsToCheckWithRestarts, PermissionCheckRequirement.Required), out var requiredFailedPaths2, writeCausesRestart:true);
|
||||
GetPathsToCheck(pathsToCheckWithRestarts, PermissionCheckRequirement.Required), out var requiredFailedPaths2, writeCausesRestart: true);
|
||||
var optionalPathCheckResult2 = _filePermissionHelper.EnsureDirectories(
|
||||
GetPathsToCheck(pathsToCheckWithRestarts, PermissionCheckRequirement.Optional), out var optionalFailedPaths2, writeCausesRestart: true);
|
||||
|
||||
@@ -139,9 +125,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private HealthCheckStatus GetStatus(bool requiredPathCheckResult, IEnumerable<string> requiredFailedPaths,
|
||||
bool optionalPathCheckResult, IEnumerable<string> optionalFailedPaths,
|
||||
PermissionCheckFor checkingFor)
|
||||
private HealthCheckStatus GetStatus(bool requiredPathCheckResult, IEnumerable<string> requiredFailedPaths, bool optionalPathCheckResult, IEnumerable<string> optionalFailedPaths, PermissionCheckFor checkingFor)
|
||||
{
|
||||
// Return error if any required paths fail the check, or warning if any optional ones do
|
||||
var resultType = StatusResultType.Success;
|
||||
@@ -164,12 +148,11 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions
|
||||
}
|
||||
|
||||
var actions = new List<HealthCheckAction>();
|
||||
return
|
||||
new HealthCheckStatus(message)
|
||||
{
|
||||
ResultType = resultType,
|
||||
Actions = actions
|
||||
};
|
||||
return new HealthCheckStatus(message)
|
||||
{
|
||||
ResultType = resultType,
|
||||
Actions = actions
|
||||
};
|
||||
}
|
||||
|
||||
private string GetMessageForPathCheckFailure(string messageKey, IEnumerable<string> failedPaths)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Permissions
|
||||
{
|
||||
internal enum PermissionCheckFor
|
||||
{
|
||||
Folder,
|
||||
File
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Permissions
|
||||
{
|
||||
internal enum PermissionCheckRequirement
|
||||
{
|
||||
Required,
|
||||
Optional
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,11 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Security
|
||||
{
|
||||
public abstract class BaseHttpHeaderCheck : HealthCheck
|
||||
{
|
||||
@@ -23,16 +21,14 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
private readonly string _localizedTextPrefix;
|
||||
private readonly bool _metaTagOptionAvailable;
|
||||
private readonly IRequestAccessor _requestAccessor;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
|
||||
protected BaseHttpHeaderCheck(
|
||||
IRequestAccessor requestAccessor,
|
||||
ILocalizedTextService textService,
|
||||
string header, string value, string localizedTextPrefix, bool metaTagOptionAvailable, IIOHelper ioHelper)
|
||||
string header, string value, string localizedTextPrefix, bool metaTagOptionAvailable)
|
||||
{
|
||||
TextService = textService ?? throw new ArgumentNullException(nameof(textService));
|
||||
_requestAccessor = requestAccessor;
|
||||
_ioHelper = ioHelper;
|
||||
_header = header;
|
||||
_value = value;
|
||||
_localizedTextPrefix = localizedTextPrefix;
|
||||
@@ -72,7 +68,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
var success = false;
|
||||
|
||||
// Access the site home page and check for the click-jack protection header or meta tag
|
||||
var url = _requestAccessor.GetApplicationUrl();
|
||||
var url = _requestAccessor.GetApplicationUrl();
|
||||
var request = WebRequest.Create(url);
|
||||
request.Method = "GET";
|
||||
try
|
||||
@@ -146,7 +142,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
private HealthCheckStatus SetHeaderInConfig()
|
||||
{
|
||||
var errorMessage = string.Empty;
|
||||
var success = SaveHeaderToConfigFile(out errorMessage);
|
||||
//TODO: edit to show fix suggestion instead of making fix
|
||||
var success = true;
|
||||
|
||||
if (success)
|
||||
{
|
||||
@@ -158,64 +155,10 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
}
|
||||
|
||||
return
|
||||
new HealthCheckStatus(TextService.Localize("healthcheck/setHeaderInConfigError", new [] { errorMessage }))
|
||||
new HealthCheckStatus(TextService.Localize("healthcheck/setHeaderInConfigError", new[] { errorMessage }))
|
||||
{
|
||||
ResultType = StatusResultType.Error
|
||||
};
|
||||
}
|
||||
|
||||
private bool SaveHeaderToConfigFile(out string errorMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
// There don't look to be any useful classes defined in https://msdn.microsoft.com/en-us/library/system.web.configuration(v=vs.110).aspx
|
||||
// for working with the customHeaders section, so working with the XML directly.
|
||||
var configFile = _ioHelper.MapPath("~/Web.config");
|
||||
var doc = XDocument.Load(configFile);
|
||||
var systemWebServerElement = doc.XPathSelectElement("/configuration/system.webServer");
|
||||
var httpProtocolElement = systemWebServerElement.Element("httpProtocol");
|
||||
if (httpProtocolElement == null)
|
||||
{
|
||||
httpProtocolElement = new XElement("httpProtocol");
|
||||
systemWebServerElement.Add(httpProtocolElement);
|
||||
}
|
||||
|
||||
var customHeadersElement = httpProtocolElement.Element("customHeaders");
|
||||
if (customHeadersElement == null)
|
||||
{
|
||||
customHeadersElement = new XElement("customHeaders");
|
||||
httpProtocolElement.Add(customHeadersElement);
|
||||
}
|
||||
|
||||
var removeHeaderElement = customHeadersElement.Elements("remove")
|
||||
.SingleOrDefault(x => x.Attribute("name")?.Value.Equals(_value, StringComparison.InvariantCultureIgnoreCase) == true);
|
||||
if (removeHeaderElement == null)
|
||||
{
|
||||
customHeadersElement.Add(
|
||||
new XElement("remove",
|
||||
new XAttribute("name", _header)));
|
||||
}
|
||||
|
||||
var addHeaderElement = customHeadersElement.Elements("add")
|
||||
.SingleOrDefault(x => x.Attribute("name")?.Value.Equals(_header, StringComparison.InvariantCultureIgnoreCase) == true);
|
||||
if (addHeaderElement == null)
|
||||
{
|
||||
customHeadersElement.Add(
|
||||
new XElement("add",
|
||||
new XAttribute("name", _header),
|
||||
new XAttribute("value", _value)));
|
||||
}
|
||||
|
||||
doc.Save(configFile);
|
||||
|
||||
errorMessage = string.Empty;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Security
|
||||
{
|
||||
[HealthCheck(
|
||||
"ED0D7E40-971E-4BE8-AB6D-8CC5D0A6A5B0",
|
||||
@@ -11,8 +10,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
Group = "Security")]
|
||||
public class ClickJackingCheck : BaseHttpHeaderCheck
|
||||
{
|
||||
public ClickJackingCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService, IIOHelper ioHelper)
|
||||
: base(requestAccessor, textService, "X-Frame-Options", "sameorigin", "clickJacking", true, ioHelper)
|
||||
public ClickJackingCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService)
|
||||
: base(requestAccessor, textService, "X-Frame-Options", "sameorigin", "clickJacking", true)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Security
|
||||
{
|
||||
[HealthCheck(
|
||||
"92ABBAA2-0586-4089-8AE2-9A843439D577",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Security
|
||||
{
|
||||
[HealthCheck(
|
||||
"E2048C48-21C5-4BE1-A80B-8062162DF124",
|
||||
@@ -16,8 +15,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
// and the blog post of Troy Hunt (https://www.troyhunt.com/understanding-http-strict-transport/)
|
||||
// If you want do to it perfectly, you have to submit it https://hstspreload.org/,
|
||||
// but then you should include subdomains and I wouldn't suggest to do that for Umbraco-sites.
|
||||
public HstsCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService, IIOHelper ioHelper)
|
||||
: base(requestAccessor, textService, "Strict-Transport-Security", "max-age=10886400", "hSTS", true, ioHelper)
|
||||
public HstsCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService)
|
||||
: base(requestAccessor, textService, "Strict-Transport-Security", "max-age=10886400", "hSTS", true)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,14 @@ using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.HealthCheck.Checks.Config;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Configuration.HealthChecks;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Security
|
||||
{
|
||||
[HealthCheck(
|
||||
"EB66BB3B-1BCD-4314-9531-9DA2C1D6D9A7",
|
||||
@@ -21,22 +20,20 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
public class HttpsCheck : HealthCheck
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private readonly IRequestAccessor _requestAccessor;
|
||||
private readonly ILogger<ConfigurationService> _logger;
|
||||
|
||||
private readonly ILogger<HttpsCheck> _logger;
|
||||
private const string FixHttpsSettingAction = "fixHttpsSetting";
|
||||
string itemPath => Constants.Configuration.ConfigGlobalUseHttps;
|
||||
|
||||
public HttpsCheck(ILocalizedTextService textService,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IOptionsMonitor<GlobalSettings> globalSettings,
|
||||
IIOHelper ioHelper,
|
||||
IRequestAccessor requestAccessor,
|
||||
ILogger<ConfigurationService> logger)
|
||||
ILogger<HttpsCheck> logger)
|
||||
{
|
||||
_textService = textService;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_ioHelper = ioHelper;
|
||||
_globalSettings = globalSettings;
|
||||
_requestAccessor = requestAccessor;
|
||||
_logger = logger;
|
||||
}
|
||||
@@ -75,7 +72,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
// Attempt to access the site over HTTPS to see if it HTTPS is supported
|
||||
// and a valid certificate has been configured
|
||||
var url = _requestAccessor.GetApplicationUrl().ToString().Replace("http:", "https:");
|
||||
var request = (HttpWebRequest) WebRequest.Create(url);
|
||||
var request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Method = "HEAD";
|
||||
|
||||
try
|
||||
@@ -119,8 +116,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
if (exception != null)
|
||||
{
|
||||
message = exception.Status == WebExceptionStatus.TrustFailure
|
||||
? _textService.Localize("healthcheck/httpsCheckInvalidCertificate", new [] { exception.Message })
|
||||
: _textService.Localize("healthcheck/healthCheckInvalidUrl", new [] { url, exception.Message });
|
||||
? _textService.Localize("healthcheck/httpsCheckInvalidCertificate", new[] { exception.Message })
|
||||
: _textService.Localize("healthcheck/healthCheckInvalidUrl", new[] { url, exception.Message });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -132,33 +129,31 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
|
||||
var actions = new List<HealthCheckAction>();
|
||||
|
||||
return
|
||||
new HealthCheckStatus(message)
|
||||
{
|
||||
ResultType = result,
|
||||
Actions = actions
|
||||
};
|
||||
return new HealthCheckStatus(message)
|
||||
{
|
||||
ResultType = result,
|
||||
Actions = actions
|
||||
};
|
||||
}
|
||||
|
||||
private HealthCheckStatus CheckIfCurrentSchemeIsHttps()
|
||||
{
|
||||
var uri = _requestAccessor.GetApplicationUrl();
|
||||
var uri = _requestAccessor.GetApplicationUrl();
|
||||
var success = uri.Scheme == "https";
|
||||
|
||||
var actions = new List<HealthCheckAction>();
|
||||
|
||||
return
|
||||
new HealthCheckStatus(_textService.Localize("healthcheck/httpsCheckIsCurrentSchemeHttps", new[] { success ? string.Empty : "not" }))
|
||||
{
|
||||
ResultType = success ? StatusResultType.Success : StatusResultType.Error,
|
||||
Actions = actions
|
||||
};
|
||||
return new HealthCheckStatus(_textService.Localize("healthcheck/httpsCheckIsCurrentSchemeHttps", new[] { success ? string.Empty : "not" }))
|
||||
{
|
||||
ResultType = success ? StatusResultType.Success : StatusResultType.Error,
|
||||
Actions = actions
|
||||
};
|
||||
}
|
||||
|
||||
private HealthCheckStatus CheckHttpsConfigurationSetting()
|
||||
{
|
||||
var httpsSettingEnabled = _globalSettings.UseHttps;
|
||||
var uri = _requestAccessor.GetApplicationUrl();
|
||||
bool httpsSettingEnabled = _globalSettings.CurrentValue.UseHttps;
|
||||
Uri uri = _requestAccessor.GetApplicationUrl();
|
||||
var actions = new List<HealthCheckAction>();
|
||||
|
||||
string resultMessage;
|
||||
@@ -171,46 +166,34 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
else
|
||||
{
|
||||
if (httpsSettingEnabled == false)
|
||||
{
|
||||
actions.Add(new HealthCheckAction(FixHttpsSettingAction, Id)
|
||||
{
|
||||
Name = _textService.Localize("healthcheck/httpsCheckEnableHttpsButton"),
|
||||
Description = _textService.Localize("healthcheck/httpsCheckEnableHttpsDescription")
|
||||
});
|
||||
}
|
||||
|
||||
resultMessage = _textService.Localize("healthcheck/httpsCheckConfigurationCheckResult",
|
||||
new[] {httpsSettingEnabled.ToString(), httpsSettingEnabled ? string.Empty : "not"});
|
||||
resultType = httpsSettingEnabled ? StatusResultType.Success: StatusResultType.Error;
|
||||
new[] { httpsSettingEnabled.ToString(), httpsSettingEnabled ? string.Empty : "not" });
|
||||
resultType = httpsSettingEnabled ? StatusResultType.Success : StatusResultType.Error;
|
||||
}
|
||||
|
||||
return
|
||||
new HealthCheckStatus(resultMessage)
|
||||
{
|
||||
ResultType = resultType,
|
||||
Actions = actions
|
||||
};
|
||||
return new HealthCheckStatus(resultMessage)
|
||||
{
|
||||
ResultType = resultType,
|
||||
Actions = actions
|
||||
};
|
||||
}
|
||||
|
||||
private HealthCheckStatus FixHttpsSetting()
|
||||
{
|
||||
var configFile = _ioHelper.MapPath("~/Web.config");
|
||||
const string xPath = "/configuration/appSettings/add[@key='Umbraco.Core.UseHttps']/@value";
|
||||
var configurationService = new ConfigurationService(configFile, xPath, _textService, _logger);
|
||||
var updateConfigFile = configurationService.UpdateConfigFile("true");
|
||||
//TODO: return message instead of actual fix
|
||||
|
||||
if (updateConfigFile.Success)
|
||||
return new HealthCheckStatus(_textService.Localize("healthcheck/httpsCheckEnableHttpsSuccess"))
|
||||
{
|
||||
return
|
||||
new HealthCheckStatus(_textService.Localize("healthcheck/httpsCheckEnableHttpsSuccess"))
|
||||
{
|
||||
ResultType = StatusResultType.Success
|
||||
};
|
||||
}
|
||||
|
||||
return
|
||||
new HealthCheckStatus(_textService.Localize("healthcheck/httpsCheckEnableHttpsError", new [] { updateConfigFile.Result }))
|
||||
{
|
||||
ResultType = StatusResultType.Error
|
||||
};
|
||||
ResultType = StatusResultType.Success
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Security
|
||||
{
|
||||
[HealthCheck(
|
||||
"1CF27DB3-EFC0-41D7-A1BB-EA912064E071",
|
||||
@@ -11,8 +10,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
Group = "Security")]
|
||||
public class NoSniffCheck : BaseHttpHeaderCheck
|
||||
{
|
||||
public NoSniffCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService, IIOHelper ioHelper)
|
||||
: base(requestAccessor, textService, "X-Content-Type-Options", "nosniff", "noSniff", false, ioHelper)
|
||||
public NoSniffCheck(IRequestAccessor requestAccessor, ILocalizedTextService textService)
|
||||
: base(requestAccessor, textService, "X-Content-Type-Options", "nosniff", "noSniff", false)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Security
|
||||
{
|
||||
[HealthCheck(
|
||||
"F4D2B02E-28C5-4999-8463-05759FA15C3A",
|
||||
@@ -16,8 +15,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
// and the blog post of Troy Hunt (https://www.troyhunt.com/understanding-http-strict-transport/)
|
||||
// If you want do to it perfectly, you have to submit it https://hstspreload.appspot.com/,
|
||||
// but then you should include subdomains and I wouldn't suggest to do that for Umbraco-sites.
|
||||
public XssProtectionCheck(IRequestAccessor requestAccessor,ILocalizedTextService textService, IIOHelper ioHelper)
|
||||
: base(requestAccessor, textService, "X-XSS-Protection", "1; mode=block", "xssProtection", true, ioHelper)
|
||||
public XssProtectionCheck(IRequestAccessor requestAccessor,ILocalizedTextService textService)
|
||||
: base(requestAccessor, textService, "X-XSS-Protection", "1; mode=block", "xssProtection", true)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Services
|
||||
namespace Umbraco.Core.HealthCheck.Checks.Services
|
||||
{
|
||||
[HealthCheck(
|
||||
"1B5D221B-CE99-4193-97CB-5F3261EC73DF",
|
||||
@@ -18,12 +16,12 @@ namespace Umbraco.Web.HealthCheck.Checks.Services
|
||||
public class SmtpCheck : HealthCheck
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
|
||||
public SmtpCheck(ILocalizedTextService textService, IOptions<GlobalSettings> globalSettings)
|
||||
public SmtpCheck(ILocalizedTextService textService, IOptionsMonitor<GlobalSettings> globalSettings)
|
||||
{
|
||||
_textService = textService;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -50,7 +48,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Services
|
||||
{
|
||||
var success = false;
|
||||
|
||||
var smtpSettings = _globalSettings.Smtp;
|
||||
var smtpSettings = _globalSettings.CurrentValue.Smtp;
|
||||
|
||||
string message;
|
||||
if (smtpSettings == null)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public class ConfigurationServiceResult
|
||||
{
|
||||
@@ -1,24 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for health checks.
|
||||
/// Provides a base class for health checks, filling in the healthcheck metadata on construction
|
||||
/// </summary>
|
||||
[DataContract(Name = "healthCheck", Namespace = "")]
|
||||
public abstract class HealthCheck : IDiscoverable
|
||||
{
|
||||
protected HealthCheck()
|
||||
{
|
||||
//Fill in the metadata
|
||||
var thisType = GetType();
|
||||
var meta = thisType.GetCustomAttribute<HealthCheckAttribute>(false);
|
||||
Type thisType = GetType();
|
||||
HealthCheckAttribute meta = thisType.GetCustomAttribute<HealthCheckAttribute>(false);
|
||||
if (meta == null)
|
||||
throw new InvalidOperationException($"The health check {thisType} requires a {typeof (HealthCheckAttribute)}");
|
||||
{
|
||||
throw new InvalidOperationException($"The health check {thisType} requires a {typeof(HealthCheckAttribute)}");
|
||||
}
|
||||
|
||||
Name = meta.Name;
|
||||
Description = meta.Description;
|
||||
Group = meta.Group;
|
||||
@@ -49,7 +50,5 @@ namespace Umbraco.Web.HealthCheck
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public abstract HealthCheckStatus ExecuteAction(HealthCheckAction action);
|
||||
|
||||
// TODO: What else?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
[DataContract(Name = "healthCheckAction", Namespace = "")]
|
||||
public class HealthCheckAction
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata attribute for Health checks
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public class HealthCheckCollection : BuilderCollectionBase<HealthCheck>
|
||||
public class HealthCheckCollection : BuilderCollectionBase<Core.HealthCheck.HealthCheck>
|
||||
{
|
||||
public HealthCheckCollection(IEnumerable<HealthCheck> items)
|
||||
public HealthCheckCollection(IEnumerable<Core.HealthCheck.HealthCheck> items)
|
||||
: base(items)
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
[DataContract(Name = "healthCheckGroup", Namespace = "")]
|
||||
public class HealthCheckGroup
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata attribute for health check notification methods
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public enum HealthCheckNotificationVerbosity
|
||||
{
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// The status returned for a health check when it performs it check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public class HealthCheckCollectionBuilder : LazyCollectionBuilderBase<HealthCheckCollectionBuilder, HealthCheckCollection, HealthCheck>
|
||||
{
|
||||
|
||||
7
src/Umbraco.Core/HealthCheck/IConfigurationService.cs
Normal file
7
src/Umbraco.Core/HealthCheck/IConfigurationService.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public interface IConfigurationService
|
||||
{
|
||||
ConfigurationServiceResult UpdateConfigFile(string value, string itemPath);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public interface INotificationMethod
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Core.Configuration.HealthChecks
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public interface INotificationMethodSettings
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public enum StatusResultType
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Config
|
||||
namespace Umbraco.Core.HealthCheck
|
||||
{
|
||||
public enum ValueComparisonType
|
||||
{
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Net.Mail;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
@@ -8,7 +8,6 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public interface IEmailSender
|
||||
{
|
||||
// TODO: This would be better if MailMessage was our own abstraction!
|
||||
Task SendAsync(MailMessage message);
|
||||
Task SendAsync(EmailMessage message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
lock (_shadowLocker)
|
||||
{
|
||||
var wrapper = new ShadowWrapper(filesystem, _ioHelper, _hostingEnvironment, _loggerFactory, shadowPath, IsScoped);
|
||||
var wrapper = new ShadowWrapper(filesystem, _ioHelper, _hostingEnvironment, _loggerFactory, shadowPath,() => IsScoped());
|
||||
if (_shadowCurrentId != null)
|
||||
wrapper.Shadow(_shadowCurrentId);
|
||||
_shadowWrappers.Add(wrapper);
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
var mappedRoot = MapPath(_hostingEnvironment.ApplicationVirtualPath);
|
||||
if (filePath.StartsWith(mappedRoot) == false)
|
||||
filePath = MapPath(filePath);
|
||||
filePath = _hostingEnvironment.MapPathContentRoot(filePath);
|
||||
|
||||
// yes we can (see above)
|
||||
//// don't trust what we get, it may contain relative segments
|
||||
@@ -132,7 +132,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
var validDir = dir;
|
||||
if (validDir.StartsWith(mappedRoot) == false)
|
||||
validDir = MapPath(validDir);
|
||||
validDir = _hostingEnvironment.MapPathContentRoot(validDir);
|
||||
|
||||
if (PathStartsWith(filePath, validDir, Path.DirectorySeparatorChar))
|
||||
return true;
|
||||
|
||||
@@ -10,7 +10,8 @@ using Umbraco.Core.Hosting;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
public class PhysicalFileSystem : IFileSystem
|
||||
public interface IPhysicalFileSystem : IFileSystem {}
|
||||
public class PhysicalFileSystem : IPhysicalFileSystem
|
||||
{
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly ILogger<PhysicalFileSystem> _logger;
|
||||
@@ -56,7 +57,6 @@ namespace Umbraco.Core.IO
|
||||
if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentException("Value can't be empty.", nameof(rootUrl));
|
||||
if (rootPath.StartsWith("~/")) throw new ArgumentException("Value can't be a virtual path and start with '~/'.", nameof(rootPath));
|
||||
|
||||
|
||||
// rootPath should be... rooted, as in, it's a root path!
|
||||
if (Path.IsPathRooted(rootPath) == false)
|
||||
{
|
||||
@@ -65,6 +65,9 @@ namespace Umbraco.Core.IO
|
||||
rootPath = Path.Combine(localRoot, rootPath);
|
||||
}
|
||||
|
||||
// clean up root path
|
||||
rootPath = Path.GetFullPath(rootPath);
|
||||
|
||||
_rootPath = EnsureDirectorySeparatorChar(rootPath).TrimEnd(Path.DirectorySeparatorChar);
|
||||
_rootPathFwd = EnsureUrlSeparatorChar(_rootPath);
|
||||
_rootUrl = EnsureUrlSeparatorChar(rootUrl).TrimEnd('/');
|
||||
@@ -328,7 +331,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
// nothing prevents us to reach the file, security-wise, yet it is outside
|
||||
// this filesystem's root - throw
|
||||
throw new UnauthorizedAccessException("File '" + opath + "' is outside this filesystem's root.");
|
||||
throw new UnauthorizedAccessException($"File original: [{opath}] full: [{path}] is outside this filesystem's root.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
public class SupportingFileSystems : TargetedServiceFactory<IFileSystem>
|
||||
{
|
||||
public SupportingFileSystems(IFactory factory)
|
||||
: base(factory)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
35
src/Umbraco.Core/Models/EmailMessage.cs
Normal file
35
src/Umbraco.Core/Models/EmailMessage.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public class EmailMessage
|
||||
{
|
||||
public string From { get; }
|
||||
public string To { get; }
|
||||
public string Subject { get; }
|
||||
public string Body { get; }
|
||||
public bool IsBodyHtml { get; }
|
||||
|
||||
public EmailMessage(string from, string to, string subject, string body, bool isBodyHtml)
|
||||
{
|
||||
if (from == null) throw new ArgumentNullException(nameof(from));
|
||||
if (from.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(from));
|
||||
|
||||
if (to == null) throw new ArgumentNullException(nameof(to));
|
||||
if (to.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(to));
|
||||
|
||||
if (subject == null) throw new ArgumentNullException(nameof(subject));
|
||||
if (subject.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(subject));
|
||||
|
||||
if (body == null) throw new ArgumentNullException(nameof(body));
|
||||
if (body.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(body));
|
||||
|
||||
From = from;
|
||||
To = to;
|
||||
Subject = subject;
|
||||
Body = body;
|
||||
|
||||
IsBodyHtml = isBodyHtml;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,13 +13,6 @@ namespace Umbraco.Core
|
||||
public static void Register<TService, TImplementing>(this IRegister register, Lifetime lifetime = Lifetime.Transient)
|
||||
=> register.Register(typeof(TService), typeof(TImplementing), lifetime);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service with an implementation type, for a target.
|
||||
/// </summary>
|
||||
public static void RegisterFor<TService, TImplementing, TTarget>(this IRegister register, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> register.RegisterFor<TService, TTarget>(typeof(TImplementing), lifetime);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service as its own implementation.
|
||||
/// </summary>
|
||||
@@ -33,12 +26,5 @@ namespace Umbraco.Core
|
||||
public static void Register<TService>(this IRegister register, TService instance)
|
||||
where TService : class
|
||||
=> register.Register(typeof(TService), instance);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a base type for auto-registration.
|
||||
/// </summary>
|
||||
public static void RegisterAuto<TServiceBase>(this IRegister register)
|
||||
where TServiceBase : class
|
||||
=> register.RegisterAuto(typeof(TServiceBase));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.8" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core.BackOffice;
|
||||
|
||||
namespace Umbraco.Infrastructure.BackOffice
|
||||
{
|
||||
public class BackOfficeIdentityBuilder : IdentityBuilder
|
||||
{
|
||||
public BackOfficeIdentityBuilder(IServiceCollection services) : base(typeof(BackOfficeIdentityUser), services)
|
||||
{
|
||||
}
|
||||
|
||||
public BackOfficeIdentityBuilder(Type role, IServiceCollection services) : base(typeof(BackOfficeIdentityUser), role, services)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a token provider for the <seealso cref="BackOfficeIdentityUser"/>.
|
||||
/// </summary>
|
||||
/// <param name="providerName">The name of the provider to add.</param>
|
||||
/// <param name="provider">The type of the <see cref="IUserTwoFactorTokenProvider{BackOfficeIdentityUser}"/> to add.</param>
|
||||
/// <returns>The current <see cref="IdentityBuilder"/> instance.</returns>
|
||||
public override IdentityBuilder AddTokenProvider(string providerName, Type provider)
|
||||
{
|
||||
if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).GetTypeInfo().IsAssignableFrom(provider.GetTypeInfo()))
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid Type for TokenProvider: {provider.FullName}");
|
||||
}
|
||||
Services.Configure<BackOfficeIdentityOptions>(options =>
|
||||
{
|
||||
options.Tokens.ProviderMap[providerName] = new TokenProviderDescriptor(provider);
|
||||
});
|
||||
Services.AddTransient(provider);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Events;
|
||||
@@ -23,13 +24,13 @@ namespace Umbraco.Core.Compose
|
||||
private readonly IIpResolver _ipResolver;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public AuditEventsComponent(IAuditService auditService, IUserService userService, IEntityService entityService, IIpResolver ipResolver, GlobalSettings globalSettings)
|
||||
public AuditEventsComponent(IAuditService auditService, IUserService userService, IEntityService entityService, IIpResolver ipResolver, IOptions<GlobalSettings> globalSettings)
|
||||
{
|
||||
_auditService = auditService;
|
||||
_userService = userService;
|
||||
_entityService = entityService;
|
||||
_ipResolver = ipResolver;
|
||||
_globalSettings = globalSettings;
|
||||
_globalSettings = globalSettings.Value;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
@@ -1,10 +1,10 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.IO.MediaPathSchemes;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.Core.Composing.CompositionExtensions
|
||||
{
|
||||
@@ -14,50 +14,9 @@ namespace Umbraco.Core.Composing.CompositionExtensions
|
||||
* HOW TO REPLACE THE MEDIA UNDERLYING FILESYSTEM
|
||||
* ----------------------------------------------
|
||||
*
|
||||
* Create a component and use it to modify the composition by adding something like:
|
||||
* composition.RegisterUnique<IMediaFileSystem>(factoryMethod);
|
||||
*
|
||||
* composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(...);
|
||||
*
|
||||
* and register whatever supporting filesystem you like.
|
||||
*
|
||||
*
|
||||
* HOW TO IMPLEMENT MY OWN FILESYSTEM
|
||||
* ----------------------------------
|
||||
*
|
||||
* Create your filesystem class:
|
||||
*
|
||||
* public class MyFileSystem : FileSystemWrapper
|
||||
* {
|
||||
* public MyFileSystem(IFileSystem innerFileSystem)
|
||||
* : base(innerFileSystem)
|
||||
* { }
|
||||
* }
|
||||
*
|
||||
* The ctor can have more parameters, that will be resolved by the container.
|
||||
*
|
||||
* Register your filesystem, in a component:
|
||||
*
|
||||
* composition.RegisterFileSystem<MyFileSystem>();
|
||||
*
|
||||
* Register the underlying filesystem:
|
||||
*
|
||||
* composition.RegisterUniqueFor<IFileSystem, MyFileSystem>(...);
|
||||
*
|
||||
* And that's it, you can inject MyFileSystem wherever it's needed.
|
||||
*
|
||||
*
|
||||
* You can also declare a filesystem interface:
|
||||
*
|
||||
* public interface IMyFileSystem : IFileSystem
|
||||
* { }
|
||||
*
|
||||
* Make the class implement the interface, then
|
||||
* register your filesystem, in a component:
|
||||
*
|
||||
* composition.RegisterFileSystem<IMyFileSystem, MyFileSystem>();
|
||||
* composition.RegisterUniqueFor<IFileSystem, IMyFileSystem>(...);
|
||||
*
|
||||
* And that's it, you can inject IMyFileSystem wherever it's needed.
|
||||
* composition.RegisterUnique<IMediaFileSystem, TImplementation>();
|
||||
*
|
||||
*
|
||||
* WHAT IS SHADOWING
|
||||
@@ -85,16 +44,8 @@ namespace Umbraco.Core.Composing.CompositionExtensions
|
||||
// register the scheme for media paths
|
||||
composition.RegisterUnique<IMediaPathScheme, UniqueMediaPathScheme>();
|
||||
|
||||
// register the IMediaFileSystem implementation
|
||||
composition.RegisterFileSystem<IMediaFileSystem, MediaFileSystem>();
|
||||
|
||||
// register the supporting filesystems provider
|
||||
composition.Register(factory => new SupportingFileSystems(factory), Lifetime.Singleton);
|
||||
|
||||
// register the IFileSystem supporting the IMediaFileSystem
|
||||
// THIS IS THE ONLY THING THAT NEEDS TO CHANGE, IN ORDER TO REPLACE THE UNDERLYING FILESYSTEM
|
||||
// and, SupportingFileSystem.For<IMediaFileSystem>() returns the underlying filesystem
|
||||
composition.SetMediaFileSystem(factory =>
|
||||
// register the default IMediaFileSystem implementation
|
||||
composition.RegisterUnique<IMediaFileSystem>(factory =>
|
||||
{
|
||||
var ioHelper = factory.GetInstance<IIOHelper>();
|
||||
var hostingEnvironment = factory.GetInstance<IHostingEnvironment>();
|
||||
@@ -103,7 +54,10 @@ namespace Umbraco.Core.Composing.CompositionExtensions
|
||||
|
||||
var rootPath = hostingEnvironment.MapPathWebRoot(globalSettings.UmbracoMediaPath);
|
||||
var rootUrl = hostingEnvironment.ToAbsolute(globalSettings.UmbracoMediaPath);
|
||||
return new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, rootPath, rootUrl);
|
||||
var inner = new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, rootPath, rootUrl);
|
||||
|
||||
var fileSystems = factory.GetInstance<IO.FileSystems>();
|
||||
return fileSystems.GetFileSystem<MediaFileSystem>(inner);
|
||||
});
|
||||
|
||||
return composition;
|
||||
|
||||
@@ -12,19 +12,19 @@ namespace Umbraco.Web.Composing.CompositionExtensions
|
||||
{
|
||||
// register the installer steps
|
||||
|
||||
composition.Register<NewInstallStep>(Lifetime.Scope);
|
||||
composition.Register<UpgradeStep>(Lifetime.Scope);
|
||||
composition.Register<FilePermissionsStep>(Lifetime.Scope);
|
||||
composition.Register<DatabaseConfigureStep>(Lifetime.Scope);
|
||||
composition.Register<DatabaseInstallStep>(Lifetime.Scope);
|
||||
composition.Register<DatabaseUpgradeStep>(Lifetime.Scope);
|
||||
composition.Register<InstallSetupStep,NewInstallStep>(Lifetime.Scope);
|
||||
composition.Register<InstallSetupStep,UpgradeStep>(Lifetime.Scope);
|
||||
composition.Register<InstallSetupStep,FilePermissionsStep>(Lifetime.Scope);
|
||||
composition.Register<InstallSetupStep,DatabaseConfigureStep>(Lifetime.Scope);
|
||||
composition.Register<InstallSetupStep,DatabaseInstallStep>(Lifetime.Scope);
|
||||
composition.Register<InstallSetupStep,DatabaseUpgradeStep>(Lifetime.Scope);
|
||||
|
||||
// TODO: Add these back once we have a compatible Starter kit
|
||||
// composition.Register<StarterKitDownloadStep>(Lifetime.Scope);
|
||||
// composition.Register<StarterKitInstallStep>(Lifetime.Scope);
|
||||
// composition.Register<StarterKitCleanupStep>(Lifetime.Scope);
|
||||
// composition.Register<InstallSetupStep,StarterKitDownloadStep>(Lifetime.Scope);
|
||||
// composition.Register<InstallSetupStep,StarterKitInstallStep>(Lifetime.Scope);
|
||||
// composition.Register<InstallSetupStep,StarterKitCleanupStep>(Lifetime.Scope);
|
||||
|
||||
composition.Register<CompleteInstallStep>(Lifetime.Scope);
|
||||
composition.Register<InstallSetupStep,CompleteInstallStep>(Lifetime.Scope);
|
||||
|
||||
composition.Register<InstallStepCollection>();
|
||||
composition.RegisterUnique<InstallHelper>();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
@@ -15,17 +14,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <returns></returns>
|
||||
public static IHostBuilder UseUmbraco(this IHostBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.UseUmbraco(new UmbracoServiceProviderFactory());
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a custom service provider factory to use Umbraco's container
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <param name="umbracoServiceProviderFactory"></param>
|
||||
/// <returns></returns>
|
||||
public static IHostBuilder UseUmbraco(this IHostBuilder builder, UmbracoServiceProviderFactory umbracoServiceProviderFactory)
|
||||
=> builder.UseServiceProviderFactory(umbracoServiceProviderFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,32 +183,6 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
public void Register(Type serviceType, object instance)
|
||||
=> Container.RegisterInstance(serviceType, instance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> RegisterFor<TService, TTarget>(typeof(TService), lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
{
|
||||
// note that there can only be one implementation or instance registered "for" a service
|
||||
Container.Register(typeof(TService), implementingType, GetTargetedServiceName<TTarget>(), GetLifetime(lifetime));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
{
|
||||
// note that there can only be one implementation or instance registered "for" a service
|
||||
Container.Register(f => factory(this), GetTargetedServiceName<TTarget>(), GetLifetime(lifetime));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(TService instance)
|
||||
where TService : class
|
||||
=> Container.RegisterInstance(typeof(TService), instance, GetTargetedServiceName<TTarget>());
|
||||
|
||||
private ILifetime GetLifetime(Lifetime lifetime)
|
||||
{
|
||||
switch (lifetime)
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Infrastructure.Composing
|
||||
{
|
||||
public class ServiceCollectionRegistryAdapter : IRegister
|
||||
{
|
||||
private readonly IServiceCollection _services;
|
||||
|
||||
public ServiceCollectionRegistryAdapter(IServiceCollection services)
|
||||
{
|
||||
_services = services ?? throw new ArgumentNullException(nameof(services));
|
||||
_services.AddTransient(typeof(Lazy<>), typeof(LazyResolve<>));
|
||||
}
|
||||
|
||||
public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient)
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case Lifetime.Request:
|
||||
case Lifetime.Scope:
|
||||
_services.AddScoped(serviceType);
|
||||
break;
|
||||
case Lifetime.Transient:
|
||||
_services.AddTransient(serviceType);
|
||||
break;
|
||||
case Lifetime.Singleton:
|
||||
_services.AddSingleton(serviceType);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Unhandled Lifetime: {lifetime}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient)
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case Lifetime.Request:
|
||||
case Lifetime.Scope:
|
||||
_services.AddScoped(serviceType, implementingType);
|
||||
break;
|
||||
case Lifetime.Transient:
|
||||
_services.AddTransient(serviceType, implementingType);
|
||||
break;
|
||||
case Lifetime.Singleton:
|
||||
_services.AddSingleton(serviceType, implementingType);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Unhandled Lifetime: {lifetime}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Register<TService>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient) where TService : class
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case Lifetime.Request:
|
||||
case Lifetime.Scope:
|
||||
_services.AddScoped(sp => factory(ServiceProviderFactoryAdapter.Wrap(sp)));
|
||||
break;
|
||||
case Lifetime.Transient:
|
||||
_services.AddTransient(sp => factory(ServiceProviderFactoryAdapter.Wrap(sp)));
|
||||
break;
|
||||
case Lifetime.Singleton:
|
||||
_services.AddSingleton(sp => factory(ServiceProviderFactoryAdapter.Wrap(sp)));
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Unhandled Lifetime: {lifetime}");
|
||||
}
|
||||
}
|
||||
public void Register(Type serviceType, object instance)
|
||||
{
|
||||
_services.AddSingleton(serviceType, instance);
|
||||
}
|
||||
|
||||
public IFactory CreateFactory()
|
||||
{
|
||||
return ServiceProviderFactoryAdapter.Wrap(_services.BuildServiceProvider());
|
||||
}
|
||||
}
|
||||
|
||||
public class LazyResolve<T> : Lazy<T>
|
||||
where T : class
|
||||
{
|
||||
public LazyResolve(IServiceProvider serviceProvider) : base(serviceProvider.GetRequiredService<T>)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Infrastructure.Composing
|
||||
{
|
||||
internal class ServiceProviderFactoryAdapter : IFactory
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
private ServiceProviderFactoryAdapter(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public object Concrete => _serviceProvider;
|
||||
|
||||
public object GetInstance(Type type)
|
||||
{
|
||||
return _serviceProvider.GetRequiredService(type);
|
||||
}
|
||||
|
||||
public object TryGetInstance(Type type)
|
||||
{
|
||||
return _serviceProvider.GetService(type);
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetAllInstances(Type serviceType)
|
||||
{
|
||||
return _serviceProvider.GetServices(serviceType);
|
||||
}
|
||||
|
||||
public IEnumerable<TService> GetAllInstances<TService>() where TService : class
|
||||
{
|
||||
return _serviceProvider.GetServices<TService>();
|
||||
}
|
||||
|
||||
public IDisposable BeginScope()
|
||||
{
|
||||
return _serviceProvider.CreateScope();
|
||||
}
|
||||
|
||||
public static IFactory Wrap(IServiceProvider serviceProvider)
|
||||
{
|
||||
return new ServiceProviderFactoryAdapter(serviceProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
using LightInject;
|
||||
using LightInject.Microsoft.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Composing;
|
||||
using Umbraco.Core.Composing.LightInject;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to create Umbraco's container and cross-wire it up before the applicaton starts
|
||||
/// </summary>
|
||||
public class UmbracoServiceProviderFactory : IServiceProviderFactory<IServiceContainer>
|
||||
{
|
||||
public UmbracoServiceProviderFactory(ServiceContainer container, bool initializeCurrent)
|
||||
{
|
||||
_container = new LightInjectContainer(container);
|
||||
_initializeCurrent = initializeCurrent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an ASP.NET Core compatible service container
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static ServiceContainer CreateServiceContainer() => new ServiceContainer(
|
||||
ContainerOptions.Default.Clone()
|
||||
.WithMicrosoftSettings()
|
||||
//.WithAspNetCoreSettings() //TODO WithAspNetCoreSettings changes behavior that we need to discuss
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Default ctor for use in Host Builder configuration
|
||||
/// </summary>
|
||||
public UmbracoServiceProviderFactory()
|
||||
{
|
||||
var container = CreateServiceContainer();
|
||||
UmbracoContainer = _container = new LightInjectContainer(container);
|
||||
IsActive = true;
|
||||
_initializeCurrent = true;
|
||||
}
|
||||
|
||||
// see here for orig lightinject version https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/blob/412566e3f70625e6b96471db5e1f7cd9e3e1eb18/src/LightInject.Microsoft.DependencyInjection/LightInject.Microsoft.DependencyInjection.cs#L263
|
||||
// we don't really need all that, we're manually creating our container with the correct options and that
|
||||
// is what we'll return in CreateBuilder
|
||||
|
||||
IServiceCollection _services;
|
||||
readonly LightInjectContainer _container;
|
||||
private readonly bool _initializeCurrent;
|
||||
|
||||
internal LightInjectContainer GetContainer() => _container;
|
||||
|
||||
/// <summary>
|
||||
/// When the empty ctor is used this returns if this factory is active
|
||||
/// </summary>
|
||||
public static bool IsActive { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// When the empty ctor is used this returns the created IRegister
|
||||
/// </summary>
|
||||
public static IRegister UmbracoContainer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create the container with the required settings for aspnetcore3
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
public IServiceContainer CreateBuilder(IServiceCollection services)
|
||||
{
|
||||
_services = services;
|
||||
return _container.Container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This cross-wires the container just before the application calls "Configure"
|
||||
/// </summary>
|
||||
/// <param name="containerBuilder"></param>
|
||||
/// <returns></returns>
|
||||
public IServiceProvider CreateServiceProvider(IServiceContainer containerBuilder)
|
||||
{
|
||||
var provider = containerBuilder.CreateServiceProvider(_services);
|
||||
|
||||
if (_initializeCurrent)
|
||||
{
|
||||
// after cross wiring, configure "Current"
|
||||
Current.Initialize(
|
||||
_container.GetInstance<ILogger<object>>(),
|
||||
_container.GetInstance<IOptions<SecuritySettings>>().Value,
|
||||
_container.GetInstance<IOptions<GlobalSettings>>().Value,
|
||||
_container.GetInstance<IIOHelper>(),
|
||||
_container.GetInstance<Umbraco.Core.Hosting.IHostingEnvironment>(),
|
||||
_container.GetInstance<IBackOfficeInfo>(),
|
||||
_container.GetInstance<Umbraco.Core.Logging.IProfiler>());
|
||||
}
|
||||
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -282,22 +282,6 @@ namespace Umbraco.Core
|
||||
composition.RegisterUnique(_ => helper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the underlying media filesystem.
|
||||
/// </summary>
|
||||
/// <param name="composition">A composition.</param>
|
||||
/// <param name="filesystemFactory">A filesystem factory.</param>
|
||||
public static void SetMediaFileSystem(this Composition composition, Func<IFactory, IFileSystem> filesystemFactory)
|
||||
=> composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(filesystemFactory);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the underlying media filesystem.
|
||||
/// </summary>
|
||||
/// <param name="composition">A composition.</param>
|
||||
/// <param name="filesystemFactory">A filesystem factory.</param>
|
||||
public static void SetMediaFileSystem(this Composition composition, Func<IFileSystem> filesystemFactory)
|
||||
=> composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(_ => filesystemFactory());
|
||||
|
||||
/// <summary>
|
||||
/// Sets the log viewer.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods to the <see cref="Composition"/> class.
|
||||
/// </summary>
|
||||
public static partial class CompositionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers a filesystem.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
|
||||
/// <typeparam name="TImplementing">The implementing type.</typeparam>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <returns>The register.</returns>
|
||||
public static void RegisterFileSystem<TFileSystem, TImplementing>(this Composition composition)
|
||||
where TImplementing : FileSystemWrapper, TFileSystem
|
||||
where TFileSystem : class
|
||||
{
|
||||
composition.RegisterUnique<TFileSystem>(factory =>
|
||||
{
|
||||
var fileSystems = factory.GetInstance<FileSystems>();
|
||||
var supporting = factory.GetInstance<SupportingFileSystems>();
|
||||
return fileSystems.GetFileSystem<TImplementing>(supporting.For<TFileSystem>());
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a filesystem.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <returns>The register.</returns>
|
||||
public static void RegisterFileSystem<TFileSystem>(this Composition composition)
|
||||
where TFileSystem : FileSystemWrapper
|
||||
{
|
||||
composition.RegisterUnique(factory =>
|
||||
{
|
||||
var fileSystems = factory.GetInstance<FileSystems>();
|
||||
var supporting = factory.GetInstance<SupportingFileSystems>();
|
||||
return fileSystems.GetFileSystem<TFileSystem>(supporting.For<TFileSystem>());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using HeyRed.MarkdownSharp;
|
||||
using Umbraco.Composing;
|
||||
using Umbraco.Core.Configuration.HealthChecks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Composing;
|
||||
using Umbraco.Core.HealthCheck;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
namespace Umbraco.Infrastructure.HealthCheck
|
||||
{
|
||||
public class HealthCheckResults
|
||||
{
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Web.HealthCheck
|
||||
|
||||
private ILogger Logger => Current.Logger; // TODO: inject
|
||||
|
||||
public HealthCheckResults(IEnumerable<HealthCheck> checks)
|
||||
public HealthCheckResults(IEnumerable<Core.HealthCheck.HealthCheck> checks)
|
||||
{
|
||||
_results = checks.ToDictionary(
|
||||
t => t.Name,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Net.Mail;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.HealthChecks;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.HealthCheck;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Infrastructure.HealthCheck;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.NotificationMethods
|
||||
{
|
||||
@@ -71,23 +71,19 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods
|
||||
var subject = _textService.Localize("healthcheck/scheduledHealthCheckEmailSubject", new[] { host.ToString() });
|
||||
|
||||
var mailSender = new EmailSender(Options.Create(_globalSettings));
|
||||
using (var mailMessage = CreateMailMessage(subject, message))
|
||||
{
|
||||
await mailSender.SendAsync(mailMessage);
|
||||
}
|
||||
var mailMessage = CreateMailMessage(subject, message);
|
||||
await mailSender.SendAsync(mailMessage);
|
||||
}
|
||||
|
||||
private MailMessage CreateMailMessage(string subject, string message)
|
||||
private EmailMessage CreateMailMessage(string subject, string message)
|
||||
{
|
||||
var to = _contentSettings.Notifications.Email;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(subject))
|
||||
subject = "Umbraco Health Check Status";
|
||||
|
||||
return new MailMessage(to, RecipientEmail, subject, message)
|
||||
{
|
||||
IsBodyHtml = message.IsNullOrWhiteSpace() == false && message.Contains("<") && message.Contains("</")
|
||||
};
|
||||
var isBodyHtml = message.IsNullOrWhiteSpace() == false && message.Contains("<") && message.Contains("</");
|
||||
return new EmailMessage(to, RecipientEmail, subject, message, isBodyHtml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Infrastructure.HealthCheck;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.NotificationMethods
|
||||
{
|
||||
|
||||
@@ -3,8 +3,10 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Configuration.HealthChecks;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.HealthCheck;
|
||||
using Umbraco.Core.HealthCheck.Checks;
|
||||
using Umbraco.Infrastructure.HealthCheck;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.NotificationMethods
|
||||
{
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
[MapperFor(typeof(PublicAccessEntry))]
|
||||
public sealed class AccessMapper : BaseMapper
|
||||
{
|
||||
public AccessMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public AccessMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -12,7 +13,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(AuditEntry))]
|
||||
public sealed class AuditEntryMapper : BaseMapper
|
||||
{
|
||||
public AuditEntryMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public AuditEntryMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -9,7 +10,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IAuditItem))]
|
||||
public sealed class AuditItemMapper : BaseMapper
|
||||
{
|
||||
public AuditItemMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public AuditItemMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -15,12 +16,12 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
|
||||
private readonly Lazy<ISqlContext> _sqlContext;
|
||||
private readonly object _definedLock = new object();
|
||||
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> _maps;
|
||||
private readonly MapperConfigurationStore _maps;
|
||||
|
||||
private ISqlSyntaxProvider _sqlSyntax;
|
||||
private bool _defined;
|
||||
|
||||
protected BaseMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
protected BaseMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
{
|
||||
_sqlContext = sqlContext;
|
||||
_maps = maps;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -12,7 +13,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(Consent))]
|
||||
public sealed class ConsentMapper : BaseMapper
|
||||
{
|
||||
public ConsentMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public ConsentMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IContent))]
|
||||
public sealed class ContentMapper : BaseMapper
|
||||
{
|
||||
public ContentMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public ContentMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IContentType))]
|
||||
public sealed class ContentTypeMapper : BaseMapper
|
||||
{
|
||||
public ContentTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public ContentTypeMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IDataType))]
|
||||
public sealed class DataTypeMapper : BaseMapper
|
||||
{
|
||||
public DataTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public DataTypeMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IDictionaryItem))]
|
||||
public sealed class DictionaryMapper : BaseMapper
|
||||
{
|
||||
public DictionaryMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public DictionaryMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IDictionaryTranslation))]
|
||||
public sealed class DictionaryTranslationMapper : BaseMapper
|
||||
{
|
||||
public DictionaryTranslationMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public DictionaryTranslationMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -9,7 +10,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(UmbracoDomain))]
|
||||
public sealed class DomainMapper : BaseMapper
|
||||
{
|
||||
public DomainMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public DomainMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -9,7 +10,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IdentityUserLogin))]
|
||||
public sealed class ExternalLoginMapper : BaseMapper
|
||||
{
|
||||
public ExternalLoginMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public ExternalLoginMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(Language))]
|
||||
public sealed class LanguageMapper : BaseMapper
|
||||
{
|
||||
public LanguageMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public LanguageMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -9,7 +10,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(IMacro))]
|
||||
internal sealed class MacroMapper : BaseMapper
|
||||
{
|
||||
public MacroMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public MacroMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
public class MapperCollectionBuilder : SetCollectionBuilderBase<MapperCollectionBuilder, MapperCollection, BaseMapper>
|
||||
{
|
||||
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> _maps
|
||||
= new ConcurrentDictionary<Type, ConcurrentDictionary<string, string>>();
|
||||
|
||||
protected override MapperCollectionBuilder This => this;
|
||||
|
||||
public override void RegisterWith(IRegister register)
|
||||
@@ -21,14 +19,10 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
// we want to register extra
|
||||
// - service IMapperCollection, returns MappersCollectionBuilder's collection
|
||||
|
||||
register.Register<MapperConfigurationStore>(Lifetime.Singleton);
|
||||
register.Register<IMapperCollection>(factory => factory.GetInstance<MapperCollection>());
|
||||
}
|
||||
|
||||
protected override BaseMapper CreateItem(IFactory factory, Type itemType)
|
||||
{
|
||||
return (BaseMapper) factory.CreateInstance(itemType, _maps);
|
||||
}
|
||||
|
||||
public MapperCollectionBuilder AddCoreMappers()
|
||||
{
|
||||
Add<AccessMapper>();
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Umbraco.Infrastructure.Persistence.Mappers
|
||||
{
|
||||
public class MapperConfigurationStore : ConcurrentDictionary<Type, ConcurrentDictionary<string, string>>
|
||||
{ }
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(Umbraco.Core.Models.Media))]
|
||||
public sealed class MediaMapper : BaseMapper
|
||||
{
|
||||
public MediaMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public MediaMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(MediaType))]
|
||||
public sealed class MediaTypeMapper : BaseMapper
|
||||
{
|
||||
public MediaTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public MediaTypeMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -9,7 +10,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof (MemberGroup))]
|
||||
public sealed class MemberGroupMapper : BaseMapper
|
||||
{
|
||||
public MemberGroupMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public MemberGroupMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -14,7 +15,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(Member))]
|
||||
public sealed class MemberMapper : BaseMapper
|
||||
{
|
||||
public MemberMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public MemberMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof (IMemberType))]
|
||||
public sealed class MemberTypeMapper : BaseMapper
|
||||
{
|
||||
public MemberTypeMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public MemberTypeMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
@@ -12,7 +13,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
[MapperFor(typeof(PropertyGroup))]
|
||||
public sealed class PropertyGroupMapper : BaseMapper
|
||||
{
|
||||
public PropertyGroupMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public PropertyGroupMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
[MapperFor(typeof(Property))]
|
||||
public sealed class PropertyMapper : BaseMapper
|
||||
{
|
||||
public PropertyMapper(Lazy<ISqlContext> sqlContext, ConcurrentDictionary<Type, ConcurrentDictionary<string, string>> maps)
|
||||
public PropertyMapper(Lazy<ISqlContext> sqlContext, MapperConfigurationStore maps)
|
||||
: base(sqlContext, maps)
|
||||
{ }
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user