diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 62872c283c..b142e08c5f 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -33,8 +33,6 @@ - - @@ -53,6 +51,13 @@ + + + + + + + diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index 9811743746..1deec235f8 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -22,7 +22,7 @@ not want this to happen as the alpha of the next major is, really, the next major already. --> - + @@ -35,7 +35,6 @@ - diff --git a/build/NuSpecs/tools/install.core.ps1 b/build/NuSpecs/tools/install.core.ps1 index ad3a00651b..0a658266a1 100644 --- a/build/NuSpecs/tools/install.core.ps1 +++ b/build/NuSpecs/tools/install.core.ps1 @@ -33,7 +33,6 @@ if ($project) { robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log # Delete files Umbraco ships with - if(Test-Path $umbracoBinFolder\log4net.dll) { Remove-Item $umbracoBinFolder\log4net.dll -Force -Confirm:$false } if(Test-Path $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll) { Remove-Item $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll -Force -Confirm:$false } if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.dll -Force -Confirm:$false } if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll -Force -Confirm:$false } diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 77bd21b673..e6118de937 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -19,4 +19,4 @@ using System.Resources; // these are FYI and changed automatically [assembly: AssemblyFileVersion("8.0.0")] -[assembly: AssemblyInformationalVersion("8.0.0-alpha.44")] +[assembly: AssemblyInformationalVersion("8.0.0-alpha.49")] diff --git a/src/Umbraco.Core/BindingRedirects.cs b/src/Umbraco.Core/BindingRedirects.cs index a93485085e..17e187b7ae 100644 --- a/src/Umbraco.Core/BindingRedirects.cs +++ b/src/Umbraco.Core/BindingRedirects.cs @@ -18,10 +18,7 @@ namespace Umbraco.Core // this only gets called when an assembly can't be resolved AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; } - - private static readonly Regex Log4NetAssemblyPattern = new Regex("log4net, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=\\w+$", RegexOptions.Compiled); - private const string Log4NetReplacement = "log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a"; - + /// /// This is used to do an assembly binding redirect via code - normally required due to signature changes in assemblies /// @@ -30,12 +27,6 @@ namespace Umbraco.Core /// private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { - //log4net: - if (Log4NetAssemblyPattern.IsMatch(args.Name) && args.Name != Log4NetReplacement) - { - return Assembly.Load(Log4NetAssemblyPattern.Replace(args.Name, Log4NetReplacement)); - } - //AutoMapper: // ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again // do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow diff --git a/src/Umbraco.Core/Components/BootLoader.cs b/src/Umbraco.Core/Components/BootLoader.cs index ee38864933..fd292990c8 100644 --- a/src/Umbraco.Core/Components/BootLoader.cs +++ b/src/Umbraco.Core/Components/BootLoader.cs @@ -111,15 +111,15 @@ namespace Umbraco.Core.Components catch (Exception e) { // in case of an error, force-dump everything to log - _logger.Info(() => GetComponentsReport(requirements)); - _logger.Error("Failed to sort components.", e); + _logger.Info("Component Report:\r\n{ComponentReport}", GetComponentsReport(requirements)); + _logger.Error(e, "Failed to sort compontents."); throw; } // bit verbose but should help for troubleshooting var text = "Ordered Components: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComponentTypes) + Environment.NewLine; Console.WriteLine(text); - _logger.Debug(text); + _logger.Debug("Ordered Components: {SortedComponentTypes}", sortedComponentTypes); return sortedComponentTypes; } diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index f244d1d1ce..a42b84e0c5 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -148,7 +148,7 @@ namespace Umbraco.Core.Composing catch (FileNotFoundException ex) { //this will occur if it cannot load the assembly - Current.Logger.Error(typeof(TypeFinder), "Could not load assembly App_Code", ex); + Current.Logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code"); } } } @@ -224,7 +224,6 @@ namespace Umbraco.Core.Composing "Dynamic,", "HtmlDiff,", "Iesi.Collections,", - "log4net,", "Microsoft.", "Newtonsoft.", "NHibernate.", @@ -440,7 +439,7 @@ namespace Umbraco.Core.Composing } catch (TypeLoadException ex) { - Current.Logger.Error(typeof(TypeFinder), $"Could not query types on {assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", ex); + Current.Logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly); continue; } @@ -506,7 +505,7 @@ namespace Umbraco.Core.Composing } catch (TypeLoadException ex) { - Current.Logger.Error(typeof(TypeFinder), $"Could not query types on {assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", ex); + Current.Logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly); continue; } @@ -573,7 +572,7 @@ namespace Umbraco.Core.Composing if (NotifiedLoadExceptionAssemblies.Contains(a.FullName) == false) { NotifiedLoadExceptionAssemblies.Add(a.FullName); - Current.Logger.Warn(typeof (TypeFinder), ex, $"Could not load all types from {a.GetName().Name}."); + Current.Logger.Warn(typeof (TypeFinder), ex, "Could not load all types from {TypeName}.", a.GetName().Name); } } return rex.Types.WhereNotNull().ToArray(); diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index d87fdfe3de..304638e017 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -494,7 +494,7 @@ namespace Umbraco.Core.Composing if (--attempts == 0) throw; - _logger.Logger.Debug(() => $"Attempted to get filestream for file {path} failed, {attempts} attempts left, pausing for {pauseMilliseconds} milliseconds"); + _logger.Logger.Debug("Attempted to get filestream for file {Path} failed, {NumberOfAttempts} attempts left, pausing for {PauseMilliseconds} milliseconds", path, attempts, pauseMilliseconds); Thread.Sleep(pauseMilliseconds); } } @@ -645,7 +645,7 @@ namespace Umbraco.Core.Composing if (typeList != null) { // need to put some logging here to try to figure out why this is happening: http://issues.umbraco.org/issue/U4-3505 - _logger.Logger.Debug(() => $"Getting {GetName(baseType, attributeType)}: found a cached type list."); + _logger.Logger.Debug("Getting {TypeName}: found a cached type list.", GetName(baseType, attributeType)); return typeList.Types; } @@ -676,7 +676,7 @@ namespace Umbraco.Core.Composing // so in this instance there will never be a result. if (cacheResult.Exception is CachedTypeNotFoundInFileException || cacheResult.Success == false) { - _logger.Logger.Debug(() => $"Getting {GetName(baseType, attributeType)}: failed to load from cache file, must scan assemblies."); + _logger.Logger.Debug("Getting {TypeName}: failed to load from cache file, must scan assemblies.", GetName(baseType, attributeType)); scan = true; } else @@ -695,7 +695,7 @@ namespace Umbraco.Core.Composing catch (Exception ex) { // in case of any exception, we have to exit, and revert to scanning - _logger.Logger.Error("Getting " + GetName(baseType, attributeType) + ": failed to load cache file type " + type + ", reverting to scanning assemblies.", ex); + _logger.Logger.Error(ex, "Getting {TypeName}: failed to load cache file type {CacheType}, reverting to scanning assemblies.", GetName(baseType, attributeType), type); scan = true; break; } @@ -703,7 +703,7 @@ namespace Umbraco.Core.Composing if (scan == false) { - _logger.Logger.Debug(() => $"Getting {GetName(baseType, attributeType)}: loaded types from cache file."); + _logger.Logger.Debug("Getting {TypeName}: loaded types from cache file.", GetName(baseType, attributeType)); } } } @@ -711,7 +711,7 @@ namespace Umbraco.Core.Composing if (scan) { // either we had to scan, or we could not get the types from the cache file - scan now - _logger.Logger.Debug(() => $"Getting {GetName(baseType, attributeType)}: scanning assemblies."); + _logger.Logger.Debug("Getting {TypeName}: scanning assemblies.", GetName(baseType, attributeType)); foreach (var t in finder()) typeList.Add(t); @@ -729,11 +729,11 @@ namespace Umbraco.Core.Composing UpdateCache(); } - _logger.Logger.Debug(() => $"Got {GetName(baseType, attributeType)}, caching ({added.ToString().ToLowerInvariant()})."); + _logger.Logger.Debug("Got {TypeName}, caching ({CacheType}).", GetName(baseType, attributeType), added.ToString().ToLowerInvariant()); } else { - _logger.Logger.Debug(() => $"Got {GetName(baseType, attributeType)}."); + _logger.Logger.Debug("Got {TypeName}.", GetName(baseType, attributeType)); } return typeList.Types; diff --git a/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs b/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs index 41076e5d91..61f9023167 100644 --- a/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs +++ b/src/Umbraco.Core/Configuration/ClientDependencyConfiguration.cs @@ -79,13 +79,13 @@ namespace Umbraco.Core.Configuration versionAttribute.SetValue(newVersion); clientDependencyConfigXml.Save(_fileName, SaveOptions.DisableFormatting); - _logger.Info(() => $"Updated version number from {oldVersion} to {newVersion}"); + _logger.Info("Updated version number from {OldVersion} to {NewVersion}", oldVersion, newVersion); return true; } } catch (Exception ex) { - _logger.Error("Couldn't update ClientDependency version number", ex); + _logger.Error(ex, "Couldn't update ClientDependency version number"); } return false; @@ -113,13 +113,13 @@ namespace Umbraco.Core.Configuration versionAttribute.SetValue(newVersion); clientDependencyConfigXml.Save(_fileName, SaveOptions.DisableFormatting); - _logger.Info(() => $"Updated version number from {oldVersion} to {newVersion}"); + _logger.Info("Updated version number from {OldVersion} to {NewVersion}", oldVersion, newVersion); return true; } } catch (Exception ex) { - _logger.Error("Couldn't update ClientDependency version number", ex); + _logger.Error(ex, "Couldn't update ClientDependency version number"); } return false; @@ -150,7 +150,7 @@ namespace Umbraco.Core.Configuration catch (Exception ex) { //invalid path format or something... try/catch to be safe - _logger.Error("Could not get path from ClientDependency.config", ex); + _logger.Error(ex, "Could not get path from ClientDependency.config"); } var success = true; @@ -167,7 +167,7 @@ namespace Umbraco.Core.Configuration catch (Exception ex) { // Something could be locking the directory or the was another error, making sure we don't break the upgrade installer - _logger.Error("Could not clear temp files", ex); + _logger.Error(ex, "Could not clear temp files"); success = false; } } diff --git a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs index a0ca2da4ff..708c563d9d 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs @@ -40,13 +40,15 @@ namespace Umbraco.Core.Configuration.Grid var gridConfig = Path.Combine(_configFolder.FullName, "grid.editors.config.js"); if (File.Exists(gridConfig)) { + var sourceString = File.ReadAllText(gridConfig); + try { - editors.AddRange(parser.ParseGridEditors(File.ReadAllText(gridConfig))); + editors.AddRange(parser.ParseGridEditors(sourceString)); } catch (Exception ex) { - _logger.Error("Could not parse the contents of grid.editors.config.js into a JSON array", ex); + _logger.Error(ex, "Could not parse the contents of grid.editors.config.js into a JSON array '{Json}", sourceString); } } diff --git a/src/Umbraco.Core/Configuration/UmbracoConfig.cs b/src/Umbraco.Core/Configuration/UmbracoConfig.cs index 248736d0fe..6dd5617992 100644 --- a/src/Umbraco.Core/Configuration/UmbracoConfig.cs +++ b/src/Umbraco.Core/Configuration/UmbracoConfig.cs @@ -88,7 +88,7 @@ namespace Umbraco.Core.Configuration if (_healthChecks == null) { var ex = new ConfigurationErrorsException("Could not load the " + typeof(IHealthChecks) + " from config file, ensure the web.config and healthchecks.config files are formatted correctly"); - Current.Logger.Error("Config error", ex); + Current.Logger.Error(ex, "Config error"); throw ex; } @@ -103,7 +103,7 @@ namespace Umbraco.Core.Configuration if (_dashboardSection == null) { var ex = new ConfigurationErrorsException("Could not load the " + typeof(IDashboardSection) + " from config file, ensure the web.config and Dashboard.config files are formatted correctly"); - Current.Logger.Error("Config error", ex); + Current.Logger.Error(ex, "Config error"); throw ex; } @@ -162,7 +162,7 @@ namespace Umbraco.Core.Configuration if (_umbracoSettings == null) { var ex = new ConfigurationErrorsException("Could not load the " + typeof (IUmbracoSettingsSection) + " from config file, ensure the web.config and umbracoSettings.config files are formatted correctly"); - Current.Logger.Error("Config error", ex); + Current.Logger.Error(ex, "Config error"); throw ex; } diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 9c0ad4c7c4..8fb650510b 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Configuration /// /// Gets the version comment of the executing code (eg "beta"). /// - public static string CurrentComment => "alpha.44"; + public static string CurrentComment => "alpha.49"; /// /// Gets the assembly version of Umbraco.Code.dll. diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index 9453678a31..dedad995eb 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -111,7 +111,7 @@ namespace Umbraco.Core.IO } catch (Exception e) { - Logger.Error("Failed to delete attached file \"" + file + "\".", e); + Logger.Error(e, "Failed to delete attached file '{File}'", file); } }); } diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index bc9968153f..217ecb51b4 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -73,11 +73,11 @@ namespace Umbraco.Core.IO } catch (UnauthorizedAccessException ex) { - Current.Logger.Error("Not authorized to get directories", ex); + Current.Logger.Error(ex, "Not authorized to get directories for '{Path}'", fullPath); } catch (DirectoryNotFoundException ex) { - Current.Logger.Error("Directory not found", ex); + Current.Logger.Error(ex, "Directory not found for '{Path}'", fullPath); } return Enumerable.Empty(); @@ -109,7 +109,7 @@ namespace Umbraco.Core.IO } catch (DirectoryNotFoundException ex) { - Current.Logger.Error("Directory not found", ex); + Current.Logger.Error(ex, "Directory not found for '{Path}'", fullPath); } } @@ -189,11 +189,11 @@ namespace Umbraco.Core.IO } catch (UnauthorizedAccessException ex) { - Current.Logger.Error("Not authorized to get directories", ex); + Current.Logger.Error(ex, "Not authorized to get directories for '{Path}'", fullPath); } catch (DirectoryNotFoundException ex) { - Current.Logger.Error("Directory not found", ex); + Current.Logger.Error(ex, "Directory not found for '{FullPath}'", fullPath); } return Enumerable.Empty(); @@ -226,7 +226,7 @@ namespace Umbraco.Core.IO } catch (FileNotFoundException ex) { - Current.Logger.Info(() => $"DeleteFile failed with FileNotFoundException: {ex.InnerException}"); + Current.Logger.Error(ex.InnerException, "DeleteFile failed with FileNotFoundException for '{Path}'", fullPath); } } diff --git a/src/Umbraco.Core/IO/ShadowFileSystems.cs b/src/Umbraco.Core/IO/ShadowFileSystems.cs index c101b9a7c2..289667b0db 100644 --- a/src/Umbraco.Core/IO/ShadowFileSystems.cs +++ b/src/Umbraco.Core/IO/ShadowFileSystems.cs @@ -54,7 +54,7 @@ namespace Umbraco.Core.IO } _logger = logger; - _logger.Debug(() => "Shadow " + id + "."); + _logger.Debug("Shadow '{ShadowId}'", id); _id = id; _wrappers = wrappers; @@ -112,7 +112,7 @@ namespace Umbraco.Core.IO { lock (Locker) { - _logger.Debug(() => "UnShadow " + _id + " (" + (_completed ? "complete" : "abort") + ")."); + _logger.Debug("UnShadow '{ShadowId}' {Status}", _id, _completed ? "complete" : "abort"); var exceptions = new List(); foreach (var wrapper in _wrappers) diff --git a/src/Umbraco.Core/Logging/AppDomainTokenConverter.cs b/src/Umbraco.Core/Logging/AppDomainTokenConverter.cs deleted file mode 100644 index 0abddc63e3..0000000000 --- a/src/Umbraco.Core/Logging/AppDomainTokenConverter.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; - -namespace Umbraco.Core.Logging -{ - /// - /// Allows for outputting a normalized appdomainappid token in a log format - /// - public sealed class AppDomainTokenConverter : log4net.Util.PatternConverter - { - protected override void Convert(TextWriter writer, object state) - { - writer.Write(HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty)); - } - } -} diff --git a/src/Umbraco.Core/Logging/AsyncForwardingAppenderBase.cs b/src/Umbraco.Core/Logging/AsyncForwardingAppenderBase.cs deleted file mode 100644 index 3ebcfe458f..0000000000 --- a/src/Umbraco.Core/Logging/AsyncForwardingAppenderBase.cs +++ /dev/null @@ -1,121 +0,0 @@ -using log4net.Appender; -using log4net.Core; -using log4net.Util; -using System; -using System.Runtime.Remoting.Messaging; - -namespace Umbraco.Core.Logging -{ - /// - /// Borrowed from https://github.com/cjbhaines/Log4Net.Async - will reference Nuget packages directly in v8 - /// - public abstract class AsyncForwardingAppenderBase : ForwardingAppender - { - #region Private Members - - private const FixFlags DefaultFixFlags = FixFlags.Partial; - private FixFlags fixFlags = DefaultFixFlags; - private LoggingEventHelper loggingEventHelper; - - #endregion Private Members - - #region Properties - - public FixFlags Fix - { - get { return fixFlags; } - set { SetFixFlags(value); } - } - - /// - /// Returns HttpContext.Current - /// - protected internal object HttpContext - { - get - { - return CallContext.HostContext; - } - set - { - CallContext.HostContext = value; - } - } - - /// - /// The logger name that will be used for logging internal errors. - /// - protected abstract string InternalLoggerName { get; } - - public abstract int? BufferSize { get; set; } - - #endregion Properties - - public override void ActivateOptions() - { - base.ActivateOptions(); - loggingEventHelper = new LoggingEventHelper(InternalLoggerName, DefaultFixFlags); - InitializeAppenders(); - } - - #region Appender Management - - public override void AddAppender(IAppender newAppender) - { - base.AddAppender(newAppender); - SetAppenderFixFlags(newAppender); - } - - private void SetFixFlags(FixFlags newFixFlags) - { - if (newFixFlags != fixFlags) - { - loggingEventHelper.Fix = newFixFlags; - fixFlags = newFixFlags; - InitializeAppenders(); - } - } - - private void InitializeAppenders() - { - foreach (var appender in Appenders) - { - SetAppenderFixFlags(appender); - } - } - - private void SetAppenderFixFlags(IAppender appender) - { - var bufferingAppender = appender as BufferingAppenderSkeleton; - if (bufferingAppender != null) - { - bufferingAppender.Fix = Fix; - } - } - - #endregion Appender Management - - #region Forwarding - - protected void ForwardInternalError(string message, Exception exception, Type thisType) - { - LogLog.Error(thisType, message, exception); - var loggingEvent = loggingEventHelper.CreateLoggingEvent(Level.Error, message, exception); - ForwardLoggingEvent(loggingEvent, thisType); - } - - protected void ForwardLoggingEvent(LoggingEvent loggingEvent, Type thisType) - { - try - { - base.Append(loggingEvent); - } - catch (Exception exception) - { - LogLog.Error(thisType, "Unable to forward logging event", exception); - } - } - - #endregion Forwarding - } -} diff --git a/src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs b/src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs index 4bb7aeb464..a8ca9b6c31 100644 --- a/src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs +++ b/src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; namespace Umbraco.Core.Logging { @@ -9,95 +8,107 @@ namespace Umbraco.Core.Logging public class DebugDiagnosticsLogger : ILogger { /// - public void Error(Type reporting, string message, Exception exception = null) + public void Fatal(Type reporting, Exception exception, string message) { System.Diagnostics.Debug.WriteLine(message + Environment.NewLine + exception, reporting.FullName); } + /// + public void Fatal(Type reporting, Exception exception) + { + System.Diagnostics.Debug.WriteLine(Environment.NewLine + exception, reporting.FullName); + } + + /// + public void Fatal(Type reporting, string message) + { + System.Diagnostics.Debug.WriteLine(message); + } + + /// + public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] args) + { + System.Diagnostics.Debug.WriteLine(string.Format(messageTemplate, args) + Environment.NewLine + exception, reporting.FullName); + } + + /// + public void Fatal(Type reporting, string messageTemplate, params object[] args) + { + System.Diagnostics.Debug.WriteLine(messageTemplate, args); + } + + /// + public void Error(Type reporting, Exception exception, string message) + { + System.Diagnostics.Debug.WriteLine(message + Environment.NewLine + exception, reporting.FullName); + } + + /// + public void Error(Type reporting, Exception exception) + { + System.Diagnostics.Debug.WriteLine(Environment.NewLine + exception, reporting.FullName); + } + + /// + public void Error(Type reporting, string message) + { + System.Diagnostics.Debug.WriteLine(message); + } + + /// + public void Error(Type reporting, Exception exception, string messageTemplate, params object[] args) + { + System.Diagnostics.Debug.WriteLine(string.Format(messageTemplate, args) + Environment.NewLine + exception, reporting.FullName); + } + + /// + public void Error(Type reporting, string messageTemplate, params object[] args) + { + System.Diagnostics.Debug.WriteLine(messageTemplate, args); + } + /// public void Warn(Type reporting, string format) { System.Diagnostics.Debug.WriteLine(format, reporting.FullName); } - - /// - public void Warn(Type reporting, Func messageBuilder) - { - System.Diagnostics.Debug.WriteLine(messageBuilder(), reporting.FullName); - } - + /// public void Warn(Type reporting, string format, params object[] args) { System.Diagnostics.Debug.WriteLine(string.Format(format, args), reporting.FullName); } - /// - public void Warn(Type reporting, string format, params Func[] args) - { - System.Diagnostics.Debug.WriteLine(string.Format(format, args.Select(x => x()).ToArray()), reporting.FullName); - } - /// public void Warn(Type reporting, Exception exception, string message) { System.Diagnostics.Debug.WriteLine(message + Environment.NewLine + exception, reporting.FullName); } - /// - public void Warn(Type reporting, Exception exception, Func messageBuilder) - { - System.Diagnostics.Debug.WriteLine(messageBuilder() + Environment.NewLine + exception, reporting.FullName); - } - /// public void Warn(Type reporting, Exception exception, string format, params object[] args) { System.Diagnostics.Debug.WriteLine(string.Format(format + Environment.NewLine + exception, args), reporting.FullName); } - /// - public void Warn(Type reporting, Exception exception, string format, params Func[] args) - { - System.Diagnostics.Debug.WriteLine(string.Format(format + Environment.NewLine + exception, args.Select(x => x()).ToArray()), reporting.FullName); - } - /// public void Info(Type reporting, string message) { System.Diagnostics.Debug.WriteLine(message, reporting.FullName); } - /// - public void Info(Type reporting, Func messageBuilder) - { - System.Diagnostics.Debug.WriteLine(messageBuilder(), reporting.FullName); - } - /// public void Info(Type reporting, string format, params object[] args) { System.Diagnostics.Debug.WriteLine(string.Format(format, args), reporting.FullName); } - /// - public void Info(Type reporting, string format, params Func[] args) - { - System.Diagnostics.Debug.WriteLine(string.Format(format, args.Select(x => x()).ToArray()), reporting.FullName); - } - /// public void Debug(Type reporting, string message) { System.Diagnostics.Debug.WriteLine(message, reporting.FullName); } - /// - public void Debug(Type reporting, Func messageBuilder) - { - System.Diagnostics.Debug.WriteLine(messageBuilder(), reporting.FullName); - } - /// public void Debug(Type reporting, string format, params object[] args) { @@ -105,9 +116,16 @@ namespace Umbraco.Core.Logging } /// - public void Debug(Type reporting, string format, params Func[] args) + public void Verbose(Type reporting, string message) { - System.Diagnostics.Debug.WriteLine(string.Format(format, args.Select(x => x()).ToArray()), reporting.FullName); + System.Diagnostics.Debug.WriteLine(message, reporting.FullName); } + + /// + public void Verbose(Type reporting, string format, params object[] args) + { + System.Diagnostics.Debug.WriteLine(string.Format(format, args), reporting.FullName); + } + } } diff --git a/src/Umbraco.Core/DisposableTimer.cs b/src/Umbraco.Core/Logging/DisposableTimer.cs similarity index 81% rename from src/Umbraco.Core/DisposableTimer.cs rename to src/Umbraco.Core/Logging/DisposableTimer.cs index 58382a50e9..869ca2cd44 100644 --- a/src/Umbraco.Core/DisposableTimer.cs +++ b/src/Umbraco.Core/Logging/DisposableTimer.cs @@ -1,8 +1,7 @@ using System; using System.Diagnostics; -using Umbraco.Core.Logging; -namespace Umbraco.Core +namespace Umbraco.Core.Logging { /// /// Starts the timer and invokes a callback upon disposal. Provides a simple way of timing an operation by wrapping it in a using (C#) statement. @@ -18,6 +17,7 @@ namespace Umbraco.Core private string _failMessage; private Exception _failException; private bool _failed; + private readonly string _timingId; internal enum LogType { @@ -38,16 +38,17 @@ namespace Umbraco.Core _endMessage = endMessage; _failMessage = failMessage; _thresholdMilliseconds = thresholdMilliseconds < 0 ? 0 : thresholdMilliseconds; + _timingId = Guid.NewGuid().ToString("N"); if (thresholdMilliseconds == 0) { switch (logType) { case LogType.Debug: - logger.Debug(loggerType, startMessage); + logger.Debug(loggerType, "[Timing {TimingId}] {StartMessage}", _timingId, startMessage); break; case LogType.Info: - logger.Info(loggerType, startMessage); + logger.Info(loggerType, "[Timing {TimingId}] {StartMessage}", _timingId, startMessage); break; default: throw new ArgumentOutOfRangeException(nameof(logType)); @@ -91,15 +92,15 @@ namespace Umbraco.Core { if (_failed) { - _logger.Error(_loggerType, $"{_failMessage} ({Stopwatch.ElapsedMilliseconds}ms)", _failException); + _logger.Error(_loggerType, _failException, "[Timing {TimingId}] {FailMessage} ({TimingDuration}ms)", _timingId, _failMessage, Stopwatch.ElapsedMilliseconds); } else switch (_logType) { case LogType.Debug: - _logger.Debug(_loggerType, () => $"{_endMessage} ({Stopwatch.ElapsedMilliseconds}ms)"); + _logger.Debug(_loggerType, "[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)", _timingId, _endMessage, Stopwatch.ElapsedMilliseconds); break; case LogType.Info: - _logger.Info(_loggerType, () => $"{_endMessage} ({Stopwatch.ElapsedMilliseconds}ms)"); + _logger.Info(_loggerType, "[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)", _timingId, _endMessage, Stopwatch.ElapsedMilliseconds); break; // filtered in the ctor //default: diff --git a/src/Umbraco.Core/Logging/ILogger.cs b/src/Umbraco.Core/Logging/ILogger.cs index 905ba9f7fa..2162b10bfe 100644 --- a/src/Umbraco.Core/Logging/ILogger.cs +++ b/src/Umbraco.Core/Logging/ILogger.cs @@ -1,22 +1,98 @@ using System; -using System.ComponentModel; namespace Umbraco.Core.Logging { /// /// Defines the logging service. /// + /// + /// Message templates in logging methods follow the Message Templates specification + /// available at https://messagetemplates.org/ in order to support structured logging. + /// Implementations must ensure that they support these templates. Note that the + /// specification includes support for traditional C# numeric placeholders. + /// For instance, "Processed {Input} in {Time}ms." + /// public interface ILogger { + /// + /// Logs a fatal message with an exception. + /// + /// The reporting type. + /// An exception. + /// A message. + void Fatal(Type reporting, Exception exception, string message); + + /// + /// Logs a fatal exception. + /// + /// The reporting type. + /// An exception. + /// The message string is unspecified and is implementation-specific. + void Fatal(Type reporting, Exception exception); + + /// + /// Logs a fatal message. + /// + /// The reporting type. + /// A message. + void Fatal(Type reporting, string message); + + /// + /// Logs a fatal message with an exception. + /// + /// The reporting type. + /// An exception. + /// A message template. + /// Property values. + void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues); + + /// + /// Logs a fatal message. + /// + /// The reporting type. + /// A message template. + /// Property values. + void Fatal(Type reporting, string messageTemplate, params object[] propertyValues); + + /// + /// Logs an error message with an exception. + /// + /// The reporting type. + /// An exception. + /// A message. + void Error(Type reporting, Exception exception, string message); + + /// + /// Logs an error exception. + /// + /// The reporting type. + /// An exception. + /// The message string is unspecified and is implementation-specific. + void Error(Type reporting, Exception exception); + /// /// Logs an error message. /// /// The reporting type. /// A message. - /// An exception. - void Error(Type reporting, string message, Exception exception = null); + void Error(Type reporting, string message); - // note: should we have more overloads for Error too? + /// + /// Logs an error message with an exception. + /// + /// The reporting type. + /// An exception. + /// A message template. + /// Property values. + void Error(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues); + + /// + /// Logs an error message. + /// + /// The reporting type. + /// A message template. + /// Property values. + void Error(Type reporting, string messageTemplate, params object[] propertyValues); /// /// Logs a warning message. @@ -29,8 +105,9 @@ namespace Umbraco.Core.Logging /// Logs a warning message. /// /// The reporting type. - /// A message builder. - void Warn(Type reporting, Func messageBuilder); + /// A message template. + /// Property values. + void Warn(Type reporting, string messageTemplate, params object[] propertyValues); /// /// Logs a warning message with an exception. @@ -45,8 +122,9 @@ namespace Umbraco.Core.Logging /// /// The reporting type. /// An exception. - /// A message builder. - void Warn(Type reporting, Exception exception, Func messageBuilder); + /// A message template. + /// Property values. + void Warn(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues); /// /// Logs an information message. @@ -56,11 +134,12 @@ namespace Umbraco.Core.Logging void Info(Type reporting, string message); /// - /// Logs an information message. + /// Logs a info message. /// /// The reporting type. - /// A message builder. - void Info(Type reporting, Func messageBuilder); + /// A message template. + /// Property values. + void Info(Type reporting, string messageTemplate, params object[] propertyValues); /// /// Logs a debugging message. @@ -70,10 +149,26 @@ namespace Umbraco.Core.Logging void Debug(Type reporting, string message); /// - /// Logs a debugging message. + /// Logs a debug message. /// /// The reporting type. - /// A message builder. - void Debug(Type reporting, Func messageBuilder); + /// A message template. + /// Property values. + void Debug(Type reporting, string messageTemplate, params object[] propertyValues); + + /// + /// Logs a verbose message. + /// + /// The reporting type. + /// A message. + void Verbose(Type reporting, string message); + + /// + /// Logs a verbose message. + /// + /// The reporting type. + /// A message template. + /// Property values. + void Verbose(Type reporting, string messageTemplate, params object[] propertyValues); } } diff --git a/src/Umbraco.Core/Logging/IQueue.cs b/src/Umbraco.Core/Logging/IQueue.cs deleted file mode 100644 index db87cb7705..0000000000 --- a/src/Umbraco.Core/Logging/IQueue.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Umbraco.Core.Logging -{ - /// - /// Borrowed from https://github.com/cjbhaines/Log4Net.Async - will reference Nuget packages directly in v8 - /// - /// - internal interface IQueue - { - void Enqueue(T item); - bool TryDequeue(out T ret); - } -} diff --git a/src/Umbraco.Core/Logging/ImageProcessorLogger.cs b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs index 02faac39aa..fa1f117e06 100644 --- a/src/Umbraco.Core/Logging/ImageProcessorLogger.cs +++ b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs @@ -27,7 +27,7 @@ namespace Umbraco.Core.Logging { // Using LogHelper since the ImageProcessor logger expects a parameterless constructor. var message = $"{callerName} {lineNumber} : {text}"; - Current.Logger.Error(string.Empty, new ImageProcessingException(message)); + Current.Logger.Error(new ImageProcessingException(message)); } /// @@ -41,7 +41,7 @@ namespace Umbraco.Core.Logging { // Using LogHelper since the ImageProcessor logger expects a parameterless constructor. var message = $"{callerName} {lineNumber} : {text}"; - Current.Logger.Error(type, string.Empty, new ImageProcessingException(message)); + Current.Logger.Error(type, new ImageProcessingException(message)); } } } diff --git a/src/Umbraco.Core/Logging/LogProfiler.cs b/src/Umbraco.Core/Logging/LogProfiler.cs index 6fbb1b2e26..b80e40942a 100644 --- a/src/Umbraco.Core/Logging/LogProfiler.cs +++ b/src/Umbraco.Core/Logging/LogProfiler.cs @@ -24,8 +24,8 @@ namespace Umbraco.Core.Logging /// public IDisposable Step(string name) { - _logger.Debug(() => $"Begin: {name}."); - return new LightDisposableTimer(duration => _logger.Info(() => $"End {name}. ({duration}ms)")); + _logger.Debug("Begin: {ProfileName}", name); + return new LightDisposableTimer(duration => _logger.Info("End {ProfileName} ({ProfileDuration}ms)", name, duration)); } /// diff --git a/src/Umbraco.Core/Logging/Logger.cs b/src/Umbraco.Core/Logging/Logger.cs index 188cf01ac9..0a20022e93 100644 --- a/src/Umbraco.Core/Logging/Logger.cs +++ b/src/Umbraco.Core/Logging/Logger.cs @@ -1,61 +1,129 @@ using System; -using System.Diagnostics; -using System.Globalization; using System.IO; -using System.Linq; using System.Reflection; using System.Threading; -using log4net; -using log4net.Config; using Umbraco.Core.Configuration; using Umbraco.Core.Diagnostics; -using log4net.Util; +using Serilog; +using Serilog.Events; +using Umbraco.Core.Logging.SerilogExtensions; namespace Umbraco.Core.Logging { /// - /// Implements on top of log4net. + /// Implements on top of Serilog. /// public class Logger : ILogger { /// - /// Initialize a new instance of the class with a log4net configuration file. + /// Initialize a new instance of the class with a configuration file. /// - /// - public Logger(FileInfo log4NetConfigFile) - : this() + /// + public Logger(FileInfo logConfigFile) { - XmlConfigurator.Configure(log4NetConfigFile); + Log.Logger = new LoggerConfiguration() + .ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + logConfigFile) + .CreateLogger(); } - // private for CreateWithDefaultLog4NetConfiguration - private Logger() + public Logger(LoggerConfiguration logConfig) { - // add custom global properties to the log4net context that we can use in our logging output - GlobalContext.Properties["processId"] = Process.GetCurrentProcess().Id; - GlobalContext.Properties["appDomainId"] = AppDomain.CurrentDomain.Id; + //Configure Serilog static global logger with config passed in + Log.Logger = logConfig.CreateLogger(); } /// - /// Creates a logger with the default log4net configuration discovered (i.e. from the web.config). + /// Creates a logger with some pre-definied configuration and remainder from config file /// /// Used by UmbracoApplicationBase to get its logger. - public static Logger CreateWithDefaultLog4NetConfiguration() + public static Logger CreateWithDefaultConfiguration() { - return new Logger(); + var loggerConfig = new LoggerConfiguration(); + loggerConfig + .MinimalConfiguration() + .OutputDefaultTextFile(LogEventLevel.Debug) + .OutputDefaultJsonFile() + .ReadFromConfigFile() + .ReadFromUserConfigFile(); + + return new Logger(loggerConfig); } /// - public void Error(Type reporting, string message, Exception exception = null) + public void Fatal(Type reporting, Exception exception, string message) { - var logger = LogManager.GetLogger(reporting); - if (logger == null) return; + Fatal(reporting, exception, message, null); + } + /// + public void Fatal(Type reporting, Exception exception) + { + Fatal(reporting, exception, string.Empty); + } + + /// + public void Fatal(Type reporting, string message) + { + //Sometimes we need to throw an error without an ex + Fatal(reporting, null, message); + } + + /// + public void Fatal(Type reporting, string messageTemplate, params object[] propertyValues) + { + //Log a structured message WITHOUT an ex + Fatal(reporting, null, messageTemplate, propertyValues); + } + + /// + public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) + { + ErrorOrFatal(Fatal, exception, ref messageTemplate); + var logger = Log.Logger; + logger?.ForContext(reporting).Fatal(exception, messageTemplate, propertyValues); + } + + /// + public void Error(Type reporting, Exception exception, string message) + { + Error(reporting, exception, message, null); + } + + /// + public void Error(Type reporting, Exception exception) + { + Error(reporting, exception, string.Empty); + } + + /// + public void Error(Type reporting, string message) + { + //Sometimes we need to throw an error without an ex + Error(reporting, null, message); + } + + /// + public void Error(Type reporting, string messageTemplate, params object[] propertyValues) + { + //Log a structured message WITHOUT an ex + Error(reporting, null, messageTemplate, propertyValues); + } + + /// + public void Error(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) + { + ErrorOrFatal(Error, exception, ref messageTemplate); + var logger = Log.Logger; + logger?.ForContext(reporting).Error(exception, messageTemplate, propertyValues); + } + + private static void ErrorOrFatal(Action logAction, Exception exception, ref string messageTemplate) + { var dump = false; if (IsTimeoutThreadAbortException(exception)) { - message += "\r\nThe thread has been aborted, because the request has timed out."; + messageTemplate += "\r\nThe thread has been aborted, because the request has timed out."; // dump if configured, or if stacktrace contains Monitor.ReliableEnter dump = UmbracoConfig.For.CoreDebug().DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception); @@ -69,23 +137,22 @@ namespace Umbraco.Core.Logging try { var dumped = MiniDump.Dump(withException: true); - message += dumped + messageTemplate += dumped ? "\r\nA minidump was created in App_Data/MiniDump" : "\r\nFailed to create a minidump"; } - catch (Exception e) + catch (Exception ex) { - message += string.Format("\r\nFailed to create a minidump ({0}: {1})", e.GetType().FullName, e.Message); + //Log a new entry (as opposed to appending to same log entry) + logAction(ex.GetType(), ex, "Failed to create a minidump at App_Data/MiniDump ({ExType}: {ExMessage}", + new object[]{ ex.GetType().FullName, ex.Message }); } } - - logger.Error(message, exception); } private static bool IsMonitorEnterThreadAbortException(Exception exception) { - var abort = exception as ThreadAbortException; - if (abort == null) return false; + if (!(exception is ThreadAbortException abort)) return false; var stacktrace = abort.StackTrace; return stacktrace.Contains("System.Threading.Monitor.ReliableEnter"); @@ -93,8 +160,7 @@ namespace Umbraco.Core.Logging private static bool IsTimeoutThreadAbortException(Exception exception) { - var abort = exception as ThreadAbortException; - if (abort == null) return false; + if (!(exception is ThreadAbortException abort)) return false; if (abort.ExceptionState == null) return false; @@ -110,135 +176,67 @@ namespace Umbraco.Core.Logging /// public void Warn(Type reporting, string format) { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsWarnEnabled == false) return; - logger.Warn(format); + Warn(reporting, null, format); } - + /// - public void Warn(Type reporting, Func messageBuilder) + public void Warn(Type reporting, string messageTemplate, params object[] propertyValues) { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsWarnEnabled == false) return; - logger.Warn(messageBuilder()); - } - - /// - public void Warn(Type reporting, string format, params object[] args) - { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsWarnEnabled == false) return; - logger.WarnFormat(format, args); - } - - /// - public void Warn(Type reporting, string format, params Func[] args) - { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsWarnEnabled == false) return; - logger.WarnFormat(format, args.Select(x => x.Invoke()).ToArray()); + Warn(reporting, null, messageTemplate, propertyValues); } /// public void Warn(Type reporting, Exception exception, string message) { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsWarnEnabled == false) return; - logger.Warn(message, exception); + Warn(reporting, exception, message, Array.Empty()); } - + /// - public void Warn(Type reporting, Exception exception, Func messageBuilder) + public void Warn(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsWarnEnabled == false) return; - logger.Warn(messageBuilder(), exception); - } - - /// - public void Warn(Type reporting, Exception exception, string format, params object[] args) - { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsWarnEnabled == false) return; - // there is no WarnFormat overload that accepts an exception - // format the message the way log4net would do it (see source code LogImpl.cs) - var message = new SystemStringFormat(CultureInfo.InvariantCulture, format, args); - logger.Warn(message, exception); - } - - /// - public void Warn(Type reporting, Exception exception, string format, params Func[] args) - { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsWarnEnabled == false) return; - // there is no WarnFormat overload that accepts an exception - // format the message the way log4net would do it (see source code LogImpl.cs) - var message = new SystemStringFormat(CultureInfo.InvariantCulture, format, args.Select(x => x.Invoke()).ToArray()); - logger.Warn(message, exception); + var logger = Log.Logger; + logger?.ForContext(reporting).Warning(exception, messageTemplate, propertyValues); } /// public void Info(Type reporting, string message) { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsInfoEnabled == false) return; - logger.Info(message); + Info(reporting, message, Array.Empty()); } /// - public void Info(Type reporting, Func generateMessage) + public void Info(Type reporting, string messageTemplate, params object[] propertyValues) { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsInfoEnabled == false) return; - logger.Info(generateMessage()); - } - - /// - public void Info(Type reporting, string format, params object[] args) - { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsInfoEnabled == false) return; - logger.InfoFormat(format, args); - } - - /// - public void Info(Type reporting, string format, params Func[] args) - { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsInfoEnabled == false) return; - logger.InfoFormat(format, args.Select(x => x.Invoke()).ToArray()); + var logger = Log.Logger; + logger?.ForContext(reporting).Information(messageTemplate, propertyValues); } /// public void Debug(Type reporting, string message) { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsDebugEnabled == false) return; - logger.Debug(message); + Debug(reporting, message, Array.Empty()); + } + + /// + public void Debug(Type reporting, string messageTemplate, params object[] propertyValues) + { + var logger = Log.Logger; + logger?.ForContext(reporting).Debug(messageTemplate, propertyValues); } /// - public void Debug(Type reporting, Func messageBuilder) + public void Verbose(Type reporting, string message) { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsDebugEnabled == false) return; - logger.Debug(messageBuilder()); + Verbose(reporting, message, Array.Empty()); } /// - public void Debug(Type reporting, string format, params object[] args) + public void Verbose(Type reporting, string messageTemplate, params object[] propertyValues) { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsDebugEnabled == false) return; - logger.DebugFormat(format, args); + var logger = Log.Logger; + logger?.ForContext(reporting).Verbose(messageTemplate, propertyValues); } - /// - public void Debug(Type reporting, string format, params Func[] args) - { - var logger = LogManager.GetLogger(reporting); - if (logger == null || logger.IsDebugEnabled == false) return; - logger.DebugFormat(format, args.Select(x => x.Invoke()).ToArray()); - } + } } diff --git a/src/Umbraco.Core/Logging/LoggerExtensions.cs b/src/Umbraco.Core/Logging/LoggerExtensions.cs index 4c0a6b026a..c29bcef4f7 100644 --- a/src/Umbraco.Core/Logging/LoggerExtensions.cs +++ b/src/Umbraco.Core/Logging/LoggerExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; namespace Umbraco.Core.Logging { @@ -9,15 +8,62 @@ namespace Umbraco.Core.Logging public static class LoggerExtensions { /// - /// Logs an error message. + /// Logs an error message /// /// The reporting type. /// The logger. /// A message. /// An exception. - public static void Error(this ILogger logger, string message, Exception exception = null) + public static void Error(this ILogger logger, Exception exception, string message) { - logger.Error(typeof(T), message, exception); + logger.Error(typeof(T), exception, message); + } + + /// + /// Logs an error message with a structured message template + /// + /// The reporting type + /// The logger. + /// A structured message template + /// An exception + /// Message property values + public static void Error(this ILogger logger, Exception exception, string messageTemplate, params object[] propertyValues) + { + logger.Error(typeof(T), exception, messageTemplate, propertyValues); + } + + /// + /// Logs an error message NOTE: This will log an empty message string + /// + /// The reporting type + /// The logger. + /// An exception + public static void Error(this ILogger logger, Exception exception) + { + logger.Error(typeof(T), exception); + } + + /// + /// Logs an error message WITHOUT EX + /// + /// + /// + /// + public static void Error(this ILogger logger, string message) + { + logger.Error(typeof(T), message); + } + + /// + /// Logs an error message - using a structured log message + /// + /// The reporting type + /// The logger. + /// A structured message template + /// Message property values + public static void Error(this ILogger logger, string messageTemplate, params object[] propertyValues) + { + logger.Error(typeof(T), messageTemplate, propertyValues); } /// @@ -32,26 +78,15 @@ namespace Umbraco.Core.Logging } /// - /// Logs a warning message. + /// Logs a warning message with a structured message template /// - /// The reporting type. + /// The reporting type /// The logger. - /// A message builder. - public static void Warn(this ILogger logger, Func messageBuilder) + /// A structured message template + /// Message property values + public static void Warn(this ILogger logger, string messageTemplate, params object[] propertyValues) { - logger.Warn(typeof(T), messageBuilder); - } - - /// - /// Logs a formatted warning message with an exception. - /// - /// The reporting type. - /// The logger. - /// An exception. - /// A message builder. - public static void Warn(this ILogger logger, Exception exception, Func messageBuilder) - { - logger.Warn(typeof(T), exception, messageBuilder); + logger.Warn(typeof(T), messageTemplate, propertyValues); } /// @@ -66,6 +101,19 @@ namespace Umbraco.Core.Logging logger.Warn(typeof(T), exception, message); } + /// + /// Logs a warning message with an exception with a structured message template + /// + /// The reporting type + /// The logger. + /// An exception + /// A structured message template + /// Message property values + public static void Warn(this ILogger logger, Exception exception, string messageTemplate, params object[] propertyValues) + { + logger.Warn(typeof(T), exception, messageTemplate, propertyValues); + } + /// /// Logs an information message. /// @@ -78,14 +126,15 @@ namespace Umbraco.Core.Logging } /// - /// Logs an information message. + /// Logs a information message with a structured message template /// - /// The reporting type. + /// The reporting type /// The logger. - /// A message builder. - public static void Info(this ILogger logger, Func messageBuilder) + /// A structured message template + /// Message property values + public static void Info(this ILogger logger, string messageTemplate, params object[] propertyValues) { - logger.Info(typeof(T), messageBuilder); + logger.Info(typeof(T), messageTemplate, propertyValues); } /// @@ -100,14 +149,66 @@ namespace Umbraco.Core.Logging } /// - /// Logs a debugging message. + /// Logs a debugging message with a structured message template + /// + /// The reporting type + /// The logger. + /// A structured message template + /// Message property values + public static void Debug(this ILogger logger, string messageTemplate, params object[] propertyValues) + { + logger.Debug(typeof(T), messageTemplate, propertyValues); + } + + /// + /// Logs a verbose message. /// /// The reporting type. /// The logger. - /// A message builder. - public static void Debug(this ILogger logger, Func messageBuilder) + /// A message. + public static void Verbose(this ILogger logger, string message) { - logger.Debug(typeof(T), messageBuilder); + logger.Verbose(typeof(T), message); } + + /// + /// Logs a Verbose message with a structured message template + /// + /// The reporting type + /// The logger. + /// A structured message template + /// Message property values + public static void Verbose(this ILogger logger, string messageTemplate, params object[] propertyValues) + { + logger.Verbose(typeof(T), messageTemplate, propertyValues); + } + + + /// + /// Logs a fatal message. + /// + /// The reporting type. + /// The logger. + /// An exception. + /// A message. + public static void Fatal(this ILogger logger, Exception exception, string message) + { + logger.Fatal(typeof(T), exception, message); + } + + + /// + /// Logs a fatal message with a structured message template + /// + /// The reporting type. + /// The logger. + /// An exception. + /// A structured message template + /// Message property values + public static void Fatal(this ILogger logger, Exception exception, string messageTemplate, params object[] propertyValues) + { + logger.Fatal(typeof(T), exception, messageTemplate, propertyValues); + } + } } diff --git a/src/Umbraco.Core/Logging/LoggingEventContext.cs b/src/Umbraco.Core/Logging/LoggingEventContext.cs deleted file mode 100644 index 2dac3c4676..0000000000 --- a/src/Umbraco.Core/Logging/LoggingEventContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -using log4net.Core; - -namespace Umbraco.Core.Logging -{ - /// - /// Borrowed from https://github.com/cjbhaines/Log4Net.Async - will reference Nuget packages directly in v8 - /// - internal sealed class LoggingEventContext - { - public LoggingEventContext(LoggingEvent loggingEvent, object httpContext) - { - LoggingEvent = loggingEvent; - HttpContext = httpContext; - } - - public LoggingEvent LoggingEvent { get; set; } - - public object HttpContext { get; set; } - } -} diff --git a/src/Umbraco.Core/Logging/LoggingEventHelper.cs b/src/Umbraco.Core/Logging/LoggingEventHelper.cs deleted file mode 100644 index a92e557f34..0000000000 --- a/src/Umbraco.Core/Logging/LoggingEventHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using log4net.Core; - -namespace Umbraco.Core.Logging -{ - /// - /// Borrowed from https://github.com/cjbhaines/Log4Net.Async - will reference Nuget packages directly in v8 - /// - internal sealed class LoggingEventHelper - { - // needs to be a seperate class so that location is determined correctly by log4net when required - - private static readonly Type HelperType = typeof(LoggingEventHelper); - private readonly string loggerName; - - public FixFlags Fix { get; set; } - - public LoggingEventHelper(string loggerName, FixFlags fix) - { - this.loggerName = loggerName; - Fix = fix; - } - - public LoggingEvent CreateLoggingEvent(Level level, string message, Exception exception) - { - var loggingEvent = new LoggingEvent(HelperType, null, loggerName, level, message, exception) - { - Fix = Fix - }; - return loggingEvent; - } - } -} diff --git a/src/Umbraco.Core/Logging/OwinLogger.cs b/src/Umbraco.Core/Logging/OwinLogger.cs index a6ae0c332a..5601cb53f2 100644 --- a/src/Umbraco.Core/Logging/OwinLogger.cs +++ b/src/Umbraco.Core/Logging/OwinLogger.cs @@ -26,34 +26,34 @@ namespace Umbraco.Core.Logging switch (eventType) { case TraceEventType.Critical: - _logger.Error(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state), exception ?? new Exception("Critical error")); + _logger.Fatal(_type.Value, exception, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; case TraceEventType.Error: - _logger.Error(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state), exception ?? new Exception("Error")); + _logger.Error(_type.Value, exception, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; case TraceEventType.Warning: - _logger.Warn(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state)); + _logger.Warn(_type.Value, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; case TraceEventType.Information: - _logger.Info(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state)); + _logger.Info(_type.Value, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; case TraceEventType.Verbose: - _logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state)); + _logger.Debug(_type.Value, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; case TraceEventType.Start: - _logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state)); + _logger.Debug(_type.Value, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; case TraceEventType.Stop: - _logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state)); + _logger.Debug(_type.Value, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; case TraceEventType.Suspend: - _logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state)); + _logger.Debug(_type.Value, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; case TraceEventType.Resume: - _logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state)); + _logger.Debug(_type.Value, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; case TraceEventType.Transfer: - _logger.Debug(_type.Value, string.Format("Event Id: {0}, state: {1}", eventId, state)); + _logger.Debug(_type.Value, "[{EventType}] Event Id: {EventId}, State: {State}", eventType, eventId, state); return true; default: throw new ArgumentOutOfRangeException("eventType"); diff --git a/src/Umbraco.Core/Logging/RingBuffer.cs b/src/Umbraco.Core/Logging/RingBuffer.cs deleted file mode 100644 index 18f28faf6b..0000000000 --- a/src/Umbraco.Core/Logging/RingBuffer.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; - -namespace Umbraco.Core.Logging -{ - /// - /// Borrowed from https://github.com/cjbhaines/Log4Net.Async - will reference Nuget packages directly in v8 - /// - /// - internal sealed class RingBuffer : IQueue - { - private readonly object lockObject = new object(); - private readonly T[] buffer; - private readonly int size; - private int readIndex = 0; - private int writeIndex = 0; - private bool bufferFull = false; - - public int Size { get { return size; } } - - public event Action BufferOverflow; - - public RingBuffer(int size) - { - this.size = size; - buffer = new T[size]; - } - - public void Enqueue(T item) - { - var bufferWasFull = false; - lock (lockObject) - { - buffer[writeIndex] = item; - writeIndex = (++writeIndex) % size; - if (bufferFull) - { - bufferWasFull = true; - readIndex = writeIndex; - } - else if (writeIndex == readIndex) - { - bufferFull = true; - } - } - - if (bufferWasFull) - { - if (BufferOverflow != null) - { - BufferOverflow(this, EventArgs.Empty); - } - } - } - - public bool TryDequeue(out T ret) - { - if (readIndex == writeIndex && !bufferFull) - { - ret = default(T); - return false; - } - lock (lockObject) - { - if (readIndex == writeIndex && !bufferFull) - { - ret = default(T); - return false; - } - - ret = buffer[readIndex]; - buffer[readIndex] = default(T); - readIndex = (++readIndex) % size; - bufferFull = false; - return true; - } - } - } -} diff --git a/src/Umbraco.Core/Logging/RollingFileCleanupAppender.cs b/src/Umbraco.Core/Logging/RollingFileCleanupAppender.cs deleted file mode 100644 index 6be2552296..0000000000 --- a/src/Umbraco.Core/Logging/RollingFileCleanupAppender.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.IO; -using log4net.Appender; -using log4net.Util; - -namespace Umbraco.Core.Logging -{ - /// - /// This class will do the exact same thing as the RollingFileAppender that comes from log4net - /// With the extension, that it is able to do automatic cleanup of the logfiles in the directory where logging happens - /// - /// By specifying the properties MaxLogFileDays and BaseFilePattern, the files will automaticly get deleted when - /// the logger is configured(typically when the app starts). To utilize this appender swap out the type of the rollingFile appender - /// that ships with Umbraco, to be Umbraco.Core.Logging.RollingFileCleanupAppender, and add the maxLogFileDays and baseFilePattern elements - /// to the configuration i.e.: - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public class RollingFileCleanupAppender : RollingFileAppender - { - public int MaxLogFileDays { get; set; } - public string BaseFilePattern { get; set; } - - /// - /// This override will delete logs older than the specified amount of days - /// - /// - /// - protected override void OpenFile(string fileName, bool append) - { - bool cleanup = true; - // Validate settings and input - if (MaxLogFileDays <= 0) - { - LogLog.Warn(typeof(RollingFileCleanupAppender), "Parameter 'MaxLogFileDays' needs to be a positive integer, aborting cleanup"); - cleanup = false; - } - - if (string.IsNullOrWhiteSpace(BaseFilePattern)) - { - LogLog.Warn(typeof(RollingFileCleanupAppender), "Parameter 'BaseFilePattern' is empty, aborting cleanup"); - cleanup = false; - } - // grab the directory we are logging to, as this is were we will search for older logfiles - var logFolder = Path.GetDirectoryName(fileName); - if (Directory.Exists(logFolder) == false) - { - LogLog.Warn(typeof(RollingFileCleanupAppender), string.Format("Directory '{0}' for logfiles does not exist, aborting cleanup", logFolder)); - cleanup = false; - } - // If everything is validated, we can do the actual cleanup - if (cleanup) - { - Cleanup(logFolder); - } - - base.OpenFile(fileName, append); - } - - private void Cleanup(string directoryPath) - { - // only take files that matches the pattern we are using i.e. UmbracoTraceLog.*.txt.* - string[] logFiles = Directory.GetFiles(directoryPath, BaseFilePattern); - LogLog.Debug(typeof(RollingFileCleanupAppender), string.Format("Found {0} files that matches the baseFilePattern: '{1}'", logFiles.Length, BaseFilePattern)); - - foreach (var logFile in logFiles) - { - DateTime lastAccessTime = System.IO.File.GetLastWriteTimeUtc(logFile); - // take the value from the config file - if (lastAccessTime < DateTime.Now.AddDays(-MaxLogFileDays)) - { - LogLog.Debug(typeof(RollingFileCleanupAppender), string.Format("Deleting file {0} as its lastAccessTime is older than {1} days speficied by MaxLogFileDays", logFile, MaxLogFileDays)); - base.DeleteFile(logFile); - } - } - } - } -} diff --git a/src/Umbraco.Core/Logging/SerilogExtensions/Log4NetLevelMapperEnricher.cs b/src/Umbraco.Core/Logging/SerilogExtensions/Log4NetLevelMapperEnricher.cs new file mode 100644 index 0000000000..681f7b4936 --- /dev/null +++ b/src/Umbraco.Core/Logging/SerilogExtensions/Log4NetLevelMapperEnricher.cs @@ -0,0 +1,49 @@ +using Serilog.Core; +using Serilog.Events; + +namespace Umbraco.Core.Logging.SerilogExtensions +{ + /// + /// This is used to create a new property in Logs called 'Log4NetLevel' + /// So that we can map Serilog levels to Log4Net levels - so log files stay consistent + /// + internal class Log4NetLevelMapperEnricher : ILogEventEnricher + { + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + var log4NetLevel = string.Empty; + + switch (logEvent.Level) + { + case LogEventLevel.Debug: + log4NetLevel = "DEBUG"; + break; + + case LogEventLevel.Error: + log4NetLevel = "ERROR"; + break; + + case LogEventLevel.Fatal: + log4NetLevel = "FATAL"; + break; + + case LogEventLevel.Information: + log4NetLevel = "INFO"; + break; + + case LogEventLevel.Verbose: + log4NetLevel = "ALL"; + break; + + case LogEventLevel.Warning: + log4NetLevel = "WARN"; + break; + } + + //Pad string so that all log levels are 5 chars long (needed to keep the txt log file lined up nicely) + log4NetLevel = log4NetLevel.PadRight(5); + + logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Log4NetLevel", log4NetLevel)); + } + } +} diff --git a/src/Umbraco.Core/Logging/SerilogExtensions/LoggerConfigExtensions.cs b/src/Umbraco.Core/Logging/SerilogExtensions/LoggerConfigExtensions.cs new file mode 100644 index 0000000000..150fb0395c --- /dev/null +++ b/src/Umbraco.Core/Logging/SerilogExtensions/LoggerConfigExtensions.cs @@ -0,0 +1,103 @@ +using System; +using System.Web; +using Serilog; +using Serilog.Events; +using Serilog.Formatting.Compact; + +namespace Umbraco.Core.Logging.SerilogExtensions +{ + public static class LoggerConfigExtensions + { + /// + /// This configures Serilog with some defaults + /// Such as adding ProcessID, Thread, AppDomain etc + /// It is highly recommended that you keep/use this default in your own logging config customizations + /// + /// A Serilog LoggerConfiguration + public static LoggerConfiguration MinimalConfiguration(this LoggerConfiguration logConfig) + { + Serilog.Debugging.SelfLog.Enable(msg => System.Diagnostics.Debug.WriteLine(msg)); + + //Set this environment variable - so that it can be used in external config file + //add key="serilog:write-to:RollingFile.pathFormat" value="%BASEDIR%\logs\log.txt" /> + Environment.SetEnvironmentVariable("BASEDIR", AppDomain.CurrentDomain.BaseDirectory, EnvironmentVariableTarget.Process); + + logConfig.MinimumLevel.Verbose() //Set to highest level of logging (as any sinks may want to restrict it to Errors only) + .Enrich.WithProcessId() + .Enrich.WithProcessName() + .Enrich.WithThreadId() + .Enrich.WithProperty("AppDomainId", AppDomain.CurrentDomain.Id) + .Enrich.WithProperty("AppDomainAppId", HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty)) + .Enrich.WithProperty("MachineName", Environment.MachineName) + .Enrich.With(); + + return logConfig; + } + + /// + /// Outputs a .txt format log at /App_Data/Logs/ + /// + /// A Serilog LoggerConfiguration + /// The log level you wish the JSON file to collect - default is Verbose (highest) + /// The number of days to keep log files. Default is set to null which means all logs are kept + public static LoggerConfiguration OutputDefaultTextFile(this LoggerConfiguration logConfig, LogEventLevel minimumLevel = LogEventLevel.Verbose, int? retainedFileCount = null) + { + //Main .txt logfile - in similar format to older Log4Net output + //Ends with ..txt as Date is inserted before file extension substring + logConfig.WriteTo.File($@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\UmbracoTraceLog.{Environment.MachineName}..txt", + shared: true, + rollingInterval: RollingInterval.Day, + restrictedToMinimumLevel: minimumLevel, + retainedFileCountLimit: null, //Setting to null means we keep all files - default is 31 days + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss,fff} [P{ProcessId}/D{AppDomainId}/T{ThreadId}] {Log4NetLevel} {SourceContext} - {Message:lj}{NewLine}{Exception}"); + + return logConfig; + } + + /// + /// Outputs a CLEF format JSON log at /App_Data/Logs/ + /// + /// A Serilog LoggerConfiguration + /// The log level you wish the JSON file to collect - default is Verbose (highest) + /// The number of days to keep log files. Default is set to null which means all logs are kept + public static LoggerConfiguration OutputDefaultJsonFile(this LoggerConfiguration logConfig, LogEventLevel minimumLevel = LogEventLevel.Verbose, int? retainedFileCount = null) + { + //.clef format (Compact log event format, that can be imported into local SEQ & will make searching/filtering logs easier) + //Ends with ..txt as Date is inserted before file extension substring + logConfig.WriteTo.File(new CompactJsonFormatter(), $@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\UmbracoTraceLog.{Environment.MachineName}..json", + shared: true, + rollingInterval: RollingInterval.Day, //Create a new JSON file every day + retainedFileCountLimit: retainedFileCount, //Setting to null means we keep all files - default is 31 days + restrictedToMinimumLevel: minimumLevel); + + return logConfig; + } + + /// + /// Reads settings from /config/serilog.config + /// That allows the main logging pipeline to be configured + /// + /// A Serilog LoggerConfiguration + public static LoggerConfiguration ReadFromConfigFile(this LoggerConfiguration logConfig) + { + //Read from main serilog.config file + logConfig.ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + @"\config\serilog.config"); + + return logConfig; + } + + /// + /// Reads settings from /config/serilog.user.config + /// That allows a seperate logging pipeline to be configured that wil not affect the main Umbraco log + /// + /// A Serilog LoggerConfiguration + public static LoggerConfiguration ReadFromUserConfigFile(this LoggerConfiguration logConfig) + { + //A nested logger - where any user configured sinks via config can not effect the main 'umbraco' logger above + logConfig.WriteTo.Logger(cfg => + cfg.ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + @"\config\serilog.user.config")); + + return logConfig; + } + } +} diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 5fcd9ee072..eb036fd441 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -117,7 +117,7 @@ namespace Umbraco.Core lock (_locko) { - _logger.Debug(() => "Signaled" + (_signaled ? " (again)" : "") + " (" + source + ")."); + _logger.Debug("Signaled {Signaled} ({SignalSource})", _signaled ? "(again)" : string.Empty, source); if (_signaled) return; if (_isMainDom == false) return; // probably not needed _signaled = true; @@ -125,7 +125,7 @@ namespace Umbraco.Core try { - _logger.Info("Stopping."); + _logger.Info("Stopping ({SignalSource})", source); foreach (var callback in _callbacks.OrderBy(x => x.Key).Select(x => x.Value)) { try @@ -134,19 +134,19 @@ namespace Umbraco.Core } catch (Exception e) { - _logger.Error("Error while running callback, remaining callbacks will not run.", e); + _logger.Error(e, "Error while running callback, remaining callbacks will not run."); throw; } } - _logger.Debug("Stopped."); + _logger.Debug("Stopped ({SignalSource})", source); } finally { // in any case... _isMainDom = false; _asyncLocker.Dispose(); - _logger.Info("Released."); + _logger.Info("Released ({SignalSource})", source); } } diff --git a/src/Umbraco.Core/Manifest/ManifestParser.cs b/src/Umbraco.Core/Manifest/ManifestParser.cs index adf3418fb0..e2363e314f 100644 --- a/src/Umbraco.Core/Manifest/ManifestParser.cs +++ b/src/Umbraco.Core/Manifest/ManifestParser.cs @@ -81,7 +81,7 @@ namespace Umbraco.Core.Manifest } catch (Exception e) { - _logger.Error($"Failed to parse manifest at \"{path}\", ignoring.", e); + _logger.Error(e, "Failed to parse manifest at '{Path}', ignoring.", path); } } diff --git a/src/Umbraco.Core/Manifest/ManifestWatcher.cs b/src/Umbraco.Core/Manifest/ManifestWatcher.cs index 3bc70e2d78..4c0ddbf822 100644 --- a/src/Umbraco.Core/Manifest/ManifestWatcher.cs +++ b/src/Umbraco.Core/Manifest/ManifestWatcher.cs @@ -54,7 +54,7 @@ namespace Umbraco.Core.Manifest if (_isRestarting) return; _isRestarting = true; - _logger.Info("manifest has changed, app pool is restarting (" + e.FullPath + ")"); + _logger.Info("Manifest has changed, app pool is restarting ({Path})", e.FullPath); HttpRuntime.UnloadAppDomain(); Dispose(); // uh? if the app restarts then this should be disposed anyways? } diff --git a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs index 4ab2bf7a7c..2045e947ac 100644 --- a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs @@ -153,7 +153,7 @@ namespace Umbraco.Core.Media } catch (Exception ex) { - _logger.Error(typeof(UploadAutoFillProperties), $"Could not populate upload auto-fill properties for file \"{filepath}\".", ex); + _logger.Error(typeof(UploadAutoFillProperties), ex, "Could not populate upload auto-fill properties for file '{File}'.", filepath); ResetProperties(content, autoFillConfig, culture, segment); } } diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs index 9e2af56b7b..4986eeab02 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseBuilder.cs @@ -309,7 +309,7 @@ namespace Umbraco.Core.Migrations.Install { var source = connectionStrings.Attribute("configSource").Value; var configFile = IOHelper.MapPath($"{SystemDirectories.Root}/{source}"); - logger.Info(() => $"Storing ConnectionString in {configFile}"); + logger.Info("Storing ConnectionString in {ConfigFile}", configFile); if (File.Exists(configFile)) { xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace); @@ -335,7 +335,7 @@ namespace Umbraco.Core.Migrations.Install } xml.Save(fileName, SaveOptions.DisableFormatting); - logger.Info(() => $"Configured a new ConnectionString using the '{providerName}' provider."); + logger.Info("Configured a new ConnectionString using the '{ProviderName}' provider.", providerName); } internal bool IsConnectionStringConfigured(ConnectionStringSettings databaseSettings) @@ -500,7 +500,7 @@ namespace Umbraco.Core.Migrations.Install message = message + "

Installation completed!

"; //now that everything is done, we need to determine the version of SQL server that is executing - _logger.Info(() => $"Database configuration status: {message}"); + _logger.Info("Database configuration status: {DbConfigStatus}", message); return new Result { Message = message, Success = true, Percentage = "100" }; } @@ -589,7 +589,7 @@ namespace Umbraco.Core.Migrations.Install //now that everything is done, we need to determine the version of SQL server that is executing - _logger.Info(() => $"Database configuration status: {message}"); + _logger.Info("Database configuration status: {DbConfigStatus}", message); return new Result { Message = message, Success = true, Percentage = "100" }; } @@ -658,11 +658,11 @@ namespace Umbraco.Core.Migrations.Install private Result HandleInstallException(Exception ex) { - _logger.Error("Database configuration failed", ex); + _logger.Error(ex, "Database configuration failed"); if (_databaseSchemaValidationResult != null) { - _logger.Info(() => $"The database schema validation produced the following summary: {Environment.NewLine}{_databaseSchemaValidationResult.GetSummary()}"); + _logger.Info("The database schema validation produced the following summary: {DbSchemaSummary}", _databaseSchemaValidationResult.GetSummary()); } return new Result diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs index be9a1b9b74..ac74e6ee66 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs @@ -29,7 +29,7 @@ namespace Umbraco.Core.Migrations.Install /// Name of the table to create base data for public void InitializeBaseData(string tableName) { - _logger.Info(() => $"Creating data in table {tableName}"); + _logger.Info("Creating data in {TableName}", tableName); if (tableName.Equals(Constants.DatabaseSchema.Tables.Node)) CreateNodeData(); @@ -76,7 +76,7 @@ namespace Umbraco.Core.Migrations.Install if (tableName.Equals(Constants.DatabaseSchema.Tables.KeyValue)) CreateKeyValueData(); - _logger.Info(() => $"Done creating table {tableName} data."); + _logger.Info("Done creating table {TableName} data.", tableName); } private void CreateNodeData() diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs index 5a4290aadd..ae3f95ec4d 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs @@ -98,7 +98,7 @@ namespace Umbraco.Core.Migrations.Install var tableNameAttribute = table.FirstAttribute(); var tableName = tableNameAttribute == null ? table.Name : tableNameAttribute.Value; - _logger.Info(() => $"Uninstall {tableName}"); + _logger.Info("Uninstall {TableName}", tableName); try { @@ -109,7 +109,7 @@ namespace Umbraco.Core.Migrations.Install { //swallow this for now, not sure how best to handle this with diff databases... though this is internal // and only used for unit tests. If this fails its because the table doesn't exist... generally! - _logger.Error("Could not drop table " + tableName, ex); + _logger.Error(ex, "Could not drop table {TableName}", tableName); } } } @@ -141,13 +141,7 @@ namespace Umbraco.Core.Migrations.Install //get the db index defs result.DbIndexDefinitions = SqlSyntax.GetDefinedIndexes(_database) - .Select(x => new DbIndexDefinition - { - TableName = x.Item1, - IndexName = x.Item2, - ColumnName = x.Item3, - IsUnique = x.Item4 - }).ToArray(); + .Select(x => new DbIndexDefinition(x)).ToArray(); result.TableDefinitions.AddRange(OrderedTables .Select(x => DefinitionFactory.GetTableDefinition(x, SqlSyntax))); @@ -160,6 +154,14 @@ namespace Umbraco.Core.Migrations.Install return result; } + /// + /// This validates the Primary/Foreign keys in the database + /// + /// + /// + /// This does not validate any database constraints that are not PKs or FKs because Umbraco does not create a database with non PK/FK contraints. + /// Any unique "constraints" in the database are done with unique indexes. + /// private void ValidateDbConstraints(DatabaseSchemaResult result) { //MySql doesn't conform to the "normal" naming of constraints, so there is currently no point in doing these checks. @@ -172,8 +174,7 @@ namespace Umbraco.Core.Migrations.Install var constraintsInDatabase = SqlSyntax.GetConstraintsPerColumn(_database).DistinctBy(x => x.Item3).ToList(); var foreignKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("FK_")).Select(x => x.Item3).ToList(); var primaryKeysInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("PK_")).Select(x => x.Item3).ToList(); - var indexesInDatabase = constraintsInDatabase.Where(x => x.Item3.InvariantStartsWith("IX_")).Select(x => x.Item3).ToList(); - var indexesInSchema = result.TableDefinitions.SelectMany(x => x.Indexes.Select(y => y.Name)).ToList(); + var unknownConstraintsInDatabase = constraintsInDatabase.Where( x => @@ -188,7 +189,7 @@ namespace Umbraco.Core.Migrations.Install // In theory you could have: FK_ or fk_ ...or really any standard that your development department (or developer) chooses to use. foreach (var unknown in unknownConstraintsInDatabase) { - if (foreignKeysInSchema.InvariantContains(unknown) || primaryKeysInSchema.InvariantContains(unknown) || indexesInSchema.InvariantContains(unknown)) + if (foreignKeysInSchema.InvariantContains(unknown) || primaryKeysInSchema.InvariantContains(unknown)) { result.ValidConstraints.Add(unknown); } @@ -230,23 +231,6 @@ namespace Umbraco.Core.Migrations.Install result.Errors.Add(new Tuple("Constraint", primaryKey)); } - //Constaints: - - //NOTE: SD: The colIndex checks above should really take care of this but I need to keep this here because it was here before - // and some schema validation checks might rely on this data remaining here! - //Add valid and invalid index differences to the result object - var validIndexDifferences = indexesInDatabase.Intersect(indexesInSchema, StringComparer.InvariantCultureIgnoreCase); - foreach (var index in validIndexDifferences) - { - result.ValidConstraints.Add(index); - } - var invalidIndexDifferences = - indexesInDatabase.Except(indexesInSchema, StringComparer.InvariantCultureIgnoreCase) - .Union(indexesInSchema.Except(indexesInDatabase, StringComparer.InvariantCultureIgnoreCase)); - foreach (var index in invalidIndexDifferences) - { - result.Errors.Add(new Tuple("Constraint", index)); - } } private void ValidateDbColumns(DatabaseSchemaResult result) @@ -392,13 +376,13 @@ namespace Umbraco.Core.Migrations.Install { //Execute the Create Table sql var created = _database.Execute(new Sql(createSql)); - _logger.Info(() => $"Create Table '{tableName}' ({created}):\n {createSql}"); + _logger.Info("Create Table '{TableName}' ({Created}): \n {Sql}", tableName, created, createSql); //If any statements exists for the primary key execute them here if (string.IsNullOrEmpty(createPrimaryKeySql) == false) { var createdPk = _database.Execute(new Sql(createPrimaryKeySql)); - _logger.Info(() => $"Create Primary Key ({createdPk}):\n {createPrimaryKeySql}"); + _logger.Info("Create Primary Key ({CreatedPk}):\n {Sql}", createdPk, createPrimaryKeySql); } //Turn on identity insert if db provider is not mysql @@ -424,21 +408,21 @@ namespace Umbraco.Core.Migrations.Install foreach (var sql in indexSql) { var createdIndex = _database.Execute(new Sql(sql)); - _logger.Info(() => $"Create Index ({createdIndex}):\n {sql}"); + _logger.Info("Create Index ({CreatedIndex}):\n {Sql}", createdIndex, sql); } //Loop through foreignkey statements and execute sql foreach (var sql in foreignSql) { var createdFk = _database.Execute(new Sql(sql)); - _logger.Info(() => $"Create Foreign Key ({createdFk}):\n {sql}"); + _logger.Info("Create Foreign Key ({CreatedFk}):\n {Sql}", createdFk, sql); } transaction.Complete(); } } - _logger.Info(() => $"Created table '{tableName}'"); + _logger.Info("Created table '{TableName}'", tableName); } public void DropTable(string tableName) diff --git a/src/Umbraco.Core/Migrations/MigrationExpressionBase.cs b/src/Umbraco.Core/Migrations/MigrationExpressionBase.cs index 351d9e9f8a..f1c535b466 100644 --- a/src/Umbraco.Core/Migrations/MigrationExpressionBase.cs +++ b/src/Umbraco.Core/Migrations/MigrationExpressionBase.cs @@ -55,7 +55,7 @@ namespace Umbraco.Core.Migrations if (string.IsNullOrWhiteSpace(sql)) { - Logger.Info(GetType(), $"SQL [{Context.Index}]: "); + Logger.Info(GetType(), "SQL [{ContextIndex}: ", Context.Index); } else { @@ -90,7 +90,7 @@ namespace Umbraco.Core.Migrations private void ExecuteStatement(StringBuilder stmtBuilder) { var stmt = stmtBuilder.ToString(); - Logger.Info(GetType(), $"SQL [{Context.Index}]: {stmt}"); + Logger.Info(GetType(), "SQL [{ContextIndex}]: {Sql}", Context.Index, stmt); Database.Execute(stmt); stmtBuilder.Clear(); } diff --git a/src/Umbraco.Core/Migrations/MigrationPlan.cs b/src/Umbraco.Core/Migrations/MigrationPlan.cs index bea98feac3..5c999ad6ef 100644 --- a/src/Umbraco.Core/Migrations/MigrationPlan.cs +++ b/src/Umbraco.Core/Migrations/MigrationPlan.cs @@ -269,14 +269,11 @@ namespace Umbraco.Core.Migrations if (_migrationBuilder == null || _logger == null) throw new InvalidOperationException("Cannot execute a non-executing plan."); - _logger.Info(() => $"Starting \"{Name}\"..."); + _logger.Info("Starting '{MigrationName}'...", Name); + var origState = fromState ?? string.Empty; - _logger.Info(() => - { - var info = "At " + (string.IsNullOrWhiteSpace(origState) ? "origin" : ("\"" + origState + "\"")) + "."; - return info.Replace("{", "{{").Replace("}", "}}"); // stupid log4net - }); + _logger.Info("At {OrigState}", string.IsNullOrWhiteSpace(origState) ? "origin": origState); if (!_transitions.TryGetValue(origState, out var transition)) throw new Exception($"Unknown state \"{origState}\"."); @@ -291,7 +288,7 @@ namespace Umbraco.Core.Migrations var nextState = transition.TargetState; origState = nextState; - _logger.Info(() => $"At \"{origState}\"."); + _logger.Info("At {OrigState}", origState); if (!_transitions.TryGetValue(origState, out transition)) throw new Exception($"Unknown state \"{origState}\"."); diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/AddRelationTypeForMediaFolderOnDelete.cs b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/AddRelationTypeForMediaFolderOnDelete.cs index 349c39b980..f34ed9fb68 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/AddRelationTypeForMediaFolderOnDelete.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/AddRelationTypeForMediaFolderOnDelete.cs @@ -1,5 +1,4 @@ -using System; -using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0 { @@ -12,20 +11,22 @@ namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0 public override void Migrate() { - var exists = Context.Database.FirstOrDefault("WHERE alias=@alias", new { alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias }); - if (exists == null) + var relationTypeCount = Context.Database.ExecuteScalar("SELECT COUNT(*) FROM umbracoRelationType WHERE alias=@alias", + new { alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias }); + + if (relationTypeCount > 0) + return; + + var uniqueId = (Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias + "____" + Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName).ToGuid(); + Insert.IntoTable("umbracoRelationType").Row(new { - var uniqueId = (Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias + "____" + Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName).ToGuid(); - Insert.IntoTable("umbracoRelationType").Row(new - { - typeUniqueId = uniqueId, - alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias, - name = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName, - childObjectType = Constants.ObjectTypes.MediaType, - parentObjectType = Constants.ObjectTypes.MediaType, - dual = false + typeUniqueId = uniqueId, + alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias, + name = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName, + childObjectType = Constants.ObjectTypes.MediaType, + parentObjectType = Constants.ObjectTypes.MediaType, + dual = false }).Do(); - } } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/IncreaseLanguageIsoCodeColumnLength.cs b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/IncreaseLanguageIsoCodeColumnLength.cs index 895a28728d..fc7a21f4fa 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/IncreaseLanguageIsoCodeColumnLength.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_7_12_0/IncreaseLanguageIsoCodeColumnLength.cs @@ -1,5 +1,6 @@ using System.Linq; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0 { @@ -11,16 +12,16 @@ namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0 public override void Migrate() { - var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database) - .Select(x => new DbIndexDefinition - { - TableName = x.Item1, - IndexName = x.Item2, - ColumnName = x.Item3, - IsUnique = x.Item4 - }).ToArray(); + // Some people seem to have a constraint in their DB instead of an index, we'd need to drop that one + // See: https://our.umbraco.com/forum/using-umbraco-and-getting-started/93282-upgrade-from-711-to-712-fails + var constraints = SqlSyntax.GetConstraintsPerTable(Context.Database).Distinct().ToArray(); + if (constraints.Any(x => x.Item2.InvariantEquals("IX_umbracoLanguage_languageISOCode"))) + { + Delete.UniqueConstraint("IX_umbracoLanguage_languageISOCode").FromTable("umbracoLanguage").Do(); + } - //Ensure the index exists before dropping it + //Now check for indexes of that name and drop that if it exists + var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database); if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoLanguage_languageISOCode"))) { Delete.Index("IX_umbracoLanguage_languageISOCode").OnTable("umbracoLanguage").Do(); @@ -35,8 +36,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_7_12_0 Create.Index("IX_umbracoLanguage_languageISOCode") .OnTable("umbracoLanguage") .OnColumn("languageISOCode") + .Ascending() + .WithOptions() .Unique() .Do(); } + } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorXmlColumns.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorXmlColumns.cs index 0becdc5a8c..db8f95bf71 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorXmlColumns.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorXmlColumns.cs @@ -37,7 +37,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 var keyName = c.Item3.ToLowerInvariant(); if (dups.Contains(keyName)) { - Logger.Warn(() => $"Duplicate constraint {c.Item3}"); + Logger.Warn("Duplicate constraint '{Constraint}'", c.Item3); continue; } dups.Add(keyName); diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index caa7a2a3e4..dd379e02f8 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -32,6 +32,7 @@ namespace Umbraco.Core.Models /// Name of the content /// Parent object /// ContentType for the current Content object + /// An optional culture. public Content(string name, IContent parent, IContentType contentType, string culture = null) : this(name, parent, contentType, new PropertyCollection(), culture) { } @@ -43,6 +44,7 @@ namespace Umbraco.Core.Models /// Parent object /// ContentType for the current Content object /// Collection of properties + /// An optional culture. public Content(string name, IContent parent, IContentType contentType, PropertyCollection properties, string culture = null) : base(name, parent, contentType, properties, culture) { @@ -57,6 +59,7 @@ namespace Umbraco.Core.Models /// Name of the content /// Id of the Parent content /// ContentType for the current Content object + /// An optional culture. public Content(string name, int parentId, IContentType contentType, string culture = null) : this(name, parentId, contentType, new PropertyCollection(), culture) { } @@ -68,6 +71,7 @@ namespace Umbraco.Core.Models /// Id of the Parent content /// ContentType for the current Content object /// Collection of properties + /// An optional culture. public Content(string name, int parentId, IContentType contentType, PropertyCollection properties, string culture = null) : base(name, parentId, contentType, properties, culture) { @@ -213,23 +217,23 @@ namespace Umbraco.Core.Models [IgnoreDataMember] public IEnumerable PublishedCultures => _publishInfos?.Keys ?? Enumerable.Empty(); - //fixme should this return false if ID == 0? - //fixme should this return false if IsCultureAvailable(culture) is false? /// public bool IsCulturePublished(string culture) + // just check _publishInfos + // a non-available culture could not become published anyways => _publishInfos != null && _publishInfos.ContainsKey(culture); - //fixme should this return false if ID == 0? - //fixme should this return false if IsCultureAvailable(culture) is false? /// public bool WasCulturePublished(string culture) + // just check _publishInfosOrig - a copy of _publishInfos + // a non-available culture could not become published anyways => _publishInfosOrig != null && _publishInfosOrig.ContainsKey(culture); - //fixme should this return false if ID == 0? - //fixme should this return false if IsCultureAvailable(culture) is false? /// public bool IsCultureEdited(string culture) - => !IsCulturePublished(culture) || (_editedCultures != null && _editedCultures.Contains(culture)); + => IsCultureAvailable(culture) && // is available, and + (!IsCulturePublished(culture) || // is not published, or + (_editedCultures != null && _editedCultures.Contains(culture))); // is edited /// [IgnoreDataMember] diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 9e79a75e25..5910d01d34 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -87,6 +87,8 @@ namespace Umbraco.Core.Models /// A culture becomes published whenever values for this culture are published, /// and the content published name for this culture is non-null. It becomes non-published /// whenever values for this culture are unpublished. + /// A culture becomes published as soon as PublishCulture has been invoked, + /// even though the document might now have been saved yet (and can have no identity). /// bool IsCulturePublished(string culture); @@ -107,8 +109,9 @@ namespace Umbraco.Core.Models /// Gets a value indicated whether a given culture is edited. ///
/// - /// A culture is edited when it is not published, or when it is published but - /// it has changes. + /// A culture is edited when it is available, and not published or published but + /// with changes. + /// A culture can be edited even though the document might now have been saved yet (and can have no identity). /// bool IsCultureEdited(string culture); diff --git a/src/Umbraco.Core/Models/MediaExtensions.cs b/src/Umbraco.Core/Models/MediaExtensions.cs index 1f219d5e10..f510377c09 100644 --- a/src/Umbraco.Core/Models/MediaExtensions.cs +++ b/src/Umbraco.Core/Models/MediaExtensions.cs @@ -41,7 +41,7 @@ namespace Umbraco.Core.Models } catch (Exception ex) { - logger.Error("Could not parse the string " + jsonString + " to a json object", ex); + logger.Error(ex, "Could not parse the string '{JsonString}' to a json object", jsonString); return string.Empty; } } diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs index e0d52f7077..7576f01ce0 100644 --- a/src/Umbraco.Core/Models/Member.cs +++ b/src/Umbraco.Core/Models/Member.cs @@ -552,9 +552,14 @@ namespace Umbraco.Core.Models private Attempt WarnIfPropertyTypeNotFoundOnGet(string propertyAlias, string propertyName, T defaultVal) { void DoLog(string logPropertyAlias, string logPropertyName) - => Current.Logger.Warn($"Trying to access the '{logPropertyName}' property on " + typeof(Member) - + $" but the {logPropertyAlias} property does not exist on the member type so a default value is returned. Ensure that you have a property type with alias: " - + logPropertyAlias + $" configured on your member type in order to use the '{logPropertyName}' property on the model correctly."); + { + Current.Logger.Warn("Trying to access the '{PropertyName}' property on '{MemberType}' " + + "but the {PropertyAlias} property does not exist on the member type so a default value is returned. " + + "Ensure that you have a property type with alias: {PropertyAlias} configured on your member type in order to use the '{PropertyName}' property on the model correctly.", + logPropertyName, + typeof(Member), + logPropertyAlias); + } // if the property doesn't exist, if (Properties.Contains(propertyAlias) == false) @@ -572,8 +577,13 @@ namespace Umbraco.Core.Models private bool WarnIfPropertyTypeNotFoundOnSet(string propertyAlias, string propertyName) { void DoLog(string logPropertyAlias, string logPropertyName) - => Current.Logger.Warn($"An attempt was made to set a value on the property '{logPropertyName}' on type " + typeof(Member) - + $" but the property type {logPropertyAlias} does not exist on the member type, ensure that this property type exists so that setting this property works correctly."); + { + Current.Logger.Warn("An attempt was made to set a value on the property '{PropertyName}' on type '{MemberType}' but the " + + "property type {PropertyAlias} does not exist on the member type, ensure that this property type exists so that setting this property works correctly.", + logPropertyName, + typeof(Member), + logPropertyAlias); + } // if the property doesn't exist, if (Properties.Contains(propertyAlias) == false) diff --git a/src/Umbraco.Core/Models/PathValidationExtensions.cs b/src/Umbraco.Core/Models/PathValidationExtensions.cs index 74c2c9faa5..2db954b316 100644 --- a/src/Umbraco.Core/Models/PathValidationExtensions.cs +++ b/src/Umbraco.Core/Models/PathValidationExtensions.cs @@ -88,7 +88,7 @@ namespace Umbraco.Core.Models if (entity.ValidatePath() == false) { - logger.Warn(typeof(PathValidationExtensions), $"The content item {entity.Id} has an invalid path: {entity.Path} with parentID: {entity.ParentId}"); + logger.Warn(typeof(PathValidationExtensions), "The content item {EntityId} has an invalid path: {EntityPath} with parentID: {EntityParentId}", entity.Id, entity.Path, entity.ParentId); if (entity.ParentId == -1) { entity.Path = string.Concat("-1,", entity.Id); @@ -112,4 +112,4 @@ namespace Umbraco.Core.Models } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/PropertyTagsExtensions.cs b/src/Umbraco.Core/Models/PropertyTagsExtensions.cs index b0e0d0bd9c..26779161a1 100644 --- a/src/Umbraco.Core/Models/PropertyTagsExtensions.cs +++ b/src/Umbraco.Core/Models/PropertyTagsExtensions.cs @@ -225,7 +225,7 @@ namespace Umbraco.Core.Models } catch (Exception ex) { - Current.Logger.Warn(typeof(PropertyTagsExtensions), ex, "Could not automatically convert stored json value to an enumerable string"); + Current.Logger.Warn(typeof(PropertyTagsExtensions), ex, "Could not automatically convert stored json value to an enumerable string '{Json}'", value.ToString()); } break; diff --git a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs index b392aca289..7cacb30bd3 100644 --- a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs +++ b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs @@ -192,7 +192,7 @@ namespace Umbraco.Core.Packaging assemblyName.Name, "' see error log for full details.")); assembliesWithErrors.Add(a); - Current.Logger.Error("An error occurred scanning package assemblies", ex); + Current.Logger.Error(ex, "An error occurred scanning package assembly '{AssemblyName}'", assemblyName.FullName); } } } @@ -236,7 +236,7 @@ namespace Umbraco.Core.Packaging a.GetName().Name, "' see error log for full details.")); assembliesWithErrors.Add(a); - Current.Logger.Error("An error occurred scanning package assemblies", ex); + Current.Logger.Error(ex, "An error occurred scanning package assembly '{AssemblyName}'", a.GetName().FullName); } } diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs index 8b618a6619..63cbbf332b 100644 --- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs +++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/ConstraintDefinition.cs @@ -6,13 +6,13 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions { public ConstraintDefinition(ConstraintType type) { - constraintType = type; + _constraintType = type; } - private ConstraintType constraintType; - public bool IsPrimaryKeyConstraint { get { return ConstraintType.PrimaryKey == constraintType; } } - public bool IsUniqueConstraint { get { return ConstraintType.Unique == constraintType; } } - public bool IsNonUniqueConstraint { get { return ConstraintType.NonUnique == constraintType; } } + private readonly ConstraintType _constraintType; + public bool IsPrimaryKeyConstraint => ConstraintType.PrimaryKey == _constraintType; + public bool IsUniqueConstraint => ConstraintType.Unique == _constraintType; + public bool IsNonUniqueConstraint => ConstraintType.NonUnique == _constraintType; public string SchemaName { get; set; } public string ConstraintName { get; set; } diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs index 33d9e20311..f447d6a560 100644 --- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs +++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/DbIndexDefinition.cs @@ -1,13 +1,23 @@ -namespace Umbraco.Core.Persistence.DatabaseModelDefinitions +using System; + +namespace Umbraco.Core.Persistence.DatabaseModelDefinitions { /// /// Represents a database index definition retreived by querying the database /// internal class DbIndexDefinition { - public virtual string IndexName { get; set; } - public virtual string TableName { get; set; } - public virtual string ColumnName { get; set; } - public virtual bool IsUnique { get; set; } + public DbIndexDefinition(Tuple data) + { + TableName = data.Item1; + IndexName = data.Item2; + ColumnName = data.Item3; + IsUnique = data.Item4; + } + + public string IndexName { get; } + public string TableName { get; } + public string ColumnName { get; } + public bool IsUnique { get; } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 13812662bd..2e0139aa30 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -487,10 +487,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (result.ContainsKey(temp.VersionId)) { - var msg = $"The query returned multiple property sets for content {temp.Id}, {temp.ContentType.Name}"; if (ContentRepositoryBase.ThrowOnWarning) - throw new InvalidOperationException(msg); - Logger.Warn>(msg); + throw new InvalidOperationException($"The query returned multiple property sets for content {temp.Id}, {temp.ContentType.Name}"); + Logger.Warn>("The query returned multiple property sets for content {ContentId}, {ContentTypeName}", temp.Id, temp.ContentType.Name); } result[temp.VersionId] = new PropertyCollection(properties); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs index 8ba25b2a50..aa61383f85 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepository.cs @@ -227,10 +227,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { if (string.IsNullOrWhiteSpace(entity.Alias)) { - var m = $"ContentType '{entity.Name}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias."; - var e = new Exception(m); - Logger.Error(m, e); - throw e; + var ex = new Exception($"ContentType '{entity.Name}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias."); + Logger.Error("ContentType '{EntityName}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias.", entity.Name); + throw ex; } ((ContentType)entity).AddingEntity(); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 9a077a320e..555fc56157 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -521,10 +521,12 @@ AND umbracoNode.id <> @id", { if (string.IsNullOrWhiteSpace(pt.Alias)) { - var m = $"Property Type '{pt.Name}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias."; - var e = new InvalidOperationException(m); - Logger.Error>(m, e); - throw e; + var ex = new InvalidOperationException($"Property Type '{pt.Name}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias."); + + Logger.Error>("Property Type '{PropertyTypeName}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias.", + pt.Name); + + throw ex; } } @@ -532,10 +534,13 @@ AND umbracoNode.id <> @id", { if (string.IsNullOrWhiteSpace(entity.Alias)) { - var m = $"{typeof(TEntity).Name} '{entity.Name}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias."; - var e = new InvalidOperationException(m); - Logger.Error>(m, e); - throw e; + var ex = new InvalidOperationException($"{typeof(TEntity).Name} '{entity.Name}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias."); + + Logger.Error>("{EntityTypeName} '{EntityName}' cannot have an empty Alias. This is most likely due to invalid characters stripped from the Alias.", + typeof(TEntity).Name, + entity.Name); + + throw ex; } } @@ -561,7 +566,7 @@ AND umbracoNode.id <> @id", } else { - Logger.Warn>(() => $"Could not assign a data type for the property type {propertyType.Alias} since no data type was found with a property editor {propertyType.PropertyEditorAlias}"); + Logger.Warn>("Could not assign a data type for the property type {PropertyTypeAlias} since no data type was found with a property editor {PropertyEditorAlias}", propertyType.Alias, propertyType.PropertyEditorAlias); } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index 093723cea5..28c0b0dec6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -1073,7 +1073,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement NodeId = content.Id, LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), Culture = culture, - Edited = !content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture)) // if not published, always edited + + // if not published, always edited + // no need to check for availability: it *is* available since it is in content.CultureNames + Edited = !content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture)) }; } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index d027cf44a9..af58604bf0 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -44,6 +44,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax string TruncateTable { get; } string CreateConstraint { get; } string DeleteConstraint { get; } + + [Obsolete("This is never used, use the Format(ForeignKeyDefinition) instead")] string CreateForeignKeyConstraint { get; } string DeleteDefaultConstraint { get; } string FormatDateTime(DateTime date, bool includeTime = true); @@ -80,8 +82,32 @@ namespace Umbraco.Core.Persistence.SqlSyntax IEnumerable GetTablesInSchema(IDatabase db); IEnumerable GetColumnsInSchema(IDatabase db); + + /// + /// Returns all constraints defined in the database (Primary keys, foreign keys, unique constraints...) (does not include indexes) + /// + /// + /// + /// A Tuple containing: TableName, ConstraintName + /// IEnumerable> GetConstraintsPerTable(IDatabase db); + + /// + /// Returns all constraints defined in the database (Primary keys, foreign keys, unique constraints...) (does not include indexes) + /// + /// + /// + /// A Tuple containing: TableName, ColumnName, ConstraintName + /// IEnumerable> GetConstraintsPerColumn(IDatabase db); + + /// + /// Returns all defined Indexes in the database excluding primary keys + /// + /// + /// + /// A Tuple containing: TableName, IndexName, ColumnName, IsUnique + /// IEnumerable> GetDefinedIndexes(IDatabase db); } } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs index e0eaa46048..4ff0545281 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/MySqlSyntaxProvider.cs @@ -79,6 +79,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax return list; } + /// public override IEnumerable> GetConstraintsPerTable(IDatabase db) { List> list; @@ -101,6 +102,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax return list; } + /// public override IEnumerable> GetConstraintsPerColumn(IDatabase db) { List> list; @@ -127,6 +129,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax return list; } + /// public override IEnumerable> GetDefinedIndexes(IDatabase db) { List> list; @@ -385,7 +388,7 @@ ORDER BY TABLE_NAME, INDEX_NAME", } catch (Exception ex) { - _logger.Error("Error querying for lower_case support", ex); + _logger.Error(ex, "Error querying for lower_case support"); } finally { diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index c41cd59f4e..75fc9c0b69 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -107,40 +107,27 @@ namespace Umbraco.Core.Persistence.SqlSyntax item.IS_NULLABLE, item.DATA_TYPE)).ToList(); } + /// public override IEnumerable> GetConstraintsPerTable(IDatabase db) { var items = db.Fetch("SELECT TABLE_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS"); - var indexItems = db.Fetch("SELECT TABLE_NAME, INDEX_NAME FROM INFORMATION_SCHEMA.INDEXES"); - return - items.Select(item => new Tuple(item.TABLE_NAME, item.CONSTRAINT_NAME)) - .Union( - indexItems.Select( - indexItem => new Tuple(indexItem.TABLE_NAME, indexItem.INDEX_NAME))) - .ToList(); + return items.Select(item => new Tuple(item.TABLE_NAME, item.CONSTRAINT_NAME)).ToList(); } + /// public override IEnumerable> GetConstraintsPerColumn(IDatabase db) { - var items = - db.Fetch( - "SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE"); - var indexItems = db.Fetch("SELECT INDEX_NAME, TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.INDEXES"); - return - items.Select( - item => new Tuple(item.TABLE_NAME, item.COLUMN_NAME, item.CONSTRAINT_NAME)) - .Union( - indexItems.Select( - indexItem => - new Tuple(indexItem.TABLE_NAME, indexItem.COLUMN_NAME, - indexItem.INDEX_NAME))).ToList(); + var items = db.Fetch("SELECT TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE"); + return items.Select(item => new Tuple(item.TABLE_NAME, item.COLUMN_NAME, item.CONSTRAINT_NAME)).ToList(); } + /// public override IEnumerable> GetDefinedIndexes(IDatabase db) { var items = db.Fetch( @"SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAME, [UNIQUE] FROM INFORMATION_SCHEMA.INDEXES -WHERE INDEX_NAME NOT LIKE 'PK_%' +WHERE PRIMARY_KEY=0 ORDER BY TABLE_NAME, INDEX_NAME"); return items.Select( diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index d7dadd9f08..9c6e95971c 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -163,6 +163,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax item.IS_NULLABLE, item.DATA_TYPE)).ToList(); } + /// public override IEnumerable> GetConstraintsPerTable(IDatabase db) { var items = @@ -171,6 +172,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax return items.Select(item => new Tuple(item.TABLE_NAME, item.CONSTRAINT_NAME)).ToList(); } + /// public override IEnumerable> GetConstraintsPerColumn(IDatabase db) { var items = @@ -179,6 +181,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax return items.Select(item => new Tuple(item.TABLE_NAME, item.COLUMN_NAME, item.CONSTRAINT_NAME)).ToList(); } + /// public override IEnumerable> GetDefinedIndexes(IDatabase db) { var items = @@ -188,7 +191,7 @@ CASE WHEN I.is_unique_constraint = 1 OR I.is_unique = 1 THEN 1 ELSE 0 END AS [U from sys.tables as T inner join sys.indexes as I on T.[object_id] = I.[object_id] inner join sys.index_columns as IC on IC.[object_id] = I.[object_id] and IC.[index_id] = I.[index_id] inner join sys.all_columns as AC on IC.[object_id] = AC.[object_id] and IC.[column_id] = AC.[column_id] -WHERE I.name NOT LIKE 'PK_%' +WHERE I.is_primary_key = 0 order by T.name, I.name"); return items.Select(item => new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME, item.UNIQUE == 1)).ToList(); diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs index b53f6224c4..02f1370b83 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs @@ -10,13 +10,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax public static IEnumerable GetDefinedIndexesDefinitions(this ISqlSyntaxProvider sql, IDatabase db) { return sql.GetDefinedIndexes(db) - .Select(x => new DbIndexDefinition - { - TableName = x.Item1, - IndexName = x.Item2, - ColumnName = x.Item3, - IsUnique = x.Item4 - }).ToArray(); + .Select(x => new DbIndexDefinition(x)).ToArray(); } /// diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index 0f337595fe..64e4c0adca 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -190,13 +190,13 @@ namespace Umbraco.Core.Persistence } #endif - protected override void OnException(Exception x) + protected override void OnException(Exception ex) { - _logger.Error("Exception (" + InstanceId + ").", x); - _logger.Debug(() => "At:\r\n" + Environment.StackTrace); + _logger.Error(ex, "Exception ({InstanceId}).", InstanceId); + _logger.Debug("At:\r\n{StackTrace}", Environment.StackTrace); if (EnableSqlTrace == false) - _logger.Debug(() => "Sql:\r\n" + CommandToString(LastSQL, LastArgs)); - base.OnException(x); + _logger.Debug("Sql:\r\n{Sql}", CommandToString(LastSQL, LastArgs)); + base.OnException(ex); } private DbCommand _cmd; @@ -208,7 +208,7 @@ namespace Umbraco.Core.Persistence cmd.CommandTimeout = cmd.Connection.ConnectionTimeout; if (EnableSqlTrace) - _logger.Debug(() => CommandToString(cmd).Replace("{", "{{").Replace("}", "}}")); // fixme these escapes should be builtin + _logger.Debug("SQL Trace:\r\n{Sql}", CommandToString(cmd).Replace("{", "{{").Replace("}", "}}")); // fixme these escapes should be builtin #if DEBUG_DATABASES // detects whether the command is already in use (eg still has an open reader...) diff --git a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs index e8d16fcd62..912bf1c367 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs @@ -261,7 +261,7 @@ namespace Umbraco.Core.PropertyEditors var result = TryConvertValueToCrlType(editorValue.Value); if (result.Success == false) { - Current.Logger.Warn(() => $"The value {editorValue.Value} cannot be converted to the type {ValueTypes.ToStorageType(ValueType)}"); + Current.Logger.Warn("The value {EditorValue} cannot be converted to the type {StorageTypeValue}", editorValue.Value, ValueTypes.ToStorageType(ValueType)); return null; } return result.Result; diff --git a/src/Umbraco.Web/PropertyEditors/SliderConfiguration.cs b/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs similarity index 93% rename from src/Umbraco.Web/PropertyEditors/SliderConfiguration.cs rename to src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs index f91da8e68d..b2bf99bdc6 100644 --- a/src/Umbraco.Web/PropertyEditors/SliderConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs @@ -1,6 +1,4 @@ -using Umbraco.Core.PropertyEditors; - -namespace Umbraco.Web.PropertyEditors +namespace Umbraco.Core.PropertyEditors { /// /// Represents the configuration for the slider value editor. @@ -8,7 +6,7 @@ namespace Umbraco.Web.PropertyEditors public class SliderConfiguration { [ConfigurationField("enableRange", "Enable range", "boolean")] - public string Range { get; set; } + public bool EnableRange { get; set; } [ConfigurationField("orientation", "Orientation", "views/propertyeditors/slider/orientation.prevalues.html")] public string Orientation { get; set; } @@ -38,7 +36,7 @@ namespace Umbraco.Web.PropertyEditors public string Tooltip { get; set; } [ConfigurationField("tooltipSplit", "Tooltip split", "boolean", Description = "If false show one tootip if true show two tooltips one for each handler")] - public string TooltipSplit { get; set; } // fixme bool? + public bool TooltipSplit { get; set; } // fixme bool? [ConfigurationField("tooltipFormat", "Tooltip format", "textstring", Description = "The value wanted to be displayed in the tooltip. Use {0} and {1} for current values - {1} is only for range slider and if not using tooltip split.")] public string TooltipFormat { get; set; } @@ -47,7 +45,7 @@ namespace Umbraco.Web.PropertyEditors public string TooltipPosition { get; set; } [ConfigurationField("reversed", "Reversed", "boolean", Description = "Whether or not the slider should be reversed")] - public string Reversed { get; set; } // fixme bool? + public bool Reversed { get; set; } // fixme bool? [ConfigurationField("ticks", "Ticks", "textstring", Description = "Comma-separated values. Used to define the values of ticks. Tick marks are indicators to denote special values in the range. This option overwrites min and max options.")] public string Ticks { get; set; } @@ -61,4 +59,4 @@ namespace Umbraco.Web.PropertyEditors [ConfigurationField("ticksSnapBounds", "Ticks snap bounds", "number", Description = "Used to define the snap bounds of a tick. Snaps to the tick if value is within these bounds.")] public int TicksSnapBounds { get; set; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/PropertyEditors/SliderPropertyEditorConfiguration.cs b/src/Umbraco.Core/PropertyEditors/SliderPropertyEditorConfiguration.cs deleted file mode 100644 index 974f9bfab0..0000000000 --- a/src/Umbraco.Core/PropertyEditors/SliderPropertyEditorConfiguration.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Newtonsoft.Json; - -namespace Umbraco.Core.PropertyEditors -{ - public class SliderPropertyEditorConfiguration - { - [JsonProperty("enableRange")] - public bool EnableRange { get; set; } - } -} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs index 5a642bedc7..2131764ad6 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/GridValueConverter.cs @@ -100,7 +100,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters } catch (Exception ex) { - Current.Logger.Error("Could not parse the string " + sourceString + " to a json object", ex); + Current.Logger.Error(ex, "Could not parse the string '{JsonString}' to a json object", sourceString); } } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs index 413e66afb7..79cb748960 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs @@ -43,7 +43,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters catch (Exception ex) { // cannot deserialize, assume it may be a raw image url - Current.Logger.Error($"Could not deserialize string \"{sourceString}\" into an image cropper value.", ex); + Current.Logger.Error(ex, "Could not deserialize string '{JsonString}' into an image cropper value.", sourceString); value = new ImageCropperValue { Src = sourceString }; } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs index aeeb1a795f..d4cee4762f 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs @@ -57,7 +57,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters } catch (Exception ex) { - Current.Logger.Error("Could not parse the string " + sourceString + " to a json object", ex); + Current.Logger.Error(ex, "Could not parse the string '{JsonString}' to a json object", sourceString); } } diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs index 8e79e2273c..a43d391dbe 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/SliderValueConverter.cs @@ -72,7 +72,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters return Storages.GetOrAdd(dataTypeId, id => { var dataType = _dataTypeService.GetDataType(id); - var configuration = dataType.ConfigurationAs(); + var configuration = dataType.ConfigurationAs(); return configuration.EnableRange; }); } diff --git a/src/Umbraco.Core/Publishing/ScheduledPublisher.cs b/src/Umbraco.Core/Publishing/ScheduledPublisher.cs index 1d2d62b929..73cc752508 100644 --- a/src/Umbraco.Core/Publishing/ScheduledPublisher.cs +++ b/src/Umbraco.Core/Publishing/ScheduledPublisher.cs @@ -35,7 +35,7 @@ namespace Umbraco.Core.Publishing var counter = 0; var contentForRelease = _contentService.GetContentForRelease().ToArray(); if (contentForRelease.Length > 0) - _logger.Debug(() => $"There's {contentForRelease.Length} item(s) of content to be published"); + _logger.Debug("There's {ContentItemsForRelease} item(s) of content to be published", contentForRelease.Length); foreach (var d in contentForRelease) { try @@ -43,26 +43,26 @@ namespace Umbraco.Core.Publishing d.ReleaseDate = null; d.PublishCulture(); // fixme variants? var result = _contentService.SaveAndPublish(d, userId: _userService.GetProfileById(d.WriterId).Id); - _logger.Debug(() => $"Result of publish attempt: {result.Result}"); + _logger.Debug("Result of publish attempt: {PublishResult}", result.Result); if (result.Success == false) { - _logger.Error($"Error publishing node {d.Id}"); + _logger.Error(null, "Error publishing node {NodeId}", d.Id); } else { counter++; } } - catch (Exception ee) + catch (Exception ex) { - _logger.Error($"Error publishing node {d.Id}", ee); + _logger.Error(ex, "Error publishing node {NodeId}", d.Id); throw; } } var contentForExpiration = _contentService.GetContentForExpiration().ToArray(); if (contentForExpiration.Length > 0) - _logger.Debug(() => $"There's {contentForExpiration.Length} item(s) of content to be unpublished"); + _logger.Debug("There's {ContentItemsForExpiration} item(s) of content to be unpublished", contentForExpiration.Length); foreach (var d in contentForExpiration) { try @@ -74,9 +74,9 @@ namespace Umbraco.Core.Publishing counter++; } } - catch (Exception ee) + catch (Exception ex) { - _logger.Error($"Error unpublishing node {d.Id}", ee); + _logger.Error(ex, "Error unpublishing node {NodeId}", d.Id); throw; } } diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 93f475f7a6..de86ea929f 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -83,7 +83,7 @@ namespace Umbraco.Core.Runtime try { - Logger.Debug(() => $"Runtime: {GetType().FullName}"); + Logger.Debug("Runtime: {Runtime}", GetType().FullName); AquireMainDom(container); DetermineRuntimeLevel(container); @@ -141,11 +141,11 @@ namespace Umbraco.Core.Runtime var dbfactory = container.GetInstance(); SetRuntimeStateLevel(dbfactory, Logger); - Logger.Debug(() => $"Runtime level: {_state.Level}"); + Logger.Debug("Runtime level: {RuntimeLevel}", _state.Level); if (_state.Level == RuntimeLevel.Upgrade) { - Logger.Debug(() => $"Configure database factory for upgrades."); + Logger.Debug("Configure database factory for upgrades."); dbfactory.ConfigureForUpgrade(); } } @@ -262,12 +262,12 @@ namespace Umbraco.Core.Runtime { // there *is* a local version, but it does not match the code version // need to upgrade - logger.Debug(() => $"Local version \"{localVersion}\" < code version \"{codeVersion}\", need to upgrade Umbraco."); + logger.Debug("Local version '{LocalVersion}' < code version '{CodeVersion}', need to upgrade Umbraco.", localVersion, codeVersion); _state.Level = RuntimeLevel.Upgrade; } else if (localVersion > codeVersion) { - logger.Warn(() => $"Local version \"{localVersion}\" > code version \"{codeVersion}\", downgrading is not supported."); + logger.Warn("Local version '{LocalVersion}' > code version '{CodeVersion}', downgrading is not supported.", localVersion, codeVersion); _state.Level = RuntimeLevel.BootFailed; // in fact, this is bad enough that we want to throw @@ -292,16 +292,14 @@ namespace Umbraco.Core.Runtime { connect = databaseFactory.CanConnect; if (connect) break; - logger.Debug(() => i == 0 - ? "Could not immediately connect to database, trying again." - : "Could not connect to database."); + logger.Debug("Could not immediately connect to database, trying again."); Thread.Sleep(1000); } if (connect == false) { // cannot connect to configured database, this is bad, fail - logger.Debug(() => "Could not connect to database."); + logger.Debug("Could not connect to database."); _state.Level = RuntimeLevel.BootFailed; // in fact, this is bad enough that we want to throw @@ -365,7 +363,7 @@ namespace Umbraco.Core.Runtime _state.CurrentMigrationState = state; _state.FinalMigrationState = umbracoPlan.FinalState; - logger.Debug(() => $"Final upgrade state is \"{_state.FinalMigrationState}\", database contains \"{state ?? ""}\"."); + logger.Debug("Final upgrade state is '{FinalMigrationState}', database contains {DatabaseState}", _state.FinalMigrationState, state ?? ""); return state == _state.FinalMigrationState; } diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs index 53a474cef7..d09bd95081 100644 --- a/src/Umbraco.Core/RuntimeState.cs +++ b/src/Umbraco.Core/RuntimeState.cs @@ -117,7 +117,7 @@ namespace Umbraco.Core var change = url != null && !_applicationUrls.Contains(url); if (change) { - _logger.Info(typeof(ApplicationUrlHelper), $"New url \"{url}\" detected, re-discovering application url."); + _logger.Info(typeof(ApplicationUrlHelper), "New url '{Url}' detected, re-discovering application url.", url); _applicationUrls.Add(url); } diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index d3e31c0d81..adc5482e68 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -318,7 +318,8 @@ namespace Umbraco.Core.Scoping if (completed.HasValue == false || completed.Value == false) { if (LogUncompletedScopes) - _logger.Debug(() => "Uncompleted Child Scope at\r\n" + Environment.StackTrace); + _logger.Debug("Uncompleted Child Scope at\r\n {StackTrace}", Environment.StackTrace); + _completed = false; } } diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index beb1ee5374..ea68a05f15 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -117,7 +117,7 @@ namespace Umbraco.Core.Scoping } // hard to inject into a static method :( - Current.Logger.Warn(() => $"Missed {typeof(T).Name} Object {objectKey.ToString("N").Substring(0, 8)}"); + Current.Logger.Warn("Missed {TypeName} Object {ObjectKey}", typeof(T).Name, objectKey.ToString("N").Substring(0, 8)); #if DEBUG_SCOPES //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); #endif diff --git a/src/Umbraco.Core/Security/MembershipProviderBase.cs b/src/Umbraco.Core/Security/MembershipProviderBase.cs index ea9ce4458a..0c8461c2f2 100644 --- a/src/Umbraco.Core/Security/MembershipProviderBase.cs +++ b/src/Umbraco.Core/Security/MembershipProviderBase.cs @@ -281,7 +281,7 @@ namespace Umbraco.Core.Security if ((PasswordFormat == MembershipPasswordFormat.Hashed) && EnablePasswordRetrieval) { var ex = new ProviderException("Provider can not retrieve a hashed password"); - Current.Logger.Error("Cannot specify a Hashed password format with the enabledPasswordRetrieval option set to true", ex); + Current.Logger.Error(ex, "Cannot specify a Hashed password format with the enabledPasswordRetrieval option set to true"); throw ex; } diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 30e76468a6..0bb00ef5a4 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -1240,11 +1240,11 @@ namespace Umbraco.Core.Services.Implement d.PublishCulture(); // fixme variants? result = SaveAndPublish(d, userId: d.WriterId); if (result.Success == false) - Logger.Error($"Failed to publish document id={d.Id}, reason={result.Result}."); + Logger.Error(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); } catch (Exception e) { - Logger.Error($"Failed to publish document id={d.Id}, an exception was thrown.", e); + Logger.Error(e, "Failed to publish document id={DocumentId}, an exception was thrown.", d.Id); throw; } yield return result; @@ -1256,11 +1256,11 @@ namespace Umbraco.Core.Services.Implement d.ExpireDate = null; var result = Unpublish(d, userId: d.WriterId); if (result.Success == false) - Logger.Error($"Failed to unpublish document id={d.Id}, reason={result.Result}."); + Logger.Error(null, "Failed to unpublish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); } catch (Exception e) { - Logger.Error($"Failed to unpublish document id={d.Id}, an exception was thrown.", e); + Logger.Error(e, "Failed to unpublish document id={DocumentId}, an exception was thrown.", d.Id); throw; } } @@ -1439,7 +1439,7 @@ namespace Umbraco.Core.Services.Implement // fixme not going to work, do it differently _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files - (file, e) => Logger.Error("An error occurred while deleting file attached to nodes: " + file, e)); + (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); } } @@ -2178,7 +2178,7 @@ namespace Umbraco.Core.Services.Implement // raise Publishing event if (scope.Events.DispatchCancelable(Publishing, this, new PublishEventArgs(content, evtMsgs))) { - Logger.Info(() => $"Document \"'{content.Name}\" (id={content.Id}) cannot be published: publishing was cancelled."); + Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled"); return new PublishResult(PublishResultType.FailedCancelledByEvent, evtMsgs, content); } @@ -2186,7 +2186,7 @@ namespace Umbraco.Core.Services.Implement // either because it is 'publishing' or because it already has a published version if (((Content) content).PublishedState != PublishedState.Publishing && content.PublishedVersionId == 0) { - Logger.Info(() => $"Document \"{content.Name}\" (id={content.Id}) cannot be published: document does not have published values."); + Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document does not have published values"); return new PublishResult(PublishResultType.FailedNoPublishedValues, evtMsgs, content); } @@ -2194,15 +2194,15 @@ namespace Umbraco.Core.Services.Implement switch (content.Status) { case ContentStatus.Expired: - Logger.Info(() => $"Document \"{content.Name}\" (id={content.Id}) cannot be published: document has expired."); + Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document has expired"); return new PublishResult(PublishResultType.FailedHasExpired, evtMsgs, content); case ContentStatus.AwaitingRelease: - Logger.Info(() => $"Document \"{content.Name}\" (id={content.Id}) cannot be published: document is awaiting release."); + Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document is awaiting release"); return new PublishResult(PublishResultType.FailedAwaitingRelease, evtMsgs, content); case ContentStatus.Trashed: - Logger.Info(() => $"Document \"{content.Name}\" (id={content.Id}) cannot be published: document is trashed."); + Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "document is trashed"); return new PublishResult(PublishResultType.FailedIsTrashed, evtMsgs, content); } @@ -2214,7 +2214,7 @@ namespace Umbraco.Core.Services.Implement var pathIsOk = content.ParentId == Constants.System.Root || IsPathPublished(GetParent(content)); if (pathIsOk == false) { - Logger.Info(() => $"Document \"{content.Name}\" (id={content.Id}) cannot be published: parent is not published."); + Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "parent is not published"); return new PublishResult(PublishResultType.FailedPathNotPublished, evtMsgs, content); } @@ -2238,7 +2238,7 @@ namespace Umbraco.Core.Services.Implement // change state to publishing ((Content) content).PublishedState = PublishedState.Publishing; - Logger.Info(() => $"Content \"{content.Name}\" (id={content.Id}) has been published."); + Logger.Info("Document '{ContentName}' (id={ContentId}) has been published.", content.Name, content.Id); return result; } @@ -2248,7 +2248,7 @@ namespace Umbraco.Core.Services.Implement // raise UnPublishing event if (scope.Events.DispatchCancelable(UnPublishing, this, new PublishEventArgs(content, evtMsgs))) { - Logger.Info(() => $"Document \"{content.Name}\" (id={content.Id}) cannot be unpublished: unpublishing was cancelled."); + Logger.Info("Document '{ContentName}' (id={ContentId}) cannot be unpublished: unpublishing was cancelled.", content.Name, content.Id); return new UnpublishResult(UnpublishResultType.FailedCancelledByEvent, evtMsgs, content); } @@ -2271,13 +2271,13 @@ namespace Umbraco.Core.Services.Implement if (content.ReleaseDate.HasValue && content.ReleaseDate.Value <= DateTime.Now) { content.ReleaseDate = null; - Logger.Info(() => $"Document \"{content.Name}\" (id={content.Id}) had its release date removed, because it was unpublished."); + Logger.Info("Document '{ContentName}' (id={ContentId}) had its release date removed, because it was unpublished.", content.Name, content.Id); } // change state to unpublishing ((Content) content).PublishedState = PublishedState.Unpublishing; - Logger.Info(() => $"Document \"{content.Name}\" (id={content.Id}) has been unpublished."); + Logger.Info("Document '{ContentName}' (id={ContentId}) has been unpublished.", content.Name, content.Id); return attempt; } diff --git a/src/Umbraco.Core/Services/Implement/LocalizedTextService.cs b/src/Umbraco.Core/Services/Implement/LocalizedTextService.cs index 953e80594f..923b9994d1 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizedTextService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizedTextService.cs @@ -104,7 +104,7 @@ namespace Umbraco.Core.Services.Implement { if (xmlSource.ContainsKey(culture) == false) { - _logger.Warn(() => $"The culture specified {culture} was not found in any configured sources for this service"); + _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); return result; } @@ -124,7 +124,7 @@ namespace Umbraco.Core.Services.Implement { if (_dictionarySource.ContainsKey(culture) == false) { - _logger.Warn(() => $"The culture specified {culture} was not found in any configured sources for this service"); + _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); return result; } @@ -207,7 +207,7 @@ namespace Umbraco.Core.Services.Implement { if (_dictionarySource.ContainsKey(culture) == false) { - _logger.Warn(() => $"The culture specified {culture} was not found in any configured sources for this service"); + _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); return "[" + key + "]"; } @@ -245,7 +245,7 @@ namespace Umbraco.Core.Services.Implement { if (xmlSource.ContainsKey(culture) == false) { - _logger.Warn(() => $"The culture specified {culture} was not found in any configured sources for this service"); + _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); return "[" + key + "]"; } diff --git a/src/Umbraco.Core/Services/Implement/LocalizedTextServiceFileSources.cs b/src/Umbraco.Core/Services/Implement/LocalizedTextServiceFileSources.cs index 44587b616b..3b3f90a412 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizedTextServiceFileSources.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizedTextServiceFileSources.cs @@ -88,7 +88,7 @@ namespace Umbraco.Core.Services.Implement } catch (CultureNotFoundException) { - Current.Logger.Warn(() => $"The culture {cultureVal} found in the file {fileInfo.FullName} is not a valid culture"); + Current.Logger.Warn("The culture {CultureValue} found in the file {CultureFile} is not a valid culture", cultureVal, fileInfo.FullName); //If the culture in the file is invalid, we'll just hope the file name is a valid culture below, otherwise // an exception will be thrown. } @@ -125,7 +125,7 @@ namespace Umbraco.Core.Services.Implement if (fileSourceFolder.Exists == false) { - Current.Logger.Warn(() => $"The folder does not exist: {fileSourceFolder.FullName}, therefore no sources will be discovered"); + Current.Logger.Warn("The folder does not exist: {FileSourceFolder}, therefore no sources will be discovered", fileSourceFolder.FullName); } else { @@ -204,7 +204,7 @@ namespace Umbraco.Core.Services.Implement } catch (Exception ex) { - _logger.Error("Could not load file into XML " + supplementaryFile.File.FullName, ex); + _logger.Error(ex, "Could not load file into XML {File}", supplementaryFile.File.FullName); continue; } diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index d29875d68b..8f6a1c6000 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -892,7 +892,7 @@ namespace Umbraco.Core.Services.Implement scope.Events.Dispatch(Deleted, this, args); _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files - (file, e) => Logger.Error("An error occurred while deleting file attached to nodes: " + file, e)); + (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); } } diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index ca2b39dee2..288809bf33 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -930,7 +930,7 @@ namespace Umbraco.Core.Services.Implement // fixme - this is MOOT because the event will not trigger immediately // it's been refactored already (think it's the dispatcher that deals with it?) _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files - (file, e) => Logger.Error("An error occurred while deleting file attached to nodes: " + file, e)); + (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); } #endregion diff --git a/src/Umbraco.Core/Services/Implement/NotificationService.cs b/src/Umbraco.Core/Services/Implement/NotificationService.cs index 9f76e087c1..3afb7c3777 100644 --- a/src/Umbraco.Core/Services/Implement/NotificationService.cs +++ b/src/Umbraco.Core/Services/Implement/NotificationService.cs @@ -629,11 +629,11 @@ namespace Umbraco.Core.Services.Implement try { if (Sendmail != null) Sendmail(s, request.Mail, _logger); else s.Send(request.Mail); - _logger.Debug(() => string.Format("Notification \"{0}\" sent to {1} ({2})", request.Action, request.UserName, request.Email)); + _logger.Debug("Notification '{Action}' sent to {Username} ({Email})", request.Action, request.UserName, request.Email); } catch (Exception ex) { - _logger.Error("An error occurred sending notification", ex); + _logger.Error(ex, "An error occurred sending notification"); s.Dispose(); s = new SmtpClient(); } diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 54e549dd08..ef22861947 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -491,7 +491,7 @@ namespace Umbraco.Core.Services.Implement var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolder); if (tryCreateFolder == false) { - _logger.Error("Could not create folder: " + rootFolder, tryCreateFolder.Exception); + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); throw tryCreateFolder.Exception; } var rootFolderId = tryCreateFolder.Result.Entity.Id; @@ -525,7 +525,7 @@ namespace Umbraco.Core.Services.Implement var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderName); if (tryCreateFolder == false) { - _logger.Error("Could not create folder: " + folderName, tryCreateFolder.Exception); + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); throw tryCreateFolder.Exception; } return _contentTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); @@ -631,7 +631,7 @@ namespace Umbraco.Core.Services.Implement } else { - _logger.Warn(() => $"Packager: Error handling allowed templates. Template with alias '{alias}' could not be found."); + _logger.Warn("Packager: Error handling allowed templates. Template with alias '{TemplateAlias}' could not be found.", alias); } } @@ -647,7 +647,7 @@ namespace Umbraco.Core.Services.Implement } else { - _logger.Warn(() => $"Packager: Error handling default template. Default template with alias '{defaultTemplateElement.Value}' could not be found."); + _logger.Warn("Packager: Error handling default template. Default template with alias '{DefaultTemplateAlias}' could not be found.", defaultTemplateElement.Value); } } } @@ -718,7 +718,8 @@ namespace Umbraco.Core.Services.Implement // This means that the property will not be created. if (dataTypeDefinition == null) { - _logger.Warn(() => $"Packager: Error handling creation of PropertyType '{property.Element("Name").Value}'. Could not find DataTypeDefintion with unique id '{dataTypeDefinitionId}' nor one referencing the DataType with a property editor alias (or legacy control id) '{property.Element("Type").Value.Trim()}'. Did the package creator forget to package up custom datatypes? This property will be converted to a label/readonly editor if one exists."); + _logger.Warn("Packager: Error handling creation of PropertyType '{PropertyType}'. Could not find DataTypeDefintion with unique id '{DataTypeDefinitionId}' nor one referencing the DataType with a property editor alias (or legacy control id) '{PropertyEditorAlias}'. Did the package creator forget to package up custom datatypes? This property will be converted to a label/readonly editor if one exists.", + property.Element("Name").Value, dataTypeDefinitionId, property.Element("Type").Value.Trim()); //convert to a label! dataTypeDefinition = _dataTypeService.GetByEditorAlias(Constants.PropertyEditors.Aliases.NoEdit).FirstOrDefault(); @@ -762,7 +763,9 @@ namespace Umbraco.Core.Services.Implement var allowedChild = _importedContentTypes.ContainsKey(alias) ? _importedContentTypes[alias] : _contentTypeService.Get(alias); if (allowedChild == null) { - _logger.Warn(() => $"Packager: Error handling DocumentType structure. DocumentType with alias '{alias}' could not be found and was not added to the structure for '{contentType.Alias}'."); + _logger.Warn( + "Packager: Error handling DocumentType structure. DocumentType with alias '{DoctypeAlias}' could not be found and was not added to the structure for '{DoctypeStructureAlias}'.", + alias, contentType.Alias); continue; } @@ -946,7 +949,7 @@ namespace Umbraco.Core.Services.Implement var tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolder); if (tryCreateFolder == false) { - _logger.Error("Could not create folder: " + rootFolder, tryCreateFolder.Exception); + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); throw tryCreateFolder.Exception; } current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); @@ -979,7 +982,7 @@ namespace Umbraco.Core.Services.Implement var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderName); if (tryCreateFolder == false) { - _logger.Error("Could not create folder: " + folderName, tryCreateFolder.Exception); + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); throw tryCreateFolder.Exception; } return _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); @@ -1526,7 +1529,10 @@ namespace Umbraco.Core.Services.Implement else if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master")) == false) { - _logger.Info(string.Format("Template '{0}' has an invalid Master '{1}', so the reference has been ignored.", (string)elementCopy.Element("Alias"), (string)elementCopy.Element("Master"))); + _logger.Info( + "Template '{TemplateAlias}' has an invalid Master '{TemplateMaster}', so the reference has been ignored.", + (string) elementCopy.Element("Alias"), + (string) elementCopy.Element("Master")); } graph.AddItem(TopoGraph.CreateNode((string) elementCopy.Element("Alias"), elementCopy, dependencies)); diff --git a/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs b/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs index 7abb8b6f9d..0a8d60e209 100644 --- a/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs +++ b/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs @@ -39,14 +39,14 @@ namespace Umbraco.Core.Sync if (string.IsNullOrWhiteSpace(umbracoApplicationUrl) == false) { umbracoApplicationUrl = umbracoApplicationUrl.TrimEnd('/'); - logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: " + umbracoApplicationUrl + " (provider)"); + logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: {UmbracoAppUrl} (provider)", umbracoApplicationUrl); return umbracoApplicationUrl; } if (request == null) return null; umbracoApplicationUrl = GetApplicationUrlFromCurrentRequest(request, globalSettings); - logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: " + umbracoApplicationUrl + " (UmbracoModule request)"); + logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: {UmbracoAppUrl} (UmbracoModule request)", umbracoApplicationUrl); return umbracoApplicationUrl; } @@ -62,7 +62,7 @@ namespace Umbraco.Core.Sync if (url.IsNullOrWhiteSpace() == false) { var umbracoApplicationUrl = url.TrimEnd('/'); - logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: " + umbracoApplicationUrl + " (using web.routing/@umbracoApplicationUrl)"); + logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: {UmbracoAppUrl} (using web.routing/@umbracoApplicationUrl)", umbracoApplicationUrl); return umbracoApplicationUrl; } @@ -78,7 +78,7 @@ namespace Umbraco.Core.Sync var ssl = globalSettings.UseHttps ? "s" : ""; url = "http" + ssl + "://" + url; var umbracoApplicationUrl = url.TrimEnd('/'); - logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: " + umbracoApplicationUrl + " (using scheduledTasks/@baseUrl)"); + logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: {UmbracoAppUrl} (using scheduledTasks/@baseUrl)", umbracoApplicationUrl); return umbracoApplicationUrl; } @@ -92,7 +92,7 @@ namespace Umbraco.Core.Sync if (url.IsNullOrWhiteSpace() == false) { var umbracoApplicationUrl = url.TrimEnd('/'); - logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: " + umbracoApplicationUrl + " (IServerRegistrar)"); + logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: {UmbracoAppUrl} (IServerRegistrar)", umbracoApplicationUrl); return umbracoApplicationUrl; } diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index a1b89e58bc..20f9276ba1 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -192,9 +192,11 @@ namespace Umbraco.Core.Sync if (count > Options.MaxProcessingInstructionCount) { //too many instructions, proceed to cold boot - Logger.Warn(() => $"The instruction count ({count}) exceeds the specified MaxProcessingInstructionCount ({Options.MaxProcessingInstructionCount})." + Logger.Warn( + "The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})." + " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id" - + " to the latest found in the database and maintain cache updates based on that Id."); + + " to the latest found in the database and maintain cache updates based on that Id.", + count, Options.MaxProcessingInstructionCount); coldboot = true; } @@ -350,7 +352,10 @@ namespace Umbraco.Core.Sync } catch (JsonException ex) { - Logger.Error($"Failed to deserialize instructions ({dto.Id}: \"{dto.Instructions}\").", ex); + Logger.Error(ex, "Failed to deserialize instructions ({DtoId}: '{DtoInstructions}').", + dto.Id, + dto.Instructions); + lastId = dto.Id; // skip continue; } @@ -406,7 +411,10 @@ namespace Umbraco.Core.Sync catch (Exception ex) { Logger.Error( - $"DISTRIBUTED CACHE IS NOT UPDATED. Failed to execute instructions ({dto.Id}: \"{dto.Instructions}\"). Instruction is being skipped/ignored", ex); + ex, + "DISTRIBUTED CACHE IS NOT UPDATED. Failed to execute instructions ({DtoId}: '{DtoInstructions}'). Instruction is being skipped/ignored", + dto.Id, + dto.Instructions); //we cannot throw here because this invalid instruction will just keep getting processed over and over and errors // will be thrown over and over. The only thing we can do is ignore and move on. diff --git a/src/Umbraco.Core/Sync/ServerMessengerBase.cs b/src/Umbraco.Core/Sync/ServerMessengerBase.cs index 68223a40e6..bbf00c3a6b 100644 --- a/src/Umbraco.Core/Sync/ServerMessengerBase.cs +++ b/src/Umbraco.Core/Sync/ServerMessengerBase.cs @@ -157,7 +157,7 @@ namespace Umbraco.Core.Sync { if (refresher == null) throw new ArgumentNullException(nameof(refresher)); - Current.Logger.Debug(() => $"Invoking refresher {refresher.GetType()} on local server for message type RefreshByPayload"); + Current.Logger.Debug("Invoking refresher {RefresherType} on local server for message type RefreshByPayload", refresher.GetType()); var payloadRefresher = refresher as IPayloadCacheRefresher; if (payloadRefresher == null) @@ -179,7 +179,7 @@ namespace Umbraco.Core.Sync { if (refresher == null) throw new ArgumentNullException(nameof(refresher)); - Current.Logger.Debug(() => $"Invoking refresher {refresher.GetType()} on local server for message type {messageType}"); + Current.Logger.Debug("Invoking refresher {RefresherType} on local server for message type {MessageType}", refresher.GetType(), messageType); switch (messageType) { @@ -240,7 +240,7 @@ namespace Umbraco.Core.Sync { if (refresher == null) throw new ArgumentNullException(nameof(refresher)); - Current.Logger.Debug(() => $"Invoking refresher {refresher.GetType()} on local server for message type {messageType}"); + Current.Logger.Debug("Invoking refresher {RefresherType} on local server for message type {MessageType}", refresher.GetType(), messageType); var typedRefresher = refresher as ICacheRefresher; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 456b647a7a..44f170b2df 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -64,8 +64,6 @@ - - @@ -76,6 +74,27 @@ + + 2.7.1 + + + 2.0.1 + + + 3.0.0 + + + 2.0.0 + + + 1.0.0 + + + 2.1.2 + + + 4.0.0 + @@ -310,7 +329,8 @@ - + + @@ -404,7 +424,7 @@ - + @@ -455,7 +475,7 @@ - + @@ -545,24 +565,18 @@ - - - - - - diff --git a/src/Umbraco.Core/UmbracoApplicationBase.cs b/src/Umbraco.Core/UmbracoApplicationBase.cs index 9930f43ca4..c461d9f824 100644 --- a/src/Umbraco.Core/UmbracoApplicationBase.cs +++ b/src/Umbraco.Core/UmbracoApplicationBase.cs @@ -3,10 +3,11 @@ using System.Reflection; using System.Threading; using System.Web; using System.Web.Hosting; -using log4net; using LightInject; +using Serilog; using Umbraco.Core.Composing; using Umbraco.Core.Logging; +using ILogger = Umbraco.Core.Logging.ILogger; namespace Umbraco.Core { @@ -27,7 +28,7 @@ namespace Umbraco.Core /// protected virtual ILogger GetLogger() { - return Logger.CreateWithDefaultLog4NetConfiguration(); + return Logger.CreateWithDefaultConfiguration(); } // events - in the order they trigger @@ -91,7 +92,7 @@ namespace Umbraco.Core var msg = "Unhandled exception in AppDomain"; if (isTerminating) msg += " (terminating)"; msg += "."; - logger.Error(msg, exception); + logger.Error(exception, msg); }; } @@ -184,14 +185,15 @@ namespace Umbraco.Core BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null); - var shutdownMsg = $"Application shutdown. Details: {HostingEnvironment.ShutdownReason}\r\n\r\n_shutDownMessage={shutDownMessage}\r\n\r\n_shutDownStack={shutDownStack}"; - - logger.Info(shutdownMsg); + logger.Info("Application shutdown. Details: {ShutdownReason}\r\n\r\n_shutDownMessage={ShutdownMessage}\r\n\r\n_shutDownStack={ShutdownStack}", + HostingEnvironment.ShutdownReason, + shutDownMessage, + shutDownStack); } catch (Exception) { //if for some reason that fails, then log the normal output - logger.Info("Application shutdown. Reason: " + HostingEnvironment.ShutdownReason); + logger.Info("Application shutdown. Reason: {ShutdownReason}", HostingEnvironment.ShutdownReason); } } @@ -201,7 +203,10 @@ namespace Umbraco.Core { HandleApplicationEnd(); OnApplicationEnd(sender, evargs); - LogManager.Shutdown(); + + //Not sure if we need to do this - as my POC approach I never had to deal with this + //As the LightInject container when tearing down will dispose of Serilog AFAIK + Log.CloseAndFlush(); } #endregion @@ -220,7 +225,7 @@ namespace Umbraco.Core // ignore HTTP errors if (exception.GetType() == typeof(HttpException)) return; - Current.Logger.Error("An unhandled exception occurred.", exception); + Current.Logger.Error(exception, "An unhandled exception occurred"); } // called by ASP.NET (auto event wireup) at any phase in the application life cycle @@ -243,7 +248,7 @@ namespace Umbraco.Core } catch (Exception ex) { - Current.Logger.Error($"Error in {name} handler.", ex); + Current.Logger.Error(ex, "Error in {Name} handler.", name); throw; } } diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index e579a5158f..d018dddfbf 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -155,7 +155,7 @@ namespace Umbraco.Core } catch (ArgumentException ex) { - Current.Logger.Error(typeof(UriExtensions), "Failed to determine if request was client side", ex); + Current.Logger.Error(typeof(UriExtensions), ex, "Failed to determine if request was client side"); return false; } } diff --git a/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs b/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs index d9c3e4b0f3..8b4755107a 100644 --- a/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs +++ b/src/Umbraco.Core/Xml/XPath/MacroNavigator.cs @@ -164,8 +164,6 @@ namespace Umbraco.Core.Xml.XPath #pragma warning disable 168 var msg = string.Format(format, args); // unused if not writing, hence #pragma #pragma warning restore 168 - //LogHelper.Debug(msg); // beware! this can quicky overflow log4net - //Console.WriteLine(msg); } #endif diff --git a/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs b/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs index d4acfe137d..4c15f1b9b0 100644 --- a/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs +++ b/src/Umbraco.Core/Xml/XPath/NavigableNavigator.cs @@ -4,7 +4,7 @@ // In Debug configuration, diagnostics code can be enabled by defining DEBUGNAVIGATOR below, // but by default nothing is writted, unless some lines are un-commented in Debug(...) below. // -// Beware! Diagnostics are extremely verbose and can overflow log4net pretty easily. +// Beware! Diagnostics are extremely verbose and can overflow logging pretty easily. #if DEBUG // define to enable diagnostics code @@ -254,8 +254,6 @@ namespace Umbraco.Core.Xml.XPath //format = "[" + _uid.ToString("00000") + "] " + Tabs.Substring(0, _tabs) + format; //var msg = string.Format(format, args); - //LogHelper.Debug(msg); // beware! this can quicky overflow log4net - //Console.WriteLine(msg); } #endif diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index bdbaf04a32..94982c8591 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -212,7 +212,7 @@ namespace Umbraco.Examine var filtered = c.RawQuery(rawQuery); var results = searcher.Search(filtered); - ProfilingLogger.Logger.Debug(GetType(), $"DeleteFromIndex with query: {rawQuery} (found {results.TotalItemCount} results)"); + ProfilingLogger.Logger.Debug(GetType(), "DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount); //need to queue a delete item for each one found foreach (var r in results) diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 0219ace270..64fd7b5c71 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -162,7 +162,7 @@ namespace Umbraco.Examine /// public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { - ProfilingLogger.Logger.Debug(GetType(), () => $"{name} indexer initializing"); + ProfilingLogger.Logger.Debug(GetType(), "{IndexerName} indexer initializing", name); if (config["enableDefaultEventHandler"] != null && bool.TryParse(config["enableDefaultEventHandler"], out var enabled)) { @@ -331,11 +331,11 @@ namespace Umbraco.Examine /// /// overridden for logging /// - /// - protected override void OnIndexingError(IndexingErrorEventArgs e) + /// + protected override void OnIndexingError(IndexingErrorEventArgs ex) { - ProfilingLogger.Logger.Error(GetType(), e.Message, e.InnerException); - base.OnIndexingError(e); + ProfilingLogger.Logger.Error(GetType(), ex.InnerException, ex.Message); + base.OnIndexingError(ex); } /// @@ -359,7 +359,12 @@ namespace Umbraco.Examine } } - ProfilingLogger.Logger.Debug(GetType(), () => $"Write lucene doc id:{docArgs.ValueSet.Id}, category:{docArgs.ValueSet.Category}, type:{docArgs.ValueSet.ItemType}"); + ProfilingLogger.Logger.Debug(GetType(), + "Write lucene doc id:{DocumentId}, category:{DocumentCategory}, type:{DocumentItemType}", + docArgs.ValueSet.Id, + docArgs.ValueSet.Category, + docArgs.ValueSet.ItemType); + base.OnDocumentWriting(docArgs); } @@ -369,7 +374,10 @@ namespace Umbraco.Examine /// protected override void AddDocument(Document doc, IndexItem item, IndexWriter writer) { - ProfilingLogger.Logger.Debug(GetType(), () => $"AddDocument {item.ValueSet.Id} with type {item.ValueSet.ItemType}"); + ProfilingLogger.Logger.Debug(GetType(), + "AddDocument {DocumentId} with type {DocumentItemType}", + item.ValueSet.Id, + item.ValueSet.ItemType); base.AddDocument(doc, item, writer); } diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 19236e0163..7c8968d93b 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -61,7 +61,7 @@ - + diff --git a/src/Umbraco.Tests.Benchmarks/app.config b/src/Umbraco.Tests.Benchmarks/app.config index d71468cbdb..227af0f591 100644 --- a/src/Umbraco.Tests.Benchmarks/app.config +++ b/src/Umbraco.Tests.Benchmarks/app.config @@ -5,10 +5,6 @@ - - - - diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config index 24c1445f01..f76f6b73b6 100644 --- a/src/Umbraco.Tests/App.config +++ b/src/Umbraco.Tests/App.config @@ -107,10 +107,6 @@ - - - - diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 8c7c1f2d07..955f6f94c8 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -356,7 +356,6 @@ namespace Umbraco.Tests.Composing "Dynamic,", "HtmlDiff,", "Iesi.Collections,", - "log4net,", "Microsoft.", "Newtonsoft.", "NHibernate.", @@ -379,7 +378,8 @@ namespace Umbraco.Tests.Composing "ICSharpCode.", "CookComputing.", /* Mono */ - "MonoDevelop.NUnit" + "MonoDevelop.NUnit", + "Serilog." }; public static IEnumerable FindClassesOfTypeWithAttribute() diff --git a/src/Umbraco.Tests/Persistence/LocksTests.cs b/src/Umbraco.Tests/Persistence/LocksTests.cs index 18113955cf..4dfb90c8fd 100644 --- a/src/Umbraco.Tests/Persistence/LocksTests.cs +++ b/src/Umbraco.Tests/Persistence/LocksTests.cs @@ -14,7 +14,7 @@ namespace Umbraco.Tests.Persistence { [TestFixture] [Timeout(60000)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Log4Net)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Serilog)] public class LocksTests : TestWithDatabaseBase { protected override void Initialize() diff --git a/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs b/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs index d6dfb2d48a..9caa4b2aa1 100644 --- a/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs +++ b/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs @@ -1,82 +1,93 @@ using System; -using System.Linq; using Umbraco.Core.Logging; namespace Umbraco.Tests.TestHelpers { public class ConsoleLogger : ILogger { - public void Error(Type reporting, string message, Exception exception) + public void Fatal(Type reporting, Exception exception, string message) { - Console.WriteLine("INFO {0} - {1}", reporting.Name, message); + Console.WriteLine("FATAL {0} - {1}", reporting.Name, message); Console.WriteLine(exception); } + public void Fatal(Type reporting, Exception exception) + { + Console.WriteLine("FATAL {0}", reporting.Name); + Console.WriteLine(exception); + } + + public void Fatal(Type reporting, string message) + { + Console.WriteLine("FATAL {0} - {1}", reporting.Name, message); + } + + public void Fatal(Type reporting, Exception exception, string format, params object[] args) + { + Console.WriteLine("FATAL {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine(exception); + } + + public void Fatal(Type reporting, string format, params object[] args) + { + Console.WriteLine("FATAL {0} - {1}", reporting.Name, string.Format(format, args)); + } + + public void Error(Type reporting, Exception exception, string message) + { + Console.WriteLine("ERROR {0} - {1}", reporting.Name, message); + Console.WriteLine(exception); + } + + public void Error(Type reporting, Exception exception) + { + Console.WriteLine("ERROR {0}", reporting.Name); + Console.WriteLine(exception); + } + + public void Error(Type reporting, string message) + { + Console.WriteLine("ERROR {0} - {1}", reporting.Name, message); + } + + public void Error(Type reporting, Exception exception, string format, params object[] args) + { + Console.WriteLine("ERROR {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine(exception); + } + + public void Error(Type reporting, string format, params object[] args) + { + Console.WriteLine("ERROR {0} - {1}", reporting.Name, string.Format(format, args)); + } + public void Warn(Type reporting, string message) { Console.WriteLine("WARN {0} - {1}", reporting.Name, message); } - public void Warn(Type reporting, Func messageBuilder) - { - Console.WriteLine("WARN {0} - {1}", reporting.Name, messageBuilder()); - } - public void Warn(Type reporting, string format, params object[] args) { Console.WriteLine("WARN {0} - {1}", reporting.Name, string.Format(format, args)); } - public void Warn(Type reporting, string format, params Func[] args) - { - Console.WriteLine("WARN {0} - {1}", reporting.Name, string.Format(format, args.Select(x => x()).ToArray())); - } - public void Warn(Type reporting, Exception exception, string message) { Console.WriteLine("WARN {0} - {1}", reporting.Name, message); Console.WriteLine(exception); } - - public void Warn(Type reporting, Exception exception, Func messageBuilder) - { - Console.WriteLine("WARN {0} - {1}", reporting.Name, messageBuilder()); - Console.WriteLine(exception); - } - + public void Warn(Type reporting, Exception exception, string format, params object[] args) { Console.WriteLine("WARN {0} - {1}", reporting.Name, string.Format(format, args)); Console.WriteLine(exception); } - public void Warn(Type reporting, Exception exception, string format, params Func[] args) - { - Console.WriteLine("WARN {0} - {1}", reporting.Name, string.Format(format, args.Select(x => x()).ToArray())); - Console.WriteLine(exception); - } - - public void WarnWithException(Type reporting, string format, Exception e, params Func[] args) - { - Console.WriteLine("WARN {0} - {1}", reporting.Name, string.Format(format, args.Select(x => x()).ToArray())); - Console.WriteLine(e); - } - - public void Info(Type reporting, Func generateMessage) - { - Console.WriteLine("INFO {0} - {1}", reporting.Name, generateMessage()); - } - public void Info(Type reporting, string format, params object[] args) { Console.WriteLine("INFO {0} - {1}", reporting.Name, string.Format(format, args)); } - public void Info(Type reporting, string format, params Func[] args) - { - Console.WriteLine("INFO {0} - {1}", reporting.Name, string.Format(format, args.Select(x => x()).ToArray())); - } - public void Info(Type reporting, string message) { Console.WriteLine("INFO {0} - {1}", reporting.Name, message); @@ -87,19 +98,19 @@ namespace Umbraco.Tests.TestHelpers Console.WriteLine("DEBUG {0} - {1}", reporting.Name, message); } - public void Debug(Type reporting, Func messageBuilder) - { - Console.WriteLine("DEBUG {0} - {1}", reporting.Name, messageBuilder()); - } - public void Debug(Type reporting, string format, params object[] args) { Console.WriteLine("DEBUG {0} - {1}", reporting.Name, string.Format(format, args)); } - public void Debug(Type reporting, string format, params Func[] args) + public void Verbose(Type reporting, string message) { - Console.WriteLine("DEBUG {0} - {1}", reporting.Name, string.Format(format, args.Select(x => x()).ToArray())); + Console.WriteLine("VERBOSE {0} - {1}", reporting.Name, message); + } + + public void Verbose(Type reporting, string format, params object[] args) + { + Console.WriteLine("VERBOSE {0} - {1}", reporting.Name, string.Format(format, args)); } } } diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 25a0dc72fa..21f1ce82b2 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -344,7 +344,7 @@ namespace Umbraco.Tests.TestHelpers } catch (Exception ex) { - Logger.Error("Could not remove the old database file", ex); + Logger.Error(ex, "Could not remove the old database file"); // swallow this exception - that's because a sub class might require further teardown logic onFail?.Invoke(ex); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 5cd884b4c6..3e40a7c3f7 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -165,9 +165,9 @@ namespace Umbraco.Tests.Testing Container.RegisterSingleton(f => Mock.Of()); Container.RegisterSingleton(f => Mock.Of()); } - else if (option == UmbracoTestOptions.Logger.Log4Net) + else if (option == UmbracoTestOptions.Logger.Serilog) { - Container.RegisterSingleton(f => new Logger(new FileInfo(TestHelper.MapPathForTest("~/unit-test-log4net.config")))); + Container.RegisterSingleton(f => new Logger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config")))); Container.RegisterSingleton(f => new LogProfiler(f.GetInstance())); } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs b/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs index 3f57ac14d8..deefd33946 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestOptions.cs @@ -6,8 +6,8 @@ { // pure mocks Mock, - // log4net for tests - Log4Net + // Serilog for tests + Serilog } public enum Database diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index e842343e96..3a715a9561 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -80,7 +80,6 @@ - @@ -107,7 +106,6 @@ - @@ -519,7 +517,7 @@ Designer - + Always diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs index 154b587a47..1e5e909bde 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.UmbracoExamine [OneTimeSetUp] public void InitializeFixture() { - var logger = new Logger(new FileInfo(TestHelper.MapPathForTest("~/unit-test-log4net.config"))); + var logger = new Logger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config"))); _profilingLogger = new ProfilingLogger(logger, new LogProfiler(logger)); } diff --git a/src/Umbraco.Tests/unit-test-log4net.config b/src/Umbraco.Tests/unit-test-log4net.config deleted file mode 100644 index 34890fd583..0000000000 --- a/src/Umbraco.Tests/unit-test-log4net.config +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Tests/unit-test-logger.config b/src/Umbraco.Tests/unit-test-logger.config new file mode 100644 index 0000000000..62fa1353b2 --- /dev/null +++ b/src/Umbraco.Tests/unit-test-logger.config @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI.Client/bower.json b/src/Umbraco.Web.UI.Client/bower.json index 6119bd5ae3..1d7096cb29 100644 --- a/src/Umbraco.Web.UI.Client/bower.json +++ b/src/Umbraco.Web.UI.Client/bower.json @@ -43,7 +43,7 @@ "clipboard": "~2.0.0", "font-awesome": "~4.2", "animejs": "^2.2.0", - "angular-ui-sortable": "0.14.3", + "angular-ui-sortable": "0.14.4", "angular-messages": "^1.7.2" }, "install": { diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index 78124ac625..823d3d526d 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -329,10 +329,15 @@ /** Converts a string/integer/bool to true/false */ Object.toBoolean = function (obj) { + if (obj === undefined || obj === null) { + return false; + } + if ((typeof obj) === "boolean") { return obj; } - if (obj === "1" || obj === 1 || obj === "true") { + + if (obj === "1" || obj === 1 || obj.toString().toLowerCase() === "true") { return true; } return false; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js index 2793a0ec57..7f906ddcc0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js @@ -10,62 +10,62 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se templateUrl: 'views/components/application/umb-sections.html', link: function (scope, element, attr, ctrl) { - var sectionItemsWidth = []; - var evts = []; - var maxSections = 7; + var sectionItemsWidth = []; + var evts = []; + var maxSections = 7; //setup scope vars - scope.maxSections = maxSections; - scope.overflowingSections = 0; + scope.maxSections = maxSections; + scope.overflowingSections = 0; scope.sections = []; scope.currentSection = appState.getSectionState("currentSection"); scope.showTray = false; //appState.getGlobalState("showTray"); scope.stickyNavigation = appState.getGlobalState("stickyNavigation"); scope.needTray = false; - function loadSections(){ - sectionService.getSectionsForUser() - .then(function (result) { - scope.sections = result; - // store the width of each section so we can hide/show them based on browser width - // we store them because the sections get removed from the dom and then we - // can't tell when to show them gain - $timeout(function(){ - $("#applications .sections li").each(function(index) { - sectionItemsWidth.push($(this).outerWidth()); - }); - }); - calculateWidth(); - }); - } - - function calculateWidth(){ - $timeout(function(){ - //total width minus room for avatar, search, and help icon - var windowWidth = $(window).width()-200; - var sectionsWidth = 0; - scope.totalSections = scope.sections.length; - scope.maxSections = maxSections; - scope.overflowingSections = 0; - scope.needTray = false; - - // detect how many sections we can show on the screen - for (var i = 0; i < sectionItemsWidth.length; i++) { - var sectionItemWidth = sectionItemsWidth[i]; - sectionsWidth += sectionItemWidth; + function loadSections() { + sectionService.getSectionsForUser() + .then(function (result) { + scope.sections = result; + // store the width of each section so we can hide/show them based on browser width + // we store them because the sections get removed from the dom and then we + // can't tell when to show them gain + $timeout(function () { + $("#applications .sections li").each(function (index) { + sectionItemsWidth.push($(this).outerWidth()); + }); + }); + calculateWidth(); + }); + } - if(sectionsWidth > windowWidth) { - scope.needTray = true; - scope.maxSections = i - 1; - scope.overflowingSections = scope.maxSections - scope.totalSections; - break; - } - } - }); - } + function calculateWidth() { + $timeout(function () { + //total width minus room for avatar, search, and help icon + var windowWidth = $(window).width() - 200; + var sectionsWidth = 0; + scope.totalSections = scope.sections.length; + scope.maxSections = maxSections; + scope.overflowingSections = 0; + scope.needTray = false; + + // detect how many sections we can show on the screen + for (var i = 0; i < sectionItemsWidth.length; i++) { + var sectionItemWidth = sectionItemsWidth[i]; + sectionsWidth += sectionItemWidth; + + if (sectionsWidth > windowWidth) { + scope.needTray = true; + scope.maxSections = i - 1; + scope.overflowingSections = scope.maxSections - scope.totalSections; + break; + } + } + }); + } //Listen for global state changes - evts.push(eventsService.on("appState.globalState.changed", function(e, args) { + evts.push(eventsService.on("appState.globalState.changed", function (e, args) { if (args.key === "showTray") { scope.showTray = args.value; } @@ -74,44 +74,44 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se } })); - evts.push(eventsService.on("appState.sectionState.changed", function(e, args) { + evts.push(eventsService.on("appState.sectionState.changed", function (e, args) { if (args.key === "currentSection") { scope.currentSection = args.value; } })); - evts.push(eventsService.on("app.reInitialize", function(e, args) { + evts.push(eventsService.on("app.reInitialize", function (e, args) { //re-load the sections if we're re-initializing (i.e. package installed) loadSections(); })); //ensure to unregister from all events! - scope.$on('$destroy', function () { - for (var e in evts) { - eventsService.unsubscribe(evts[e]); - } - }); + scope.$on('$destroy', function () { + for (var e in evts) { + eventsService.unsubscribe(evts[e]); + } + }); - //on page resize - window.onresize = calculateWidth; + //on page resize + window.onresize = calculateWidth; - scope.sectionClick = function (event, section) { + scope.sectionClick = function (event, section) { - if (event.ctrlKey || - event.shiftKey || - event.metaKey || // apple - (event.button && event.button === 1) // middle click, >IE9 + everyone else - ) { - return; - } + if (event.ctrlKey || + event.shiftKey || + event.metaKey || // apple + (event.button && event.button === 1) // middle click, >IE9 + everyone else + ) { + return; + } if (scope.userDialog) { closeUserDialog(); - } - + } - navigationService.hideSearch(); - navigationService.showTree(section.alias); + + navigationService.hideSearch(); + navigationService.showTree(section.alias); //in some cases the section will have a custom route path specified, if there is one we'll use it if (section.routePath) { @@ -123,22 +123,22 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se $location.path(path); } navigationService.clearSearch(); - - }; - scope.sectionDblClick = function(section){ - navigationService.reloadSection(section.alias); - }; + }; - scope.trayClick = function () { - if (appState.getGlobalState("showTray") === true) { - navigationService.hideTray(); - } else { - navigationService.showTray(); - } - }; + scope.sectionDblClick = function (section) { + navigationService.reloadSection(section.alias); + }; - loadSections(); + scope.trayClick = function () { + if (appState.getGlobalState("showTray") === true) { + navigationService.hideTray(); + } else { + navigationService.showTray(); + } + }; + + loadSections(); } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js index ac4fce8d44..1d1c9d8f00 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js @@ -208,6 +208,7 @@ Use this directive to construct a header inside the main editor window. function link(scope, el, attr, ctrl) { + scope.vm = {}; scope.vm.dropdownOpen = false; scope.vm.currentVariant = ""; @@ -231,6 +232,7 @@ Use this directive to construct a header inside the main editor window. }; scope.selectVariant = function (event, variant) { + if (scope.onSelectVariant) { scope.vm.dropdownOpen = false; scope.onSelectVariant({ "variant": variant }); @@ -293,6 +295,7 @@ Use this directive to construct a header inside the main editor window. } } + var directive = { transclude: true, restrict: 'E', diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditornavigation.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditornavigation.directive.js index 6a8896b609..3eda8974d6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditornavigation.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditornavigation.directive.js @@ -1,18 +1,79 @@ (function () { 'use strict'; - function EditorNavigationDirective(eventsService) { + function EditorNavigationDirective($window, $timeout, eventsService, windowResizeListener) { - function link(scope, el, attr, ctrl) { + function link(scope) { scope.showNavigation = true; + scope.showMoreButton = false; + scope.showDropdown = false; + scope.overflowingItems = 0; + scope.itemsLimit = 6; + + scope.moreButton = { + alias: "more", + active: false, + name: "More" + }; scope.clickNavigationItem = function (selectedItem) { - setItemToActive(selectedItem); + scope.showDropdown = false; runItemAction(selectedItem); eventsService.emit("app.tabChange", selectedItem); + setItemToActive(selectedItem); }; + scope.toggleDropdown = function () { + scope.showDropdown = !scope.showDropdown; + }; + + scope.hideDropdown = function() { + scope.showDropdown = false; + }; + + function onInit() { + + // hide navigation if there is only 1 item + if (scope.navigation.length <= 1) { + scope.showNavigation = false; + } + + $timeout(function(){ + if($window && $window.innerWidth) { + calculateVisibleItems($window.innerWidth); + } + }); + + } + + function calculateVisibleItems(windowWidth) { + + // if we don't get a windowWidth stick with the default item limit + if(!windowWidth) { + return; + } + + scope.itemsLimit = 0; + + // set visible items based on browser width + if (windowWidth > 1500) { + scope.itemsLimit = 6; + } + else if (windowWidth > 700) { + scope.itemsLimit = 4; + } + + // toggle more button + if(scope.navigation.length > scope.itemsLimit) { + scope.showMoreButton = true; + scope.overflowingItems = scope.itemsLimit - scope.navigation.length; + } else { + scope.showMoreButton = false; + scope.overflowingItems = 0; + } + } + function runItemAction(selectedItem) { if (selectedItem.action) { selectedItem.action(selectedItem); @@ -20,29 +81,41 @@ } function setItemToActive(selectedItem) { - // set all other views to inactive if (selectedItem.view) { - - for (var index = 0; index < scope.navigation.length; index++) { - var item = scope.navigation[index]; + + // deselect all items + angular.forEach(scope.navigation, function(item, index){ item.active = false; + }); + + // set clicked item to active + selectedItem.active = true; + + // set more button to active if item in dropdown is clicked + var selectedItemIndex = scope.navigation.indexOf(selectedItem); + if (selectedItemIndex + 1 > scope.itemsLimit) { + scope.moreButton.active = true; + } else { + scope.moreButton.active = false; } - // set view to active - selectedItem.active = true; } } - function activate() { - - // hide navigation if there is only 1 item - if (scope.navigation.length <= 1) { - scope.showNavigation = false; + var resizeCallback = function(size) { + if(size && size.width) { + calculateVisibleItems(size.width); } + }; - } + windowResizeListener.register(resizeCallback); - activate(); + //ensure to unregister from all events and kill jquery plugins + scope.$on('$destroy', function () { + windowResizeListener.unregister(resizeCallback); + }); + + onInit(); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js index edb08f1066..c852228205 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js @@ -206,6 +206,10 @@ removeEditor(args.editor); })); + evts.push(eventsService.on("appState.editors.closeAll", function (name, args) { + scope.editors = []; + })); + //ensure to unregister from all events! scope.$on('$destroy', function () { for (var e in evts) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js index 5a6fbb6963..11c94d04c0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js @@ -245,51 +245,21 @@ scope.openCompositionsDialog = function() { scope.compositionsDialogModel = { - title: "Compositions", contentType: scope.model, compositeContentTypes: scope.model.compositeContentTypes, - view: "views/common/overlays/contenttypeeditor/compositions/compositions.html", - confirmSubmit: { - title: "Warning", - description: "Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?", - checkboxLabel: "I know what I'm doing", - enable: true - }, - submit: function(model, oldModel, confirmed) { + view: "views/common/infiniteeditors/compositions/compositions.html", + size: "small", + submit: function() { + + // make sure that all tabs has an init property + if (scope.model.groups.length !== 0) { + angular.forEach(scope.model.groups, function(group) { + addInitProperty(group); + }); + } - var compositionRemoved = false; - - // check if any compositions has been removed - for(var i = 0; oldModel.compositeContentTypes.length > i; i++) { - - var oldComposition = oldModel.compositeContentTypes[i]; - - if(_.contains(model.compositeContentTypes, oldComposition) === false) { - compositionRemoved = true; - } - - } - - // show overlay confirm box if compositions has been removed. - if(compositionRemoved && confirmed === false) { - - scope.compositionsDialogModel.confirmSubmit.show = true; - - // submit overlay if no compositions has been removed - // or the action has been confirmed - } else { - - // make sure that all tabs has an init property - if (scope.model.groups.length !== 0) { - angular.forEach(scope.model.groups, function(group) { - addInitProperty(group); - }); - } - - // remove overlay - scope.compositionsDialogModel.show = false; - scope.compositionsDialogModel = null; - } + // remove overlay + editorService.close(); }, close: function(oldModel) { @@ -299,8 +269,7 @@ scope.model.compositeContentTypes = oldModel.contentType.compositeContentTypes; // remove overlay - scope.compositionsDialogModel.show = false; - scope.compositionsDialogModel = null; + editorService.close(); }, selectCompositeContentType: function (selectedContentType) { @@ -348,39 +317,40 @@ } }; - //select which resource methods to use, eg document Type or Media Type versions - var availableContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes; - var whereUsedContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getWhereCompositionIsUsedInContentTypes : mediaTypeResource.getWhereCompositionIsUsedInContentTypes; - var countContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getCount : mediaTypeResource.getCount; - //get the currently assigned property type aliases - ensure we pass these to the server side filer - var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) { - return _.map(g.properties, function(p) { - return p.alias; - }); - })), function(f) { - return f !== null && f !== undefined; - }); - $q.all([ - //get available composite types - availableContentTypeResource(scope.model.id, [], propAliasesExisting).then(function (result) { - setupAvailableContentTypesModel(result); - }), - //get where used document types - whereUsedContentTypeResource(scope.model.id).then(function (whereUsed) { - //pass to the dialog model the content type eg documentType or mediaType - scope.compositionsDialogModel.section = scope.contentType; - //pass the list of 'where used' document types - scope.compositionsDialogModel.whereCompositionUsed = whereUsed; - }), - //get content type count - countContentTypeResource().then(function(result) { - scope.compositionsDialogModel.totalContentTypes = parseInt(result, 10); - }) - ]).then(function() { - //resolves when both other promises are done, now show it - scope.compositionsDialogModel.show = true; - }); + //select which resource methods to use, eg document Type or Media Type versions + var availableContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes; + var whereUsedContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getWhereCompositionIsUsedInContentTypes : mediaTypeResource.getWhereCompositionIsUsedInContentTypes; + var countContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getCount : mediaTypeResource.getCount; + + //get the currently assigned property type aliases - ensure we pass these to the server side filer + var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) { + return _.map(g.properties, function(p) { + return p.alias; + }); + })), function(f) { + return f !== null && f !== undefined; + }); + $q.all([ + //get available composite types + availableContentTypeResource(scope.model.id, [], propAliasesExisting).then(function (result) { + setupAvailableContentTypesModel(result); + }), + //get where used document types + whereUsedContentTypeResource(scope.model.id).then(function (whereUsed) { + //pass to the dialog model the content type eg documentType or mediaType + scope.compositionsDialogModel.section = scope.contentType; + //pass the list of 'where used' document types + scope.compositionsDialogModel.whereCompositionUsed = whereUsed; + }), + //get content type count + countContentTypeResource().then(function(result) { + scope.compositionsDialogModel.totalContentTypes = parseInt(result, 10); + }) + ]).then(function() { + //resolves when both other promises are done, now show it + editorService.open(scope.compositionsDialogModel); + }); }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index 726103caf9..948702a4e3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -12,7 +12,7 @@ * Another thing this directive does is to ensure that any .control-group that contains form elements that are invalid will * be marked with the 'error' css class. This ensures that labels included in that control group are styled correctly. **/ -function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService, eventsService, $routeParams, navigationService) { +function valFormManager(serverValidationManager, $rootScope, $timeout, $location, overlayService, eventsService, $routeParams, navigationService, editorService, localizationService) { var SHOW_VALIDATION_CLASS_NAME = "show-validation"; var SAVING_EVENT_NAME = "formSubmitting"; @@ -44,6 +44,22 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not }, link: function (scope, element, attr, formCtrl) { + var labels = {}; + + var labelKeys = [ + "prompt_unsavedChanges", + "prompt_unsavedChangesWarning", + "prompt_discardChanges", + "prompt_stay" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + labels.unsavedChangesTitle = values[0]; + labels.unsavedChangesContent = values[1]; + labels.discardChangesButton = values[2]; + labels.stayButton = values[3]; + }); + //watch the list of validation errors to notify the application of any validation changes scope.$watch(function () { //the validators are in the $error collection: https://docs.angularjs.org/api/ng/type/form.FormController#$error @@ -104,28 +120,62 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not formCtrl.$setPristine(); })); + var confirmed = false; + //This handles the 'unsaved changes' dialog which is triggered when a route is attempting to be changed but // the form has pending changes var locationEvent = $rootScope.$on('$locationChangeStart', function(event, nextLocation, currentLocation) { - if (!formCtrl.$dirty || isSavingNewItem) { + + var infiniteEditors = editorService.getEditors(); + + if (!formCtrl.$dirty && infiniteEditors.length === 0 || isSavingNewItem && infiniteEditors.length === 0) { + confirmed = true; return; } var nextPath = nextLocation.split("#")[1]; - if (nextPath) { + if (nextPath && !confirmed) { if (navigationService.isRouteChangingNavigation(currentLocation, nextLocation)) { - if (!notificationsService.hasView()) { - if (nextPath.indexOf("%253") || nextPath.indexOf("%252")) { - nextPath = decodeURIComponent(nextPath); - } - - var msg = { view: "confirmroutechange", args: { path: nextPath, listener: locationEvent } }; - notificationsService.add(msg); + if (nextPath.indexOf("%253") || nextPath.indexOf("%252")) { + nextPath = decodeURIComponent(nextPath); } + // Open discard changes overlay + var overlay = { + "view": "default", + "title": labels.unsavedChangesTitle, + "content": labels.unsavedChangesContent, + "disableBackdropClick": true, + "submitButtonLabel": labels.stayButton, + "closeButtonLabel": labels.discardChangesButton, + submit: function() { + overlayService.close(); + }, + close: function() { + // close all editors + editorService.closeAll(); + // allow redirection + navigationService.clearSearch(); + //we need to break the path up into path and query + var parts = nextPath.split("?"); + var query = {}; + if (parts.length > 1) { + _.each(parts[1].split("&"), function(q) { + var keyVal = q.split("="); + query[keyVal[0]] = keyVal[1]; + }); + } + $location.path(parts[0]).search(query); + overlayService.close(); + confirmed = true; + } + }; + + overlayService.open(overlay); + //prevent the route! event.preventDefault(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 16d6cc20c9..7b80694e3b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -682,7 +682,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica // /belle/#/content/edit/9876 (where 9876 is the new id) //clear the query strings - navigationService.clearSearch(); + navigationService.clearSearch(["cculture"]); //change to new path $location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js index 800f150ee8..eab167c2ec 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js @@ -53,7 +53,7 @@ * @methodOf umbraco.services.editorService * * @description - * Opens a media editor in infinite editing, the submit callback returns the updated content item + * Method to close the latest opened editor */ function close() { var length = editors.length; @@ -69,6 +69,26 @@ eventsService.emit("appState.editors.close", args); } + /** + * @ngdoc method + * @name umbraco.services.editorService#closeAll + * @methodOf umbraco.services.editorService + * + * @description + * Method to close all open editors + */ + function closeAll() { + + editors = []; + + var args = { + editors: editors, + editor: null + }; + + eventsService.emit("appState.editors.closeAll", args); + } + /** * @ngdoc method * @name umbraco.services.editorService#contentEditor @@ -102,6 +122,82 @@ open(editor); } + /** + * @ngdoc method + * @name umbraco.services.editorService#copy + * @methodOf umbraco.services.editorService + * + * @description + * Opens a copy editor in infinite editing, the submit callback returns an array of selected items + * @param {String} editor.section The node entity type + * @param {String} editor.currentNode The current node id + * @param {Callback} editor.submit Saves, submits, and closes the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ + + function copy(editor) { + editor.view = "views/common/infiniteeditors/copy/copy.html"; + editor.size = "small"; + open(editor); + } + + /** + * @ngdoc method + * @name umbraco.services.editorService#move + * @methodOf umbraco.services.editorService + * + * @description + * Opens a move editor in infinite editing. + * @param {String} editor.section The node entity type + * @param {String} editor.currentNode The current node id + * @param {Callback} editor.submit Saves, submits, and closes the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ + + function move(editor) { + editor.view = "views/common/infiniteeditors/move/move.html"; + editor.size = "small"; + open(editor); + } + + /** + * @ngdoc method + * @name umbraco.services.editorService#embed + * @methodOf umbraco.services.editorService + * + * @description + * Opens an embed editor in infinite editing. + * @param {Callback} editor.submit Saves, submits, and closes the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ + + function embed(editor) { + editor.view = "views/common/infiniteeditors/embed/embed.html"; + editor.size = "small"; + open(editor); + } + + /** + * @ngdoc method + * @name umbraco.services.editorService#linkPicker + * @methodOf umbraco.services.editorService + * + * @description + * Opens an embed editor in infinite editing. + * @param {Callback} editor.submit Saves, submits, and closes the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ + + function linkPicker(editor) { + editor.view = "views/common/infiniteeditors/linkpicker/linkpicker.html"; + editor.size = "small"; + open(editor); + } + /** * @ngdoc method * @name umbraco.services.editorService#mediaEditor @@ -356,13 +452,36 @@ open(editor); } + /** + * @ngdoc method + * @name umbraco.services.editorService#macroPicker + * @methodOf umbraco.services.editorService + * + * @description + * Opens a member group picker in infinite editing. + * + * @param {Callback} editor.submit Submits the editor. + * @param {Callback} editor.close Closes the editor. + * @returns {Object} editor object + */ + function memberGroupPicker(editor) { + editor.view = "views/common/infiniteeditors/membergrouppicker/membergrouppicker.html"; + editor.size = "small"; + open(editor); + } + var service = { getEditors: getEditors, open: open, close: close, + closeAll: closeAll, mediaEditor: mediaEditor, contentEditor: contentEditor, contentPicker: contentPicker, + copy: copy, + move: move, + embed: embed, + linkPicker: linkPicker, mediaPicker: mediaPicker, iconPicker: iconPicker, documentTypeEditor: documentTypeEditor, @@ -378,7 +497,8 @@ templateSections: templateSections, userPicker: userPicker, itemPicker: itemPicker, - macroPicker: macroPicker + macroPicker: macroPicker, + memberGroupPicker: memberGroupPicker }; return service; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 13940b5124..3ad2864fa8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -30,6 +30,7 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q //A list of query strings defined that when changed will not cause a reload of the route var nonRoutingQueryStrings = ["mculture", "cculture"]; + var retainedQueryStrings = ['mculture']; //used to track the current dialog object var currentDialog = null; @@ -192,8 +193,8 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q * @description * utility to clear the querystring/search params while maintaining a known list of parameters that should be maintained throughout the app */ - clearSearch: function () { - var toRetain = ["mculture"]; + clearSearch: function (toRetain) { + var toRetain = _.union(retainedQueryStrings, toRetain); var currentSearch = $location.search(); $location.search(''); _.each(toRetain, function (k) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js index a202c6ca6b..16d51add92 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js @@ -10,17 +10,37 @@ function overlayService(eventsService, backdropService) { - function open(overlay) { + var currentOverlay = null; + + function open(newOverlay) { + + // prevent two open overlays at the same time + if(currentOverlay) { + return; + } + + var backdropOptions = {}; + var overlay = newOverlay; + + // set the default overlay position to center if(!overlay.position) { overlay.position = "center"; } + + // option to disable backdrop clicks + if(overlay.disableBackdropClick) { + backdropOptions.disableEventsOnClick = true; + } + overlay.show = true; - backdropService.open(); + backdropService.open(backdropOptions); + currentOverlay = overlay; eventsService.emit("appState.overlay", overlay); } function close() { backdropService.close(); + currentOverlay = null; eventsService.emit("appState.overlay", null); } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index 77ca2b6157..b18cb73eae 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -316,10 +316,10 @@ // by looking at the key switch (foundAlias[0]) { case "umbracoMemberLockedOut": - saveModel.isLockedOut = prop.value ? (prop.value.toString() === "1" ? true : false) : false; + saveModel.isLockedOut = Object.toBoolean(prop.value); break; case "umbracoMemberApproved": - saveModel.isApproved = prop.value ? (prop.value.toString() === "1" ? true : false) : true; + saveModel.isApproved = Object.toBoolean(prop.value); break; case "umbracoMemberComments": saveModel.comments = prop.value; diff --git a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js index a47f552c9d..24318cce02 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js @@ -8,7 +8,7 @@ * The main application controller * */ -function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale, localStorageService, tourService, editorService) { +function MainController($scope, $location, appState, treeService, notificationsService, userService, historyService, updateChecker, assetsService, eventsService, tmhDynamicLocale, localStorageService, editorService, overlayService) { //the null is important because we do an explicit bool check on this in the view $scope.authenticated = null; @@ -87,6 +87,8 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $ $location.path("/").search(""); historyService.removeAll(); treeService.clearCache(); + editorService.closeAll(); + overlayService.close(); //if the user changed, clearout local storage too - could contain sensitive data localStorageService.clearAll(); diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index e7564ca2ca..3a39ec9e1c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -64,11 +64,9 @@ .umb-editor-header { background: @white; - // flex: 1 1 60px; position: absolute; padding: 0 20px; - z-index: 1; - // box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.16); + z-index: @zIndexEditor; border-bottom: 1px solid @gray-9; width: 100%; box-sizing: border-box; @@ -266,3 +264,14 @@ a.umb-variant-switcher__toggle { margin-right: auto; padding-right: 10px; } + +/* Confirm */ +.umb-editor-confirm { + background-color: @white; + padding: 20px; + position: absolute; + left: 0; + bottom: 0; + z-index: 10; + box-shadow: 0 -3px 12px 0px rgba(0,0,0,0.16); +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less index 79d941bbac..640a276443 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less @@ -13,7 +13,7 @@ bottom: 0; left: 0; background: @gray-10; - z-index: 100; + z-index: @zIndexEditor; } .umb-editor--animating { @@ -43,5 +43,5 @@ right: 0; left: 0; background: rgba(0,0,0,0.2); - z-index: 100; + z-index: @zIndexEditor; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less index ee865f1321..3fbe32134b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less @@ -4,7 +4,7 @@ background: @white; z-index: @zindexUmbOverlay; animation: fadeIn 0.2s; - box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); + box-shadow: 0 10px 50px rgba(0,0,0,0.1), 0 6px 20px rgba(0,0,0,0.16); } .umb-overlay__form { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less index c5f9cdde88..407cfaf6a7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation.less @@ -50,3 +50,34 @@ font-size: 12px; line-height: 1em; } + +.umb-sub-views-nav-item__more { + margin-bottom: 10px; +} + +.umb-sub-views-nav-item__more i { + height: 5px; + width: 5px; + border-radius: 50%; + background: @gray-3; + display: inline-block; + margin: 0 5px 0 0; +} + +.umb-sub-views-nav-item__more i:last-of-type { + margin-right: 0; +} + +// make dots green the an item is active +.umb-sub-views-nav-item.is-active .umb-sub-views-nav-item__more i { + background-color: @turquoise-d1; +} + +.umb-sub-views-nav__dropdown.umb-sub-views-nav__dropdown { + left: auto; + right: 0; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + min-width: auto; + margin-top: 10px; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less index 7dfcc493a8..a4c744bf7f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-grid.less @@ -4,6 +4,7 @@ // ------------------------- .umb-grid IFRAME { overflow: hidden; + width: 100%; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index e2a8c8fc81..d1ad8ee8ff 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -1,16 +1,13 @@ -.nested-content -{ +.umb-nested-content { text-align: center; } -.nested-content--not-supported -{ +.umb-nested-content--not-supported { opacity: 0.3; pointer-events: none; } -.nested-content-overlay -{ +.umb-nested-content-overlay { position: absolute; top: 0; left: 0; @@ -19,47 +16,39 @@ z-index: 1000; } -.nested-content__item -{ +.umb-nested-content__item { position: relative; text-align: left; border-top: solid 1px transparent; - background: white; - - + background: @white; } -.nested-content__item--active:not(.nested-content__item--single) -{ - background: #f8f8f8; +.umb-nested-content__item--active:not(.umb-nested-content__item--single) { + background: @gray-10; } -.nested-content__item.ui-sortable-placeholder -{ - background: #f8f8f8; - border: 1px dashed #d9d9d9; +.umb-nested-content__item.ui-sortable-placeholder { + background: @gray-10; + border: 1px dashed @gray-8; visibility: visible !important; height: 55px; margin-top: -1px; } -.nested-content__item--single > .nested-content__content -{ +.umb-nested-content__item--single > .umb-nested-content__content { border: 0; } -.nested-content__item--single > .nested-content__content > .umb-pane -{ +.umb-nested-content__item--single > .umb-nested-content__content > .umb-pane { margin: 0; } -.nested-content__header-bar -{ +.umb-nested-content__header-bar { padding: 15px 20px; - border-bottom: 1px dashed #e0e0e0; + border-bottom: 1px dashed @gray-8; text-align: right; cursor: pointer; - background-color: white; + background-color: @white; -moz-user-select: none; -khtml-user-select: none; @@ -67,24 +56,21 @@ -o-user-select: none; } -.nested-content__heading { +.umb-nested-content__heading { line-height: 20px; position: relative; - &.-with-icon - { + &.-with-icon { padding-left: 20px; } - i - { - color: #999; /* same icon color as the icons in the item type picker */ + i { + color: @gray-2; position: absolute; left: 0; } - .nested-content__item-name - { + .umb-nested-content__item-name { max-height: 20px; text-align: left; white-space: nowrap; @@ -94,22 +80,16 @@ } } -.nested-content__icons -{ +.umb-nested-content__icons { opacity: 0; - transition: opacity .15s ease-in-out; - -moz-transition: opacity .15s ease-in-out; - -webkit-transition: opacity .15s ease-in-out; - position: absolute; right: 0px; top: 2px; - background-color: white; + background-color: @white; padding: 5px; - &:before - { + &:before { content: ' '; position: absolute; display: block; @@ -117,126 +97,118 @@ left: -30px; top: 0; bottom: 0; - background: -webkit-linear-gradient(90deg, rgba(255,255,255,0), white); - background: -moz-linear-gradient(90deg, rgba(255,255,255,0), white); background: linear-gradient(90deg, rgba(255,255,255,0), white); } } -.nested-content__header-bar:hover .nested-content__icons, -.nested-content__item--active > .nested-content__header-bar .nested-content__icons -{ +.umb-nested-content__header-bar:hover .umb-nested-content__icons, +.umb-nested-content__item--active > .umb-nested-content__header-bar .umb-nested-content__icons { opacity: 1; } -.nested-content__icon, -.nested-content__icon.nested-content__icon--disabled:hover -{ +.umb-nested-content__icon, +.umb-nested-content__icon.umb-nested-content__icon--disabled:hover { display: inline-block; padding: 4px 6px; margin: 2px; cursor: pointer; - background: #fff; - border: 1px solid #b6b6b6; + background: @white; + border: 1px solid @gray-7; border-radius: 200px; text-decoration: none !important; } -.nested-content__icon:hover, -.nested-content__icon--active +.umb-nested-content__icon:hover, +.umb-nested-content__icon--active { - color: white; - background: #2e8aea; - border-color: #2e8aea; + color: @white; + background: @turquoise-d1; + border-color: @turquoise-d1; text-decoration: none; } -.nested-content__icon .icon, -.nested-content__icon.nested-content__icon--disabled:hover .icon -{ +.umb-nested-content__icon .icon, +.umb-nested-content__icon.umb-nested-content__icon--disabled:hover .icon { display: block; font-size: 16px !important; - color: #5f5f5f; + color: @gray-3; } -.nested-content__icon:hover .icon, -.nested-content__icon--active .icon -{ - color: white; +.umb-nested-content__icon:hover .icon, +.umb-nested-content__icon--active .icon { + color: @white; } -.nested-content__icon--disabled -{ +.umb-nested-content__icon--disabled { opacity: 0.3; } -.nested-content__footer-bar -{ +.umb-nested-content__footer-bar { text-align: center; padding-top: 20px; } -.nested-content__content -{ - border-bottom: 1px dashed #e0e0e0; +.umb-nested-content__content { + border-bottom: 1px dashed @gray-8; } -.nested-content__content .umb-control-group { +.umb-nested-content__content .umb-control-group { padding-bottom: 0; } -.nested-content__item.ui-sortable-helper .nested-content__content -{ +.umb-nested-content__item.ui-sortable-helper .umb-nested-content__content { display: none !important; } -.nested-content__help-text -{ +.umb-nested-content__help-text { display: inline-block; padding: 10px 20px 10px 20px; clear: both; font-size: 14px; - color: #555; - background: #f8f8f8; + color: @gray-3; + background: @gray-10; border-radius: 15px; } -.nested-content__doctypepicker table input, .nested-content__doctypepicker table select { +.umb-nested-content__doctypepicker table input, +.umb-nested-content__doctypepicker table select { width: 100%; padding-right: 0; } -.nested-content__doctypepicker table td.icon-navigation, .nested-content__doctypepicker i.nested-content__help-icon { +.umb-nested-content__doctypepicker table td.icon-navigation, +.umb-nested-content__doctypepicker i.umb-nested-content__help-icon { vertical-align: middle; - color: #CCC; + color: @gray-7; } -.nested-content__doctypepicker table td.icon-navigation:hover, .nested-content__doctypepicker i.nested-content__help-icon:hover { - color: #343434; +.umb-nested-content__doctypepicker table td.icon-navigation:hover, +.umb-nested-content__doctypepicker i.umb-nested-content__help-icon:hover { + color: @gray-2; } -.nested-content__doctypepicker i.nested-content__help-icon { +.umb-nested-content__doctypepicker i.umb-nested-content__help-icon { margin-left: 10px; } -.form-horizontal .nested-content--narrow .controls-row +.form-horizontal .umb-nested-content--narrow .controls-row { margin-left: 40% !important; } -.form-horizontal .nested-content--narrow .controls-row .umb-textstring, -.form-horizontal .nested-content--narrow .controls-row .umb-textarea +.form-horizontal .umb-nested-content--narrow .controls-row .umb-textstring, +.form-horizontal .umb-nested-content--narrow .controls-row .umb-textarea { width: 95%; } -.form-horizontal .nested-content--narrow .controls-row .umb-dropdown { +.form-horizontal .umb-nested-content--narrow .controls-row .umb-dropdown { width: 99%; } -.usky-grid.nested-content__node-type-picker .cell-tools-menu { +.usky-grid.umb-nested-content__node-type-picker .cell-tools-menu { position: relative; transform: translate(-50%, -25%); } diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index c7d2e8d6ed..c6e87a74fe 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -319,6 +319,7 @@ // ------------------------- // Used for a bird's eye view of components dependent on the z-axis // Try to avoid customizing these :) +@zIndexEditor: 100; @zIndexTree: 100; @zindexDropdown: 1000; @zindexPopover: 1010; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js new file mode 100644 index 0000000000..54d5da29a8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js @@ -0,0 +1,70 @@ + (function() { + "use strict"; + + function CompositionsController($scope,$location) { + + var vm = this; + var oldModel = null; + + vm.showConfirmSubmit = false; + + vm.isSelected = isSelected; + vm.openContentType = openContentType; + vm.submit = submit; + vm.close = close; + + function onInit() { + + /* make a copy of the init model so it is possible to roll + back the changes on cancel */ + oldModel = angular.copy($scope.model); + + if(!$scope.model.title) { + $scope.model.title = "Compositions"; + } + + } + + function isSelected(alias) { + if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) { + return true; + } + } + + function openContentType(contentType, section) { + var url = (section === "documentType" ? "/settings/documenttypes/edit/" : "/settings/mediaTypes/edit/") + contentType.id; + $location.path(url); + } + + function submit() { + if ($scope.model && $scope.model.submit) { + + // check if any compositions has been removed + vm.compositionRemoved = false; + for(var i = 0; oldModel.compositeContentTypes.length > i; i++) { + var oldComposition = oldModel.compositeContentTypes[i]; + if(_.contains($scope.model.compositeContentTypes, oldComposition) === false) { + vm.compositionRemoved = true; + } + } + + /* submit the form if there havne't been removed any composition + or the confirm checkbox has been checked */ + if(!vm.compositionRemoved || vm.allowSubmit) { + $scope.model.submit($scope.model); + } + } + } + + function close() { + if ($scope.model && $scope.model.close) { + $scope.model.close(oldModel); + } + } + + onInit(); + } + + angular.module("umbraco").controller("Umbraco.Editors.CompositionsController", CompositionsController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html new file mode 100644 index 0000000000..5d7a5420db --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html @@ -0,0 +1,123 @@ +
+ + + +
+ + + + + + + + +
+ +
+ +
+ +
+ + + + + + + + + + + +
    +
  • + +
    + +
    + + + +
  • +
+ +
+
+ +
+
Warning
+

Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?

+ +
+ +
+ + + + + + + + + + +
+ +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js similarity index 84% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js index 5de36dfd44..060e17a55e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js @@ -1,13 +1,15 @@ (function() { "use strict"; - function CopyOverlay($scope, localizationService, eventsService, entityHelper) { + function CopyController($scope, localizationService, eventsService, entityHelper) { var vm = this; vm.hideSearch = hideSearch; vm.selectResult = selectResult; vm.onSearchResults = onSearchResults; + vm.submit = submit; + vm.close = close; var dialogOptions = $scope.model; var searchText = "Search..."; @@ -105,10 +107,22 @@ $scope.miniListView = node; } - onInit(); + function submit() { + if($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + function close() { + if($scope.model && $scope.model.close) { + $scope.model.close(); + } + } + + onInit(); } - angular.module("umbraco").controller("Umbraco.Overlays.CopyOverlay", CopyOverlay); + angular.module("umbraco").controller("Umbraco.Editors.CopyController", CopyController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.html new file mode 100644 index 0000000000..18a35394ec --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.html @@ -0,0 +1,94 @@ +
+ + + +
+ + + + + + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+ +
+ +
+
+
+
+ + + + + + + + + + +
+ +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.controller.js new file mode 100644 index 0000000000..fb66552731 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.controller.js @@ -0,0 +1,125 @@ +(function() { + "use strict"; + + function EmbedController($scope, $http, $sce, umbRequestHelper, localizationService) { + + var vm = this; + var origWidth = 500; + var origHeight = 300; + + vm.trustedPreview = null; + + $scope.model.embed = { + url: "", + width: 360, + height: 240, + constrain: true, + preview: "", + success: false, + info: "", + supportsDimensions: "" + }; + + vm.showPreview = showPreview; + vm.changeSize = changeSize; + vm.submit = submit; + vm.close = close; + + function onInit() { + if(!$scope.model.title) { + localizationService.localize("general_embed").then(function(value){ + $scope.model.title = value; + }); + } + } + + function showPreview() { + + if ($scope.model.embed.url) { + $scope.model.embed.show = true; + $scope.model.embed.preview = "
"; + $scope.model.embed.info = ""; + $scope.model.embed.success = false; + + + $http({ + method: 'GET', + url: umbRequestHelper.getApiUrl("embedApiBaseUrl", "GetEmbed"), + params: { + url: $scope.model.embed.url, + width: $scope.model.embed.width, + height: $scope.model.embed.height + } + }).then(function(response) { + + $scope.model.embed.preview = ""; + + switch (response.data.Status) { + case 0: + //not supported + $scope.model.embed.info = "Not supported"; + break; + case 1: + //error + $scope.model.embed.info = "Could not embed media - please ensure the URL is valid"; + break; + case 2: + $scope.model.embed.preview = response.data.Markup; + vm.trustedPreview = $sce.trustAsHtml(response.data.Markup); + $scope.model.embed.supportsDimensions = response.data.SupportsDimensions; + $scope.model.embed.success = true; + break; + } + }, function() { + $scope.model.embed.supportsDimensions = false; + $scope.model.embed.preview = ""; + $scope.model.embed.info = "Could not embed media - please ensure the URL is valid"; + }); + + } else { + $scope.model.embed.supportsDimensions = false; + $scope.model.embed.preview = ""; + $scope.model.embed.info = "Please enter a URL"; + } + } + + function changeSize(type) { + + var width, height; + + if ($scope.model.embed.constrain) { + width = parseInt($scope.model.embed.width, 10); + height = parseInt($scope.model.embed.height, 10); + if (type == 'width') { + origHeight = Math.round((width / origWidth) * height); + $scope.model.embed.height = origHeight; + } else { + origWidth = Math.round((height / origHeight) * width); + $scope.model.embed.width = origWidth; + } + } + if ($scope.model.embed.url !== "") { + showPreview(); + } + + } + + function submit() { + if($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + function close() { + if($scope.model && $scope.model.close) { + $scope.model.close(); + } + } + + onInit(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.EmbedController", EmbedController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html new file mode 100644 index 0000000000..f6a641f2af --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html @@ -0,0 +1,73 @@ +
+
+ + + + + + + + + + + + + + + + + +

+
+
+ +
+ + + + + + + + + + + +
+ +
+
+
+ + + + + + + + + + +
+ +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html index 405a56e79b..58b422ceb2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html @@ -54,11 +54,5 @@ - - - diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js similarity index 84% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js index 720edc2114..36640eae75 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js @@ -1,17 +1,24 @@ //used for the media picker dialog -angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", - function ($scope, eventsService, dialogService, entityResource, contentResource, mediaHelper, userService, localizationService, tinyMceService) { +angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController", + function ($scope, eventsService, entityResource, contentResource, mediaHelper, userService, localizationService, tinyMceService, editorService) { + + var vm = this; var dialogOptions = $scope.model; - var anchorPattern = //gi; - var searchText = "Search..."; + + vm.submit = submit; + vm.close = close; + localizationService.localize("general_search").then(function (value) { searchText = value + "..."; }); if (!$scope.model.title) { - $scope.model.title = localizationService.localize("defaultdialogs_selectLink"); + localizationService.localize("defaultdialogs_selectLink") + .then(function(value) { + $scope.model.title = value; + }); } $scope.dialogTreeApi = {}; @@ -83,7 +90,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", } else { contentResource.getById(args.node.id).then(function (resp) { $scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties)); - $scope.model.target.url = resp.urls[0]; + $scope.model.target.url = resp.urls[0].text; }); } @@ -101,11 +108,9 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", $scope.switchToMediaPicker = function () { userService.getCurrentUser().then(function (userData) { - $scope.mediaPickerOverlay = { - view: "mediapicker", + var mediaPicker = { startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0], startNodeIsVirtual: userData.startMediaIds.length !== 1, - show: true, submit: function (model) { var media = model.selectedImages[0]; @@ -115,12 +120,13 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", $scope.model.target.name = media.name; $scope.model.target.url = mediaHelper.resolveFile(media); - debugger; - - $scope.mediaPickerOverlay.show = false; - $scope.mediaPickerOverlay = null; + editorService.close(); + }, + close: function() { + editorService.close(); } }; + editorService.mediaPicker(mediaPicker); }); }; @@ -167,4 +173,16 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", $scope.miniListView = node; } + function close() { + if($scope.model && $scope.model.close) { + $scope.model.close(); + } + } + + function submit() { + if($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html new file mode 100644 index 0000000000..dd258f6c75 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html @@ -0,0 +1,137 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 4de506ce50..298c90c822 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.MediaPickerController", - function($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, localStorageService, localizationService, editorService) { + function($scope, mediaResource, entityResource, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) { if (!$scope.model.title) { localizationService.localize("defaultdialogs_selectMedia") @@ -20,6 +20,7 @@ angular.module("umbraco") $scope.cropSize = dialogOptions.cropSize; $scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId"); $scope.lockedFolder = true; + $scope.allowMediaEdit = dialogOptions.allowMediaEdit ? dialogOptions.allowMediaEdit : false; var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings; var allowedUploadFiles = mediaHelper.formatFileTypes(umbracoSettings.allowedUploadFiles); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index d0982fb95f..5a858accbb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -78,14 +78,14 @@ hide-dropzone="{{!activeDrag && images.length > 0 || searchOptions.filter }}" compact="{{ images.length > 0 }}"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.controller.js similarity index 85% rename from src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.controller.js rename to src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.controller.js index bccf7487bb..c2a66dddf1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.controller.js @@ -1,13 +1,15 @@ (function() { "use strict"; - function MoveOverlay($scope, localizationService, eventsService, entityHelper) { + function MoveController($scope, localizationService, entityHelper) { var vm = this; vm.hideSearch = hideSearch; vm.selectResult = selectResult; vm.onSearchResults = onSearchResults; + vm.submit = submit; + vm.close = close; var dialogOptions = $scope.model; var searchText = "Search..."; @@ -105,10 +107,22 @@ $scope.miniListView = node; } + function submit() { + if ($scope.model && $scope.model.submit) { + $scope.model.submit($scope.model); + } + } + + function close() { + if ($scope.model && $scope.model.close) { + $scope.model.close(); + } + } + onInit(); } - angular.module("umbraco").controller("Umbraco.Overlays.MoveOverlay", MoveOverlay); + angular.module("umbraco").controller("Umbraco.Editors.MoveController", MoveController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.html new file mode 100644 index 0000000000..03d7e84342 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/move/move.html @@ -0,0 +1,84 @@ +
+ + + + + + + + + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + + +
+
+
+ + + + + + + + + + +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.controller.js deleted file mode 100644 index 7e768336a6..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.controller.js +++ /dev/null @@ -1,25 +0,0 @@ - (function() { - "use strict"; - - function CompositionsOverlay($scope,$location) { - - var vm = this; - - vm.isSelected = isSelected; - vm.openContentType = openContentType; - - function isSelected(alias) { - if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) { - return true; - } - } - function openContentType(contentType, section) { - - var url = (section === "documentType" ? "/settings/documenttypes/edit/" : "/settings/mediaTypes/edit/") + contentType.id; - $location.path(url); - } - } - - angular.module("umbraco").controller("Umbraco.Overlays.CompositionsOverlay", CompositionsOverlay); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html deleted file mode 100644 index f2d8902f9d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html +++ /dev/null @@ -1,58 +0,0 @@ -
- -
- -
- -
- -
- - - - - - - - -
    -
  • - -
    - -
    - - - -
  • -
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html deleted file mode 100644 index 16d00c8689..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html +++ /dev/null @@ -1,55 +0,0 @@ -
- -
-
- - -
- -
- - -
- -
- - -
- -
- -
- - -
- -
- - -
- -
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/default/default.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/default/default.html new file mode 100644 index 0000000000..04201dde70 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/default/default.html @@ -0,0 +1 @@ +
{{model.content}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.controller.js deleted file mode 100644 index c64430d9fe..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.controller.js +++ /dev/null @@ -1,108 +0,0 @@ -(function() { - "use strict"; - - function EmbedOverlay($scope, $http, umbRequestHelper, localizationService) { - - var vm = this; - var origWidth = 500; - var origHeight = 300; - - $scope.model.embed = { - url: "", - width: 360, - height: 240, - constrain: true, - preview: "", - success: false, - info: "", - supportsDimensions: "" - }; - - vm.showPreview = showPreview; - vm.changeSize = changeSize; - - function onInit() { - if(!$scope.model.title) { - localizationService.localize("general_embed").then(function(value){ - $scope.model.title = value; - }); - } - } - - function showPreview() { - - if ($scope.model.embed.url) { - $scope.model.embed.show = true; - $scope.model.embed.preview = "
"; - $scope.model.embed.info = ""; - $scope.model.embed.success = false; - - $http({ - method: 'GET', - url: umbRequestHelper.getApiUrl("embedApiBaseUrl", "GetEmbed"), - params: { - url: $scope.model.embed.url, - width: $scope.model.embed.width, - height: $scope.model.embed.height - } - }) - .success(function(data) { - - $scope.model.embed.preview = ""; - - switch (data.Status) { - case 0: - //not supported - $scope.model.embed.info = "Not supported"; - break; - case 1: - //error - $scope.model.embed.info = "Could not embed media - please ensure the URL is valid"; - break; - case 2: - $scope.model.embed.preview = data.Markup; - $scope.model.embed.supportsDimensions = data.SupportsDimensions; - $scope.model.embed.success = true; - break; - } - }) - .error(function() { - $scope.model.embed.supportsDimensions = false; - $scope.model.embed.preview = ""; - $scope.model.embed.info = "Could not embed media - please ensure the URL is valid"; - }); - } else { - $scope.model.embed.supportsDimensions = false; - $scope.model.embed.preview = ""; - $scope.model.embed.info = "Please enter a URL"; - } - } - - function changeSize(type) { - - var width, height; - - if ($scope.model.embed.constrain) { - width = parseInt($scope.model.embed.width, 10); - height = parseInt($scope.model.embed.height, 10); - if (type == 'width') { - origHeight = Math.round((width / origWidth) * height); - $scope.model.embed.height = origHeight; - } else { - origWidth = Math.round((height / origHeight) * width); - $scope.model.embed.width = origWidth; - } - } - if ($scope.model.embed.url !== "") { - showPreview(); - } - - } - - onInit(); - - } - - angular.module("umbraco").controller("Umbraco.Overlays.EmbedOverlay", EmbedOverlay); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.html deleted file mode 100644 index 401acca2c5..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/embed/embed.html +++ /dev/null @@ -1,27 +0,0 @@ -
- - - - - - - -

-
-
- -
- - - - - - - - - - - -
- -
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js deleted file mode 100644 index 3c36505fcb..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.PropertyController - * @function - * - * @description - * The controller for the content type editor property dialog - */ -function IconPickerOverlay($scope, iconHelper, localizationService) { - - $scope.loading = true; - $scope.model.hideSubmitButton = false; - - $scope.colors = [ - { name: "Black", value: "color-black" }, - { name: "Blue Grey", value: "color-blue-grey" }, - { name: "Grey", value: "color-grey" }, - { name: "Brown", value: "color-brown" }, - { name: "Blue", value: "color-blue" }, - { name: "Light Blue", value: "color-light-blue" }, - { name: "Indigo", value: "color-indigo" }, - { name: "Purple", value: "color-purple" }, - { name: "Deep Purple", value: "color-deep-purple" }, - { name: "Cyan", value: "color-cyan" }, - { name: "Green", value: "color-green" }, - { name: "Light Green", value: "color-light-green" }, - { name: "Lime", value: "color-lime" }, - { name: "Yellow", value: "color-yellow" }, - { name: "Amber", value: "color-amber" }, - { name: "Orange", value: "color-orange" }, - { name: "Deep Orange", value: "color-deep-orange" }, - { name: "Red", value: "color-red" }, - { name: "Pink", value: "color-pink" } - ]; - - if (!$scope.color) { - // Set default selected color to black - $scope.color = $scope.colors[0].value; - }; - - if (!$scope.model.title) { - $scope.model.title = localizationService.localize("defaultdialogs_selectIcon"); - }; - - if ($scope.model.color) { - $scope.color = $scope.model.color; - }; - - if ($scope.model.icon) { - $scope.icon = $scope.model.icon; - }; - - iconHelper.getIcons().then(function (icons) { - $scope.icons = icons; - $scope.loading = false; - }); - - $scope.selectIcon = function (icon, color) { - $scope.model.icon = icon; - $scope.model.color = color; - $scope.submitForm($scope.model); - }; - - var unsubscribe = $scope.$on("formSubmitting", - function () { - if ($scope.color) { - $scope.model.color = $scope.color; - } - }); - - //when the scope is destroyed we need to unsubscribe - $scope.$on("$destroy", - function () { - unsubscribe(); - }); -} - -angular.module("umbraco").controller("Umbraco.Overlays.IconPickerOverlay", IconPickerOverlay); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html deleted file mode 100644 index 0098463cb4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html +++ /dev/null @@ -1,40 +0,0 @@ - - -
- -
- -
- -
- -
- - - -
- -
- - - No icons were found. - -
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html deleted file mode 100644 index 5dd1275014..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html +++ /dev/null @@ -1,100 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - -
-
- Link to page -
- -
- - - -
- - - - -
- - -
-
- - - - -
- -
-
- Link to media -
- - Select media - -
- - - - -
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js deleted file mode 100644 index 2071d21356..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ /dev/null @@ -1,400 +0,0 @@ -//used for the media picker dialog -angular.module("umbraco") - .controller("Umbraco.Overlays.MediaPickerController", - function($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, localStorageService, localizationService) { - - if (!$scope.model.title) { - localizationService.localize("defaultdialogs_selectMedia").then(function(value){ - $scope.model.title = value; - }); - } - - var dialogOptions = $scope.model; - - $scope.disableFolderSelect = dialogOptions.disableFolderSelect; - $scope.onlyImages = dialogOptions.onlyImages; - $scope.showDetails = dialogOptions.showDetails; - $scope.multiPicker = (dialogOptions.multiPicker && dialogOptions.multiPicker !== "0") ? true : false; - $scope.startNodeId = dialogOptions.startNodeId ? dialogOptions.startNodeId : -1; - $scope.cropSize = dialogOptions.cropSize; - $scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId"); - $scope.lockedFolder = true; - - var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings; - var allowedUploadFiles = mediaHelper.formatFileTypes(umbracoSettings.allowedUploadFiles); - if ($scope.onlyImages) { - $scope.acceptedFileTypes = mediaHelper.formatFileTypes(umbracoSettings.imageFileTypes); - } else { - // Use whitelist of allowed file types if provided - if (allowedUploadFiles !== '') { - $scope.acceptedFileTypes = allowedUploadFiles; - } else { - // If no whitelist, we pass in a blacklist by adding ! to the file extensions, allowing everything EXCEPT for disallowedUploadFiles - $scope.acceptedFileTypes = !mediaHelper.formatFileTypes(umbracoSettings.disallowedUploadFiles); - } - } - - $scope.maxFileSize = umbracoSettings.maxFileSize + "KB"; - - $scope.model.selectedImages = []; - - $scope.acceptedMediatypes = []; - mediaTypeHelper.getAllowedImagetypes($scope.startNodeId) - .then(function(types) { - $scope.acceptedMediatypes = types; - }); - - $scope.searchOptions = { - pageNumber: 1, - pageSize: 100, - totalItems: 0, - totalPages: 0, - filter: '' - }; - - //preload selected item - $scope.target = undefined; - if (dialogOptions.currentTarget) { - $scope.target = dialogOptions.currentTarget; - } - - function onInit() { - if ($scope.startNodeId !== -1) { - entityResource.getById($scope.startNodeId, "media") - .then(function (ent) { - $scope.startNodeId = ent.id; - run(); - }); - } else { - run(); - } - } - - function run() { - //default root item - if (!$scope.target) { - if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) { - entityResource.getById($scope.lastOpenedNode, "media") - .then(ensureWithinStartNode, gotoStartNode); - } else { - gotoStartNode(); - } - } else { - //if a target is specified, go look it up - generally this target will just contain ids not the actual full - //media object so we need to look it up - var id = $scope.target.udi ? $scope.target.udi : $scope.target.id - var altText = $scope.target.altText; - if (id) { - mediaResource.getById(id) - .then(function (node) { - $scope.target = node; - if (ensureWithinStartNode(node)) { - selectImage(node); - $scope.target.url = mediaHelper.resolveFile(node); - $scope.target.altText = altText; - $scope.openDetailsDialog(); - } - }, - gotoStartNode); - } else { - gotoStartNode(); - } - } - } - - $scope.upload = function(v) { - angular.element(".umb-file-dropzone-directive .file-select").click(); - }; - - $scope.dragLeave = function(el, event) { - $scope.activeDrag = false; - }; - - $scope.dragEnter = function(el, event) { - $scope.activeDrag = true; - }; - - $scope.submitFolder = function() { - if ($scope.newFolderName) { - $scope.creatingFolder = true; - mediaResource - .addFolder($scope.newFolderName, $scope.currentFolder.id) - .then(function(data) { - //we've added a new folder so lets clear the tree cache for that specific item - treeService.clearCache({ - cacheKey: "__media", //this is the main media tree cache key - childrenOf: data.parentId //clear the children of the parent - }); - $scope.creatingFolder = false; - $scope.gotoFolder(data); - $scope.showFolderInput = false; - $scope.newFolderName = ""; - }); - } else { - $scope.showFolderInput = false; - } - }; - - $scope.enterSubmitFolder = function(event) { - if (event.keyCode === 13) { - $scope.submitFolder(); - event.stopPropagation(); - } - }; - - $scope.gotoFolder = function(folder) { - if (!$scope.multiPicker) { - deselectAllImages($scope.model.selectedImages); - } - - if (!folder) { - folder = { id: -1, name: "Media", icon: "icon-folder" }; - } - - if (folder.id > 0) { - entityResource.getAncestors(folder.id, "media") - .then(function(anc) { - $scope.path = _.filter(anc, - function(f) { - return f.path.indexOf($scope.startNodeId) !== -1; - }); - }); - - } else { - $scope.path = []; - } - - mediaTypeHelper.getAllowedImagetypes(folder.id) - .then(function (types) { - $scope.acceptedMediatypes = types; - }); - - $scope.lockedFolder = folder.id === -1 && $scope.model.startNodeIsVirtual; - - $scope.currentFolder = folder; - localStorageService.set("umbLastOpenedMediaNodeId", folder.id); - return getChildren(folder.id); - }; - - $scope.clickHandler = function(image, event, index) { - if (image.isFolder) { - if ($scope.disableFolderSelect) { - $scope.gotoFolder(image); - } else { - eventsService.emit("dialogs.mediaPicker.select", image); - selectImage(image); - } - } else { - eventsService.emit("dialogs.mediaPicker.select", image); - if ($scope.showDetails) { - - $scope.target = image; - - // handle both entity and full media object - if (image.image) { - $scope.target.url = image.image; - } else { - $scope.target.url = mediaHelper.resolveFile(image); - } - - $scope.openDetailsDialog(); - } else { - selectImage(image); - } - } - }; - - $scope.clickItemName = function(item) { - if (item.isFolder) { - $scope.gotoFolder(item); - } - }; - - function selectImage(image) { - if (image.selected) { - for (var i = 0; $scope.model.selectedImages.length > i; i++) { - var imageInSelection = $scope.model.selectedImages[i]; - if (image.key === imageInSelection.key) { - image.selected = false; - $scope.model.selectedImages.splice(i, 1); - } - } - } else { - if (!$scope.multiPicker) { - deselectAllImages($scope.model.selectedImages); - } - image.selected = true; - $scope.model.selectedImages.push(image); - } - } - - function deselectAllImages(images) { - for (var i = 0; i < images.length; i++) { - var image = images[i]; - image.selected = false; - } - images.length = 0; - } - - $scope.onUploadComplete = function(files) { - $scope.gotoFolder($scope.currentFolder).then(function() { - if (files.length === 1 && $scope.model.selectedImages.length === 0) { - selectImage($scope.images[$scope.images.length - 1]); - } - }); - }; - - $scope.onFilesQueue = function() { - $scope.activeDrag = false; - }; - - function ensureWithinStartNode(node) { - // make sure that last opened node is on the same path as start node - var nodePath = node.path.split(","); - - if (nodePath.indexOf($scope.startNodeId.toString()) !== -1) { - $scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" }); - return true; - } else { - $scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); - return false; - } - } - - function gotoStartNode(err) { - $scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); - } - - $scope.openDetailsDialog = function() { - - $scope.mediaPickerDetailsOverlay = {}; - $scope.mediaPickerDetailsOverlay.show = true; - - $scope.mediaPickerDetailsOverlay.submit = function(model) { - $scope.model.selectedImages.push($scope.target); - $scope.model.submit($scope.model); - - $scope.mediaPickerDetailsOverlay.show = false; - $scope.mediaPickerDetailsOverlay = null; - }; - - $scope.mediaPickerDetailsOverlay.close = function(oldModel) { - $scope.mediaPickerDetailsOverlay.show = false; - $scope.mediaPickerDetailsOverlay = null; - }; - }; - - var debounceSearchMedia = _.debounce(function() { - $scope.$apply(function() { - if ($scope.searchOptions.filter) { - searchMedia(); - } else { - // reset pagination - $scope.searchOptions = { - pageNumber: 1, - pageSize: 100, - totalItems: 0, - totalPages: 0, - filter: '' - }; - getChildren($scope.currentFolder.id); - } - }); - }, 500); - - $scope.changeSearch = function() { - $scope.loading = true; - debounceSearchMedia(); - }; - - $scope.changePagination = function(pageNumber) { - $scope.loading = true; - $scope.searchOptions.pageNumber = pageNumber; - searchMedia(); - }; - - function searchMedia() { - $scope.loading = true; - entityResource.getPagedDescendants($scope.startNodeId, "Media", $scope.searchOptions) - .then(function(data) { - // update image data to work with image grid - angular.forEach(data.items, - function(mediaItem) { - // set thumbnail and src - mediaItem.thumbnail = mediaHelper.resolveFileFromEntity(mediaItem, true); - mediaItem.image = mediaHelper.resolveFileFromEntity(mediaItem, false); - // set properties to match a media object - mediaItem.properties = []; - if (mediaItem.metaData) { - if (mediaItem.metaData.umbracoWidth && mediaItem.metaData.umbracoHeight) { - mediaItem.properties.push({ - alias: "umbracoWidth", - value: mediaItem.metaData.umbracoWidth.Value - }); - mediaItem.properties.push({ - alias: "umbracoHeight", - value: mediaItem.metaData.umbracoHeight.Value - }); - } - if (mediaItem.metaData.umbracoFile) { - mediaItem.properties.push({ - alias: "umbracoFile", - editor: mediaItem.metaData.umbracoFile.PropertyEditorAlias, - value: mediaItem.metaData.umbracoFile.Value - }); - } - } - }); - // update images - $scope.images = data.items ? data.items : []; - // update pagination - if (data.pageNumber > 0) - $scope.searchOptions.pageNumber = data.pageNumber; - if (data.pageSize > 0) - $scope.searchOptions.pageSize = data.pageSize; - $scope.searchOptions.totalItems = data.totalItems; - $scope.searchOptions.totalPages = data.totalPages; - // set already selected images to selected - preSelectImages(); - $scope.loading = false; - }); - } - - function getChildren(id) { - $scope.loading = true; - return mediaResource.getChildren(id) - .then(function(data) { - $scope.searchOptions.filter = ""; - $scope.images = data.items ? data.items : []; - // set already selected images to selected - preSelectImages(); - $scope.loading = false; - }); - } - - function preSelectImages() { - for (var folderImageIndex = 0; folderImageIndex < $scope.images.length; folderImageIndex++) { - var folderImage = $scope.images[folderImageIndex]; - var imageIsSelected = false; - - if ($scope.model && angular.isArray($scope.model.selectedImages)) { - for (var selectedImageIndex = 0; - selectedImageIndex < $scope.model.selectedImages.length; - selectedImageIndex++) { - var selectedImage = $scope.model.selectedImages[selectedImageIndex]; - - if (folderImage.key === selectedImage.key) { - imageIsSelected = true; - } - } - } - - if (imageIsSelected) { - folderImage.selected = true; - } - } - } - - onInit(); - - }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html deleted file mode 100644 index 482485c4b2..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html +++ /dev/null @@ -1,150 +0,0 @@ -
- -
- -
- - - - - - -
- - -
- -
- -
- -
-
- - - - - - - -
- - -
- - - - - -
- - - -
- -
- - -
- -
- -
- Preview -
- - - - -
- -
- -
- - -
- -
- - -
- - -
- -
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/membergrouppicker/membergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/membergrouppicker/membergrouppicker.html deleted file mode 100644 index ab481d5f0d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/membergrouppicker/membergrouppicker.html +++ /dev/null @@ -1,13 +0,0 @@ -
- - - - -
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.html deleted file mode 100644 index 30afe56d4b..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/move/move.html +++ /dev/null @@ -1,44 +0,0 @@ -
-
-
- - -
- -
- - -
- -
- - -
-
- - - - -
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html index ba9257bac7..fc62485521 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-toggle.html @@ -1,4 +1,4 @@ -