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

# Conflicts:
#	src/Umbraco.Abstractions/Configuration/ConfigsExtensions.cs
#	src/Umbraco.Configuration/GlobalSettings.cs
#	src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs
#	src/Umbraco.Tests/Persistence/Repositories/StylesheetRepositoryTest.cs
#	src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs
#	src/Umbraco.Tests/PublishedContent/NuCacheTests.cs
#	src/Umbraco.Tests/TestHelpers/TestHelper.cs
This commit is contained in:
Bjarke Berg
2019-11-19 08:13:35 +01:00
279 changed files with 1557 additions and 1190 deletions

View File

@@ -5,9 +5,9 @@
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)" xdt:Transform="Remove" /> <sectionGroup name="applicationSettings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<sectionGroup name="system.web.webPages.razor" xdt:Locator="Match(name)" xdt:Transform="Remove" /> <sectionGroup name="system.web.webPages.razor" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<sectionGroup name="umbracoConfiguration" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing"> <sectionGroup name="umbracoConfiguration" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing">
<section name="settings" type="Umbraco.Core.Configuration.UmbracoSettings.UmbracoSettingsSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" /> <section name="settings" type="Umbraco.Core.Configuration.UmbracoSettings.UmbracoSettingsSection, Umbraco.Configuration" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="dashBoard" xdt:Locator="Match(name)" xdt:Transform="Remove" /> <section name="dashBoard" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<section name="HealthChecks" type="Umbraco.Core.Configuration.HealthChecks.HealthChecksSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" /> <section name="HealthChecks" type="Umbraco.Core.Configuration.HealthChecks.HealthChecksSection, Umbraco.Configuration" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</sectionGroup> </sectionGroup>
</configSections> </configSections>

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Entities;
@@ -80,6 +81,7 @@ namespace Umbraco.Core.Collections
} }
} }
#region IRememberBeingDirty
public bool IsDirty() public bool IsDirty()
{ {
return this.OfType<IRememberBeingDirty>().Any(x => x.IsDirty()); return this.OfType<IRememberBeingDirty>().Any(x => x.IsDirty());
@@ -150,5 +152,8 @@ namespace Umbraco.Core.Collections
{ {
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
} }
public event PropertyChangedEventHandler PropertyChanged; // noop
#endregion
} }
} }

View File

@@ -27,19 +27,12 @@ namespace Umbraco.Core.Composing
/// <param name="logger">A logger.</param> /// <param name="logger">A logger.</param>
/// <param name="runtimeState">The runtime state.</param> /// <param name="runtimeState">The runtime state.</param>
/// <param name="configs">Optional configs.</param> /// <param name="configs">Optional configs.</param>
public Composition(IRegister register, TypeLoader typeLoader, IProfilingLogger logger, IRuntimeState runtimeState, Configs configs = null) public Composition(IRegister register, TypeLoader typeLoader, IProfilingLogger logger, IRuntimeState runtimeState, Configs configs)
{ {
_register = register; _register = register;
TypeLoader = typeLoader; TypeLoader = typeLoader;
Logger = logger; Logger = logger;
RuntimeState = runtimeState; RuntimeState = runtimeState;
if (configs == null)
{
configs = new Configs();
configs.AddCoreConfigs();
}
Configs = configs; Configs = configs;
} }
@@ -131,12 +124,14 @@ namespace Umbraco.Core.Composing
builder.RegisterWith(_register); builder.RegisterWith(_register);
_builders.Clear(); // no point keep them around _builders.Clear(); // no point keep them around
Configs.RegisterWith(_register);
IFactory factory = null; IFactory factory = null;
Configs.RegisterWith(_register, () => factory);
// ReSharper disable once AccessToModifiedClosure -- on purpose // ReSharper disable once AccessToModifiedClosure -- on purpose
_register.Register(_ => factory, Lifetime.Singleton); _register.Register(_ => factory, Lifetime.Singleton);
factory = _register.CreateFactory(); factory = _register.CreateFactory();
return factory; return factory;
} }

View File

@@ -0,0 +1,40 @@
using System;
using System.IO;
using System.Linq;
using Umbraco.Core.Configuration;
namespace Umbraco.Core
{
public static class ConfigConnectionStringExtensions
{
public static bool IsConnectionStringConfigured(this ConfigConnectionString databaseSettings)
{
var dbIsSqlCe = false;
if (databaseSettings?.ProviderName != null)
dbIsSqlCe = databaseSettings.ProviderName == Constants.DbProviderNames.SqlCe;
var sqlCeDatabaseExists = false;
if (dbIsSqlCe)
{
var parts = databaseSettings.ConnectionString.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
var dataSourcePart = parts.FirstOrDefault(x => x.InvariantStartsWith("Data Source="));
if (dataSourcePart != null)
{
var datasource = dataSourcePart.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString());
var filePath = datasource.Replace("Data Source=", string.Empty);
sqlCeDatabaseExists = File.Exists(filePath);
}
}
// Either the connection details are not fully specified or it's a SQL CE database that doesn't exist yet
if (databaseSettings == null
|| string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) || string.IsNullOrWhiteSpace(databaseSettings.ProviderName)
|| (dbIsSqlCe && sqlCeDatabaseExists == false))
{
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,16 @@
namespace Umbraco.Core.Configuration
{
public class ConfigConnectionString
{
public ConfigConnectionString(string connectionString, string providerName, string name)
{
ConnectionString = connectionString;
ProviderName = providerName;
Name = name;
}
public string ConnectionString { get; }
public string ProviderName { get; }
public string Name { get; }
}
}

View File

@@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Configuration;
using Umbraco.Core.Composing; using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Configuration namespace Umbraco.Core.Configuration
{ {
@@ -15,8 +13,16 @@ namespace Umbraco.Core.Configuration
/// </remarks> /// </remarks>
public class Configs public class Configs
{ {
private readonly Func<string, object> _configSectionResolver;
public Configs(Func<string, object> configSectionResolver)
{
_configSectionResolver = configSectionResolver ?? throw new ArgumentNullException(nameof(configSectionResolver));
}
private readonly Dictionary<Type, Lazy<object>> _configs = new Dictionary<Type, Lazy<object>>(); private readonly Dictionary<Type, Lazy<object>> _configs = new Dictionary<Type, Lazy<object>>();
private Dictionary<Type, Action<IRegister>> _registerings = new Dictionary<Type, Action<IRegister>>(); private Dictionary<Type, Action<IRegister>> _registerings = new Dictionary<Type, Action<IRegister>>();
private Lazy<IFactory> _factory;
/// <summary> /// <summary>
/// Gets a configuration. /// Gets a configuration.
@@ -60,7 +66,7 @@ namespace Umbraco.Core.Configuration
_configs[typeOfConfig] = new Lazy<object>(() => _configs[typeOfConfig] = new Lazy<object>(() =>
{ {
if (Current.HasFactory) return Current.Factory.GetInstance<TConfig>(); if (!(_factory is null)) return _factory.Value.GetInstance<TConfig>();
throw new InvalidOperationException($"Cannot get configuration of type {typeOfConfig} during composition."); throw new InvalidOperationException($"Cannot get configuration of type {typeOfConfig} during composition.");
}); });
_registerings[typeOfConfig] = register => register.Register(configFactory, Lifetime.Singleton); _registerings[typeOfConfig] = register => register.Register(configFactory, Lifetime.Singleton);
@@ -75,7 +81,7 @@ namespace Umbraco.Core.Configuration
Add(() => GetConfig<TConfig>(sectionName)); Add(() => GetConfig<TConfig>(sectionName));
} }
private static TConfig GetConfig<TConfig>(string sectionName) private TConfig GetConfig<TConfig>(string sectionName)
where TConfig : class where TConfig : class
{ {
// note: need to use SafeCallContext here because ConfigurationManager.GetSection ends up getting AppDomain.Evidence // note: need to use SafeCallContext here because ConfigurationManager.GetSection ends up getting AppDomain.Evidence
@@ -83,10 +89,9 @@ namespace Umbraco.Core.Configuration
using (new SafeCallContext()) using (new SafeCallContext())
{ {
if ((ConfigurationManager.GetSection(sectionName) is TConfig config)) if ((_configSectionResolver(sectionName) is TConfig config))
return config; return config;
var ex = new ConfigurationErrorsException($"Could not get configuration section \"{sectionName}\" from config files."); var ex = new InvalidOperationException($"Could not get configuration section \"{sectionName}\" from config files.");
Current.Logger.Error<Configs>(ex, "Config error");
throw ex; throw ex;
} }
} }
@@ -94,12 +99,14 @@ namespace Umbraco.Core.Configuration
/// <summary> /// <summary>
/// Registers configurations in a register. /// Registers configurations in a register.
/// </summary> /// </summary>
public void RegisterWith(IRegister register) public void RegisterWith(IRegister register, Func<IFactory> factory)
{ {
// do it only once // do it only once
if (_registerings == null) if (_registerings == null)
throw new InvalidOperationException("Configurations have already been registered."); throw new InvalidOperationException("Configurations have already been registered.");
_factory = new Lazy<IFactory>(factory);
register.Register(this); register.Register(this);
foreach (var registering in _registerings.Values) foreach (var registering in _registerings.Values)

View File

@@ -19,6 +19,9 @@ namespace Umbraco.Core
public static IGlobalSettings Global(this Configs configs) public static IGlobalSettings Global(this Configs configs)
=> configs.GetConfig<IGlobalSettings>(); => configs.GetConfig<IGlobalSettings>();
public static IConnectionStrings ConnectionStrings(this Configs configs)
=> configs.GetConfig<IConnectionStrings>();
public static IUmbracoSettingsSection Settings(this Configs configs) public static IUmbracoSettingsSection Settings(this Configs configs)
=> configs.GetConfig<IUmbracoSettingsSection>(); => configs.GetConfig<IUmbracoSettingsSection>();
@@ -28,25 +31,20 @@ namespace Umbraco.Core
public static IGridConfig Grids(this Configs configs) public static IGridConfig Grids(this Configs configs)
=> configs.GetConfig<IGridConfig>(); => configs.GetConfig<IGridConfig>();
internal static CoreDebug CoreDebug(this Configs configs) public static ICoreDebug CoreDebug(this Configs configs)
=> configs.GetConfig<CoreDebug>(); => configs.GetConfig<ICoreDebug>();
public static void AddCoreConfigs(this Configs configs) public static void AddCoreConfigs(this Configs configs, IIOHelper ioHelper)
{ {
var configDir = new DirectoryInfo(Current.IOHelper.MapPath(Constants.SystemDirectories.Config)); var configDir = new DirectoryInfo(ioHelper.MapPath(Constants.SystemDirectories.Config));
configs.Add<IGlobalSettings>(() => new GlobalSettings(Current.IOHelper));
configs.Add<IUmbracoSettingsSection>("umbracoConfiguration/settings");
configs.Add<IHealthChecks>("umbracoConfiguration/HealthChecks");
configs.Add(() => new CoreDebug());
// GridConfig depends on runtime caches, manifest parsers... and cannot be available during composition // GridConfig depends on runtime caches, manifest parsers... and cannot be available during composition
configs.Add<IGridConfig>(factory => new GridConfig( configs.Add<IGridConfig>(factory => new GridConfig(
factory.GetInstance<ILogger>(), factory.GetInstance<ILogger>(),
factory.GetInstance<AppCaches>(), factory.GetInstance<AppCaches>(),
configDir, configDir,
factory.GetInstance<ManifestParser>(), factory.GetInstance<IManifestParser>(),
factory.GetInstance<IRuntimeState>().Debug)); factory.GetInstance<IRuntimeState>().Debug));
} }
} }

View File

@@ -0,0 +1,45 @@
using System;
using Umbraco.Core.IO;
namespace Umbraco.Core.Configuration
{
public static class GlobalSettingsExtensions
{
private static string _mvcArea;
/// <summary>
/// This returns the string of the MVC Area route.
/// </summary>
/// <remarks>
/// This will return the MVC area that we will route all custom routes through like surface controllers, etc...
/// We will use the 'Path' (default ~/umbraco) to create it but since it cannot contain '/' and people may specify a path of ~/asdf/asdf/admin
/// we will convert the '/' to '-' and use that as the path. its a bit lame but will work.
///
/// We also make sure that the virtual directory (SystemDirectories.Root) is stripped off first, otherwise we'd end up with something
/// like "MyVirtualDirectory-Umbraco" instead of just "Umbraco".
/// </remarks>
public static string GetUmbracoMvcArea(this IGlobalSettings globalSettings, IIOHelper ioHelper)
{
if (_mvcArea != null) return _mvcArea;
_mvcArea = GetUmbracoMvcAreaNoCache(globalSettings, ioHelper);
return _mvcArea;
}
internal static string GetUmbracoMvcAreaNoCache(this IGlobalSettings globalSettings, IIOHelper ioHelper)
{
if (globalSettings.Path.IsNullOrWhiteSpace())
{
throw new InvalidOperationException("Cannot create an MVC Area path without the umbracoPath specified");
}
var path = globalSettings.Path;
if (path.StartsWith(ioHelper.Root)) // beware of TrimStart, see U4-2518
path = path.Substring(ioHelper.Root.Length);
return path.TrimStart('~').TrimStart('/').Replace('/', '-').Trim().ToLower();
}
}
}

View File

@@ -5,9 +5,9 @@ using Umbraco.Core.Manifest;
namespace Umbraco.Core.Configuration.Grid namespace Umbraco.Core.Configuration.Grid
{ {
class GridConfig : IGridConfig public class GridConfig : IGridConfig
{ {
public GridConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, ManifestParser manifestParser, bool isDebug) public GridConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, IManifestParser manifestParser, bool isDebug)
{ {
EditorsConfig = new GridEditorsConfig(logger, appCaches, configFolder, manifestParser, isDebug); EditorsConfig = new GridEditorsConfig(logger, appCaches, configFolder, manifestParser, isDebug);
} }

View File

@@ -13,10 +13,10 @@ namespace Umbraco.Core.Configuration.Grid
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly AppCaches _appCaches; private readonly AppCaches _appCaches;
private readonly DirectoryInfo _configFolder; private readonly DirectoryInfo _configFolder;
private readonly ManifestParser _manifestParser; private readonly IManifestParser _manifestParser;
private readonly bool _isDebug; private readonly bool _isDebug;
public GridEditorsConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, ManifestParser manifestParser, bool isDebug) public GridEditorsConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, IManifestParser manifestParser, bool isDebug)
{ {
_logger = logger; _logger = logger;
_appCaches = appCaches; _appCaches = appCaches;
@@ -29,9 +29,9 @@ namespace Umbraco.Core.Configuration.Grid
{ {
get get
{ {
List<GridEditor> GetResult() List<IGridEditorConfig> GetResult()
{ {
var editors = new List<GridEditor>(); var editors = new List<IGridEditorConfig>();
var gridConfig = Path.Combine(_configFolder.FullName, "grid.editors.config.js"); var gridConfig = Path.Combine(_configFolder.FullName, "grid.editors.config.js");
if (File.Exists(gridConfig)) if (File.Exists(gridConfig))
{ {
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Configuration.Grid
//cache the result if debugging is disabled //cache the result if debugging is disabled
var result = _isDebug var result = _isDebug
? GetResult() ? GetResult()
: _appCaches.RuntimeCache.GetCacheItem<List<GridEditor>>(typeof(GridEditorsConfig) + ".Editors",GetResult, TimeSpan.FromMinutes(10)); : _appCaches.RuntimeCache.GetCacheItem<List<IGridEditorConfig>>(typeof(GridEditorsConfig) + ".Editors",GetResult, TimeSpan.FromMinutes(10));
return result; return result;
} }

View File

@@ -1,7 +1,10 @@
namespace Umbraco.Core.Configuration.HealthChecks using System.Runtime.Serialization;
namespace Umbraco.Core.Configuration.HealthChecks
{ {
public enum HealthCheckNotificationVerbosity public enum HealthCheckNotificationVerbosity
{ {
Summary, Summary,
Detailed Detailed
} }

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Core.Configuration
{
public interface IConfigsFactory
{
Configs Create();
}
}

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core.Configuration
{
public interface IConnectionStrings
{
ConfigConnectionString this[string key]
{
get;
}
}
}

View File

@@ -0,0 +1,17 @@
namespace Umbraco.Core.Configuration
{
public interface ICoreDebug
{
/// <summary>
/// When set to true, Scope logs the stack trace for any scope that gets disposed without being completed.
/// this helps troubleshooting rogue scopes that we forget to complete
/// </summary>
bool LogUncompletedScopes { get; }
/// <summary>
/// When set to true, the Logger creates a mini dump of w3wp in ~/App_Data/MiniDump whenever it logs
/// an error due to a ThreadAbortException that is due to a timeout.
/// </summary>
bool DumpOnTimeoutThreadAbort { get; }
}
}

View File

@@ -5,6 +5,9 @@
/// </summary> /// </summary>
public interface IGlobalSettings public interface IGlobalSettings
{ {
// fixme: Review this class, it is now just a dumping ground for config options (based basically on whatever might be in appSettings),
// our config classes should be named according to what they are configuring.
/// <summary> /// <summary>
/// Gets the reserved urls from web.config. /// Gets the reserved urls from web.config.
/// </summary> /// </summary>
@@ -63,14 +66,39 @@
/// </summary> /// </summary>
LocalTempStorage LocalTempStorageLocation { get; } LocalTempStorage LocalTempStorageLocation { get; }
/// <summary>
/// Gets the location of temporary files.
/// </summary>
string LocalTempPath { get; }
string UmbracoPath { get; } string UmbracoPath { get; }
string UmbracoCssPath { get; } string UmbracoCssPath { get; }
string UmbracoScriptsPath { get; } string UmbracoScriptsPath { get; }
string UmbracoMediaPath { get; } string UmbracoMediaPath { get; }
bool DebugMode { get; }
bool IsSmtpServerConfigured { get; }
/// <summary>
/// Gets a value indicating whether the runtime should enter Install level when the database is missing.
/// </summary>
/// <remarks>
/// <para>By default, when a database connection string is configured but it is not possible to
/// connect to the database, the runtime enters the BootFailed level. If this options is set to true,
/// it enters the Install level instead.</para>
/// <para>It is then up to the implementor, that is setting this value, to take over the installation
/// sequence.</para>
/// </remarks>
bool InstallMissingDatabase { get; }
/// <summary>
/// Gets a value indicating whether the runtime should enter Install level when the database is empty.
/// </summary>
/// <remarks>
/// <para>By default, when a database connection string is configured and it is possible to connect to
/// the database, but the database is empty, the runtime enters the BootFailed level. If this options
/// is set to true, it enters the Install level instead.</para>
/// <para>It is then up to the implementor, that is setting this value, to take over the installation
/// sequence.</para>
/// </remarks>
bool InstallEmptyDatabase { get; }
bool DisableElectionForSingleServer { get; }
string RegisterType { get; }
string DatabaseFactoryServerVersion { get; }
} }
} }

View File

@@ -0,0 +1,56 @@
using System;
using Semver;
namespace Umbraco.Core.Configuration
{
public interface IUmbracoVersion
{
/// <summary>
/// Gets the non-semantic version of the Umbraco code.
/// </summary>
Version Current { get; }
/// <summary>
/// Gets the semantic version comments of the Umbraco code.
/// </summary>
string Comment { get; }
/// <summary>
/// Gets the assembly version of the Umbraco code.
/// </summary>
/// <remarks>
/// <para>The assembly version is the value of the <see cref="AssemblyVersionAttribute"/>.</para>
/// <para>Is the one that the CLR checks for compatibility. Therefore, it changes only on
/// hard-breaking changes (for instance, on new major versions).</para>
/// </remarks>
Version AssemblyVersion { get; }
/// <summary>
/// Gets the assembly file version of the Umbraco code.
/// </summary>
/// <remarks>
/// <para>The assembly version is the value of the <see cref="AssemblyFileVersionAttribute"/>.</para>
/// </remarks>
Version AssemblyFileVersion { get; }
/// <summary>
/// Gets the semantic version of the Umbraco code.
/// </summary>
/// <remarks>
/// <para>The semantic version is the value of the <see cref="AssemblyInformationalVersionAttribute"/>.</para>
/// <para>It is the full version of Umbraco, including comments.</para>
/// </remarks>
SemVersion SemanticVersion { get; }
/// <summary>
/// Gets the "local" version of the site.
/// </summary>
/// <remarks>
/// <para>Three things have a version, really: the executing code, the database model,
/// and the site/files. The database model version is entirely managed via migrations,
/// and changes during an upgrade. The executing code version changes when new code is
/// deployed. The site/files version changes during an upgrade.</para>
/// </remarks>
SemVersion LocalVersion { get; }
}
}

View File

@@ -8,11 +8,19 @@ namespace Umbraco.Core.Configuration
/// <summary> /// <summary>
/// Represents the version of the executing code. /// Represents the version of the executing code.
/// </summary> /// </summary>
public static class UmbracoVersion public class UmbracoVersion : IUmbracoVersion
{ {
static UmbracoVersion() private readonly IGlobalSettings _globalSettings;
public UmbracoVersion(IGlobalSettings globalSettings)
:this()
{ {
var umbracoCoreAssembly = typeof(UmbracoVersion).Assembly; _globalSettings = globalSettings;
}
public UmbracoVersion()
{
var umbracoCoreAssembly = typeof(SemVersion).Assembly;
// gets the value indicated by the AssemblyVersion attribute // gets the value indicated by the AssemblyVersion attribute
AssemblyVersion = umbracoCoreAssembly.GetName().Version; AssemblyVersion = umbracoCoreAssembly.GetName().Version;
@@ -32,12 +40,12 @@ namespace Umbraco.Core.Configuration
/// Gets the non-semantic version of the Umbraco code. /// Gets the non-semantic version of the Umbraco code.
/// </summary> /// </summary>
// TODO: rename to Version // TODO: rename to Version
public static Version Current { get; } public Version Current { get; }
/// <summary> /// <summary>
/// Gets the semantic version comments of the Umbraco code. /// Gets the semantic version comments of the Umbraco code.
/// </summary> /// </summary>
public static string Comment => SemanticVersion.Prerelease; public string Comment => SemanticVersion.Prerelease;
/// <summary> /// <summary>
/// Gets the assembly version of the Umbraco code. /// Gets the assembly version of the Umbraco code.
@@ -47,7 +55,7 @@ namespace Umbraco.Core.Configuration
/// <para>Is the one that the CLR checks for compatibility. Therefore, it changes only on /// <para>Is the one that the CLR checks for compatibility. Therefore, it changes only on
/// hard-breaking changes (for instance, on new major versions).</para> /// hard-breaking changes (for instance, on new major versions).</para>
/// </remarks> /// </remarks>
public static Version AssemblyVersion {get; } public Version AssemblyVersion {get; }
/// <summary> /// <summary>
/// Gets the assembly file version of the Umbraco code. /// Gets the assembly file version of the Umbraco code.
@@ -55,7 +63,7 @@ namespace Umbraco.Core.Configuration
/// <remarks> /// <remarks>
/// <para>The assembly version is the value of the <see cref="AssemblyFileVersionAttribute"/>.</para> /// <para>The assembly version is the value of the <see cref="AssemblyFileVersionAttribute"/>.</para>
/// </remarks> /// </remarks>
public static Version AssemblyFileVersion { get; } public Version AssemblyFileVersion { get; }
/// <summary> /// <summary>
/// Gets the semantic version of the Umbraco code. /// Gets the semantic version of the Umbraco code.
@@ -64,7 +72,7 @@ namespace Umbraco.Core.Configuration
/// <para>The semantic version is the value of the <see cref="AssemblyInformationalVersionAttribute"/>.</para> /// <para>The semantic version is the value of the <see cref="AssemblyInformationalVersionAttribute"/>.</para>
/// <para>It is the full version of Umbraco, including comments.</para> /// <para>It is the full version of Umbraco, including comments.</para>
/// </remarks> /// </remarks>
public static SemVersion SemanticVersion { get; } public SemVersion SemanticVersion { get; }
/// <summary> /// <summary>
/// Gets the "local" version of the site. /// Gets the "local" version of the site.
@@ -75,21 +83,11 @@ namespace Umbraco.Core.Configuration
/// and changes during an upgrade. The executing code version changes when new code is /// and changes during an upgrade. The executing code version changes when new code is
/// deployed. The site/files version changes during an upgrade.</para> /// deployed. The site/files version changes during an upgrade.</para>
/// </remarks> /// </remarks>
public static SemVersion LocalVersion public SemVersion LocalVersion {
{
get get
{ {
try var value = _globalSettings.ConfigurationStatus;
{ return value.IsNullOrWhiteSpace() ? null : SemVersion.TryParse(value, out var semver) ? semver : null;
// TODO: https://github.com/umbraco/Umbraco-CMS/issues/4238 - stop having version in web.config appSettings } }
var value = ConfigurationManager.AppSettings[Constants.AppSettings.ConfigurationStatus];
return value.IsNullOrWhiteSpace() ? null : SemVersion.TryParse(value, out var semver) ? semver : null;
}
catch
{
return null;
}
}
}
} }
} }

View File

@@ -100,7 +100,7 @@ namespace Umbraco.Core.Events
public IEnumerable<TEntity> DeletedEntities public IEnumerable<TEntity> DeletedEntities
{ {
get => EventObject; get => EventObject;
internal set => EventObject = value; set => EventObject = value;
} }
/// <summary> /// <summary>

View File

@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Umbraco.Core.Models;
namespace Umbraco.Core.Events namespace Umbraco.Core.Events
{ {

View File

@@ -3,7 +3,7 @@
/// <summary> /// <summary>
/// A simple/default transient messages factory /// A simple/default transient messages factory
/// </summary> /// </summary>
internal class TransientEventMessagesFactory : IEventMessagesFactory public class TransientEventMessagesFactory : IEventMessagesFactory
{ {
public EventMessages Get() public EventMessages Get()
{ {

View File

@@ -19,6 +19,7 @@ namespace Umbraco.Core.IO
string MapPath(string path, bool useHttpContext); string MapPath(string path, bool useHttpContext);
string MapPath(string path); string MapPath(string path);
/// <summary> /// <summary>
/// Verifies that the current filepath matches a directory where the user is allowed to edit a file. /// Verifies that the current filepath matches a directory where the user is allowed to edit a file.
/// </summary> /// </summary>

View File

@@ -7,6 +7,13 @@ namespace Umbraco.Core.Logging
/// </summary> /// </summary>
public class DebugDiagnosticsLogger : ILogger public class DebugDiagnosticsLogger : ILogger
{ {
private readonly IMessageTemplates _messageTemplates;
public DebugDiagnosticsLogger(IMessageTemplates messageTemplates)
{
_messageTemplates = messageTemplates;
}
public bool IsEnabled(Type reporting, LogLevel level) public bool IsEnabled(Type reporting, LogLevel level)
=> true; => true;
@@ -31,7 +38,7 @@ namespace Umbraco.Core.Logging
/// <inheritdoc/> /// <inheritdoc/>
public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues)
{ {
System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues) + Environment.NewLine + exception, reporting.FullName); System.Diagnostics.Debug.WriteLine(_messageTemplates.Render(messageTemplate, propertyValues) + Environment.NewLine + exception, reporting.FullName);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -61,7 +68,7 @@ namespace Umbraco.Core.Logging
/// <inheritdoc/> /// <inheritdoc/>
public void Error(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) public void Error(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues)
{ {
System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues) + Environment.NewLine + exception, reporting.FullName); System.Diagnostics.Debug.WriteLine(_messageTemplates.Render(messageTemplate, propertyValues) + Environment.NewLine + exception, reporting.FullName);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -79,7 +86,7 @@ namespace Umbraco.Core.Logging
/// <inheritdoc/> /// <inheritdoc/>
public void Warn(Type reporting, string message, params object[] propertyValues) public void Warn(Type reporting, string message, params object[] propertyValues)
{ {
System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(message, propertyValues), reporting.FullName); System.Diagnostics.Debug.WriteLine(_messageTemplates.Render(message, propertyValues), reporting.FullName);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -91,7 +98,7 @@ namespace Umbraco.Core.Logging
/// <inheritdoc/> /// <inheritdoc/>
public void Warn(Type reporting, Exception exception, string message, params object[] propertyValues) public void Warn(Type reporting, Exception exception, string message, params object[] propertyValues)
{ {
System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(message + Environment.NewLine + exception, propertyValues), reporting.FullName); System.Diagnostics.Debug.WriteLine(_messageTemplates.Render(message + Environment.NewLine + exception, propertyValues), reporting.FullName);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -103,7 +110,7 @@ namespace Umbraco.Core.Logging
/// <inheritdoc/> /// <inheritdoc/>
public void Info(Type reporting, string messageTemplate, params object[] propertyValues) public void Info(Type reporting, string messageTemplate, params object[] propertyValues)
{ {
System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues), reporting.FullName); System.Diagnostics.Debug.WriteLine(_messageTemplates.Render(messageTemplate, propertyValues), reporting.FullName);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -115,7 +122,7 @@ namespace Umbraco.Core.Logging
/// <inheritdoc/> /// <inheritdoc/>
public void Debug(Type reporting, string messageTemplate, params object[] propertyValues) public void Debug(Type reporting, string messageTemplate, params object[] propertyValues)
{ {
System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues), reporting.FullName); System.Diagnostics.Debug.WriteLine(_messageTemplates.Render(messageTemplate, propertyValues), reporting.FullName);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -127,7 +134,7 @@ namespace Umbraco.Core.Logging
/// <inheritdoc/> /// <inheritdoc/>
public void Verbose(Type reporting, string messageTemplate, params object[] propertyValues) public void Verbose(Type reporting, string messageTemplate, params object[] propertyValues)
{ {
System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues), reporting.FullName); System.Diagnostics.Debug.WriteLine(_messageTemplates.Render(messageTemplate, propertyValues), reporting.FullName);
} }
} }
} }

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core.Logging
{
/// <summary>
/// Provides tools to support message templates.
/// </summary>
public interface IMessageTemplates
{
string Render(string messageTemplate, params object[] args);
}
}

View File

@@ -6,7 +6,7 @@ namespace Umbraco.Core.Logging
/// <summary> /// <summary>
/// Implements <see cref="IProfiler"/> by writing profiling results to an <see cref="ILogger"/>. /// Implements <see cref="IProfiler"/> by writing profiling results to an <see cref="ILogger"/>.
/// </summary> /// </summary>
internal class LogProfiler : IProfiler public class LogProfiler : IProfiler
{ {
private readonly ILogger _logger; private readonly ILogger _logger;

View File

@@ -2,7 +2,7 @@
namespace Umbraco.Core.Logging namespace Umbraco.Core.Logging
{ {
internal class VoidProfiler : IProfiler public class VoidProfiler : IProfiler
{ {
private readonly VoidDisposable _disposable = new VoidDisposable(); private readonly VoidDisposable _disposable = new VoidDisposable();

View File

@@ -11,7 +11,6 @@ namespace Umbraco.Core.Models.Entities
{ {
var now = DateTime.Now; var now = DateTime.Now;
// just in case
if (entity.CreateDate == default) if (entity.CreateDate == default)
{ {
entity.CreateDate = now; entity.CreateDate = now;

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Umbraco.Core.Exceptions; using Umbraco.Core.Exceptions;
@@ -23,7 +24,7 @@ namespace Umbraco.Core.Models.Entities
/// Gets an entity representing "root". /// Gets an entity representing "root".
/// </summary> /// </summary>
public static readonly IEntitySlim Root = new EntitySlim { Path = "-1", Name = "root", HasChildren = true }; public static readonly IEntitySlim Root = new EntitySlim { Path = "-1", Name = "root", HasChildren = true };
// implement IEntity // implement IEntity
/// <inheritdoc /> /// <inheritdoc />
@@ -121,66 +122,10 @@ namespace Umbraco.Core.Models.Entities
#endregion #endregion
#region IRememberBeingDirty public void ResetIdentity()
// IEntitySlim does *not* track changes, but since it indirectly implements IUmbracoEntity,
// and therefore IRememberBeingDirty, we have to have those methods - which all throw.
public bool IsDirty()
{ {
throw new WontImplementException(); Id = default;
Key = Guid.Empty;
} }
public bool IsPropertyDirty(string propName)
{
throw new WontImplementException();
}
public IEnumerable<string> GetDirtyProperties()
{
throw new WontImplementException();
}
public void ResetDirtyProperties()
{
throw new WontImplementException();
}
public void DisableChangeTracking()
{
// noop
}
public void EnableChangeTracking()
{
// noop
}
public bool WasDirty()
{
throw new WontImplementException();
}
public bool WasPropertyDirty(string propertyName)
{
throw new WontImplementException();
}
public void ResetWereDirtyProperties()
{
throw new WontImplementException();
}
public void ResetDirtyProperties(bool rememberDirty)
{
throw new WontImplementException();
}
public IEnumerable<string> GetWereDirtyProperties()
{
throw new WontImplementException();
}
#endregion
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
namespace Umbraco.Core.Models.Entities namespace Umbraco.Core.Models.Entities
{ {
@@ -36,5 +37,7 @@ namespace Umbraco.Core.Models.Entities
/// Enables change tracking. /// Enables change tracking.
/// </summary> /// </summary>
void EnableChangeTracking(); void EnableChangeTracking();
event PropertyChangedEventHandler PropertyChanged;
} }
} }

View File

@@ -41,5 +41,7 @@ namespace Umbraco.Core.Models.Entities
/// Gets a value indicating whether the entity has an identity. /// Gets a value indicating whether the entity has an identity.
/// </summary> /// </summary>
bool HasIdentity { get; } bool HasIdentity { get; }
void ResetIdentity();
} }
} }

View File

@@ -2,6 +2,7 @@
namespace Umbraco.Core.Models.Entities namespace Umbraco.Core.Models.Entities
{ {
/// <summary> /// <summary>
/// Represents an entity that can be managed by the entity service. /// Represents an entity that can be managed by the entity service.
/// </summary> /// </summary>
@@ -10,6 +11,6 @@ namespace Umbraco.Core.Models.Entities
/// <para>IUmbracoEntities can be retrieved with the IEntityService.</para> /// <para>IUmbracoEntities can be retrieved with the IEntityService.</para>
/// <para>An IUmbracoEntity can participate in notifications.</para> /// <para>An IUmbracoEntity can participate in notifications.</para>
/// </remarks> /// </remarks>
public interface IUmbracoEntity : ITreeEntity, IRememberBeingDirty public interface IUmbracoEntity : ITreeEntity
{ } { }
} }

View File

@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models
/// <para>Content items are documents, medias and members.</para> /// <para>Content items are documents, medias and members.</para>
/// <para>Content items have a content type, and properties.</para> /// <para>Content items have a content type, and properties.</para>
/// </remarks> /// </remarks>
public interface IContentBase : IUmbracoEntity public interface IContentBase : IUmbracoEntity, IRememberBeingDirty
{ {
/// <summary> /// <summary>
/// Integer Id of the default ContentType /// Integer Id of the default ContentType

View File

@@ -8,7 +8,7 @@ namespace Umbraco.Core.Models
/// Defines the base for a ContentType with properties that /// Defines the base for a ContentType with properties that
/// are shared between ContentTypes and MediaTypes. /// are shared between ContentTypes and MediaTypes.
/// </summary> /// </summary>
public interface IContentTypeBase : IUmbracoEntity public interface IContentTypeBase : IUmbracoEntity, IRememberBeingDirty
{ {
/// <summary> /// <summary>
/// Gets or Sets the Alias of the ContentType /// Gets or Sets the Alias of the ContentType
@@ -103,17 +103,17 @@ namespace Umbraco.Core.Models
/// <summary> /// <summary>
/// Gets all local property types all local property groups or ungrouped. /// Gets all local property types all local property groups or ungrouped.
/// </summary> /// </summary>
IEnumerable<PropertyType> PropertyTypes { get; } IEnumerable<IPropertyType> PropertyTypes { get; }
/// <summary> /// <summary>
/// Gets or sets the local property types that do not belong to a group. /// Gets or sets the local property types that do not belong to a group.
/// </summary> /// </summary>
IEnumerable<PropertyType> NoGroupPropertyTypes { get; set; } IEnumerable<IPropertyType> NoGroupPropertyTypes { get; set; }
/// <summary> /// <summary>
/// Removes a PropertyType from the current ContentType /// Removes a PropertyType from the current ContentType
/// </summary> /// </summary>
/// <param name="propertyTypeAlias">Alias of the <see cref="PropertyType"/> to remove</param> /// <param name="propertyTypeAlias">Alias of the <see cref="IPropertyType"/> to remove</param>
void RemovePropertyType(string propertyTypeAlias); void RemovePropertyType(string propertyTypeAlias);
/// <summary> /// <summary>
@@ -132,17 +132,17 @@ namespace Umbraco.Core.Models
/// <summary> /// <summary>
/// Adds a PropertyType to a specific PropertyGroup /// Adds a PropertyType to a specific PropertyGroup
/// </summary> /// </summary>
/// <param name="propertyType"><see cref="PropertyType"/> to add</param> /// <param name="propertyType"><see cref="IPropertyType"/> to add</param>
/// <param name="propertyGroupName">Name of the PropertyGroup to add the PropertyType to</param> /// <param name="propertyGroupName">Name of the PropertyGroup to add the PropertyType to</param>
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns> /// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
bool AddPropertyType(PropertyType propertyType, string propertyGroupName); bool AddPropertyType(IPropertyType propertyType, string propertyGroupName);
/// <summary> /// <summary>
/// Adds a PropertyType, which does not belong to a PropertyGroup. /// Adds a PropertyType, which does not belong to a PropertyGroup.
/// </summary> /// </summary>
/// <param name="propertyType"><see cref="PropertyType"/> to add</param> /// <param name="propertyType"><see cref="IPropertyType"/> to add</param>
/// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns> /// <returns>Returns <c>True</c> if PropertyType was added, otherwise <c>False</c></returns>
bool AddPropertyType(PropertyType propertyType); bool AddPropertyType(IPropertyType propertyType);
/// <summary> /// <summary>
/// Adds a PropertyGroup. /// Adds a PropertyGroup.

View File

@@ -21,7 +21,7 @@ namespace Umbraco.Core.Models
/// <summary> /// <summary>
/// Gets the property types for the entire composition. /// Gets the property types for the entire composition.
/// </summary> /// </summary>
IEnumerable<PropertyType> CompositionPropertyTypes { get; } IEnumerable<IPropertyType> CompositionPropertyTypes { get; }
/// <summary> /// <summary>
/// Adds a new ContentType to the list of composite ContentTypes /// Adds a new ContentType to the list of composite ContentTypes

View File

@@ -6,7 +6,7 @@ namespace Umbraco.Core.Models
/// <summary> /// <summary>
/// Represents a data type. /// Represents a data type.
/// </summary> /// </summary>
public interface IDataType : IUmbracoEntity public interface IDataType : IUmbracoEntity, IRememberBeingDirty
{ {
/// <summary> /// <summary>
/// Gets or sets the property editor. /// Gets or sets the property editor.

View File

@@ -3,7 +3,7 @@ using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models
{ {
public interface IDictionaryTranslation : IEntity public interface IDictionaryTranslation : IEntity, IRememberBeingDirty
{ {
/// <summary> /// <summary>
/// Gets or sets the <see cref="Language"/> for the translation /// Gets or sets the <see cref="Language"/> for the translation

View File

@@ -7,7 +7,7 @@ namespace Umbraco.Core.Models
/// Defines a File /// Defines a File
/// </summary> /// </summary>
/// <remarks>Used for Scripts, Stylesheets and Templates</remarks> /// <remarks>Used for Scripts, Stylesheets and Templates</remarks>
public interface IFile : IEntity public interface IFile : IEntity, IRememberBeingDirty
{ {
/// <summary> /// <summary>
/// Gets the Name of the File including extension /// Gets the Name of the File including extension

View File

@@ -1,6 +1,4 @@
using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Core.Models
namespace Umbraco.Core.Models
{ {
/// <summary> /// <summary>
/// Defines a ContentType, which Media is based on /// Defines a ContentType, which Media is based on

View File

@@ -33,13 +33,9 @@ namespace Umbraco.Core.Models
/// </summary> /// </summary>
void SetValue(object value, string culture = null, string segment = null); void SetValue(object value, string culture = null, string segment = null);
/// <summary>
/// Resets the entity identity.
/// </summary>
void ResetIdentity();
int PropertyTypeId { get; } int PropertyTypeId { get; }
void PublishValues(string culture = "*", string segment = "*"); void PublishValues(string culture = "*", string segment = "*");
void UnpublishValues(string culture = "*", string segment = "*"); void UnpublishValues(string culture = "*", string segment = "*");
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Entities;
@@ -9,62 +10,62 @@ namespace Umbraco.Core.Models
/// <summary> /// <summary>
/// Gets of sets the name of the property type. /// Gets of sets the name of the property type.
/// </summary> /// </summary>
string Name { get; } string Name { get; set; }
/// <summary> /// <summary>
/// Gets of sets the alias of the property type. /// Gets of sets the alias of the property type.
/// </summary> /// </summary>
string Alias { get; } string Alias { get; set; }
/// <summary> /// <summary>
/// Gets of sets the description of the property type. /// Gets of sets the description of the property type.
/// </summary> /// </summary>
string Description { get; } string Description { get; set; }
/// <summary> /// <summary>
/// Gets or sets the identifier of the datatype for this property type. /// Gets or sets the identifier of the datatype for this property type.
/// </summary> /// </summary>
int DataTypeId { get; } int DataTypeId { get; set; }
Guid DataTypeKey { get; } Guid DataTypeKey { get; set; }
/// <summary> /// <summary>
/// Gets or sets the alias of the property editor for this property type. /// Gets or sets the alias of the property editor for this property type.
/// </summary> /// </summary>
string PropertyEditorAlias { get; } string PropertyEditorAlias { get; set; }
/// <summary> /// <summary>
/// Gets or sets the database type for storing value for this property type. /// Gets or sets the database type for storing value for this property type.
/// </summary> /// </summary>
ValueStorageType ValueStorageType { get; } ValueStorageType ValueStorageType { get; set; }
/// <summary> /// <summary>
/// Gets or sets the identifier of the property group this property type belongs to. /// Gets or sets the identifier of the property group this property type belongs to.
/// </summary> /// </summary>
/// <remarks>For generic properties, the value is <c>null</c>.</remarks> /// <remarks>For generic properties, the value is <c>null</c>.</remarks>
Lazy<int> PropertyGroupId { get; } Lazy<int> PropertyGroupId { get; set; }
/// <summary> /// <summary>
/// Gets of sets a value indicating whether a value for this property type is required. /// Gets of sets a value indicating whether a value for this property type is required.
/// </summary> /// </summary>
bool Mandatory { get; } bool Mandatory { get; set; }
/// <summary> /// <summary>
/// Gets of sets the sort order of the property type. /// Gets of sets the sort order of the property type.
/// </summary> /// </summary>
int SortOrder { get; } int SortOrder { get; set; }
/// <summary> /// <summary>
/// Gets or sets the regular expression validating the property values. /// Gets or sets the regular expression validating the property values.
/// </summary> /// </summary>
string ValidationRegExp { get; } string ValidationRegExp { get; set; }
bool SupportsPublishing { get; } bool SupportsPublishing { get; set; }
/// <summary> /// <summary>
/// Gets or sets the content variation of the property type. /// Gets or sets the content variation of the property type.
/// </summary> /// </summary>
ContentVariation Variations { get; } ContentVariation Variations { get; set; }
/// <summary> /// <summary>
/// Determines whether the property type supports a combination of culture and segment. /// Determines whether the property type supports a combination of culture and segment.
@@ -74,14 +75,5 @@ namespace Umbraco.Core.Models
/// <param name="wildcards">A value indicating whether wildcards are valid.</param> /// <param name="wildcards">A value indicating whether wildcards are valid.</param>
bool SupportsVariation(string culture, string segment, bool wildcards = false); bool SupportsVariation(string culture, string segment, bool wildcards = false);
/// <summary>
/// Converts a value assigned to a property.
/// </summary>
/// <remarks>
/// <para>The input value can be pretty much anything, and is converted to the actual CLR type
/// expected by the property (eg an integer if the property values are integers).</para>
/// <para>Throws if the value cannot be converted.</para>
/// </remarks>
object ConvertAssignedValue(object value);
} }
} }

View File

@@ -1,14 +1,15 @@
using Umbraco.Core.Models.Entities; using System;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models
{ {
/// <summary> /// <summary>
/// Represents a simplified view of a content type. /// Represents a simplified view of a content type.
/// </summary> /// </summary>
public interface ISimpleContentType : IUmbracoEntity public interface ISimpleContentType
{ {
new int Id { get; } int Id { get; }
new string Name { get; } Guid Key { get; }
string Name { get; }
/// <summary> /// <summary>
/// Gets the alias of the content type. /// Gets the alias of the content type.

View File

@@ -3,7 +3,7 @@ using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models
{ {
public interface IStylesheet : IFile, IRememberBeingDirty public interface IStylesheet : IFile
{ {
/// <summary> /// <summary>
/// Returns a list of umbraco back office enabled stylesheet properties /// Returns a list of umbraco back office enabled stylesheet properties

View File

@@ -9,7 +9,7 @@ namespace Umbraco.Core.Models
/// </summary> /// </summary>
[Serializable] [Serializable]
[DataContract(IsReference = true)] [DataContract(IsReference = true)]
public class MacroProperty : BeingDirtyBase, IMacroProperty, IRememberBeingDirty, IDeepCloneable public class MacroProperty : BeingDirtyBase, IMacroProperty
{ {
public MacroProperty() public MacroProperty()
{ {

View File

@@ -8,7 +8,7 @@ namespace Umbraco.Core.Models.Membership
/// Represents an <see cref="IContent"/> -> user group & permission key value pair collection /// Represents an <see cref="IContent"/> -> user group & permission key value pair collection
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This implements <see cref="IAggregateRoot"/> purely so it can be used with the repository layer which is why it's explicitly implemented. /// This implements <see cref="IEntity"/> purely so it can be used with the repository layer which is why it's explicitly implemented.
/// </remarks> /// </remarks>
public class ContentPermissionSet : EntityPermissionSet, IEntity public class ContentPermissionSet : EntityPermissionSet, IEntity
{ {
@@ -37,6 +37,8 @@ namespace Umbraco.Core.Models.Membership
get { return EntityId > 0; } get { return EntityId > 0; }
} }
void IEntity.ResetIdentity() => throw new InvalidOperationException($"Resetting identity on {nameof(ContentPermissionSet)} is invalid");
Guid IEntity.Key { get; set; } Guid IEntity.Key { get; set; }
DateTime IEntity.CreateDate { get; set; } DateTime IEntity.CreateDate { get; set; }

View File

@@ -19,7 +19,7 @@ namespace Umbraco.Core.Models
{ {
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim(); private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
internal PropertyGroupCollection() public PropertyGroupCollection()
{ } { }
public PropertyGroupCollection(IEnumerable<PropertyGroup> groups) public PropertyGroupCollection(IEnumerable<PropertyGroup> groups)
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Models
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
} }
internal new void Add(PropertyGroup item) public new void Add(PropertyGroup item)
{ {
try try
{ {

View File

@@ -8,24 +8,26 @@ using System.Threading;
namespace Umbraco.Core.Models namespace Umbraco.Core.Models
{ {
//public interface IPropertyTypeCollection: IEnumerable<IPropertyType>
/// <summary> /// <summary>
/// Represents a collection of <see cref="PropertyType"/> objects. /// Represents a collection of <see cref="IPropertyType"/> objects.
/// </summary> /// </summary>
[Serializable] [Serializable]
[DataContract] [DataContract]
// TODO: Change this to ObservableDictionary so we can reduce the INotifyCollectionChanged implementation details // TODO: Change this to ObservableDictionary so we can reduce the INotifyCollectionChanged implementation details
public class PropertyTypeCollection : KeyedCollection<string, PropertyType>, INotifyCollectionChanged, IDeepCloneable public class PropertyTypeCollection : KeyedCollection<string, IPropertyType>, INotifyCollectionChanged, IDeepCloneable, ICollection<IPropertyType>
{ {
[IgnoreDataMember] [IgnoreDataMember]
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim(); private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
internal PropertyTypeCollection(bool supportsPublishing) public PropertyTypeCollection(bool supportsPublishing)
{ {
SupportsPublishing = supportsPublishing; SupportsPublishing = supportsPublishing;
} }
public PropertyTypeCollection(bool supportsPublishing, IEnumerable<PropertyType> properties) public PropertyTypeCollection(bool supportsPublishing, IEnumerable<IPropertyType> properties)
: this(supportsPublishing) : this(supportsPublishing)
{ {
Reset(properties); Reset(properties);
@@ -34,25 +36,25 @@ namespace Umbraco.Core.Models
public bool SupportsPublishing { get; } public bool SupportsPublishing { get; }
/// <summary> /// <summary>
/// Resets the collection to only contain the <see cref="PropertyType"/> instances referenced in the <paramref name="properties"/> parameter. /// Resets the collection to only contain the <see cref="IPropertyType"/> instances referenced in the <paramref name="properties"/> parameter.
/// </summary> /// </summary>
/// <param name="properties">The properties.</param> /// <param name="properties">The properties.</param>
/// <remarks></remarks> /// <remarks></remarks>
internal void Reset(IEnumerable<PropertyType> properties) internal void Reset(IEnumerable<IPropertyType> properties)
{ {
//collection events will be raised in each of these calls //collection events will be raised in each of these calls
Clear(); Clear();
//collection events will be raised in each of these calls //collection events will be raised in each of these calls
foreach (var property in properties) foreach (var property in properties)
Add(property); Add(property);
} }
protected override void SetItem(int index, PropertyType item) protected override void SetItem(int index, IPropertyType item)
{ {
item.SupportsPublishing = SupportsPublishing; item.SupportsPublishing = SupportsPublishing;
var oldItem = index >= 0 ? this[index] : item; var oldItem = index >= 0 ? this[index] : item;
base.SetItem(index, item); base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem));
item.PropertyChanged += Item_PropertyChanged; item.PropertyChanged += Item_PropertyChanged;
} }
@@ -65,10 +67,10 @@ namespace Umbraco.Core.Models
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
} }
protected override void InsertItem(int index, PropertyType item) protected override void InsertItem(int index, IPropertyType item)
{ {
item.SupportsPublishing = SupportsPublishing; item.SupportsPublishing = SupportsPublishing;
base.InsertItem(index, item); base.InsertItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
item.PropertyChanged += Item_PropertyChanged; item.PropertyChanged += Item_PropertyChanged;
} }
@@ -81,8 +83,9 @@ namespace Umbraco.Core.Models
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
} }
// TODO: Instead of 'new' this should explicitly implement one of the collection interfaces members // 'new' keyword is required! we can explicitly implement ICollection<IPropertyType>.Add BUT since normally a concrete PropertyType type
internal new void Add(PropertyType item) // is passed in, the explicit implementation doesn't get called, this ensures it does get called.
public new void Add(IPropertyType item)
{ {
item.SupportsPublishing = SupportsPublishing; item.SupportsPublishing = SupportsPublishing;
@@ -120,13 +123,13 @@ namespace Umbraco.Core.Models
} }
/// <summary> /// <summary>
/// Occurs when a property changes on a PropertyType that exists in this collection /// Occurs when a property changes on a IPropertyType that exists in this collection
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private void Item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) private void Item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{ {
var propType = (PropertyType)sender; var propType = (IPropertyType)sender;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, propType, propType)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, propType, propType));
} }
@@ -156,7 +159,7 @@ namespace Umbraco.Core.Models
return -1; return -1;
} }
protected override string GetKeyForItem(PropertyType item) protected override string GetKeyForItem(IPropertyType item)
{ {
return item.Alias; return item.Alias;
} }
@@ -172,7 +175,7 @@ namespace Umbraco.Core.Models
{ {
var clone = new PropertyTypeCollection(SupportsPublishing); var clone = new PropertyTypeCollection(SupportsPublishing);
foreach (var propertyType in this) foreach (var propertyType in this)
clone.Add((PropertyType) propertyType.DeepClone()); clone.Add((IPropertyType) propertyType.DeepClone());
return clone; return clone;
} }
} }

View File

@@ -6,7 +6,7 @@ using System.Text;
namespace Umbraco.Core namespace Umbraco.Core
{ {
internal static class NameValueCollectionExtensions public static class NameValueCollectionExtensions
{ {
public static IEnumerable<KeyValuePair<string, string>> AsEnumerable(this NameValueCollection nvc) public static IEnumerable<KeyValuePair<string, string>> AsEnumerable(this NameValueCollection nvc)
{ {

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
[assembly: Guid("DA322714-FB89-4943-92BD-BB122B82F66B")]
// Umbraco Cms
[assembly: InternalsVisibleTo("Umbraco.Web")]
[assembly: InternalsVisibleTo("Umbraco.Web.UI")]
[assembly: InternalsVisibleTo("Umbraco.Examine")]
[assembly: InternalsVisibleTo("Umbraco.ModelsBuilder.Embedded")]
[assembly: InternalsVisibleTo("Umbraco.Tests")]
[assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")]
// Allow this to be mocked in our unit tests
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
// Umbraco Deploy
[assembly: InternalsVisibleTo("Umbraco.Deploy")]
[assembly: InternalsVisibleTo("Umbraco.Deploy.UI")]
[assembly: InternalsVisibleTo("Umbraco.Deploy.Cloud")]
// Umbraco Forms
[assembly: InternalsVisibleTo("Umbraco.Forms.Core")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Web")]
// Umbraco Headless
[assembly: InternalsVisibleTo("Umbraco.Cloud.Headless")]
// code analysis
// IDE1006 is broken, wants _value syntax for consts, etc - and it's even confusing ppl at MS, kill it
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "~_~")]

View File

@@ -3,7 +3,7 @@
/// <summary> /// <summary>
/// Marker interface for any editor configuration that supports Ignoring user start nodes /// Marker interface for any editor configuration that supports Ignoring user start nodes
/// </summary> /// </summary>
internal interface IIgnoreUserStartNodesConfig public interface IIgnoreUserStartNodesConfig
{ {
bool IgnoreUserStartNodes { get; set; } bool IgnoreUserStartNodes { get; set; }
} }

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Services namespace Umbraco.Core.Services

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Entities;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Services namespace Umbraco.Core.Services
@@ -27,28 +26,28 @@ namespace Umbraco.Core.Services
/// <param name="id">The identifier of the entity.</param> /// <param name="id">The identifier of the entity.</param>
/// <param name="objectType">The object type of the entity.</param> /// <param name="objectType">The object type of the entity.</param>
IEntitySlim Get(int id, UmbracoObjectTypes objectType); IEntitySlim Get(int id, UmbracoObjectTypes objectType);
/// <summary> /// <summary>
/// Gets an entity. /// Gets an entity.
/// </summary> /// </summary>
/// <param name="key">The unique key of the entity.</param> /// <param name="key">The unique key of the entity.</param>
/// <param name="objectType">The object type of the entity.</param> /// <param name="objectType">The object type of the entity.</param>
IEntitySlim Get(Guid key, UmbracoObjectTypes objectType); IEntitySlim Get(Guid key, UmbracoObjectTypes objectType);
/// <summary> /// <summary>
/// Gets an entity. /// Gets an entity.
/// </summary> /// </summary>
/// <typeparam name="T">The type used to determine the object type of the entity.</typeparam> /// <typeparam name="T">The type used to determine the object type of the entity.</typeparam>
/// <param name="id">The identifier of the entity.</param> /// <param name="id">The identifier of the entity.</param>
IEntitySlim Get<T>(int id) where T : IUmbracoEntity; IEntitySlim Get<T>(int id) where T : IUmbracoEntity;
/// <summary> /// <summary>
/// Gets an entity. /// Gets an entity.
/// </summary> /// </summary>
/// <typeparam name="T">The type used to determine the object type of the entity.</typeparam> /// <typeparam name="T">The type used to determine the object type of the entity.</typeparam>
/// <param name="key">The unique key of the entity.</param> /// <param name="key">The unique key of the entity.</param>
IEntitySlim Get<T>(Guid key) where T : IUmbracoEntity; IEntitySlim Get<T>(Guid key) where T : IUmbracoEntity;
/// <summary> /// <summary>
/// Determines whether an entity exists. /// Determines whether an entity exists.
/// </summary> /// </summary>

View File

@@ -1,12 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence;
namespace Umbraco.Core.Services namespace Umbraco.Core.Services
{ {

View File

@@ -1,8 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel;
using Umbraco.Core.Models; using Umbraco.Core.Models;
using Umbraco.Core.Security;
namespace Umbraco.Core.Services namespace Umbraco.Core.Services
{ {

View File

@@ -1,4 +1,5 @@
using Umbraco.Core.Composing; using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
namespace Umbraco.Core namespace Umbraco.Core
{ {

View File

@@ -4,6 +4,10 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.3</LangVersion> <LangVersion>7.3</LangVersion>
<RootNamespace>Umbraco.Core</RootNamespace> <RootNamespace>Umbraco.Core</RootNamespace>
<AssemblyVersion>9.0.0</AssemblyVersion>
<InformationalVersion>9.0.0</InformationalVersion>
<FileVersion>9.0.0</FileVersion>
<Product>Umbraco CMS</Product>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -0,0 +1,34 @@
using System.Configuration;
using Umbraco.Core.Configuration.HealthChecks;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
namespace Umbraco.Core.Configuration
{
public class ConfigsFactory : IConfigsFactory
{
private readonly IIOHelper _ioHelper;
public ConfigsFactory(IIOHelper ioHelper)
{
_ioHelper = ioHelper;
GlobalSettings = new GlobalSettings(_ioHelper);
}
public IGlobalSettings GlobalSettings { get; }
public Configs Create()
{
var configs = new Configs(section => ConfigurationManager.GetSection(section));
configs.Add<IGlobalSettings>(() => GlobalSettings);
configs.Add<IUmbracoSettingsSection>("umbracoConfiguration/settings");
configs.Add<IHealthChecks>("umbracoConfiguration/HealthChecks");
configs.Add<ICoreDebug>(() => new CoreDebug());
configs.Add<IConnectionStrings>(() => new ConnectionStrings());
configs.AddCoreConfigs(_ioHelper);
return configs;
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Configuration;
using System.Linq;
using System.Xml.Linq;
using Umbraco.Core.IO;
namespace Umbraco.Core.Configuration
{
public class ConnectionStrings : IConnectionStrings
{
public ConfigConnectionString this[string key]
{
get
{
var settings = ConfigurationManager.ConnectionStrings[key];
return new ConfigConnectionString(settings.ConnectionString, settings.ProviderName, settings.Name);
}
}
}
}

View File

@@ -1,22 +1,21 @@
using System; using System;
using System.Configuration;
namespace Umbraco.Core.Configuration namespace Umbraco.Core.Configuration
{ {
internal class CoreDebug public class CoreDebug : ICoreDebug
{ {
public CoreDebug() public CoreDebug()
{ {
var appSettings = System.Configuration.ConfigurationManager.AppSettings; var appSettings = ConfigurationManager.AppSettings;
LogUncompletedScopes = string.Equals("true", appSettings[Constants.AppSettings.Debug.LogUncompletedScopes], StringComparison.OrdinalIgnoreCase); LogUncompletedScopes = string.Equals("true", appSettings[Constants.AppSettings.Debug.LogUncompletedScopes], StringComparison.OrdinalIgnoreCase);
DumpOnTimeoutThreadAbort = string.Equals("true", appSettings[Constants.AppSettings.Debug.DumpOnTimeoutThreadAbort], StringComparison.OrdinalIgnoreCase); DumpOnTimeoutThreadAbort = string.Equals("true", appSettings[Constants.AppSettings.Debug.DumpOnTimeoutThreadAbort], StringComparison.OrdinalIgnoreCase);
} }
// when true, Scope logs the stack trace for any scope that gets disposed without being completed. /// <inheritdoc />
// this helps troubleshooting rogue scopes that we forget to complete
public bool LogUncompletedScopes { get; } public bool LogUncompletedScopes { get; }
// when true, the Logger creates a mini dump of w3wp in ~/App_Data/MiniDump whenever it logs /// <inheritdoc />
// an error due to a ThreadAbortException that is due to a timeout.
public bool DumpOnTimeoutThreadAbort { get; } public bool DumpOnTimeoutThreadAbort { get; }
} }
} }

View File

@@ -53,20 +53,42 @@ namespace Umbraco.Core.Configuration
{ {
ResetInternal(); ResetInternal();
} }
public bool IsSmtpServerConfigured
public static bool HasSmtpServerConfigured(string appPath)
{ {
if (HasSmtpServer.HasValue) return HasSmtpServer.Value; get
{
var smtpSection = ConfigurationManager.GetSection("system.net/mailSettings/smtp") as ConfigurationSection;
if (smtpSection is null) return false;
var config = WebConfigurationManager.OpenWebConfiguration(appPath); var from = smtpSection.ElementInformation.Properties["from"];
var settings = (MailSettingsSectionGroup)config.GetSectionGroup("system.net/mailSettings"); if (@from != null
// note: "noreply@example.com" is/was the sample SMTP from email - we'll regard that as "not configured" && @from.Value is string fromPropValue
if (settings == null || settings.Smtp == null || "noreply@example.com".Equals(settings.Smtp.From, StringComparison.OrdinalIgnoreCase)) return false; && string.IsNullOrEmpty(fromPropValue) == false
if (settings.Smtp.SpecifiedPickupDirectory != null && string.IsNullOrEmpty(settings.Smtp.SpecifiedPickupDirectory.PickupDirectoryLocation) == false) && !string.Equals("noreply@example.com", fromPropValue, StringComparison.OrdinalIgnoreCase))
return true; {
if (settings.Smtp.Network != null && string.IsNullOrEmpty(settings.Smtp.Network.Host) == false) return true;
return true; }
return false;
var networkSection = ConfigurationManager.GetSection("system.net/mailSettings/smtp/network") as ConfigurationSection;
var host = networkSection?.ElementInformation.Properties["host"];
if (host != null
&& host.Value is string hostPropValue
&& string.IsNullOrEmpty(hostPropValue) == false)
{
return true;
}
var specifiedPickupDirectorySection = ConfigurationManager.GetSection("system.net/mailSettings/smtp/specifiedPickupDirectory") as ConfigurationSection;
var pickupDirectoryLocation = specifiedPickupDirectorySection?.ElementInformation.Properties["pickupDirectoryLocation"];
if (pickupDirectoryLocation != null
&& pickupDirectoryLocation.Value is string pickupDirectoryLocationPropValue
&& string.IsNullOrEmpty(pickupDirectoryLocationPropValue) == false)
{
return true;
}
return false;
}
} }
/// <summary> /// <summary>
@@ -166,7 +188,7 @@ namespace Umbraco.Core.Configuration
} }
set set
{ {
SaveSetting(Constants.AppSettings.ConfigurationStatus, value); SaveSetting(Constants.AppSettings.ConfigurationStatus, value, _ioHelper);
} }
} }
@@ -175,9 +197,9 @@ namespace Umbraco.Core.Configuration
/// </summary> /// </summary>
/// <param name="key">Key of the setting to be saved.</param> /// <param name="key">Key of the setting to be saved.</param>
/// <param name="value">Value of the setting to be saved.</param> /// <param name="value">Value of the setting to be saved.</param>
internal static void SaveSetting(string key, string value) internal static void SaveSetting(string key, string value, IIOHelper ioHelper)
{ {
var fileName = Current.IOHelper.MapPath(string.Format("{0}/web.config", Current.IOHelper.Root)); var fileName = ioHelper.MapPath(string.Format("{0}/web.config", ioHelper.Root));
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
var appSettings = xml.Root.DescendantsAndSelf("appSettings").Single(); var appSettings = xml.Root.DescendantsAndSelf("appSettings").Single();
@@ -197,9 +219,9 @@ namespace Umbraco.Core.Configuration
/// Removes a setting from the configuration file. /// Removes a setting from the configuration file.
/// </summary> /// </summary>
/// <param name="key">Key of the setting to be removed.</param> /// <param name="key">Key of the setting to be removed.</param>
internal static void RemoveSetting(string key) public static void RemoveSetting(string key, IIOHelper ioHelper)
{ {
var fileName = Current.IOHelper.MapPath(string.Format("{0}/web.config", Current.IOHelper.Root)); var fileName = ioHelper.MapPath(string.Format("{0}/web.config", ioHelper.Root));
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
var appSettings = xml.Root.DescendantsAndSelf("appSettings").Single(); var appSettings = xml.Root.DescendantsAndSelf("appSettings").Single();
@@ -217,24 +239,25 @@ namespace Umbraco.Core.Configuration
/// Gets a value indicating whether umbraco is running in [debug mode]. /// Gets a value indicating whether umbraco is running in [debug mode].
/// </summary> /// </summary>
/// <value><c>true</c> if [debug mode]; otherwise, <c>false</c>.</value> /// <value><c>true</c> if [debug mode]; otherwise, <c>false</c>.</value>
public static bool DebugMode public bool DebugMode
{ {
get get
{ {
try try
{ {
if (HttpContext.Current != null) if (ConfigurationManager.GetSection("system.web/compilation") is ConfigurationSection compilation)
{ {
return HttpContext.Current.IsDebuggingEnabled; var debugElement = compilation.ElementInformation.Properties["debug"];
return debugElement != null && (debugElement.Value is bool debug && debug);
} }
//go and get it from config directly
var section = ConfigurationManager.GetSection("system.web/compilation") as CompilationSection;
return section != null && section.Debug;
} }
catch catch
{ {
return false; // ignored
} }
return false;
} }
} }
@@ -289,47 +312,6 @@ namespace Umbraco.Core.Configuration
} }
} }
/// <inheritdoc />
public string LocalTempPath
{
get
{
if (_localTempPath != null)
return _localTempPath;
switch (LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
return _localTempPath = System.IO.Path.Combine(HttpRuntime.CodegenDir, "UmbracoData");
case LocalTempStorage.EnvironmentTemp:
// environment temp is unique, we need a folder per site
// use a hash
// combine site name and application id
// site name is a Guid on Cloud
// application id is eg /LM/W3SVC/123456/ROOT
// the combination is unique on one server
// and, if a site moves from worker A to B and then back to A...
// hopefully it gets a new Guid or new application id?
var siteName = HostingEnvironment.SiteName;
var applicationId = HostingEnvironment.ApplicationID; // ie HttpRuntime.AppDomainAppId
var hashString = siteName + "::" + applicationId;
var hash = hashString.GenerateHash();
var siteTemp = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", hash);
return _localTempPath = siteTemp;
//case LocalTempStorage.Default:
//case LocalTempStorage.Unknown:
default:
return _localTempPath = _ioHelper.MapPath("~/App_Data/TEMP");
}
}
}
/// <summary> /// <summary>
/// Gets the default UI language. /// Gets the default UI language.
@@ -397,6 +379,21 @@ namespace Umbraco.Core.Configuration
private string _umbracoPath = null; private string _umbracoPath = null;
public string UmbracoPath => GetterWithDefaultValue(Constants.AppSettings.UmbracoPath, "~/umbraco", ref _umbracoPath); public string UmbracoPath => GetterWithDefaultValue(Constants.AppSettings.UmbracoPath, "~/umbraco", ref _umbracoPath);
private bool _installMissingDatabase;
public bool InstallMissingDatabase => GetterWithDefaultValue("Umbraco.Core.RuntimeState.InstallMissingDatabase", false, ref _installMissingDatabase);
private bool _installEmptyDatabase;
public bool InstallEmptyDatabase => GetterWithDefaultValue("Umbraco.Core.RuntimeState.InstallEmptyDatabase", false, ref _installEmptyDatabase);
private bool _disableElectionForSingleServer;
public bool DisableElectionForSingleServer => GetterWithDefaultValue(Constants.AppSettings.DisableElectionForSingleServer, false, ref _disableElectionForSingleServer);
private string _registerType;
public string RegisterType => GetterWithDefaultValue(Constants.AppSettings.RegisterType, string.Empty, ref _registerType);
private string _databaseFactoryServerVersion;
public string DatabaseFactoryServerVersion => GetterWithDefaultValue(Constants.AppSettings.Debug.DatabaseFactoryServerVersion, string.Empty, ref _databaseFactoryServerVersion);
private T GetterWithDefaultValue<T>(string appSettingKey, T defaultValue, ref T backingField) private T GetterWithDefaultValue<T>(string appSettingKey, T defaultValue, ref T backingField)
{ {
if (backingField != null) return backingField; if (backingField != null) return backingField;

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