diff --git a/build/build.ps1 b/build/build.ps1 index 892654d3cd..ea07e4516f 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -456,10 +456,10 @@ $ubuild.DefineMethod("PrepareAngularDocs", { Write-Host "Prepare Angular Documentation" - + $src = "$($this.SolutionRoot)\src" $out = $this.BuildOutput - + "Moving to Umbraco.Web.UI.Docs folder" cd ..\src\Umbraco.Web.UI.Docs diff --git a/src/Umbraco.Abstractions/Composing/ComponentCollection.cs b/src/Umbraco.Abstractions/Composing/ComponentCollection.cs index 9b5319dc41..fa4a1849b6 100644 --- a/src/Umbraco.Abstractions/Composing/ComponentCollection.cs +++ b/src/Umbraco.Abstractions/Composing/ComponentCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Umbraco.Core.Logging; @@ -43,8 +44,15 @@ namespace Umbraco.Core.Composing var componentType = component.GetType(); using (_logger.DebugDuration($"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds)) { - component.Terminate(); - component.DisposeIfDisposable(); + try + { + component.Terminate(); + component.DisposeIfDisposable(); + } + catch (Exception ex) + { + _logger.Error(componentType, ex, "Error while terminating component {ComponentType}.", componentType.FullName); + } } } } diff --git a/src/Umbraco.Abstractions/Composing/Composers.cs b/src/Umbraco.Abstractions/Composing/Composers.cs index 0510740e42..b2e6c9d068 100644 --- a/src/Umbraco.Abstractions/Composing/Composers.cs +++ b/src/Umbraco.Abstractions/Composing/Composers.cs @@ -18,19 +18,52 @@ namespace Umbraco.Core.Composing private readonly Composition _composition; private readonly IProfilingLogger _logger; private readonly IEnumerable _composerTypes; + private readonly IEnumerable _enableDisableAttributes; private const int LogThresholdMilliseconds = 100; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The composition. - /// The composer types. - /// A profiling logger. + /// The types. + /// The profiling logger. + [Obsolete("This overload only gets the EnableComposer/DisableComposer attributes from the composerTypes assemblies.")] public Composers(Composition composition, IEnumerable composerTypes, IProfilingLogger logger) + : this(composition, composerTypes, Enumerable.Empty(), logger) + { + var enableDisableAttributes = new List(); + + var assemblies = composerTypes.Select(t => t.Assembly).Distinct(); + foreach (var assembly in assemblies) + { + enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(EnableComposerAttribute))); + enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(DisableComposerAttribute))); + } + + _enableDisableAttributes = enableDisableAttributes; + } + + /// + /// Initializes a new instance of the class. + /// + /// The composition. + /// The types. + /// The and/or attributes. + /// The profiling logger. + /// composition + /// or + /// composerTypes + /// or + /// enableDisableAttributes + /// or + /// logger + + public Composers(Composition composition, IEnumerable composerTypes, IEnumerable enableDisableAttributes, IProfilingLogger logger) { _composition = composition ?? throw new ArgumentNullException(nameof(composition)); _composerTypes = composerTypes ?? throw new ArgumentNullException(nameof(composerTypes)); + _enableDisableAttributes = enableDisableAttributes ?? throw new ArgumentNullException(nameof(enableDisableAttributes)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -103,7 +136,7 @@ namespace Umbraco.Core.Composing .ToList(); // enable or disable composers - EnableDisableComposers(composerTypeList); + EnableDisableComposers(_enableDisableAttributes, composerTypeList); void GatherInterfaces(Type type, Func getTypeInAttribute, HashSet iset, List set2) where TAttribute : Attribute @@ -218,7 +251,7 @@ namespace Umbraco.Core.Composing return text.ToString(); } - private static void EnableDisableComposers(ICollection types) + private static void EnableDisableComposers(IEnumerable enableDisableAttributes, ICollection types) { var enabled = new Dictionary(); @@ -240,20 +273,16 @@ namespace Umbraco.Core.Composing enableInfo.Weight = weight2; } - var assemblies = types.Select(x => x.Assembly).Distinct(); - foreach (var assembly in assemblies) + foreach (var attr in enableDisableAttributes.OfType()) { - foreach (var attr in assembly.GetCustomAttributes()) - { - var type = attr.EnabledType; - UpdateEnableInfo(type, 2, enabled, true); - } + var type = attr.EnabledType; + UpdateEnableInfo(type, 2, enabled, true); + } - foreach (var attr in assembly.GetCustomAttributes()) - { - var type = attr.DisabledType; - UpdateEnableInfo(type, 2, enabled, false); - } + foreach (var attr in enableDisableAttributes.OfType()) + { + var type = attr.DisabledType; + UpdateEnableInfo(type, 2, enabled, false); } foreach (var composerType in types) diff --git a/src/Umbraco.Abstractions/Composing/TypeLoader.cs b/src/Umbraco.Abstractions/Composing/TypeLoader.cs index 6feb915f4a..76d00c472d 100644 --- a/src/Umbraco.Abstractions/Composing/TypeLoader.cs +++ b/src/Umbraco.Abstractions/Composing/TypeLoader.cs @@ -514,6 +514,49 @@ namespace Umbraco.Core.Composing #endregion + #region Get Assembly Attributes + + /// + /// Gets the assembly attributes of the specified type . + /// + /// The attribute type. + /// + /// The assembly attributes of the specified type . + /// + public IEnumerable GetAssemblyAttributes() + where T : Attribute + { + return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); + } + + /// + /// Gets all the assembly attributes. + /// + /// + /// All assembly attributes. + /// + public IEnumerable GetAssemblyAttributes() + { + return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); + } + + /// + /// Gets the assembly attributes of the specified . + /// + /// The attribute types. + /// + /// The assembly attributes of the specified types. + /// + /// attributeTypes + public IEnumerable GetAssemblyAttributes(params Type[] attributeTypes) + { + if (attributeTypes == null) throw new ArgumentNullException(nameof(attributeTypes)); + + return AssembliesToScan.SelectMany(a => attributeTypes.SelectMany(at => a.GetCustomAttributes(at))).ToList(); + } + + #endregion + #region Get Types /// diff --git a/src/Umbraco.Abstractions/Configuration/ConfigsExtensions.cs b/src/Umbraco.Abstractions/Configuration/ConfigsExtensions.cs index a41665a59e..1723785069 100644 --- a/src/Umbraco.Abstractions/Configuration/ConfigsExtensions.cs +++ b/src/Umbraco.Abstractions/Configuration/ConfigsExtensions.cs @@ -28,9 +28,6 @@ namespace Umbraco.Core public static IUmbracoSettingsSection Settings(this Configs configs) => configs.GetConfig(); - public static IUserPasswordConfiguration UserPasswordConfig(this Configs configs) - => configs.GetConfig(); - public static IHealthChecks HealthChecks(this Configs configs) => configs.GetConfig(); @@ -40,11 +37,28 @@ namespace Umbraco.Core public static ICoreDebug CoreDebug(this Configs configs) => configs.GetConfig(); + public static IUserPasswordConfiguration UserPasswordConfiguration(this Configs configs) + => configs.GetConfig(); + + public static IMemberPasswordConfiguration MemberPasswordConfiguration(this Configs configs) + => configs.GetConfig(); + + public static void AddPasswordConfigurations(this Configs configs) + { + configs.Add(() => + { + return new UserPasswordConfiguration(configs.Settings().Security.UserPasswordConfiguration); + }); + configs.Add(() => + { + return new MemberPasswordConfiguration(configs.Settings().Security.MemberPasswordConfiguration); + }); + } + public static void AddCoreConfigs(this Configs configs, IIOHelper ioHelper) { var configDir = new DirectoryInfo(ioHelper.MapPath(Constants.SystemDirectories.Config)); - // GridConfig depends on runtime caches, manifest parsers... and cannot be available during composition configs.Add(factory => new GridConfig( factory.GetInstance(), diff --git a/src/Umbraco.Abstractions/Configuration/MemberPasswordConfiguration.cs b/src/Umbraco.Abstractions/Configuration/MemberPasswordConfiguration.cs new file mode 100644 index 0000000000..58c907c31f --- /dev/null +++ b/src/Umbraco.Abstractions/Configuration/MemberPasswordConfiguration.cs @@ -0,0 +1,15 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core.Configuration +{ + /// + /// The password configuration for back office users + /// + public class MemberPasswordConfiguration : PasswordConfiguration, IMemberPasswordConfiguration + { + public MemberPasswordConfiguration(IMemberPasswordConfigurationSection configSection) + : base(configSection) + { + } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/PasswordConfiguration.cs b/src/Umbraco.Abstractions/Configuration/PasswordConfiguration.cs new file mode 100644 index 0000000000..9edf1a462e --- /dev/null +++ b/src/Umbraco.Abstractions/Configuration/PasswordConfiguration.cs @@ -0,0 +1,41 @@ +using System; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core.Configuration +{ + public abstract class PasswordConfiguration : IPasswordConfiguration + { + protected PasswordConfiguration(IPasswordConfigurationSection configSection) + { + if (configSection == null) + { + throw new ArgumentNullException(nameof(configSection)); + } + + RequiredLength = configSection.RequiredLength; + RequireNonLetterOrDigit = configSection.RequireNonLetterOrDigit; + RequireDigit = configSection.RequireDigit; + RequireLowercase = configSection.RequireLowercase; + RequireUppercase = configSection.RequireUppercase; + UseLegacyEncoding = configSection.UseLegacyEncoding; + HashAlgorithmType = configSection.HashAlgorithmType; + MaxFailedAccessAttemptsBeforeLockout = configSection.MaxFailedAccessAttemptsBeforeLockout; + } + + public int RequiredLength { get; } + + public bool RequireNonLetterOrDigit { get; } + + public bool RequireDigit { get; } + + public bool RequireLowercase { get; } + + public bool RequireUppercase { get; } + + public bool UseLegacyEncoding { get; } + + public string HashAlgorithmType { get; } + + public int MaxFailedAccessAttemptsBeforeLockout { get; } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IKeepAliveSection.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IKeepAliveSection.cs new file mode 100644 index 0000000000..3a0ad258c5 --- /dev/null +++ b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IKeepAliveSection.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IKeepAliveSection : IUmbracoConfigurationSection + { + bool DisableKeepAliveTask { get; } + string KeepAlivePingUrl { get; } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs new file mode 100644 index 0000000000..cbbb933857 --- /dev/null +++ b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IMemberPasswordConfigurationSection : IPasswordConfigurationSection + { + } +} diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IPasswordConfigurationSection.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IPasswordConfigurationSection.cs new file mode 100644 index 0000000000..a561b7808e --- /dev/null +++ b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IPasswordConfigurationSection.cs @@ -0,0 +1,21 @@ +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IPasswordConfigurationSection : IUmbracoConfigurationSection + { + int RequiredLength { get; } + + bool RequireNonLetterOrDigit { get; } + + bool RequireDigit { get; } + + bool RequireLowercase { get; } + + bool RequireUppercase { get; } + + bool UseLegacyEncoding { get; } + + string HashAlgorithmType { get; } + + int MaxFailedAccessAttemptsBeforeLockout { get; } + } +} diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ISecuritySection.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ISecuritySection.cs index b8b8c86f8d..a6ed188713 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ISecuritySection.cs +++ b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ISecuritySection.cs @@ -3,7 +3,7 @@ public interface ISecuritySection : IUmbracoConfigurationSection { bool KeepUserLoggedIn { get; } - + bool HideDisabledUsersInBackoffice { get; } /// @@ -23,5 +23,9 @@ /// When this is false, the username and email fields will be shown in the user section. /// bool UsernameIsEmail { get; } + + IUserPasswordConfigurationSection UserPasswordConfiguration { get; } + + IMemberPasswordConfigurationSection MemberPasswordConfiguration { get; } } } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs index 81d27e7bae..acd9c92588 100644 --- a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs @@ -15,5 +15,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings ILoggingSection Logging { get; } IWebRoutingSection WebRouting { get; } + + IKeepAliveSection KeepAlive { get; } } } diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs new file mode 100644 index 0000000000..d80dd2b7e5 --- /dev/null +++ b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface IUserPasswordConfigurationSection : IPasswordConfigurationSection + { + } +} diff --git a/src/Umbraco.Abstractions/Configuration/UserPasswordConfiguration.cs b/src/Umbraco.Abstractions/Configuration/UserPasswordConfiguration.cs new file mode 100644 index 0000000000..4cfee5280b --- /dev/null +++ b/src/Umbraco.Abstractions/Configuration/UserPasswordConfiguration.cs @@ -0,0 +1,15 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core.Configuration +{ + /// + /// The password configuration for back office users + /// + public class UserPasswordConfiguration : PasswordConfiguration, IUserPasswordConfiguration + { + public UserPasswordConfiguration(IUserPasswordConfigurationSection configSection) + : base(configSection) + { + } + } +} diff --git a/src/Umbraco.Abstractions/IMainDom.cs b/src/Umbraco.Abstractions/IMainDom.cs index 3a8cd13ff1..31b2e2eee0 100644 --- a/src/Umbraco.Abstractions/IMainDom.cs +++ b/src/Umbraco.Abstractions/IMainDom.cs @@ -14,6 +14,9 @@ namespace Umbraco.Core /// /// Gets a value indicating whether the current domain is the main domain. /// + /// + /// When the first call is made to this there will generally be some logic executed to acquire a distributed lock lease. + /// bool IsMainDom { get; } /// @@ -35,4 +38,4 @@ namespace Umbraco.Core /// is guaranteed to execute before the AppDomain releases the main domain status. bool Register(Action install, Action release, int weight = 100); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Abstractions/Manifest/ManifestContentAppFactory.cs b/src/Umbraco.Abstractions/Manifest/ManifestContentAppFactory.cs index e2c3ee48fa..903efe2897 100644 --- a/src/Umbraco.Abstractions/Manifest/ManifestContentAppFactory.cs +++ b/src/Umbraco.Abstractions/Manifest/ManifestContentAppFactory.cs @@ -20,6 +20,8 @@ namespace Umbraco.Core.Manifest // '-content/foo', // hide for content type 'foo' // '+content/*', // show for all other content types // '+media/*', // show for all media types + // '-member/foo' // hide for member type 'foo' + // '+member/*' // show for all member types // '+role/admin' // show for admin users. Role based permissions will override others. // ] // }, @@ -59,6 +61,10 @@ namespace Umbraco.Core.Manifest partA = "media"; partB = media.ContentType.Alias; break; + case IMember member: + partA = "member"; + partB = member.ContentType.Alias; + break; default: return null; diff --git a/src/Umbraco.Abstractions/Models/IDataEditorWithMediaPath.cs b/src/Umbraco.Abstractions/Models/IDataEditorWithMediaPath.cs new file mode 100644 index 0000000000..e8af1b0ac3 --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IDataEditorWithMediaPath.cs @@ -0,0 +1,19 @@ +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Must be implemented by property editors that store media and return media paths + /// + /// + /// Currently there are only 2x core editors that do this: upload and image cropper. + /// It would be possible for developers to know implement their own media property editors whereas previously this was not possible. + /// + public interface IDataEditorWithMediaPath + { + /// + /// Returns the media path for the value stored for a property + /// + /// + /// + string GetMediaPath(object value); + } +} diff --git a/src/Umbraco.Abstractions/SafeCallContext.cs b/src/Umbraco.Abstractions/SafeCallContext.cs index 7a64f3f7b5..aeaf4af17e 100644 --- a/src/Umbraco.Abstractions/SafeCallContext.cs +++ b/src/Umbraco.Abstractions/SafeCallContext.cs @@ -4,6 +4,14 @@ using System.Collections.Generic; namespace Umbraco.Core { + /// + /// Provides a way to stop the data flow of a logical call context (i.e. CallContext or AsyncLocal) from within + /// a SafeCallContext and then have the original data restored to the current logical call context. + /// + /// + /// Some usages of this might be when spawning async thread or background threads in which the current logical call context will be flowed but + /// you don't want it to flow there yet you don't want to clear it either since you want the data to remain on the current thread. + /// public class SafeCallContext : IDisposable { private static readonly List> EnterFuncs = new List>(); diff --git a/src/Umbraco.Abstractions/StringExtensions.cs b/src/Umbraco.Abstractions/StringExtensions.cs index 67c44c9793..d61c0a87ca 100644 --- a/src/Umbraco.Abstractions/StringExtensions.cs +++ b/src/Umbraco.Abstractions/StringExtensions.cs @@ -14,13 +14,11 @@ using Umbraco.Core.Strings; namespace Umbraco.Core { - /// /// String extension methods /// public static class StringExtensions { - private static readonly char[] ToCSharpHexDigitLower = "0123456789abcdef".ToCharArray(); private static readonly char[] ToCSharpEscapeChars; @@ -72,13 +70,10 @@ namespace Umbraco.Core return string.Format("{0}", fileName.Substring(0, fileName.IndexOf(ext, StringComparison.Ordinal))); } + return fileName; - - } - - /// /// This tries to detect a json string, this is not a fail safe way but it is quicker than doing /// a try/catch when deserializing when it is not json. @@ -100,8 +95,6 @@ namespace Umbraco.Core return JsonEmpties.Contains(Whitespace.Value.Replace(input, string.Empty)); } - - public static string ReplaceNonAlphanumericChars(this string input, string replacement) { //any character that is not alphanumeric, convert to a hyphen @@ -190,9 +183,6 @@ namespace Umbraco.Core } - - - //this is from SqlMetal and just makes it a bit of fun to allow pluralization public static string MakePluralName(this string name) { @@ -1232,7 +1222,7 @@ namespace Umbraco.Core && Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) == false; } - /// + /// /// Based on the input string, this will detect if the string is a JS path or a JS snippet. /// If a path cannot be determined, then it is assumed to be a snippet the original text is returned /// with an invalid attempt, otherwise a valid attempt is returned with the resolved path @@ -1277,77 +1267,83 @@ namespace Umbraco.Core } - // FORMAT STRINGS + // FORMAT STRINGS - /// - /// Cleans a string to produce a string that can safely be used in an alias. - /// - /// The text to filter. - /// The safe alias. - public static string ToSafeAlias(this string alias, IShortStringHelper shortStringHelper) - { - return shortStringHelper.CleanStringForSafeAlias(alias); - } + /// + /// Cleans a string to produce a string that can safely be used in an alias. + /// + /// The text to filter. + /// The short string helper. + /// The safe alias. + public static string ToSafeAlias(this string alias, IShortStringHelper shortStringHelper) + { + return shortStringHelper.CleanStringForSafeAlias(alias); + } - /// - /// Cleans a string to produce a string that can safely be used in an alias. - /// - /// The text to filter. - /// A value indicating that we want to camel-case the alias. - /// The safe alias. - public static string ToSafeAlias(this string alias, IShortStringHelper shortStringHelper, bool camel) - { - var a = shortStringHelper.CleanStringForSafeAlias(alias); - if (string.IsNullOrWhiteSpace(a) || camel == false) return a; - return char.ToLowerInvariant(a[0]) + a.Substring(1); - } + /// + /// Cleans a string to produce a string that can safely be used in an alias. + /// + /// The text to filter. + /// A value indicating that we want to camel-case the alias. + /// The short string helper. + /// The safe alias. + public static string ToSafeAlias(this string alias, IShortStringHelper shortStringHelper, bool camel) + { + var a = shortStringHelper.CleanStringForSafeAlias(alias); + if (string.IsNullOrWhiteSpace(a) || camel == false) return a; + return char.ToLowerInvariant(a[0]) + a.Substring(1); + } - /// - /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an alias. - /// - /// The text to filter. - /// The culture. - /// The safe alias. - public static string ToSafeAlias(this string alias, IShortStringHelper shortStringHelper, string culture) - { - return shortStringHelper.CleanStringForSafeAlias(alias, culture); - } + /// + /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an alias. + /// + /// The text to filter. + /// The culture. + /// The short string helper. + /// The safe alias. + public static string ToSafeAlias(this string alias, IShortStringHelper shortStringHelper, string culture) + { + return shortStringHelper.CleanStringForSafeAlias(alias, culture); + } - // the new methods to get a url segment + // the new methods to get a url segment - /// - /// Cleans a string to produce a string that can safely be used in an url segment. - /// - /// The text to filter. - /// The safe url segment. - public static string ToUrlSegment(this string text, IShortStringHelper shortStringHelper) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text)); + /// + /// Cleans a string to produce a string that can safely be used in an url segment. + /// + /// The text to filter. + /// The short string helper. + /// The safe url segment. + public static string ToUrlSegment(this string text, IShortStringHelper shortStringHelper) + { + if (text == null) throw new ArgumentNullException(nameof(text)); + if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text)); - return shortStringHelper.CleanStringForUrlSegment(text); - } + return shortStringHelper.CleanStringForUrlSegment(text); + } - /// - /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an url segment. - /// - /// The text to filter. - /// The culture. - /// The safe url segment. - public static string ToUrlSegment(this string text, IShortStringHelper shortStringHelper, string culture) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text)); + /// + /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an url segment. + /// + /// The text to filter. + /// The short string helper. + /// The culture. + /// The safe url segment. + public static string ToUrlSegment(this string text, IShortStringHelper shortStringHelper, string culture) + { + if (text == null) throw new ArgumentNullException(nameof(text)); + if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text)); - return shortStringHelper.CleanStringForUrlSegment(text, culture); - } + return shortStringHelper.CleanStringForUrlSegment(text, culture); + } - /// + /// /// Cleans a string. /// /// The text to clean. + /// The short string helper. /// A flag indicating the target casing and encoding of the string. By default, /// strings are cleaned up to camelCase and Ascii. /// The clean string. @@ -1361,6 +1357,7 @@ namespace Umbraco.Core /// Cleans a string, using a specified separator. /// /// The text to clean. + /// The short string helper. /// A flag indicating the target casing and encoding of the string. By default, /// strings are cleaned up to camelCase and Ascii. /// The separator. @@ -1375,6 +1372,7 @@ namespace Umbraco.Core /// Cleans a string in the context of a specified culture. /// /// The text to clean. + /// The short string helper. /// A flag indicating the target casing and encoding of the string. By default, /// strings are cleaned up to camelCase and Ascii. /// The culture. @@ -1388,6 +1386,7 @@ namespace Umbraco.Core /// Cleans a string in the context of a specified culture, using a specified separator. /// /// The text to clean. + /// The short string helper. /// A flag indicating the target casing and encoding of the string. By default, /// strings are cleaned up to camelCase and Ascii. /// The separator. diff --git a/src/Umbraco.Abstractions/AsyncLock.cs b/src/Umbraco.Abstractions/SystemLock.cs similarity index 77% rename from src/Umbraco.Abstractions/AsyncLock.cs rename to src/Umbraco.Abstractions/SystemLock.cs index 47868a4150..704fc65123 100644 --- a/src/Umbraco.Abstractions/AsyncLock.cs +++ b/src/Umbraco.Abstractions/SystemLock.cs @@ -21,18 +21,18 @@ namespace Umbraco.Core // been closed, the Semaphore system object is destroyed - so in any case // an iisreset should clean up everything // - public class AsyncLock + public class SystemLock { private readonly SemaphoreSlim _semaphore; private readonly Semaphore _semaphore2; private readonly IDisposable _releaser; private readonly Task _releaserTask; - public AsyncLock() - : this (null) + public SystemLock() + : this(null) { } - public AsyncLock(string name) + public SystemLock(string name) { // WaitOne() waits until count > 0 then decrements count // Release() increments count @@ -67,35 +67,6 @@ namespace Umbraco.Core : new NamedSemaphoreReleaser(_semaphore2); } - //NOTE: We don't use the "Async" part of this lock at all - //TODO: Remove this and rename this class something like SystemWideLock, then we can re-instate this logic if we ever need an Async lock again - - //public Task LockAsync() - //{ - // var wait = _semaphore != null - // ? _semaphore.WaitAsync() - // : _semaphore2.WaitOneAsync(); - - // return wait.IsCompleted - // ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named - // : wait.ContinueWith((_, state) => (((AsyncLock) state).CreateReleaser()), - // this, CancellationToken.None, - // TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - //} - - //public Task LockAsync(int millisecondsTimeout) - //{ - // var wait = _semaphore != null - // ? _semaphore.WaitAsync(millisecondsTimeout) - // : _semaphore2.WaitOneAsync(millisecondsTimeout); - - // return wait.IsCompleted - // ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named - // : wait.ContinueWith((_, state) => (((AsyncLock)state).CreateReleaser()), - // this, CancellationToken.None, - // TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - //} - public IDisposable Lock() { if (_semaphore != null) @@ -121,14 +92,18 @@ namespace Umbraco.Core private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable { private readonly Semaphore _semaphore; - private GCHandle _handle; internal NamedSemaphoreReleaser(Semaphore semaphore) { _semaphore = semaphore; - _handle = GCHandle.Alloc(_semaphore); } + #region IDisposable Support + + // This code added to correctly implement the disposable pattern. + + private bool disposedValue = false; // To detect redundant calls + public void Dispose() { Dispose(true); @@ -137,10 +112,22 @@ namespace Umbraco.Core private void Dispose(bool disposing) { - // critical - _handle.Free(); - _semaphore.Release(); - _semaphore.Dispose(); + if (!disposedValue) + { + try + { + _semaphore.Release(); + } + finally + { + try + { + _semaphore.Dispose(); + } + catch { } + } + disposedValue = true; + } } // we WANT to release the semaphore because it's a system object, ie a critical @@ -171,6 +158,9 @@ namespace Umbraco.Core // we do NOT want the finalizer to throw - never ever } } + + #endregion + } private class SemaphoreSlimReleaser : IDisposable diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index c843467e93..fea0c23f29 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -13,6 +13,7 @@ namespace Umbraco.Core.Configuration } public IHostingSettings HostingSettings { get; } = new HostingSettings(); + public ICoreDebug CoreDebug { get; } = new CoreDebug(); public IUmbracoSettingsSection UmbracoSettings { get; } @@ -21,33 +22,19 @@ namespace Umbraco.Core.Configuration { var configs = new Configs(section => ConfigurationManager.GetSection(section)); configs.Add(() => new GlobalSettings(ioHelper)); - configs.Add(() => HostingSettings); + configs.Add(() => HostingSettings); configs.Add("umbracoConfiguration/settings"); configs.Add("umbracoConfiguration/HealthChecks"); - configs.Add(() => new DefaultPasswordConfig()); - configs.Add(() => new DefaultPasswordConfig()); - configs.Add(() => CoreDebug); + // Password configuration is held within IUmbracoSettingsSection from umbracoConfiguration/settings but we'll add explicitly + // so it can be independently retrieved in classes that need it. + configs.AddPasswordConfigurations(); + + configs.Add(() => CoreDebug); configs.Add(() => new ConnectionStrings()); configs.AddCoreConfigs(ioHelper); return configs; } } - - // Default/static user password configs - // TODO: Make this configurable somewhere - we've removed membership providers for users, so could be a section in the umbracosettings.config file? - // keeping in mind that we will also be removing the members membership provider so there will be 2x the same/similar configuration. - // TODO: Currently it doesn't actually seem possible to replace any sub-configuration unless totally replacing the IConfigsFactory?? - internal class DefaultPasswordConfig : IUserPasswordConfiguration, IMemberPasswordConfiguration - { - public int RequiredLength => 12; - public bool RequireNonLetterOrDigit => false; - public bool RequireDigit => false; - public bool RequireLowercase => false; - public bool RequireUppercase => false; - public bool UseLegacyEncoding => false; - public string HashAlgorithmType => "HMACSHA256"; - public int MaxFailedAccessAttemptsBeforeLockout => 5; - } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs b/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs new file mode 100644 index 0000000000..89ba9be54d --- /dev/null +++ b/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs @@ -0,0 +1,13 @@ +using System.Configuration; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + internal class KeepAliveElement : ConfigurationElement, IKeepAliveSection + { + [ConfigurationProperty("disableKeepAliveTask", DefaultValue = "false")] + public bool DisableKeepAliveTask => (bool)base["disableKeepAliveTask"]; + + [ConfigurationProperty("keepAlivePingUrl", DefaultValue = "{umbracoApplicationUrl}/api/keepalive/ping")] + public string KeepAlivePingUrl => (string)base["keepAlivePingUrl"]; + } +} diff --git a/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs b/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs new file mode 100644 index 0000000000..93c7c20159 --- /dev/null +++ b/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + internal class MemberPasswordConfigurationElement : PasswordConfigurationElement, IMemberPasswordConfigurationSection + { + } +} diff --git a/src/Umbraco.Configuration/UmbracoSettings/PasswordConfigurationElement.cs b/src/Umbraco.Configuration/UmbracoSettings/PasswordConfigurationElement.cs new file mode 100644 index 0000000000..fcc0e1d017 --- /dev/null +++ b/src/Umbraco.Configuration/UmbracoSettings/PasswordConfigurationElement.cs @@ -0,0 +1,31 @@ +using System.Configuration; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + internal class PasswordConfigurationElement : UmbracoConfigurationElement + { + [ConfigurationProperty("requiredLength", DefaultValue = "12")] + public int RequiredLength => (int)base["requiredLength"]; + + [ConfigurationProperty("requireNonLetterOrDigit", DefaultValue = "false")] + public bool RequireNonLetterOrDigit => (bool)base["requireNonLetterOrDigit"]; + + [ConfigurationProperty("requireDigit", DefaultValue = "false")] + public bool RequireDigit => (bool)base["requireDigit"]; + + [ConfigurationProperty("requireLowercase", DefaultValue = "false")] + public bool RequireLowercase => (bool)base["requireLowercase"]; + + [ConfigurationProperty("requireUppercase", DefaultValue = "false")] + public bool RequireUppercase => (bool)base["requireUppercase"]; + + [ConfigurationProperty("useLegacyEncoding", DefaultValue = "false")] + public bool UseLegacyEncoding => (bool)base["useLegacyEncoding"]; + + [ConfigurationProperty("hashAlgorithmType", DefaultValue = "HMACSHA256")] + public string HashAlgorithmType => (string)base["hashAlgorithmType"]; + + [ConfigurationProperty("maxFailedAccessAttemptsBeforeLockout", DefaultValue = "5")] + public int MaxFailedAccessAttemptsBeforeLockout => (int)base["maxFailedAccessAttemptsBeforeLockout"]; + } +} diff --git a/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs b/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs index d45a3e7d85..82012cfd0f 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs @@ -32,6 +32,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("authCookieDomain")] internal InnerTextConfigurationElement AuthCookieDomain => GetOptionalTextElement("authCookieDomain", null); + [ConfigurationProperty("userPasswordConfiguration")] + public UserPasswordConfigurationElement UserPasswordConfiguration => (UserPasswordConfigurationElement)this["userPasswordConfiguration"]; + + [ConfigurationProperty("memberPasswordConfiguration")] + public MemberPasswordConfigurationElement MemberPasswordConfiguration => (MemberPasswordConfigurationElement)this["memberPasswordConfiguration"]; + bool ISecuritySection.KeepUserLoggedIn => KeepUserLoggedIn; bool ISecuritySection.HideDisabledUsersInBackoffice => HideDisabledUsersInBackoffice; @@ -53,5 +59,9 @@ namespace Umbraco.Core.Configuration.UmbracoSettings string ISecuritySection.AuthCookieName => AuthCookieName; string ISecuritySection.AuthCookieDomain => AuthCookieDomain; + + IUserPasswordConfigurationSection ISecuritySection.UserPasswordConfiguration => UserPasswordConfiguration; + + IMemberPasswordConfigurationSection ISecuritySection.MemberPasswordConfiguration => MemberPasswordConfiguration; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs index 98fdcaff93..e605b86edf 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -19,10 +19,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("logging")] public LoggingElement Logging => (LoggingElement)this["logging"]; - [ConfigurationProperty("web.routing")] public WebRoutingElement WebRouting => (WebRoutingElement)this["web.routing"]; + [ConfigurationProperty("keepAlive")] + internal KeepAliveElement KeepAlive => (KeepAliveElement)this["keepAlive"]; + IContentSection IUmbracoSettingsSection.Content => Content; ISecuritySection IUmbracoSettingsSection.Security => Security; @@ -34,5 +36,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings ILoggingSection IUmbracoSettingsSection.Logging => Logging; IWebRoutingSection IUmbracoSettingsSection.WebRouting => WebRouting; + + IKeepAliveSection IUmbracoSettingsSection.KeepAlive => KeepAlive; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs b/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs new file mode 100644 index 0000000000..8128f3d8e7 --- /dev/null +++ b/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + internal class UserPasswordConfigurationElement : PasswordConfigurationElement, IUserPasswordConfigurationSection + { + } +} diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs index 440025a042..eba7492563 100644 --- a/src/Umbraco.Core/MainDom.cs +++ b/src/Umbraco.Core/MainDom.cs @@ -16,25 +16,26 @@ namespace Umbraco.Core /// When an AppDomain starts, it tries to acquire the main domain status. /// When an AppDomain stops (eg the application is restarting) it should release the main domain status. /// - internal class MainDom : IMainDom, IRegisteredObject + internal class MainDom : IMainDom, IRegisteredObject, IDisposable { #region Vars private readonly ILogger _logger; // our own lock for local consistency - private readonly object _locko = new object(); + private object _locko = new object(); // async lock representing the main domain lock - private readonly AsyncLock _asyncLock; - private IDisposable _asyncLocker; + private readonly SystemLock _systemLock; + private IDisposable _systemLocker; // event wait handle used to notify current main domain that it should // release the lock because a new domain wants to be the main domain private readonly EventWaitHandle _signal; + private bool _isInitialized; // indicates whether... - private volatile bool _isMainDom; // we are the main domain + private bool _isMainDom; // we are the main domain private volatile bool _signaled; // we have been signaled // actions to run before releasing the main domain @@ -49,12 +50,12 @@ namespace Umbraco.Core // initializes a new instance of MainDom public MainDom(ILogger logger, IHostingEnvironment hostingEnvironment) { + HostingEnvironment.RegisterObject(this); + _logger = logger; - var appId = string.Empty; // HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail - if (hostingEnvironment.ApplicationId != null) - appId = hostingEnvironment.ApplicationId.ReplaceNonAlphanumericChars(string.Empty); + var appId = hostingEnvironment.ApplicationId?.ReplaceNonAlphanumericChars(string.Empty); // combining with the physical path because if running on eg IIS Express, // two sites could have the same appId even though they are different. @@ -65,11 +66,11 @@ namespace Umbraco.Core // we *cannot* use the process ID here because when an AppPool restarts it is // a new process for the same application path - var appPath = hostingEnvironment.ApplicationPhysicalPath; + var appPath = hostingEnvironment.ApplicationPhysicalPath?.ToLowerInvariant() ?? string.Empty; var hash = (appId + ":::" + appPath).GenerateHash(); var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK"; - _asyncLock = new AsyncLock(lockName); + _systemLock = new SystemLock(lockName); var eventName = "UMBRACO-" + hash + "-MAINDOM-EVT"; _signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); @@ -100,6 +101,12 @@ namespace Umbraco.Core lock (_locko) { if (_signaled) return false; + if (_isMainDom == false) + { + _logger.Warn("Register called when MainDom has not been acquired"); + return false; + } + install?.Invoke(); if (release != null) _callbacks.Add(new KeyValuePair(weight, release)); @@ -119,64 +126,65 @@ namespace Umbraco.Core if (_signaled) return; if (_isMainDom == false) return; // probably not needed _signaled = true; - } - try - { - _logger.Info("Stopping ({SignalSource})", source); - foreach (var callback in _callbacks.OrderBy(x => x.Key).Select(x => x.Value)) + try { - try + _logger.Info("Stopping ({SignalSource})", source); + foreach (var callback in _callbacks.OrderBy(x => x.Key).Select(x => x.Value)) { - callback(); // no timeout on callbacks + try + { + callback(); // no timeout on callbacks + } + catch (Exception e) + { + _logger.Error(e, "Error while running callback"); + continue; + } } - catch (Exception e) - { - _logger.Error(e, "Error while running callback, remaining callbacks will not run."); - throw; - } - + _logger.Debug("Stopped ({SignalSource})", source); } - _logger.Debug("Stopped ({SignalSource})", source); - } - finally - { - // in any case... - _isMainDom = false; - _asyncLocker.Dispose(); - _logger.Info("Released ({SignalSource})", source); + finally + { + // in any case... + _isMainDom = false; + _systemLocker?.Dispose(); + _logger.Info("Released ({SignalSource})", source); + } + } } // acquires the main domain - internal bool Acquire() + private bool Acquire() { - lock (_locko) // we don't want the hosting environment to interfere by signaling + // if signaled, too late to acquire, give up + // the handler is not installed so that would be the hosting environment + if (_signaled) { - // if signaled, too late to acquire, give up - // the handler is not installed so that would be the hosting environment - if (_signaled) - { - _logger.Info("Cannot acquire (signaled)."); - return false; - } + _logger.Info("Cannot acquire (signaled)."); + return false; + } - _logger.Info("Acquiring."); + _logger.Info("Acquiring."); - // signal other instances that we want the lock, then wait one the lock, - // which may timeout, and this is accepted - see comments below + // signal other instances that we want the lock, then wait one the lock, + // which may timeout, and this is accepted - see comments below - // signal, then wait for the lock, then make sure the event is - // reset (maybe there was noone listening..) - _signal.Set(); + // signal, then wait for the lock, then make sure the event is + // reset (maybe there was noone listening..) + _signal.Set(); - // if more than 1 instance reach that point, one will get the lock - // and the other one will timeout, which is accepted - - //TODO: This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset? - _asyncLocker = _asyncLock.Lock(LockTimeoutMilliseconds); - _isMainDom = true; + // if more than 1 instance reach that point, one will get the lock + // and the other one will timeout, which is accepted + //This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset. + try + { + _systemLocker = _systemLock.Lock(LockTimeoutMilliseconds); + } + finally + { // we need to reset the event, because otherwise we would end up // signaling ourselves and committing suicide immediately. // only 1 instance can reach that point, but other instances may @@ -184,35 +192,58 @@ namespace Umbraco.Core // which is accepted _signal.Reset(); - - //WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread - - _signal.WaitOneAsync() - .ContinueWith(_ => OnSignal("signal")); - - HostingEnvironment.RegisterObject(this); - - _logger.Info("Acquired."); - return true; } + + //WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread + + _signal.WaitOneAsync() + .ContinueWith(_ => OnSignal("signal")); + + _logger.Info("Acquired."); + return true; } /// /// Gets a value indicating whether the current domain is the main domain. /// - public bool IsMainDom => _isMainDom; + public bool IsMainDom => LazyInitializer.EnsureInitialized(ref _isMainDom, ref _isInitialized, ref _locko, () => Acquire()); // IRegisteredObject void IRegisteredObject.Stop(bool immediate) { - try + OnSignal("environment"); // will run once + + // The web app is stopping, need to wind down + Dispose(true); + + HostingEnvironment.UnregisterObject(this); + } + + #region IDisposable Support + + // This code added to correctly implement the disposable pattern. + + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) { - OnSignal("environment"); // will run once - } - finally - { - HostingEnvironment.UnregisterObject(this); + if (disposing) + { + _signal?.Close(); + _signal?.Dispose(); + } + + disposedValue = true; } } + + public void Dispose() + { + Dispose(true); + } + + #endregion } } diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index a210f3c4a1..6dbb41093c 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -38,7 +38,8 @@ namespace Umbraco.Core.Runtime IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo, IDbProviderFactoryCreator dbProviderFactoryCreator, - IBulkSqlInsertProvider bulkSqlInsertProvider) + IBulkSqlInsertProvider bulkSqlInsertProvider, + IMainDom mainDom) { IOHelper = ioHelper; Configs = configs; @@ -52,12 +53,14 @@ namespace Umbraco.Core.Runtime _umbracoBootPermissionChecker = umbracoBootPermissionChecker; Logger = logger; + MainDom = mainDom; + // runtime state // beware! must use '() => _factory.GetInstance()' and NOT '_factory.GetInstance' // as the second one captures the current value (null) and therefore fails _state = new RuntimeState(Logger, Configs.Settings(), Configs.Global(), - new Lazy(() => _factory.GetInstance()), + new Lazy(() => mainDom), new Lazy(() => _factory.GetInstance()), UmbracoVersion,HostingEnvironment, BackOfficeInfo) { @@ -100,6 +103,8 @@ namespace Umbraco.Core.Runtime /// public IRuntimeState State => _state; + public IMainDom MainDom { get; private set; } + /// public virtual IFactory Boot(IRegister register) { @@ -179,15 +184,21 @@ namespace Umbraco.Core.Runtime Compose(composition); // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate - AcquireMainDom(mainDom); + AcquireMainDom(MainDom); // determine our runtime level DetermineRuntimeLevel(databaseFactory, ProfilingLogger); // get composers, and compose var composerTypes = ResolveComposerTypes(typeLoader); - composition.WithCollectionBuilder(); - var composers = new Composers(composition, composerTypes, ProfilingLogger); + + IEnumerable enableDisableAttributes; + using (ProfilingLogger.DebugDuration("Scanning enable/disable composer attributes")) + { + enableDisableAttributes = typeLoader.GetAssemblyAttributes(typeof(EnableComposerAttribute), typeof(DisableComposerAttribute)); + } + + var composers = new Composers(composition, composerTypes, enableDisableAttributes, ProfilingLogger); composers.Compose(); // create the factory @@ -196,6 +207,8 @@ namespace Umbraco.Core.Runtime // create & initialize the components _components = _factory.GetInstance(); _components.Initialize(); + + } catch (Exception e) { @@ -262,13 +275,13 @@ namespace Umbraco.Core.Runtime IOHelper.SetRootDirectory(path); } - private bool AcquireMainDom(MainDom mainDom) + private bool AcquireMainDom(IMainDom mainDom) { using (var timer = ProfilingLogger.DebugDuration("Acquiring MainDom.", "Acquired.")) { try { - return mainDom.Acquire(); + return mainDom.IsMainDom; } catch { diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index e21fe72278..333b837e2e 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -152,6 +152,7 @@ + Properties\SolutionInfo.cs diff --git a/src/Umbraco.Examine/MediaIndexPopulator.cs b/src/Umbraco.Examine/MediaIndexPopulator.cs index 6dadcbe4b3..1f5b11e54f 100644 --- a/src/Umbraco.Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Examine/MediaIndexPopulator.cs @@ -10,7 +10,7 @@ namespace Umbraco.Examine /// /// Performs the data lookups required to rebuild a media index /// - public class MediaIndexPopulator : IndexPopulator + public class MediaIndexPopulator : IndexPopulator { private readonly int? _parentId; private readonly IMediaService _mediaService; @@ -69,6 +69,6 @@ namespace Umbraco.Examine pageIndex++; } while (media.Length == pageSize); } - + } } diff --git a/src/Umbraco.Infrastructure/ContentExtensions.cs b/src/Umbraco.Infrastructure/ContentExtensions.cs index f74ecd5693..158e365958 100644 --- a/src/Umbraco.Infrastructure/ContentExtensions.cs +++ b/src/Umbraco.Infrastructure/ContentExtensions.cs @@ -8,6 +8,7 @@ using Umbraco.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index b702730a40..2bb9b404f4 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -192,6 +192,7 @@ namespace Umbraco.Core.Migrations.Upgrade // to 8.6.0 To("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}"); + To("{EE288A91-531B-4995-8179-1D62D9AA3E2E}"); //FINAL } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs new file mode 100644 index 0000000000..75de01dd7f --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs @@ -0,0 +1,24 @@ +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 +{ + public class MissingContentVersionsIndexes : MigrationBase + { + public MissingContentVersionsIndexes(IMigrationContext context) : base(context) + { + } + + public override void Migrate() + { + Create + .Index("IX_" + ContentVersionDto.TableName + "_NodeId") + .OnTable(ContentVersionDto.TableName) + .OnColumn("nodeId") + .Ascending() + .OnColumn("current") + .Ascending() + .WithOptions().NonClustered() + .Do(); + } + } +} diff --git a/src/Umbraco.Infrastructure/Models/MediaExtensions.cs b/src/Umbraco.Infrastructure/Models/MediaExtensions.cs index 1166698adb..08612d2810 100644 --- a/src/Umbraco.Infrastructure/Models/MediaExtensions.cs +++ b/src/Umbraco.Infrastructure/Models/MediaExtensions.cs @@ -1,10 +1,8 @@ -using System; -using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using System.Linq; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; -using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Models { @@ -13,34 +11,17 @@ namespace Umbraco.Core.Models /// /// Gets the url of a media item. /// - public static string GetUrl(this IMedia media, string propertyAlias, ILogger logger) + public static string GetUrl(this IMedia media, string propertyAlias, ILogger logger, PropertyEditorCollection propertyEditors) { if (!media.Properties.TryGetValue(propertyAlias, out var property)) return string.Empty; - // TODO: would need to be adjusted to variations, when media become variants - if (!(property.GetValue() is string jsonString)) - return string.Empty; - - if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.UploadField) - return jsonString; - - if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.ImageCropper) + if (propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor) + && editor is IDataEditorWithMediaPath dataEditor) { - if (jsonString.DetectIsJson() == false) - return jsonString; - - try - { - var json = JsonConvert.DeserializeObject(jsonString); - if (json["src"] != null) - return json["src"].Value(); - } - catch (Exception ex) - { - logger.Error(ex, "Could not parse the string '{JsonString}' to a json object", jsonString); - return string.Empty; - } + // TODO: would need to be adjusted to variations, when media become variants + var value = property.GetValue(); + return dataEditor.GetMediaPath(value); } // Without knowing what it is, just adding a string here might not be very nice @@ -50,10 +31,10 @@ namespace Umbraco.Core.Models /// /// Gets the urls of a media item. /// - public static string[] GetUrls(this IMedia media, IContentSection contentSection, ILogger logger) + public static string[] GetUrls(this IMedia media, IContentSection contentSection, ILogger logger, PropertyEditorCollection propertyEditors) { return contentSection.ImageAutoFillProperties - .Select(field => media.GetUrl(field.Alias, logger)) + .Select(field => media.GetUrl(field.Alias, logger, propertyEditors)) .Where(link => string.IsNullOrWhiteSpace(link) == false) .ToArray(); } diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index f6902d77b8..1699365dfd 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -1231,7 +1231,7 @@ namespace Umbraco.Core.Packaging var name = prop.Element("Name")?.Value; if (sp == null) { - sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(_shortStringHelper), ""); + sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(_shortStringHelper), string.Empty); s.AddProperty(sp); } else diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs index 9b7a3ff001..f5292357e8 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionDto.cs @@ -19,6 +19,7 @@ namespace Umbraco.Core.Persistence.Dtos [Column("nodeId")] [ForeignKey(typeof(ContentDto))] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId,current")] public int NodeId { get; set; } [Column("versionDate")] // TODO: db rename to 'updateDate' @@ -30,7 +31,6 @@ namespace Umbraco.Core.Persistence.Dtos [NullSetting(NullSetting = NullSettings.Null)] public int? UserId { get => _userId == 0 ? null : _userId; set => _userId = value; } //return null if zero - // TODO: we need an index on this it is used almost always in querying and sorting [Column("current")] public bool Current { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Factories/ContentBaseFactory.cs b/src/Umbraco.Infrastructure/Persistence/Factories/ContentBaseFactory.cs index f5dc2612f4..4ae859ba80 100644 --- a/src/Umbraco.Infrastructure/Persistence/Factories/ContentBaseFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/Factories/ContentBaseFactory.cs @@ -1,17 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Persistence.Factories { internal class ContentBaseFactory { - private static readonly Regex MediaPathPattern = new Regex(@"(/media/.+?)(?:['""]|$)", RegexOptions.Compiled); - /// /// Builds an IContent item from a dto and content type. /// @@ -187,7 +185,7 @@ namespace Umbraco.Core.Persistence.Factories /// /// Builds a dto from an IMedia item. /// - public static MediaDto BuildDto(IMedia entity) + public static MediaDto BuildDto(PropertyEditorCollection propertyEditors, IMedia entity) { var contentDto = BuildContentDto(entity, Constants.ObjectTypes.Media); @@ -195,7 +193,7 @@ namespace Umbraco.Core.Persistence.Factories { NodeId = entity.Id, ContentDto = contentDto, - MediaVersionDto = BuildMediaVersionDto(entity, contentDto) + MediaVersionDto = BuildMediaVersionDto(propertyEditors, entity, contentDto) }; return dto; @@ -289,12 +287,20 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - private static MediaVersionDto BuildMediaVersionDto(IMedia entity, ContentDto contentDto) + private static MediaVersionDto BuildMediaVersionDto(PropertyEditorCollection propertyEditors, IMedia entity, ContentDto contentDto) { // try to get a path from the string being stored for media // TODO: only considering umbracoFile - TryMatch(entity.GetValue("umbracoFile"), out var path); + string path = null; + + if (entity.Properties.TryGetValue(Constants.Conventions.Media.File, out var property) + && propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor) + && editor is IDataEditorWithMediaPath dataEditor) + { + var value = property.GetValue(); + path = dataEditor.GetMediaPath(value); + } var dto = new MediaVersionDto { @@ -306,22 +312,5 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - - // TODO: this should NOT be here?! - // more dark magic ;-( - internal static bool TryMatch(string text, out string path) - { - // In v8 we should allow exposing this via the property editor in a much nicer way so that the property editor - // can tell us directly what any URL is for a given property if it contains an asset - - path = null; - if (string.IsNullOrWhiteSpace(text)) return false; - - var m = MediaPathPattern.Match(text); - if (!m.Success || m.Groups.Count != 2) return false; - - path = m.Groups[1].Value; - return true; - } } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs index a05e8cfe7f..ff4ac4e4dd 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using NPoco; using Umbraco.Core.Cache; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; @@ -255,6 +254,21 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return MapDtosToContent(Database.Fetch(sql), true); } + // TODO: This method needs to return a readonly version of IContent! The content returned + // from this method does not contain all of the data required to re-persist it and if that + // is attempted some odd things will occur. + // Either we create an IContentReadOnly (which ultimately we should for vNext so we can + // differentiate between methods that return entities that can be re-persisted or not), or + // in the meantime to not break API compatibility, we can add a property to IContentBase + // (or go further and have it on IUmbracoEntity): "IsReadOnly" and if that is true we throw + // an exception if that entity is passed to a Save method. + // Ideally we return "Slim" versions of content for all sorts of methods here and in ContentService. + // Perhaps another non-breaking alternative is to have new services like IContentServiceReadOnly + // which can return IContentReadOnly. + // We have the ability with `MapDtosToContent` to reduce the amount of data looked up for a + // content item. Ideally for paged data that populates list views, these would be ultra slim + // content items, there's no reason to populate those with really anything apart from property data, + // but until we do something like the above, we can't do that since it would be breaking and unclear. public override IEnumerable GetAllVersionsSlim(int nodeId, int skip, int take) { var sql = GetBaseQuery(QueryType.Many, false) @@ -262,7 +276,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .OrderByDescending(x => x.Current) .AndByDescending(x => x.VersionDate); - return MapDtosToContent(Database.Fetch(sql), true, true).Skip(skip).Take(take); + return MapDtosToContent(Database.Fetch(sql), true, + // load bare minimum, need variants though since this is used to rollback with variants + false, false, false, true).Skip(skip).Take(take); } public override IContent GetVersion(int versionId) @@ -1086,7 +1102,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return base.ApplySystemOrdering(ref sql, ordering); } - private IEnumerable MapDtosToContent(List dtos, bool withCache = false, bool slim = false) + private IEnumerable MapDtosToContent(List dtos, + bool withCache = false, + bool loadProperties = true, + bool loadTemplates = true, + bool loadSchedule = true, + bool loadVariants = true) { var temps = new List>(); var contentTypes = new Dictionary(); @@ -1119,7 +1140,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var c = content[i] = ContentBaseFactory.BuildEntity(dto, contentType); - if (!slim) + if (loadTemplates) { // need templates var templateId = dto.DocumentVersionDto.TemplateId; @@ -1144,49 +1165,71 @@ namespace Umbraco.Core.Persistence.Repositories.Implement temps.Add(temp); } - if (!slim) + Dictionary templates = null; + if (loadTemplates) { // load all required templates in 1 query, and index - var templates = _templateRepository.GetMany(templateIds.ToArray()) + templates = _templateRepository.GetMany(templateIds.ToArray()) .ToDictionary(x => x.Id, x => x); + } + IDictionary properties = null; + if (loadProperties) + { // load all properties for all documents from database in 1 query - indexed by version id - var properties = GetPropertyCollections(temps); - var schedule = GetContentSchedule(temps.Select(x => x.Content.Id).ToArray()); + properties = GetPropertyCollections(temps); + } - // assign templates and properties - foreach (var temp in temps) + var schedule = GetContentSchedule(temps.Select(x => x.Content.Id).ToArray()); + + // assign templates and properties + foreach (var temp in temps) + { + if (loadTemplates) { // set the template ID if it matches an existing template if (temp.Template1Id.HasValue && templates.ContainsKey(temp.Template1Id.Value)) temp.Content.TemplateId = temp.Template1Id; if (temp.Template2Id.HasValue && templates.ContainsKey(temp.Template2Id.Value)) temp.Content.PublishTemplateId = temp.Template2Id; + } - // set properties + + // set properties + if (loadProperties) + { if (properties.ContainsKey(temp.VersionId)) temp.Content.Properties = properties[temp.VersionId]; else throw new InvalidOperationException($"No property data found for version: '{temp.VersionId}'."); + } + if (loadSchedule) + { // load in the schedule if (schedule.TryGetValue(temp.Content.Id, out var s)) temp.Content.ContentSchedule = s; } + } - // set variations, if varying - temps = temps.Where(x => x.ContentType.VariesByCulture()).ToList(); - if (temps.Count > 0) + if (loadVariants) { - // load all variations for all documents from database, in one query - var contentVariations = GetContentVariations(temps); - var documentVariations = GetDocumentVariations(temps); - foreach (var temp in temps) - SetVariations(temp.Content, contentVariations, documentVariations); + // set variations, if varying + temps = temps.Where(x => x.ContentType.VariesByCulture()).ToList(); + if (temps.Count > 0) + { + // load all variations for all documents from database, in one query + var contentVariations = GetContentVariations(temps); + var documentVariations = GetDocumentVariations(temps); + foreach (var temp in temps) + SetVariations(temp.Content, contentVariations, documentVariations); + } } - foreach(var c in content) + + + foreach (var c in content) c.ResetDirtyProperties(false); // reset dirty initial properties (U4-1946) return content; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs index a48012ac26..b0afc3b5f8 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs @@ -239,7 +239,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement entity.SanitizeEntityPropertiesForXmlStorage(); // create the dto - var dto = ContentBaseFactory.BuildDto(entity); + var dto = ContentBaseFactory.BuildDto(PropertyEditors, entity); // derive path and level from parent var parent = GetParentNodeDto(entity.ParentId); @@ -330,7 +330,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } // create the dto - var dto = ContentBaseFactory.BuildDto(entity); + var dto = ContentBaseFactory.BuildDto(PropertyEditors, entity); // update the node dto var nodeDto = dto.ContentDto.NodeDto; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/DataEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DataEditor.cs index b0b12b8370..8ccb1680cc 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/DataEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DataEditor.cs @@ -25,7 +25,6 @@ namespace Umbraco.Core.PropertyEditors private readonly IDataTypeService _dataTypeService; private readonly ILocalizationService _localizationService; private readonly ILocalizedTextService _localizedTextService; - private readonly IShortStringHelper _shortStringHelper; private IDictionary _defaultConfiguration; private IDataValueEditor _dataValueEditor; @@ -37,7 +36,7 @@ namespace Umbraco.Core.PropertyEditors _dataTypeService = dataTypeService; _localizationService = localizationService; _localizedTextService = localizedTextService; - _shortStringHelper = shortStringHelper; + ShortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); Logger = logger ?? throw new ArgumentNullException(nameof(logger)); // defaults @@ -62,6 +61,8 @@ namespace Umbraco.Core.PropertyEditors /// protected DataEditorAttribute Attribute { get; } + protected IShortStringHelper ShortStringHelper { get; } + /// /// Gets a logger. /// @@ -178,7 +179,7 @@ namespace Umbraco.Core.PropertyEditors if (Attribute == null) throw new InvalidOperationException($"The editor is not attributed with {nameof(DataEditorAttribute)}"); - return new DataValueEditor(_dataTypeService, _localizationService, _localizedTextService, _shortStringHelper, Attribute); + return new DataValueEditor(_dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, Attribute); } /// diff --git a/src/Umbraco.Infrastructure/Scoping/CallContext.cs b/src/Umbraco.Infrastructure/Scoping/CallContext.cs index 7b256434cd..2937990eab 100644 --- a/src/Umbraco.Infrastructure/Scoping/CallContext.cs +++ b/src/Umbraco.Infrastructure/Scoping/CallContext.cs @@ -5,37 +5,35 @@ using System.Threading; namespace Umbraco.Core.Scoping { /// - /// Provides a way to set contextual data that flows with the call and - /// async context of a test or invocation. + /// Represents ambient data that is local to a given asynchronous control flow, such as an asynchronous method. /// - public static class CallContext + /// + /// This is just a simple wrapper around + /// + public static class CallContext { - private static readonly ConcurrentDictionary _state = new ConcurrentDictionary(); + private static ConcurrentDictionary> _state = new ConcurrentDictionary>(); /// /// Stores a given object and associates it with the specified name. /// /// The name with which to associate the new item in the call context. /// The object to store in the call context. - public static void SetData(string name, Guid? data) - { - _state[name + Thread.CurrentThread.ManagedThreadId] = data; - } - + public static void SetData(string name, T data) => _state.GetOrAdd(name, _ => new AsyncLocal()).Value = data; /// - /// Retrieves an object with the specified name from the . + /// Retrieves an object with the specified name from the . /// + /// The type of the data being retrieved. Must match the type used when the was set via . /// The name of the item in the call context. - /// The object in the call context associated with the specified name, or if not found. - public static Guid? GetData(string name) - { - return _state.TryGetValue(name + Thread.CurrentThread.ManagedThreadId, out var data) ? data : null; - } + /// The object in the call context associated with the specified name, or a default value for if none is found. + public static T GetData(string name) => _state.TryGetValue(name, out var data) ? data.Value : default; + + // NOTE: If you have used the old CallContext in the past you might be thinking you need to clean this up but that is not the case. + // With CallContext you had to call FreeNamedDataSlot to prevent leaks but with AsyncLocal this is not the case, there is no way to clean this up. + // The above dictionary is sort of a trick because sure, there is always going to be a string key that will exist in the collection but the values + // themselves are managed per ExecutionContext so they don't build up. + // There's an SO article relating to this here https://stackoverflow.com/questions/36511243/safety-of-asynclocal-in-asp-net-core - public static bool RemoveData(string name) - { - return _state.TryRemove(name+ Thread.CurrentThread.ManagedThreadId, out _); - } } } diff --git a/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs index 6c9eb63ba0..4a7ccae481 100644 --- a/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs @@ -88,7 +88,7 @@ namespace Umbraco.Core.Scoping ISqlContext SqlContext { get; } #if DEBUG_SCOPES - Dictionary CallContextObjects { get; } + IEnumerable ScopeInfos { get; } ScopeInfo GetScopeInfo(IScope scope); #endif diff --git a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs index ef3afb2dac..0dba73b55b 100644 --- a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs @@ -49,23 +49,23 @@ namespace Umbraco.Core.Scoping SafeCallContext.Register( () => { - var scope = GetCallContextObject(ScopeItemKey); - var context = GetCallContextObject(ContextItemKey); - SetCallContextObject(ScopeItemKey, null); - SetCallContextObject(ContextItemKey, null); + var scope = GetCallContextObject(ScopeItemKey); + var context = GetCallContextObject(ContextItemKey); + SetCallContextObject(ScopeItemKey, null); + SetCallContextObject(ContextItemKey, null); return Tuple.Create(scope, context); }, o => { // cannot re-attached over leaked scope/context - if (GetCallContextObject(ScopeItemKey) != null) + if (GetCallContextObject(ScopeItemKey) != null) throw new Exception("Found leaked scope when restoring call context."); - if (GetCallContextObject(ContextItemKey) != null) + if (GetCallContextObject(ContextItemKey) != null) throw new Exception("Found leaked context when restoring call context."); - var t = (Tuple) o; - SetCallContextObject(ScopeItemKey, t.Item1); - SetCallContextObject(ContextItemKey, t.Item2); + var t = (Tuple) o; + SetCallContextObject(ScopeItemKey, t.Item1); + SetCallContextObject(ContextItemKey, t.Item2); }); } @@ -75,65 +75,16 @@ namespace Umbraco.Core.Scoping #region Context - // objects that go into the logical call context better be serializable else they'll eventually - // cause issues whenever some cross-AppDomain code executes - could be due to ReSharper running - // tests, any other things (see https://msdn.microsoft.com/en-us/library/dn458353(v=vs.110).aspx), - // but we don't want to make all of our objects serializable since they are *not* meant to be - // used in cross-AppDomain scenario anyways. - // in addition, whatever goes into the logical call context is serialized back and forth any - // time cross-AppDomain code executes, so if we put an "object" there, we'll can *another* - // "object" instance - and so we cannot use a random object as a key. - // so what we do is: we register a guid in the call context, and we keep a table mapping those - // guids to the actual objects. the guid serializes back and forth without causing any issue, - // and we can retrieve the actual objects from the table. - // only issue: how are we supposed to clear the table? we can't, really. objects should take - // care of de-registering themselves from context. - - private static readonly object StaticCallContextObjectsLock = new object(); - private static readonly Dictionary StaticCallContextObjects - = new Dictionary(); - -#if DEBUG_SCOPES - public Dictionary CallContextObjects - { - get - { - lock (StaticCallContextObjectsLock) - { - // capture in a dictionary - return StaticCallContextObjects.ToDictionary(x => x.Key, x => x.Value); - } - } - } -#endif - private static T GetCallContextObject(string key) - where T : class + where T : class, IInstanceIdentifiable { - var objectKey = CallContext.GetData(key); - if (objectKey is null) return null; - - lock (StaticCallContextObjectsLock) - { - if (StaticCallContextObjects.TryGetValue(objectKey.Value, out object callContextObject)) - { -#if DEBUG_SCOPES - Current.Logger.Debug("Got " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8)); - //_logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); -#endif - return (T)callContextObject; - } - - // hard to inject into a static method :( - Current.Logger.Warn("Missed {TypeName} Object {ObjectKey}", typeof(T).Name, objectKey.Value.ToString("N").Substring(0, 8)); -#if DEBUG_SCOPES - //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); -#endif - return null; - } + var obj = CallContext.GetData(key); + if (obj == default(T)) return null; + return obj; } - private static void SetCallContextObject(string key, IInstanceIdentifiable value) + private static void SetCallContextObject(string key, T value) + where T: class, IInstanceIdentifiable { #if DEBUG_SCOPES // manage the 'context' that contains the scope (null, "http" or "call") @@ -141,14 +92,8 @@ namespace Umbraco.Core.Scoping if (key == ScopeItemKey) { // first, null-register the existing value - var ambientKey = CallContext.GetData(ScopeItemKey).AsGuid(); - object o = null; - lock (StaticCallContextObjectsLock) - { - if (ambientKey != default(Guid)) - StaticCallContextObjects.TryGetValue(ambientKey, out o); - } - var ambientScope = o as IScope; + var ambientScope = CallContext.GetData(ScopeItemKey); + if (ambientScope != null) RegisterContext(ambientScope, null); // then register the new value var scope = value as IScope; @@ -157,33 +102,18 @@ namespace Umbraco.Core.Scoping #endif if (value == null) { - var objectKey = CallContext.GetData(key); - CallContext.RemoveData(key); - if (objectKey is null) return; - lock (StaticCallContextObjectsLock) - { -#if DEBUG_SCOPES - Current.Logger.Debug("Remove Object " + objectKey.ToString("N").Substring(0, 8)); - //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); -#endif - StaticCallContextObjects.Remove(objectKey.Value); - } + var obj = CallContext.GetData(key); + CallContext.SetData(key, default); // aka remove + if (obj == null) return; } else { - // note - we are *not* detecting an already-existing value - // because our code in this class *always* sets to null before - // setting to a real value - var objectKey = value.InstanceId; - lock (StaticCallContextObjectsLock) - { + #if DEBUG_SCOPES - Current.Logger.Debug("AddObject " + objectKey.ToString("N").Substring(0, 8)); - //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); + Current.Logger.Debug("AddObject " + value.InstanceId.ToString("N").Substring(0, 8)); #endif - StaticCallContextObjects.Add(objectKey, value); - } - CallContext.SetData(key, objectKey); + + CallContext.SetData(key, value); } } @@ -245,12 +175,12 @@ namespace Umbraco.Core.Scoping { // clear both SetHttpContextObject(ContextItemKey, null, false); - SetCallContextObject(ContextItemKey, null); + SetCallContextObject(ContextItemKey, null); if (value == null) return; // set http/call context if (SetHttpContextObject(ContextItemKey, value, false) == false) - SetCallContextObject(ContextItemKey, value); + SetCallContextObject(ContextItemKey, value); } } @@ -270,21 +200,22 @@ namespace Umbraco.Core.Scoping public Scope AmbientScope { // try http context, fallback onto call context - get => GetHttpContextObject(ScopeItemKey, false) - ?? GetCallContextObject(ScopeItemKey); + // we are casting here because we know its a concrete type + get => (Scope)GetHttpContextObject(ScopeItemKey, false) + ?? (Scope)GetCallContextObject(ScopeItemKey); set { // clear both SetHttpContextObject(ScopeItemKey, null, false); SetHttpContextObject(ScopeRefItemKey, null, false); - SetCallContextObject(ScopeItemKey, null); + SetCallContextObject(ScopeItemKey, null); if (value == null) return; // set http/call context if (value.CallContext == false && SetHttpContextObject(ScopeItemKey, value, false)) SetHttpContextObject(ScopeRefItemKey, _scopeReference); else - SetCallContextObject(ScopeItemKey, value); + SetCallContextObject(ScopeItemKey, value); } } @@ -295,9 +226,9 @@ namespace Umbraco.Core.Scoping // clear all SetHttpContextObject(ScopeItemKey, null, false); SetHttpContextObject(ScopeRefItemKey, null, false); - SetCallContextObject(ScopeItemKey, null); + SetCallContextObject(ScopeItemKey, null); SetHttpContextObject(ContextItemKey, null, false); - SetCallContextObject(ContextItemKey, null); + SetCallContextObject(ContextItemKey, null); if (scope == null) { if (context != null) @@ -312,8 +243,8 @@ namespace Umbraco.Core.Scoping } else { - SetCallContextObject(ScopeItemKey, scope); - SetCallContextObject(ContextItemKey, context); + SetCallContextObject(ScopeItemKey, scope); + SetCallContextObject(ContextItemKey, context); } } diff --git a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs index 6847c45129..d856cae1e7 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComponent.cs @@ -17,21 +17,20 @@ using Umbraco.Web.Mvc; namespace Umbraco.ModelsBuilder.Embedded.Compose { - internal class ModelsBuilderComponent : IComponent { - private readonly IModelsBuilderConfig _config; private readonly IShortStringHelper _shortStringHelper; private readonly LiveModelsProvider _liveModelsProvider; private readonly OutOfDateModelsStatus _outOfDateModels; - public ModelsBuilderComponent(IModelsBuilderConfig config, IShortStringHelper shortStringHelper, LiveModelsProvider liveModelsProvider, OutOfDateModelsStatus outOfDateModels) + public ModelsBuilderComponent(IModelsBuilderConfig config, IShortStringHelper shortStringHelper, LiveModelsProvider liveModelsProvider, OutOfDateModelsStatus outOfDateModels) { _config = config; _shortStringHelper = shortStringHelper; _liveModelsProvider = liveModelsProvider; _outOfDateModels = outOfDateModels; + _shortStringHelper = shortStringHelper; } public void Initialize() diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 7166d17c3f..8e200aacf8 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -6,7 +6,6 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Compose; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; @@ -16,8 +15,6 @@ using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; -[assembly:DisableComposer(typeof(Umbraco.Tests.Components.ComponentTests.Composer26))] - namespace Umbraco.Tests.Components { [TestFixture] @@ -73,10 +70,11 @@ namespace Umbraco.Tests.Components public void Boot1A() { var register = MockRegister(); + var typeLoader = MockTypeLoader(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - goes away with RuntimeLevel.Unknown @@ -115,7 +113,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Run), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 3 is User - stays with RuntimeLevel.Run @@ -131,7 +129,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // 21 is required by 20 // => reorder components accordingly @@ -146,7 +144,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // i23 requires 22 // 24, 25 implement i23 @@ -163,7 +161,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); try { @@ -186,7 +184,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); var types = TypeArray(); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); // 2 is Core and requires 4 // 13 is required by 1 @@ -204,6 +202,7 @@ namespace Umbraco.Tests.Components Terminated.Clear(); var register = MockRegister(); + var typeLoader = MockTypeLoader(); var factory = MockFactory(m => { m.Setup(x => x.TryGetInstance(It.Is(t => t == typeof (ISomeResource)))).Returns(() => new SomeResource()); @@ -221,7 +220,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); var types = new[] { typeof(Composer1), typeof(Composer5), typeof(Composer5a) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Assert.IsEmpty(Composed); composers.Compose(); @@ -247,7 +246,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); var types = new[] { typeof(Composer6), typeof(Composer7), typeof(Composer8) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); @@ -262,7 +261,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), Configs, TestHelper.IOHelper, AppCaches.NoCache); var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); @@ -275,11 +274,12 @@ namespace Umbraco.Tests.Components public void Requires2B() { var register = MockRegister(); + var typeLoader = MockTypeLoader(); var factory = MockFactory(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Run), Configs, TestHelper.IOHelper, AppCaches.NoCache); var types = new[] { typeof(Composer9), typeof(Composer2), typeof(Composer4) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); var builder = composition.WithCollectionBuilder(); @@ -298,32 +298,32 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), Configs, TestHelper.IOHelper, AppCaches.NoCache); var types = new[] { typeof(Composer10) }; - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(1, Composed.Count); Assert.AreEqual(typeof(Composer10), Composed[0]); types = new[] { typeof(Composer11) }; - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); Assert.Throws(() => composers.Compose()); Console.WriteLine("throws:"); - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); var requirements = composers.GetRequirements(false); Console.WriteLine(Composers.GetComposersReport(requirements)); types = new[] { typeof(Composer2) }; - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); Assert.Throws(() => composers.Compose()); Console.WriteLine("throws:"); - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); requirements = composers.GetRequirements(false); Console.WriteLine(Composers.GetComposersReport(requirements)); types = new[] { typeof(Composer12) }; - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(1, Composed.Count); @@ -337,7 +337,7 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), Configs, TestHelper.IOHelper, AppCaches.NoCache); var types = new[] { typeof(Composer6), typeof(Composer8) }; // 8 disables 7 which is not in the list - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); @@ -351,14 +351,15 @@ namespace Umbraco.Tests.Components var register = MockRegister(); var composition = new Composition(register, MockTypeLoader(), Mock.Of(), MockRuntimeState(RuntimeLevel.Unknown), Configs, TestHelper.IOHelper, AppCaches.NoCache); - var types = new[] { typeof(Composer26) }; // 26 disabled by assembly attribute - var composers = new Composers(composition, types, Mock.Of()); + var types = new[] { typeof(Composer26) }; + var enableDisableAttributes = new[] { new DisableComposerAttribute(typeof(Composer26)) }; + var composers = new Composers(composition, types, enableDisableAttributes, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(0, Composed.Count); // 26 gone types = new[] { typeof(Composer26), typeof(Composer27) }; // 26 disabled by assembly attribute, enabled by 27 - composers = new Composers(composition, types, Mock.Of()); + composers = new Composers(composition, types, enableDisableAttributes, Mock.Of()); Composed.Clear(); composers.Compose(); Assert.AreEqual(2, Composed.Count); // both @@ -378,7 +379,7 @@ namespace Umbraco.Tests.Components MockRuntimeState(RuntimeLevel.Run), Configs, TestHelper.IOHelper, AppCaches.NoCache); var types = typeLoader.GetTypes().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")); - var composers = new Composers(composition, types, Mock.Of()); + var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); var requirements = composers.GetRequirements(); var report = Composers.GetComposersReport(requirements); Console.WriteLine(report); @@ -517,7 +518,6 @@ namespace Umbraco.Tests.Components public class Composer25 : TestComposerBase, IComposer23 { } - // disabled by assembly attribute public class Composer26 : TestComposerBase { } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs index 3adfd17d9f..9300c88a67 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs @@ -35,5 +35,101 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings { Assert.IsTrue(SettingsSection.Security.AuthCookieName == "UMB_UCONTEXT"); } + + [Test] + public void UserPasswordConfiguration_RequiredLength() + { + Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequiredLength == 12); + } + + [Test] + public void UserPasswordConfiguration_RequireNonLetterOrDigit() + { + Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireNonLetterOrDigit == false); + } + + [Test] + public void UserPasswordConfiguration_RequireDigit() + { + Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireDigit == false); + } + + [Test] + public void UserPasswordConfiguration_RequireLowercase() + { + Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireLowercase == false); + } + + [Test] + public void UserPasswordConfiguration_RequireUppercase() + { + Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireUppercase == false); + } + + [Test] + public void UserPasswordConfiguration_UseLegacyEncoding() + { + Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.UseLegacyEncoding == false); + } + + [Test] + public void UserPasswordConfiguration_HashAlgorithmType() + { + Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); + } + + [Test] + public void UserPasswordConfiguration_MaxFailedAccessAttemptsBeforeLockout() + { + Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); + } + + [Test] + public void MemberPasswordConfiguration_RequiredLength() + { + Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequiredLength == 12); + } + + [Test] + public void MemberPasswordConfiguration_RequireNonLetterOrDigit() + { + Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireNonLetterOrDigit == false); + } + + [Test] + public void MemberPasswordConfiguration_RequireDigit() + { + Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireDigit == false); + } + + [Test] + public void MemberPasswordConfiguration_RequireLowercase() + { + Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireLowercase == false); + } + + [Test] + public void MemberPasswordConfiguration_RequireUppercase() + { + Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireUppercase == false); + } + + [Test] + public void MemberPasswordConfiguration_UseLegacyEncoding() + { + Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.UseLegacyEncoding == false); + } + + [Test] + public void MemberPasswordConfiguration_HashAlgorithmType() + { + Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); + } + + [Test] + public void MemberPasswordConfiguration_MaxFailedAccessAttemptsBeforeLockout() + { + Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); + } } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs index dad1173c3f..0fa1fb6681 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs @@ -14,7 +14,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public void InternalRedirectPreservesTemplate() { - Assert.IsTrue(SettingsSection.WebRouting.TrySkipIisCustomErrors == false); + Assert.IsTrue(SettingsSection.WebRouting.InternalRedirectPreservesTemplate == false); } [Test] diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config index 8cbb799d88..666a98c1b7 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config @@ -69,6 +69,13 @@ true + + + diff --git a/src/Umbraco.Tests/CoreThings/CallContextTests.cs b/src/Umbraco.Tests/CoreThings/CallContextTests.cs index e01534c8d9..b97a87542c 100644 --- a/src/Umbraco.Tests/CoreThings/CallContextTests.cs +++ b/src/Umbraco.Tests/CoreThings/CallContextTests.cs @@ -1,6 +1,7 @@ -using System.Runtime.Remoting.Messaging; -using NUnit.Framework; +using NUnit.Framework; +using System; using Umbraco.Core; +using Umbraco.Core.Scoping; namespace Umbraco.Tests.CoreThings { @@ -13,10 +14,10 @@ namespace Umbraco.Tests.CoreThings { SafeCallContext.Register(() => { - CallContext.FreeNamedDataSlot("test1"); - CallContext.FreeNamedDataSlot("test2"); + CallContext.SetData("test1", null); + CallContext.SetData("test2", null); return null; - }, o => {}); + }, o => { }); } [OneTimeSetUp] @@ -44,10 +45,10 @@ namespace Umbraco.Tests.CoreThings [Test] public void Test1() { - CallContext.LogicalSetData("test1", "test1"); - Assert.IsNull(CallContext.LogicalGetData("test2")); + CallContext.SetData("test1", "test1"); + Assert.IsNull(CallContext.GetData("test2")); - CallContext.LogicalSetData("test3b", "test3b"); + CallContext.SetData("test3b", "test3b"); if (_first) { @@ -55,21 +56,21 @@ namespace Umbraco.Tests.CoreThings } else { - Assert.IsNotNull(CallContext.LogicalGetData("test3a")); // leak! + Assert.IsNotNull(CallContext.GetData("test3a")); // leak! } } [Test] public void Test2() { - CallContext.LogicalSetData("test2", "test2"); - Assert.IsNull(CallContext.LogicalGetData("test1")); + CallContext.SetData("test2", "test2"); + Assert.IsNull(CallContext.GetData("test1")); } [Test] public void Test3() { - CallContext.LogicalSetData("test3a", "test3a"); + CallContext.SetData("test3a", "test3a"); if (_first) { @@ -77,7 +78,7 @@ namespace Umbraco.Tests.CoreThings } else { - Assert.IsNotNull(CallContext.LogicalGetData("test3b")); // leak! + Assert.IsNotNull(CallContext.GetData("test3b")); // leak! } } } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs index c0b9383b57..aa88f28dc0 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache return scopeProvider?.Context?.GetEnlisted(EnlistKey); } - public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider, AsyncLock xmlLock, XmlDocument xml, Action refresh, Action apply, bool writer) + public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider, SystemLock xmlLock, XmlDocument xml, Action refresh, Action apply, bool writer) { var scopeContext = scopeProvider.Context; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs index 4da218cb48..a744a8d488 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs @@ -47,10 +47,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly IEntityXmlSerializer _entitySerializer; private readonly IHostingEnvironment _hostingEnvironment; private readonly IShortStringHelper _shortStringHelper; - private XmlStoreFilePersister _persisterTask; - private volatile bool _released; - private bool _withRepositoryEvents; - private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly PublishedContentTypeCache _contentTypeCache; private readonly RoutesCache _routesCache; @@ -58,6 +54,10 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private readonly IContentService _contentService; private readonly IScopeProvider _scopeProvider; + private XmlStoreFilePersister _persisterTask; + private volatile bool _released; + private bool _withRepositoryEvents; + #region Constructors /// @@ -65,7 +65,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache /// /// The default constructor will boot the cache, load data from file or database, /// wire events in order to manage changes, etc. public XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IShortStringHelper shortStringHelper) + IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IShortStringHelper shortStringHelper) : this(contentTypeService, contentService, scopeProvider, routesCache, contentTypeCache, publishedSnapshotAccessor, mainDom, false, false, documentRepository, mediaRepository, memberRepository, globalSettings, entitySerializer, hostingEnvironment, shortStringHelper) { } @@ -74,7 +74,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache // TODO: er, we DO have a DB? internal XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, - bool testing, bool enableRepositoryEvents, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IShortStringHelper shortStringHelper) + bool testing, bool enableRepositoryEvents, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IShortStringHelper shortStringHelper) { if (testing == false) EnsureConfigurationIsValid(); @@ -312,7 +312,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private XmlDocument _xmlDocument; // supplied xml document (for tests) private volatile XmlDocument _xml; // master xml document - private readonly AsyncLock _xmlLock = new AsyncLock(); // protects _xml + private readonly SystemLock _xmlLock = new SystemLock(); // protects _xml // to be used by PublishedContentCache only // for non-preview content only diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs index 145a19872a..56c09b18ac 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs @@ -24,7 +24,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache private bool _released; private Timer _timer; private DateTime _initialTouch; - private readonly AsyncLock _runLock = new AsyncLock(); // ensure we run once at a time + private readonly SystemLock _runLock = new SystemLock(); // ensure we run once at a time // note: // as long as the runner controls the runs, we know that we run once at a time, but diff --git a/src/Umbraco.Tests/Migrations/MigrationTests.cs b/src/Umbraco.Tests/Migrations/MigrationTests.cs index 6b2d21e4a5..bfadd45b0d 100644 --- a/src/Umbraco.Tests/Migrations/MigrationTests.cs +++ b/src/Umbraco.Tests/Migrations/MigrationTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Data; using Moq; using NUnit.Framework; @@ -45,8 +46,18 @@ namespace Umbraco.Tests.Migrations throw new NotImplementedException(); } + + public IScopeContext Context { get; set; } public ISqlContext SqlContext { get; set; } + +#if DEBUG_SCOPES + public ScopeInfo GetScopeInfo(IScope scope) + { + throw new NotImplementedException(); + } + public IEnumerable ScopeInfos => throw new NotImplementedException(); +#endif } [Test] diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index 36961ebdd3..f312744db6 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -3,13 +3,12 @@ using System.Xml.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; -using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Strings; +using Umbraco.Tests.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; @@ -21,8 +20,6 @@ namespace Umbraco.Tests.Models [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] public class MediaXmlTest : TestWithDatabaseBase { - - [Test] public void Can_Generate_Xml_Representation_Of_Media() { @@ -39,7 +36,7 @@ namespace Umbraco.Tests.Models var localizationService = Mock.Of(); var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, logger, ShortStringHelper); - var ignored = new FileUploadPropertyEditor(Mock.Of(), mediaFileSystem, config, dataTypeService, localizationService); + var ignored = new FileUploadPropertyEditor(Mock.Of(), mediaFileSystem, config, dataTypeService, localizationService, ShortStringHelper); var media = MockedMedia.CreateMediaImage(mediaType, -1); media.WriterId = -1; // else it's zero and that's not a user and it breaks the tests diff --git a/src/Umbraco.Tests/Persistence/LocksTests.cs b/src/Umbraco.Tests/Persistence/LocksTests.cs index afcd481f9f..d4e3d23a70 100644 --- a/src/Umbraco.Tests/Persistence/LocksTests.cs +++ b/src/Umbraco.Tests/Persistence/LocksTests.cs @@ -13,7 +13,7 @@ namespace Umbraco.Tests.Persistence { [TestFixture] [Timeout(60000)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Serilog)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)] public class LocksTests : TestWithDatabaseBase { protected override void Initialize() diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index a5b4372283..069d93f409 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -36,9 +36,9 @@ namespace Umbraco.Tests.Persistence.Repositories using (provider.CreateScope()) { var dtRepo = CreateRepository(); - IDataType dataType1 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper)) { Name = "dt1" }; + IDataType dataType1 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper)) { Name = "dt1" }; dtRepo.Save(dataType1); - IDataType dataType2 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper)) { Name = "dt2" }; + IDataType dataType2 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper)) { Name = "dt2" }; dtRepo.Save(dataType2); var ctRepo = Factory.GetInstance(); @@ -106,14 +106,14 @@ namespace Umbraco.Tests.Persistence.Repositories var container2 = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "blah2", ParentId = container1.Id }; containerRepository.Save(container2); - var dataType = (IDataType) new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper), container2.Id) + var dataType = (IDataType) new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper), container2.Id) { Name = "dt1" }; repository.Save(dataType); //create a - var dataType2 = (IDataType)new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper), dataType.Id) + var dataType2 = (IDataType)new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper), dataType.Id) { Name = "dt2" }; @@ -185,7 +185,7 @@ namespace Umbraco.Tests.Persistence.Repositories var container = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "blah" }; containerRepository.Save(container); - var dataTypeDefinition = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper), container.Id) { Name = "test" }; + var dataTypeDefinition = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper), container.Id) { Name = "test" }; repository.Save(dataTypeDefinition); Assert.AreEqual(container.Id, dataTypeDefinition.ParentId); @@ -205,7 +205,7 @@ namespace Umbraco.Tests.Persistence.Repositories var container = new EntityContainer(Constants.ObjectTypes.DataType) { Name = "blah" }; containerRepository.Save(container); - IDataType dataType = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper), container.Id) { Name = "test" }; + IDataType dataType = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper), container.Id) { Name = "test" }; repository.Save(dataType); // Act @@ -228,7 +228,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (provider.CreateScope()) { var repository = CreateRepository(); - IDataType dataType = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper)) {Name = "test"}; + IDataType dataType = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService, IOHelper, ShortStringHelper)) {Name = "test"}; repository.Save(dataType); diff --git a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs index 5c97bdf034..17a01b128a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs @@ -359,7 +359,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository, out DataTypeRepository dataTypeDefinitionRepository); - var editor = new DecimalPropertyEditor(Logger); + var editor = new DecimalPropertyEditor(Logger, ShortStringHelper); var dtd = new DataType(editor) { Name = "test", DatabaseType = ValueStorageType.Decimal }; dataTypeDefinitionRepository.Save(dtd); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index f9c7b566db..dac3fab96b 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Linq; -using System.Reflection; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -11,7 +9,6 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -21,6 +18,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Strings; +using Umbraco.Tests.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; using Umbraco.Tests.Testing.Objects.Accessors; @@ -29,7 +27,6 @@ using Umbraco.Web.Cache; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; -using Umbraco.Web.PublishedCache.NuCache.Snap; namespace Umbraco.Tests.PublishedContent { @@ -167,7 +164,8 @@ namespace Umbraco.Tests.PublishedContent Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), typeFinder, - hostingEnvironment); + hostingEnvironment, + new MockShortStringHelper()); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 00bad48a53..4beddbb036 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -8,7 +8,6 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Events; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -18,6 +17,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Strings; +using Umbraco.Tests.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; using Umbraco.Tests.Testing.Objects.Accessors; @@ -206,7 +206,8 @@ namespace Umbraco.Tests.PublishedContent Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), typeFinder, - TestHelper.GetHostingEnvironment()); + TestHelper.GetHostingEnvironment(), + new MockShortStringHelper()); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 6489417dc7..e2ce265ae3 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -4,13 +4,18 @@ using Moq; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.Services; using Umbraco.Tests.PublishedContent; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Web.PropertyEditors; using Umbraco.Web.Routing; namespace Umbraco.Tests.Routing @@ -25,7 +30,17 @@ namespace Umbraco.Tests.Routing { base.SetUp(); - _mediaUrlProvider = new DefaultMediaUrlProvider(); + var logger = Mock.Of(); + var mediaFileSystemMock = Mock.Of(); + var contentSection = Mock.Of(); + var dataTypeService = Mock.Of(); + + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(new IDataEditor[] + { + new FileUploadPropertyEditor(logger, mediaFileSystemMock, contentSection, dataTypeService, LocalizationService, ShortStringHelper), + new ImageCropperPropertyEditor(logger, mediaFileSystemMock, contentSection, dataTypeService, LocalizationService, IOHelper, ShortStringHelper, LocalizedTextService), + })); + _mediaUrlProvider = new DefaultMediaUrlProvider(propertyEditors); } public override void TearDown() @@ -54,10 +69,10 @@ namespace Umbraco.Tests.Routing const string expected = "/media/rfeiw584/test.jpg"; var configuration = new ImageCropperConfiguration(); - var imageCropperValue = new ImageCropperValue + var imageCropperValue = JsonConvert.SerializeObject(new ImageCropperValue { Src = expected - }; + }); var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider }); var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.ImageCropper, imageCropperValue, configuration); @@ -121,8 +136,8 @@ namespace Umbraco.Tests.Routing PropertyType = umbracoFilePropertyType, }; - property.SetValue("en", enMediaUrl, true); - property.SetValue("da", daMediaUrl); + property.SetSourceValue("en", enMediaUrl, true); + property.SetSourceValue("da", daMediaUrl); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) {Properties = new[] {property}}; @@ -131,7 +146,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(daMediaUrl, resolvedUrl); } - private static IPublishedContent CreatePublishedContent(string propertyEditorAlias, object propertyValue, object dataTypeConfiguration) + private static IPublishedContent CreatePublishedContent(string propertyEditorAlias, string propertyValue, object dataTypeConfiguration) { var umbracoFilePropertyType = CreatePropertyType(propertyEditorAlias, dataTypeConfiguration, ContentVariation.Nothing); @@ -147,7 +162,7 @@ namespace Umbraco.Tests.Routing new SolidPublishedProperty { Alias = "umbracoFile", - SolidValue = propertyValue, + SolidSourceValue = propertyValue, SolidHasValue = true, PropertyType = umbracoFilePropertyType } diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index dccc658639..7bf520b0cf 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -5,6 +5,7 @@ using System.Web.Routing; using System.Web.Security; using Moq; using NUnit.Framework; +using NUnit.Framework.Internal; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Logging; @@ -32,6 +33,7 @@ using Umbraco.Web.Runtime; using Umbraco.Web.Security; using Current = Umbraco.Web.Composing.Current; using Umbraco.Web.Security.Providers; +using ILogger = Umbraco.Core.Logging.ILogger; namespace Umbraco.Tests.Routing { @@ -54,7 +56,7 @@ namespace Umbraco.Tests.Routing public class TestRuntime : WebRuntime { public TestRuntime(UmbracoApplicationBase umbracoApplication, Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - : base(umbracoApplication, configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider) + : base(umbracoApplication, configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider, TestHelper.MainDom) { } @@ -147,16 +149,16 @@ namespace Umbraco.Tests.Routing ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContextAccessor, Mock.Of(), context => - { - var membershipHelper = new MembershipHelper( - umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of()); - return new CustomDocumentController(Factory.GetInstance(), - umbracoContextAccessor, - Factory.GetInstance(), - Factory.GetInstance(), - Factory.GetInstance(), - new UmbracoHelper(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), membershipHelper)); - }), ShortStringHelper); + { + var membershipHelper = new MembershipHelper( + umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of(), ShortStringHelper); + return new CustomDocumentController(Factory.GetInstance(), + umbracoContextAccessor, + Factory.GetInstance(), + Factory.GetInstance(), + Factory.GetInstance(), + new UmbracoHelper(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), membershipHelper)); + }), ShortStringHelper); handler.GetHandlerForRoute(umbracoContext.HttpContext.Request.RequestContext, frequest); Assert.AreEqual("CustomDocument", routeData.Values["controller"].ToString()); diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index e805c59b9e..64716754b0 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -122,7 +122,7 @@ namespace Umbraco.Tests.Runtimes public class TestRuntime : CoreRuntime { public TestRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IProfiler profiler, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - :base(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider) + :base(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider, TestHelper.MainDom) { } diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 6e35aca30d..2b8559741d 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -79,7 +79,7 @@ namespace Umbraco.Tests.Runtimes composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); // create the core runtime and have it compose itself - var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider);coreRuntime.Compose(composition); + var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider, TestHelper.MainDom);coreRuntime.Compose(composition); // determine actual runtime level runtimeState.DetermineRuntimeLevel(databaseFactory, logger); @@ -90,7 +90,7 @@ namespace Umbraco.Tests.Runtimes var composerTypes = typeLoader.GetTypes() // all of them .Where(x => !x.FullName.StartsWith("Umbraco.Tests.")) // exclude test components .Where(x => x != typeof(WebInitialComposer) && x != typeof(WebFinalComposer)); // exclude web runtime - var composers = new Composers(composition, composerTypes, profilingLogger); + var composers = new Composers(composition, composerTypes, Enumerable.Empty(), profilingLogger); composers.Compose(); // must registers stuff that WebRuntimeComponent would register otherwise @@ -274,7 +274,7 @@ namespace Umbraco.Tests.Runtimes composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); // create the core runtime and have it compose itself - var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); + var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider, TestHelper.MainDom); coreRuntime.Compose(composition); // get the components @@ -285,7 +285,7 @@ namespace Umbraco.Tests.Runtimes .Where(x => !x.FullName.StartsWith("Umbraco.Tests")); // single? //var componentTypes = new[] { typeof(CoreRuntimeComponent) }; - var composers = new Composers(composition, composerTypes, profilingLogger); + var composers = new Composers(composition, composerTypes, Enumerable.Empty(), profilingLogger); // get components to compose themselves composers.Compose(); diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs index 27abca7cbd..5658434017 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Logging; +using Umbraco.Tests.TestHelpers; using Umbraco.Web.Scheduling; namespace Umbraco.Tests.Scheduling @@ -21,7 +22,7 @@ namespace Umbraco.Tests.Scheduling [OneTimeSetUp] public void InitializeFixture() { - _logger = new DebugDiagnosticsLogger(new MessageTemplates()); + _logger = new ConsoleLogger(new MessageTemplates()); } [Test] @@ -102,12 +103,12 @@ namespace Umbraco.Tests.Scheduling { using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) { - MyTask t; + MyTask t1, t2, t3; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(t1 = new MyTask(5000)); + runner.Add(t2 = new MyTask()); + runner.Add(t3 = new MyTask()); Assert.IsTrue(runner.IsRunning); // is running tasks // shutdown -force => run all queued tasks @@ -115,7 +116,7 @@ namespace Umbraco.Tests.Scheduling Assert.IsTrue(runner.IsRunning); // is running tasks await runner.StoppedAwaitable; // runner stops, within test's timeout - Assert.AreNotEqual(DateTime.MinValue, t.Ended); // t has run + Assert.AreNotEqual(DateTime.MinValue, t3.Ended); // t3 has run } } @@ -124,20 +125,25 @@ namespace Umbraco.Tests.Scheduling { using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) { - MyTask t; + MyTask t1, t2, t3; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(t1 = new MyTask(5000)); + runner.Add(t2 = new MyTask()); + runner.Add(t3 = new MyTask()); Assert.IsTrue(runner.IsRunning); // is running tasks + Thread.Sleep(1000); // since we are forcing shutdown, we need to give it a chance to start, else it will be canceled before the queue is started + // shutdown +force => tries to cancel the current task, ignores queued tasks runner.Shutdown(true, false); // +force -wait Assert.IsTrue(runner.IsRunning); // is running that long task it cannot cancel - await runner.StoppedAwaitable; // runner stops, within test's timeout - Assert.AreEqual(DateTime.MinValue, t.Ended); // t has *not* run + await runner.StoppedAwaitable; // runner stops, within test's timeout (no cancelation token used, no need to catch OperationCanceledException) + + Assert.AreNotEqual(DateTime.MinValue, t1.Ended); // t1 *has* run + Assert.AreEqual(DateTime.MinValue, t2.Ended); // t2 has *not* run + Assert.AreEqual(DateTime.MinValue, t3.Ended); // t3 has *not* run } } @@ -163,7 +169,15 @@ namespace Umbraco.Tests.Scheduling // shutdown +force => tries to cancel the current task, ignores queued tasks runner.Shutdown(true, false); // +force -wait - await runner.StoppedAwaitable; // runner stops, within test's timeout + try + { + await runner.StoppedAwaitable; // runner stops, within test's timeout ... maybe + } + catch (OperationCanceledException) + { + // catch exception, this can occur because we are +force shutting down which will + // cancel a pending task if the queue hasn't completed in time + } } } @@ -183,25 +197,20 @@ namespace Umbraco.Tests.Scheduling runner.Terminated += (sender, args) => { terminated = true; }; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(t = new MyTask()); // sleeps 500 ms ... total = 1500 ms until it's done Assert.IsTrue(runner.IsRunning); // is running the task - runner.Stop(false); // -immediate = -force, -wait + runner.Stop(false); // -immediate = -force, -wait (max 2000 ms delay before +immediate) + await runner.TerminatedAwaitable; + + Assert.IsTrue(stopped); // raised that one Assert.IsTrue(terminating); // has raised that event - Assert.IsFalse(terminated); // but not terminated yet + Assert.IsTrue(terminated); // and that event - // all this before we await because -wait Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner - Assert.IsTrue(runner.IsRunning); // still running the task - - await runner.StoppedAwaitable; // runner stops, within test's timeout - Assert.IsFalse(runner.IsRunning); - Assert.IsTrue(stopped); - - await runner.TerminatedAwaitable; // runner terminates, within test's timeout - Assert.IsTrue(terminated); // has raised that event + Assert.IsFalse(runner.IsRunning); // done running Assert.AreNotEqual(DateTime.MinValue, t.Ended); // t has run } @@ -222,23 +231,21 @@ namespace Umbraco.Tests.Scheduling runner.Terminated += (sender, args) => { terminated = true; }; Assert.IsFalse(runner.IsRunning); // because AutoStart is false - runner.Add(new MyTask(5000)); - runner.Add(new MyTask()); - runner.Add(t = new MyTask()); + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(new MyTask()); // sleeps 500 ms + runner.Add(t = new MyTask()); // sleeps 500 ms ... total = 1500 ms until it's done Assert.IsTrue(runner.IsRunning); // is running the task - runner.Stop(true); // +immediate = +force, +wait + runner.Stop(true); // +immediate = +force, +wait (no delay) + await runner.TerminatedAwaitable; + + Assert.IsTrue(stopped); // raised that one Assert.IsTrue(terminating); // has raised that event Assert.IsTrue(terminated); // and that event - Assert.IsTrue(stopped); // and that one - // and all this before we await because +wait Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner Assert.IsFalse(runner.IsRunning); // done running - await runner.StoppedAwaitable; // runner stops, within test's timeout - await runner.TerminatedAwaitable; // runner terminates, within test's timeout - Assert.AreEqual(DateTime.MinValue, t.Ended); // t has *not* run } } @@ -264,8 +271,7 @@ namespace Umbraco.Tests.Scheduling }, _logger)) { Assert.IsTrue(runner.IsRunning); // because AutoStart is true - runner.Stop(false); // keepalive = must be stopped - await runner.StoppedAwaitable; // runner stops, within test's timeout + await runner.StopInternal(false); // keepalive = must be stopped } } @@ -291,13 +297,19 @@ namespace Umbraco.Tests.Scheduling // dispose will stop it } - await runner.StoppedAwaitable; // runner stops, within test's timeout - //await runner.TerminatedAwaitable; // NO! see note below + try + { + await runner.StoppedAwaitable; + } + catch (OperationCanceledException) + { + // swallow this exception, it can be expected to throw since when disposing we are calling Shutdown +force + // which depending on a timing operation may cancel the cancelation token + } + + Assert.Throws(() => runner.Add(new MyTask())); - // but do NOT await on TerminatedAwaitable - disposing just shuts the runner down - // so that we don't have a runaway task in tests, etc - but it does NOT terminate - // the runner - it really is NOT a nice way to end a runner - it's there for tests } [Test] @@ -564,7 +576,7 @@ namespace Umbraco.Tests.Scheduling Thread.Sleep(1000); Assert.IsTrue(runner.IsRunning); // still waiting for the task to release Assert.IsFalse(task.HasRun); - task.Release(); + task.Release(); // unlatch var runnerTask = runner.CurrentThreadingTask; // may be null if things go fast enough if (runnerTask != null) await runnerTask; // wait for current task to complete @@ -574,7 +586,7 @@ namespace Umbraco.Tests.Scheduling } [Test] - public async Task LatchedTaskStops() + public async Task LatchedTaskStops_Runs_On_Shutdown() { using (var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions(), _logger)) { @@ -584,7 +596,7 @@ namespace Umbraco.Tests.Scheduling Thread.Sleep(5000); Assert.IsTrue(runner.IsRunning); // still waiting for the task to release Assert.IsFalse(task.HasRun); - runner.Shutdown(false, false); + runner.Shutdown(false, false); // -force, -wait await runner.StoppedAwaitable; // wait for the entire runner operation to complete Assert.IsTrue(task.HasRun); } @@ -880,7 +892,9 @@ namespace Umbraco.Tests.Scheduling public override void PerformRun() { + Console.WriteLine($"Sleeping {_milliseconds}..."); Thread.Sleep(_milliseconds); + Console.WriteLine("Wake up!"); } } @@ -997,7 +1011,9 @@ namespace Umbraco.Tests.Scheduling public DateTime Ended { get; set; } public virtual void Dispose() - { } + { + + } } } } diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index d1f77d4ae0..eb4a01d06b 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -1,17 +1,10 @@ using System; -using System.Collections; -using System.Runtime.Remoting.Messaging; -using System.Threading; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Persistence; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using CallContext = Umbraco.Core.Scoping.CallContext; - -//using CallContext = Umbraco.Core.Scoping.CallContext; - namespace Umbraco.Tests.Scoping { @@ -126,8 +119,8 @@ namespace Umbraco.Tests.Scoping Assert.AreSame(scope, ((Scope) nested).ParentScope); // it's moved over to call context - var callContextKey = CallContext.GetData(ScopeProvider.ScopeItemKey).AsGuid(); - Assert.AreNotEqual(Guid.Empty, callContextKey); + var callContextScope = CallContext.GetData(ScopeProvider.ScopeItemKey); + Assert.IsNotNull(callContextScope); // only if Core.DEBUG_SCOPES are defined //var ccnested = scopeProvider.CallContextObjects[callContextKey]; diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 366f7ce64a..4fa9601257 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -14,11 +14,11 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Core.Sync; +using Umbraco.Tests.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Tests.Testing.Objects.Accessors; @@ -105,7 +105,8 @@ namespace Umbraco.Tests.Scoping Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(ShortStringHelper) }), typeFinder, - hostingEnvironment); + hostingEnvironment, + new MockShortStringHelper()); } protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) diff --git a/src/Umbraco.Tests/Services/ContentServicePublishBranchTests.cs b/src/Umbraco.Tests/Services/ContentServicePublishBranchTests.cs index 3a3f8f369e..d856f3bd82 100644 --- a/src/Umbraco.Tests/Services/ContentServicePublishBranchTests.cs +++ b/src/Umbraco.Tests/Services/ContentServicePublishBranchTests.cs @@ -200,7 +200,7 @@ namespace Umbraco.Tests.Services //update the child iv1.SetValue("vp", "UPDATED-iv1.de", "de"); - ServiceContext.ContentService.Save(iv1); + var saveResult = ServiceContext.ContentService.Save(iv1); var r = ServiceContext.ContentService.SaveAndPublishBranch(vRoot, false, "de").ToArray(); Assert.AreEqual(PublishResultType.SuccessPublishAlready, r[0].Result); @@ -344,7 +344,7 @@ namespace Umbraco.Tests.Services ServiceContext.ContentService.Save(iv11); iv11.SetCultureName("iv11.ru", "ru"); - ServiceContext.ContentService.SaveAndPublish(iv11, new []{"de", "ru"}); + var xxx = ServiceContext.ContentService.SaveAndPublish(iv11, new []{"de", "ru"}); Assert.AreEqual("iv11.de", iv11.GetValue("vp", "de", published: true)); Assert.AreEqual("iv11.ru", iv11.GetValue("vp", "ru", published: true)); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 0f66b6e273..0f1b1fed32 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -15,10 +15,10 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Core.Sync; +using Umbraco.Tests.Strings; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; using Umbraco.Web.PublishedCache; @@ -78,7 +78,8 @@ namespace Umbraco.Tests.Services Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(ShortStringHelper) }), typeFinder, - hostingEnvironment); + hostingEnvironment, + new MockShortStringHelper()); } public class LocalServerMessenger : ServerMessengerBase diff --git a/src/Umbraco.Tests/Services/MediaServiceTests.cs b/src/Umbraco.Tests/Services/MediaServiceTests.cs index 17711fbd31..b3dc274c5e 100644 --- a/src/Umbraco.Tests/Services/MediaServiceTests.cs +++ b/src/Umbraco.Tests/Services/MediaServiceTests.cs @@ -184,7 +184,7 @@ namespace Umbraco.Tests.Services public void Can_Get_Media_With_Crop_By_Path() { var mediaService = ServiceContext.MediaService; - var mediaType = MockedContentTypes.CreateImageMediaType("Image2"); + var mediaType = MockedContentTypes.CreateImageMediaTypeWithCrop("Image2"); ServiceContext.MediaTypeService.Save(mediaType); var media = MockedMedia.CreateMediaImageWithCrop(mediaType, -1); diff --git a/src/Umbraco.Tests/Strings/MockShortStringHelper.cs b/src/Umbraco.Tests/Strings/MockShortStringHelper.cs index a39f962908..1cc959414d 100644 --- a/src/Umbraco.Tests/Strings/MockShortStringHelper.cs +++ b/src/Umbraco.Tests/Strings/MockShortStringHelper.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Umbraco.Core.Strings; +using Umbraco.Core.Strings; namespace Umbraco.Tests.Strings { diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index 694101b684..ef8ababf6e 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -15,18 +15,7 @@ namespace Umbraco.Tests.Strings [TestFixture] public class StringExtensionsTests : UmbracoTestBase { - protected override void Compose() - { - base.Compose(); - Composition.RegisterUnique(_ => new MockShortStringHelper()); - } - - [Test] - public void CurrentHelper() - { - var helper = Current.ShortStringHelper; - Assert.IsInstanceOf(helper); - } + private readonly IShortStringHelper _mockShortStringHelper = new MockShortStringHelper(); [TestCase("hello-world.png", "Hello World")] [TestCase("hello-world .png", "Hello World")] @@ -227,77 +216,77 @@ namespace Umbraco.Tests.Strings [Test] public void ToUrlAlias() { - var output = "JUST-ANYTHING".ToUrlSegment(ShortStringHelper); + var output = "JUST-ANYTHING".ToUrlSegment(_mockShortStringHelper); Assert.AreEqual("URL-SEGMENT::JUST-ANYTHING", output); } [Test] public void FormatUrl() { - var output = "JUST-ANYTHING".ToUrlSegment(ShortStringHelper); + var output = "JUST-ANYTHING".ToUrlSegment(_mockShortStringHelper); Assert.AreEqual("URL-SEGMENT::JUST-ANYTHING", output); } [Test] public void ToUmbracoAlias() { - var output = "JUST-ANYTHING".ToSafeAlias(ShortStringHelper); + var output = "JUST-ANYTHING".ToSafeAlias(_mockShortStringHelper); Assert.AreEqual("SAFE-ALIAS::JUST-ANYTHING", output); } [Test] public void ToSafeAlias() { - var output = "JUST-ANYTHING".ToSafeAlias(ShortStringHelper); + var output = "JUST-ANYTHING".ToSafeAlias(_mockShortStringHelper); Assert.AreEqual("SAFE-ALIAS::JUST-ANYTHING", output); } [Test] public void ToSafeAliasWithCulture() { - var output = "JUST-ANYTHING".ToSafeAlias(ShortStringHelper, (string)null); + var output = "JUST-ANYTHING".ToSafeAlias(_mockShortStringHelper, (string)null); Assert.AreEqual("SAFE-ALIAS-CULTURE::JUST-ANYTHING", output); } [Test] public void ToUrlSegment() { - var output = "JUST-ANYTHING".ToUrlSegment(ShortStringHelper); + var output = "JUST-ANYTHING".ToUrlSegment(_mockShortStringHelper); Assert.AreEqual("URL-SEGMENT::JUST-ANYTHING", output); } [Test] public void ToUrlSegmentWithCulture() { - var output = "JUST-ANYTHING".ToUrlSegment(ShortStringHelper, (string)null); + var output = "JUST-ANYTHING".ToUrlSegment(_mockShortStringHelper, (string)null); Assert.AreEqual("URL-SEGMENT-CULTURE::JUST-ANYTHING", output); } [Test] public void ToSafeFileName() { - var output = "JUST-ANYTHING".ToSafeFileName(ShortStringHelper); + var output = "JUST-ANYTHING".ToSafeFileName(_mockShortStringHelper); Assert.AreEqual("SAFE-FILE-NAME::JUST-ANYTHING", output); } [Test] public void ToSafeFileNameWithCulture() { - var output = "JUST-ANYTHING".ToSafeFileName(ShortStringHelper, null); + var output = "JUST-ANYTHING".ToSafeFileName(_mockShortStringHelper, null); Assert.AreEqual("SAFE-FILE-NAME-CULTURE::JUST-ANYTHING", output); } [Test] public void ConvertCase() { - var output = "JUST-ANYTHING".ToCleanString(ShortStringHelper, CleanStringType.Unchanged); + var output = "JUST-ANYTHING".ToCleanString(_mockShortStringHelper, CleanStringType.Unchanged); Assert.AreEqual("CLEAN-STRING-A::JUST-ANYTHING", output); } [Test] public void SplitPascalCasing() { - var output = "JUST-ANYTHING".SplitPascalCasing(ShortStringHelper); + var output = "JUST-ANYTHING".SplitPascalCasing(_mockShortStringHelper); Assert.AreEqual("SPLIT-PASCAL-CASING::JUST-ANYTHING", output); } diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index fdc2e6a5bd..cf6438b673 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -23,6 +23,7 @@ using Umbraco.Web.WebApi; using Umbraco.Core.Logging; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Security.Providers; +using Umbraco.Tests.Strings; namespace Umbraco.Tests.TestHelpers.ControllerTesting { @@ -152,7 +153,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/hello/world/1234")); - var membershipHelper = new MembershipHelper(umbCtx.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of()); + var membershipHelper = new MembershipHelper(umbCtx.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of(), new MockShortStringHelper()); var umbHelper = new UmbracoHelper(Mock.Of(), Mock.Of(), diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index 35340940df..8bc1453e01 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -1,7 +1,9 @@ using System; using System.Linq; +using Moq; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Core.Strings; namespace Umbraco.Tests.TestHelpers.Entities { @@ -420,10 +422,39 @@ namespace Umbraco.Tests.TestHelpers.Entities var contentCollection = new PropertyTypeCollection(false); contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.UploadField, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.File, Name = "File", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); + contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + + mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); + + //ensure that nothing is marked as dirty + mediaType.ResetDirtyProperties(false); + + return mediaType; + } + + public static MediaType CreateImageMediaTypeWithCrop(string alias = Constants.Conventions.MediaTypes.Image) + { + var mediaType = new MediaType(TestHelper.ShortStringHelper, -1) + { + Alias = alias, + Name = "Image", + Description = "ContentType used for images", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + CreatorId = 0, + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(false); + contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.ImageCropper, ValueStorageType.Ntext) { Alias = Constants.Conventions.Media.File, Name = "File", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = 1043 }); + contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); + contentCollection.Add(new PropertyType(TestHelper.ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs index e7b3f68c85..41ea230e7d 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs @@ -67,6 +67,8 @@ namespace Umbraco.Tests.TestHelpers.Entities media.SetValue(Constants.Conventions.Media.Bytes, "100"); media.SetValue(Constants.Conventions.Media.Extension, "png"); + + return media; } diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index 058b274fc5..56fdc96aa7 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -48,6 +48,11 @@ namespace Umbraco.Tests.TestHelpers var logging = new Mock(); var routing = new Mock(); + var userPasswordConfig = new Mock(); + var memberPasswordConfig = new Mock(); + security.Setup(x => x.UserPasswordConfiguration).Returns(userPasswordConfig.Object); + security.Setup(x => x.MemberPasswordConfiguration).Returns(memberPasswordConfig.Object); + settings.Setup(x => x.Content).Returns(content.Object); settings.Setup(x => x.Security).Returns(security.Object); settings.Setup(x => x.RequestHandler).Returns(requestHandler.Object); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestUserPasswordConfig.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestUserPasswordConfig.cs new file mode 100644 index 0000000000..ac89c1e2b5 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestUserPasswordConfig.cs @@ -0,0 +1,23 @@ +using Umbraco.Core.Configuration; + +namespace Umbraco.Tests.TestHelpers.Stubs +{ + internal class TestUserPasswordConfig : IUserPasswordConfiguration + { + public int RequiredLength => 12; + + public bool RequireNonLetterOrDigit => false; + + public bool RequireDigit => false; + + public bool RequireLowercase => false; + + public bool RequireUppercase => false; + + public bool UseLegacyEncoding => false; + + public string HashAlgorithmType => "HMACSHA256"; + + public int MaxFailedAccessAttemptsBeforeLockout => 5; + } +} diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index d919ccfc6a..2064051a11 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -96,7 +96,7 @@ namespace Umbraco.Tests.TestHelpers public static IIOHelper IOHelper { get; } = new IOHelper(GetHostingEnvironment()); - + public static IMainDom MainDom { get; } = new MainDom(Mock.Of(), GetHostingEnvironment()); /// /// Maps the given making it rooted on . must start with ~/ /// diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 1b1d3172d3..52e86b36db 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -71,7 +71,7 @@ namespace Umbraco.Tests.Testing.TestingTests Mock.Of(), Mock.Of(), Mock.Of(), - new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of())); + new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of(), ShortStringHelper)); Assert.Pass(); } @@ -103,7 +103,7 @@ namespace Umbraco.Tests.Testing.TestingTests var memberService = Mock.Of(); var memberTypeService = Mock.Of(); var membershipProvider = new MembersMembershipProvider(memberService, memberTypeService, Mock.Of(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver()); - var membershipHelper = new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), membershipProvider, Mock.Of(), memberService, memberTypeService, Mock.Of(), AppCaches.Disabled, logger); + var membershipHelper = new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), membershipProvider, Mock.Of(), memberService, memberTypeService, Mock.Of(), AppCaches.Disabled, logger, ShortStringHelper); var umbracoHelper = new UmbracoHelper(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), membershipHelper); var umbracoMapper = new UmbracoMapper(new MapDefinitionCollection(new[] { Mock.Of() })); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index ab8a6d1ac2..0ea43742da 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -67,7 +67,6 @@ namespace Umbraco.Tests.Testing /// provides all the necessary environment, through DI. Yes, DI is bad in tests - unit tests. /// But it is OK in integration tests. /// - [Apartment(ApartmentState.STA)] public abstract class UmbracoTestBase { // this class diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 82a2bc5dab..6ad9ca5f78 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -168,6 +168,7 @@ + diff --git a/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs index 1e0e592252..b03c581657 100644 --- a/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs @@ -16,7 +16,6 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Web.Composing; using Umbraco.Core.Configuration; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Persistence; @@ -26,6 +25,7 @@ using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.ControllerTesting; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web; using Umbraco.Web.Editors; @@ -76,7 +76,7 @@ namespace Umbraco.Tests.Web.Controllers } IOHelper.ForceNotHosted = true; var usersController = new AuthenticationController( - new DefaultPasswordConfig(), + new TestUserPasswordConfig(), Factory.GetInstance(), umbracoContextAccessor, Factory.GetInstance(), diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs index 737e0fdcb1..e8f1c1f986 100644 --- a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; @@ -8,32 +10,32 @@ using Moq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Logging; using Umbraco.Web.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.ControllerTesting; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; using Umbraco.Web; +using Umbraco.Web.Actions; using Umbraco.Web.Editors; using Umbraco.Web.Models.ContentEditing; -using Task = System.Threading.Tasks.Task; -using Umbraco.Core.Dictionary; using Umbraco.Web.PropertyEditors; -using System; -using Umbraco.Web.WebApi; using Umbraco.Web.Trees; -using System.Globalization; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Web.Actions; +using Umbraco.Web.WebApi; +using Task = System.Threading.Tasks.Task; namespace Umbraco.Tests.Web.Controllers { diff --git a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs index e36d2bdb12..25cb767eaf 100644 --- a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs @@ -24,6 +24,7 @@ using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.ControllerTesting; using Umbraco.Tests.TestHelpers.Entities; @@ -87,8 +88,7 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), helper, Factory.GetInstance(), - ShortStringHelper - ); + ShortStringHelper); return usersController; } diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 4299b57e07..0660564a52 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -124,7 +124,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(), Mock.Of(), Mock.Of(query => query.Content(2) == content.Object), - new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of())); + new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), AppCaches.Disabled, Mock.Of(), ShortStringHelper)); var ctrl = new TestSurfaceController(umbracoContextAccessor, helper); var result = ctrl.GetContent(2) as PublishedContentResult; diff --git a/src/Umbraco.Web.UI.Client/gulp/config.js b/src/Umbraco.Web.UI.Client/gulp/config.js index ee63d0085e..59e8bf6c05 100755 --- a/src/Umbraco.Web.UI.Client/gulp/config.js +++ b/src/Umbraco.Web.UI.Client/gulp/config.js @@ -1,6 +1,14 @@ 'use strict'; module.exports = { + compile: { + build: { + sourcemaps: false + }, + dev: { + sourcemaps: true + } + }, sources: { // less files used by backoffice and preview diff --git a/src/Umbraco.Web.UI.Client/gulp/modes.js b/src/Umbraco.Web.UI.Client/gulp/modes.js new file mode 100644 index 0000000000..dc2947f2cc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/gulp/modes.js @@ -0,0 +1,13 @@ +'use strict'; + +var config = require('./config'); +var gulp = require('gulp'); + +function setDevelopmentMode(cb) { + + config.compile.current = config.compile.dev; + + return cb(); +}; + +module.exports = { setDevelopmentMode: setDevelopmentMode }; diff --git a/src/Umbraco.Web.UI.Client/gulp/util/processLess.js b/src/Umbraco.Web.UI.Client/gulp/util/processLess.js index 94150043c1..e33fc0389b 100644 --- a/src/Umbraco.Web.UI.Client/gulp/util/processLess.js +++ b/src/Umbraco.Web.UI.Client/gulp/util/processLess.js @@ -6,25 +6,36 @@ var postcss = require('gulp-postcss'); var less = require('gulp-less'); var autoprefixer = require('autoprefixer'); var cssnano = require('cssnano'); -var cleanCss = require("gulp-clean-css"); +var cleanCss = require('gulp-clean-css'); var rename = require('gulp-rename'); +var sourcemaps = require('gulp-sourcemaps'); module.exports = function(files, out) { - + var processors = [ autoprefixer, cssnano({zindex: false}) ]; - + console.log("LESS: ", files, " -> ", config.root + config.targets.css + out) - - var task = gulp.src(files) - .pipe(less()) - .pipe(cleanCss()) - .pipe(postcss(processors)) - .pipe(rename(out)) - .pipe(gulp.dest(config.root + config.targets.css)); - + + var task = gulp.src(files); + + if(config.compile.current.sourcemaps === true) { + task = task.pipe(sourcemaps.init()); + } + + task = task.pipe(less()); + task = task.pipe(cleanCss()); + task = task.pipe(postcss(processors)); + task = task.pipe(rename(out)); + + if(config.compile.current.sourcemaps === true) { + task = task.pipe(sourcemaps.write('./maps')); + } + + task = task.pipe(gulp.dest(config.root + config.targets.css)); + return task; - + }; diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index 1e4dc591ca..705c54bf04 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -12,6 +12,8 @@ const { src, dest, series, parallel, lastRun } = require('gulp'); +const config = require('./gulp/config'); +const { setDevelopmentMode } = require('./gulp/modes'); const { dependencies } = require('./gulp/tasks/dependencies'); const { js } = require('./gulp/tasks/js'); const { less } = require('./gulp/tasks/less'); @@ -19,25 +21,14 @@ const { testE2e, testUnit } = require('./gulp/tasks/test'); const { views } = require('./gulp/tasks/views'); const { watchTask } = require('./gulp/tasks/watchTask'); -// Load local overwrites, can be used to overwrite paths in your local setup. -var fs = require('fs'); -var onlyScripts = require('./gulp/util/scriptFilter'); -try { - if (fs.existsSync('./gulp/overwrites/')) { - var overwrites = fs.readdirSync('./gulp/overwrites/').filter(onlyScripts); - overwrites.forEach(function(overwrite) { - require('./gulp/overwrites/' + overwrite); - }); - } -} catch (err) { - console.error(err) - } +// set default current compile mode: +config.compile.current = config.compile.build; // *********************************************************** // These Exports are the new way of defining Tasks in Gulp 4.x // *********************************************************** exports.build = series(parallel(dependencies, js, less, views), testUnit); -exports.dev = series(parallel(dependencies, js, less, views), watchTask); +exports.dev = series(setDevelopmentMode, parallel(dependencies, js, less, views), watchTask); exports.watch = series(watchTask); // exports.runTests = series(js, testUnit); diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 2652368819..4b3afbad18 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -862,6 +862,43 @@ } } }, + "@gulp-sourcemaps/identity-map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", + "integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==", + "dev": true, + "requires": { + "acorn": "^5.0.3", + "css": "^2.2.1", + "normalize-path": "^2.1.1", + "source-map": "^0.6.0", + "through2": "^2.0.3" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", + "dev": true, + "requires": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + } + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -3136,6 +3173,26 @@ "which": "^1.2.9" } }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -3358,6 +3415,34 @@ "ms": "^2.1.1" } }, + "debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, + "requires": { + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + } + } + }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -3611,6 +3696,12 @@ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, "di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -6601,6 +6692,39 @@ "through2": "^2.0.1" } }, + "gulp-sourcemaps": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", + "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", + "dev": true, + "requires": { + "@gulp-sourcemaps/identity-map": "1.X", + "@gulp-sourcemaps/map-sources": "1.X", + "acorn": "5.X", + "convert-source-map": "1.X", + "css": "2.X", + "debug-fabulous": "1.X", + "detect-newline": "2.X", + "graceful-fs": "4.X", + "source-map": "~0.6.0", + "strip-bom-string": "1.X", + "through2": "2.X" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "gulp-util": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", @@ -14750,6 +14874,12 @@ "strip-bom": "^2.0.0" } }, + "strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "dev": true + }, "strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 44ecb4026f..0f02aba5e2 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -38,7 +38,7 @@ "lazyload-js": "1.0.0", "moment": "2.22.2", "ng-file-upload": "12.2.13", - "nouislider": "14.0.2", + "nouislider": "14.1.1", "npm": "6.12.0", "signalr": "2.4.0", "spectrum-colorpicker": "1.8.0", @@ -66,6 +66,7 @@ "gulp-postcss": "8.0.0", "gulp-rename": "1.4.0", "gulp-sort": "2.0.0", + "gulp-sourcemaps": "^2.6.5", "gulp-watch": "5.0.1", "gulp-wrap": "0.15.0", "gulp-wrap-js": "0.4.1", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js index b106f35efb..9117ab548c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js @@ -76,7 +76,6 @@ // Check if it is a new user const inviteVal = $location.search().invite; - vm.baseTitle = $scope.$root.locationTitle; //1 = enter password, 2 = password set, 3 = invalid token if (inviteVal && (inviteVal === "1" || inviteVal === "2")) { @@ -457,9 +456,7 @@ break; } - if (title != null) { - $scope.$root.locationTitle = title + " - " + vm.baseTitle; - } + $scope.$emit("$changeTitle", title); } } 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 6559e16206..431a05778c 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 @@ -224,7 +224,8 @@ Use this directive to construct a header inside the main editor window. if (editorState.current) { //to do make work for user create/edit // to do make it work for user group create/ edit - // to make it work for language edit/create + // to do make it work for language edit/create + // to do make it work for log viewer scope.isNew = editorState.current.id === 0 || editorState.current.id === "0" || editorState.current.id === -1 || diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautoresize.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautoresize.directive.js index 56dfb6b180..69ec1be805 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautoresize.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbautoresize.directive.js @@ -138,7 +138,11 @@ angular.module("umbraco.directives") var unbindModelWatcher = scope.$watch(function() { return ngModelController.$modelValue; }, function(newValue) { - update(true); + $timeout( + function() { + update(true); + } + ); }); scope.$on('$destroy', function() { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js index eb3c134f0b..ff51b1ae90 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js @@ -80,7 +80,8 @@ disabled: "<", required: "<", onChange: "&?", - cssClass: "@?" + cssClass: "@?", + iconClass: "@?" } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/member/umbmembernodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/member/umbmembernodeinfo.directive.js new file mode 100644 index 0000000000..3b6a2c069a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/member/umbmembernodeinfo.directive.js @@ -0,0 +1,73 @@ +(function () { + 'use strict'; + + function MemberNodeInfoDirective($timeout, $location, eventsService, userService, dateHelper, editorService) { + + function link(scope, element, attrs, ctrl) { + + var evts = []; + + //TODO: Infinite editing is not working yet. + scope.allowChangeMemberType = false; + + function onInit() { + // make sure dates are formatted to the user's locale + formatDatesToLocal(); + } + + function formatDatesToLocal() { + // get current backoffice user and format dates + userService.getCurrentUser().then(function (currentUser) { + scope.node.createDateFormatted = dateHelper.getLocalDate(scope.node.createDate, currentUser.locale, 'LLL'); + scope.node.updateDateFormatted = dateHelper.getLocalDate(scope.node.updateDate, currentUser.locale, 'LLL'); + }); + } + + scope.openMemberType = function (memberType) { + var editor = { + id: memberType.id, + submit: function (model) { + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.memberTypeEditor(editor); + }; + + // watch for content updates - reload content when node is saved, published etc. + scope.$watch('node.updateDate', function (newValue, oldValue) { + if (!newValue) { return; } + if (newValue === oldValue) { return; } + + // Update the create and update dates + formatDatesToLocal(); + }); + + //ensure to unregister from all events! + scope.$on('$destroy', function () { + for (var e in evts) { + eventsService.unsubscribe(evts[e]); + } + }); + + onInit(); + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/member/umb-member-node-info.html', + scope: { + node: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbMemberNodeInfo', MemberNodeInfoDirective); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js index 31e797c6b4..9c33b35e82 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js @@ -16,9 +16,6 @@ angular.module("umbraco.directives") replace: true, templateUrl: 'views/components/property/umb-property.html', link: function (scope) { - - scope.propertyActions = []; - userService.getCurrentUser().then(function (u) { var isAdmin = u.userGroups.indexOf('admin') !== -1; scope.propertyAlias = (Umbraco.Sys.ServerVariables.isDebuggingEnabled === true || isAdmin) ? scope.property.alias : null; @@ -36,6 +33,7 @@ angular.module("umbraco.directives") $scope.property.propertyErrorMessage = errorMsg; }; + $scope.propertyActions = []; self.setPropertyActions = function(actions) { $scope.propertyActions = actions; }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js index 32cbbb31ec..5eac7e5e24 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js @@ -15,7 +15,7 @@ function umbPropEditor(umbPropEditorHelper) { preview: "<" }, - require: "^^form", + require: ["^^form", "?^umbProperty"], restrict: 'E', replace: true, templateUrl: 'views/components/property/umb-property-editor.html', @@ -24,7 +24,10 @@ function umbPropEditor(umbPropEditorHelper) { //we need to copy the form controller val to our isolated scope so that //it get's carried down to the child scopes of this! //we'll also maintain the current form name. - scope[ctrl.$name] = ctrl; + scope[ctrl[0].$name] = ctrl[0]; + + // We will capture a reference to umbProperty in this Directive and pass it on to the Scope, so Property-Editor controllers can use it. + scope["umbProperty"] = ctrl[1]; if(!scope.model.alias){ scope.model.alias = Math.random().toString(36).slice(2); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js index 975b10d678..0a6eeb8835 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js @@ -90,7 +90,9 @@ angular.module("umbraco.directives") css.push("umb-tree-item--deleted"); } - if (actionNode) { + // checking the nodeType to ensure that this node and actionNode is from the same treeAlias + if (actionNode && actionNode.nodeType === node.nodeType) { + if (actionNode.id === node.id && String(node.id) !== "-1") { css.push("active"); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcheckmark.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcheckmark.directive.js index b0899f0f8b..d1fefeae5a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcheckmark.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcheckmark.directive.js @@ -10,7 +10,8 @@ templateUrl: 'views/components/umb-checkmark.html', scope: { size: "@?", - checked: "=" + checked: "=", + readonly: "@?" } }; 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 3c9e300f92..a9b9cc52b1 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 @@ -554,6 +554,7 @@ property.validation.patternMessage = propertyModel.validation.patternMessage; property.showOnMemberProfile = propertyModel.showOnMemberProfile; property.memberCanEdit = propertyModel.memberCanEdit; + property.isSensitiveData = propertyModel.isSensitiveData; property.isSensitiveValue = propertyModel.isSensitiveValue; property.allowCultureVariant = propertyModel.allowCultureVariant; 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 3c64401933..8b922d7ec8 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 @@ -359,6 +359,7 @@ When building a custom infinite editor view you can use the same components as a * * @param {Object} editor rendering options * @param {Boolean} editor.multiPicker Pick one or multiple items + * @param {Int} editor.startNodeId Set the startnode of the picker (optional) * @param {Function} editor.submit Callback function when the submit button is clicked. Returns the editor model object * @param {Function} editor.close Callback function when the close button is clicked. * @@ -564,6 +565,7 @@ When building a custom infinite editor view you can use the same components as a * @description * Opens a media picker in infinite editing, the submit callback returns an array of selected media items * @param {Object} editor rendering options + * @param {Int} editor.startNodeId Set the startnode of the picker (optional) * @param {Boolean} editor.multiPicker Pick one or multiple items * @param {Boolean} editor.onlyImages Only display files that have an image file-extension * @param {Boolean} editor.disableFolderSelect Disable folder selection @@ -608,8 +610,11 @@ When building a custom infinite editor view you can use the same components as a * @description * Opens the document type editor in infinite editing, the submit callback returns the alias of the saved document type. * @param {Object} editor rendering options - * @param {Callback} editor.id Indicates the ID of the document type to be edited. Alternatively the ID may be set to `-1` in combination with `create` being set to `true` to open the document type editor for creating a new document type. - * @param {Callback} editor.create Set to `true` to open the document type editor for creating a new document type. + * @param {Number} editor.id Indicates the ID of the document type to be edited. Alternatively the ID may be set to `-1` in combination with `create` being set to `true` to open the document type editor for creating a new document type. + * @param {Boolean} editor.create Set to `true` to open the document type editor for creating a new document type. + * @param {Boolean} editor.noTemplate If `true` and in combination with `create` being set to `true`, the document type editor will not create a corresponding template by default. This is similar to selecting the "Document Type without a template" in the Create dialog. + * @param {Boolean} editor.isElement If `true` and in combination with `create` being set to `true`, the "Is an Element type" option will be selected by default in the document type editor. + * @param {Boolean} editor.allowVaryByCulture If `true` and in combination with `create`, the "Allow varying by culture" option will be selected by default in the document type editor. * @param {Callback} editor.submit Submits the editor. * @param {Callback} editor.close Closes the editor. * @returns {Object} editor object @@ -636,6 +641,23 @@ When building a custom infinite editor view you can use the same components as a open(editor); } + /** + * @ngdoc method + * @name umbraco.services.editorService#memberTypeEditor + * @methodOf umbraco.services.editorService + * + * @description + * Opens the member type editor in infinite editing, the submit callback returns the saved member type + * @param {Object} editor rendering options + * @param {Callback} editor.submit Submits the editor + * @param {Callback} editor.close Closes the editor + * @returns {Object} editor object + */ + function memberTypeEditor(editor) { + editor.view = "views/membertypes/edit.html"; + open(editor); + } + /** * @ngdoc method * @name umbraco.services.editorService#queryBuilder diff --git a/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js b/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js index 8fe6761c94..41614a3bee 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js @@ -16,7 +16,7 @@ function fileManager($rootScope) { var mgr = { /** * @ngdoc function - * @name umbraco.services.fileManager#addFiles + * @name umbraco.services.fileManager#setFiles * @methodOf umbraco.services.fileManager * @function * diff --git a/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js b/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js index f66b0b0ed0..31375c5c56 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js @@ -1,323 +1,325 @@ -// This service was based on OpenJS library available in BSD License -// http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php - -function keyboardService($window, $timeout) { - - var keyboardManagerService = {}; - - var defaultOpt = { - 'type': 'keydown', - 'propagate': false, - 'inputDisabled': false, - 'target': $window.document, - 'keyCode': false - }; - - // Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken - var shift_nums = { - "`": "~", - "1": "!", - "2": "@", - "3": "#", - "4": "$", - "5": "%", - "6": "^", - "7": "&", - "8": "*", - "9": "(", - "0": ")", - "-": "_", - "=": "+", - ";": ":", - "'": "\"", - ",": "<", - ".": ">", - "/": "?", - "\\": "|" - }; - - // Special Keys - and their codes - var special_keys = { - 'esc': 27, - 'escape': 27, - 'tab': 9, - 'space': 32, - 'return': 13, - 'enter': 13, - 'backspace': 8, - - 'scrolllock': 145, - 'scroll_lock': 145, - 'scroll': 145, - 'capslock': 20, - 'caps_lock': 20, - 'caps': 20, - 'numlock': 144, - 'num_lock': 144, - 'num': 144, - - 'pause': 19, - 'break': 19, - - 'insert': 45, - 'home': 36, - 'delete': 46, - 'end': 35, - - 'pageup': 33, - 'page_up': 33, - 'pu': 33, - - 'pagedown': 34, - 'page_down': 34, - 'pd': 34, - - 'left': 37, - 'up': 38, - 'right': 39, - 'down': 40, - - 'f1': 112, - 'f2': 113, - 'f3': 114, - 'f4': 115, - 'f5': 116, - 'f6': 117, - 'f7': 118, - 'f8': 119, - 'f9': 120, - 'f10': 121, - 'f11': 122, - 'f12': 123 - }; - - var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0; - - // The event handler for bound element events - function eventHandler(e) { - e = e || $window.event; - - var code, k; - - // Find out which key is pressed - if (e.keyCode) - { - code = e.keyCode; - } - else if (e.which) { - code = e.which; - } - - var character = String.fromCharCode(code).toLowerCase(); - - if (code === 188){character = ",";} // If the user presses , when the type is onkeydown - if (code === 190){character = ".";} // If the user presses , when the type is onkeydown - - var propagate = true; - - //Now we need to determine which shortcut this event is for, we'll do this by iterating over each - //registered shortcut to find the match. We use Find here so that the loop exits as soon - //as we've found the one we're looking for - _.find(_.keys(keyboardManagerService.keyboardEvent), function(key) { - - var shortcutLabel = key; - var shortcutVal = keyboardManagerService.keyboardEvent[key]; - - // Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked - var kp = 0; - - // Some modifiers key - var modifiers = { - shift: { - wanted: false, - pressed: e.shiftKey ? true : false - }, - ctrl: { - wanted: false, - pressed: e.ctrlKey ? true : false - }, - alt: { - wanted: false, - pressed: e.altKey ? true : false - }, - meta: { //Meta is Mac specific - wanted: false, - pressed: e.metaKey ? true : false - } - }; - - var keys = shortcutLabel.split("+"); - var opt = shortcutVal.opt; - var callback = shortcutVal.callback; - - // Foreach keys in label (split on +) - var l = keys.length; - for (var i = 0; i < l; i++) { - - var k = keys[i]; - switch (k) { - case 'ctrl': - case 'control': - kp++; - modifiers.ctrl.wanted = true; - break; - case 'shift': - case 'alt': - case 'meta': - kp++; - modifiers[k].wanted = true; - break; - } - - if (k.length > 1) { // If it is a special key - if (special_keys[k] === code) { - kp++; - } - } - else if (opt['keyCode']) { // If a specific key is set into the config - if (opt['keyCode'] === code) { - kp++; - } - } - else { // The special keys did not match - if (character === k) { - kp++; - } - else { - if (shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase - character = shift_nums[character]; - if (character === k) { - kp++; - } - } - } - } - - } //for end - - if (kp === keys.length && - modifiers.ctrl.pressed === modifiers.ctrl.wanted && - modifiers.shift.pressed === modifiers.shift.wanted && - modifiers.alt.pressed === modifiers.alt.wanted && - modifiers.meta.pressed === modifiers.meta.wanted) { - - //found the right callback! - - // Disable event handler when focus input and textarea - if (opt['inputDisabled']) { - var elt; - if (e.target) { - elt = e.target; - } else if (e.srcElement) { - elt = e.srcElement; - } - - if (elt.nodeType === 3) { elt = elt.parentNode; } - if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA') { - //This exits the Find loop - return true; - } - } - - $timeout(function () { - callback(e); - }, 1); - - if (!opt['propagate']) { // Stop the event - propagate = false; - } - - //This exits the Find loop - return true; - } - - //we haven't found one so continue looking - return false; - - }); - - // Stop the event if required - if (!propagate) { - // e.cancelBubble is supported by IE - this will kill the bubbling process. - e.cancelBubble = true; - e.returnValue = false; - - // e.stopPropagation works in Firefox. - if (e.stopPropagation) { - e.stopPropagation(); - e.preventDefault(); - } - return false; - } - } - - // Store all keyboard combination shortcuts - keyboardManagerService.keyboardEvent = {}; - - // Add a new keyboard combination shortcut - keyboardManagerService.bind = function (label, callback, opt) { - - //replace ctrl key with meta key - if(isMac && label !== "ctrl+space"){ - label = label.replace("ctrl","meta"); - } - - var elt; - // Initialize opt object - opt = angular.extend({}, defaultOpt, opt); - label = label.toLowerCase(); - elt = opt.target; - if(typeof opt.target === 'string'){ - elt = document.getElementById(opt.target); - } - - //Ensure we aren't double binding to the same element + type otherwise we'll end up multi-binding - // and raising events for now reason. So here we'll check if the event is already registered for the element - var boundValues = _.values(keyboardManagerService.keyboardEvent); - var found = _.find(boundValues, function (i) { - return i.target === elt && i.event === opt['type']; - }); - - // Store shortcut - keyboardManagerService.keyboardEvent[label] = { - 'callback': callback, - 'target': elt, - 'opt': opt - }; - - if (!found) { - //Attach the function with the event - if (elt.addEventListener) { - elt.addEventListener(opt['type'], eventHandler, false); - } else if (elt.attachEvent) { - elt.attachEvent('on' + opt['type'], eventHandler); - } else { - elt['on' + opt['type']] = eventHandler; - } - } - - }; - // Remove the shortcut - just specify the shortcut and I will remove the binding - keyboardManagerService.unbind = function (label) { - label = label.toLowerCase(); - var binding = keyboardManagerService.keyboardEvent[label]; - delete(keyboardManagerService.keyboardEvent[label]); - - if(!binding){return;} - - var type = binding['event'], - elt = binding['target'], - callback = binding['callback']; - - if(elt.detachEvent){ - elt.detachEvent('on' + type, callback); - }else if(elt.removeEventListener){ - elt.removeEventListener(type, callback, false); - }else{ - elt['on'+type] = false; - } - }; - // - - return keyboardManagerService; -} angular.module('umbraco.services').factory('keyboardService', ['$window', '$timeout', keyboardService]); \ No newline at end of file +// This service was based on OpenJS library available in BSD License +// http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php + +function keyboardService($window, $timeout) { + + var keyboardManagerService = {}; + + var defaultOpt = { + 'type': 'keydown', + 'propagate': false, + 'inputDisabled': false, + 'target': $window.document, + 'keyCode': false + }; + + // Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken + var shift_nums = { + "`": "~", + "1": "!", + "2": "@", + "3": "#", + "4": "$", + "5": "%", + "6": "^", + "7": "&", + "8": "*", + "9": "(", + "0": ")", + "-": "_", + "=": "+", + ";": ":", + "'": "\"", + ",": "<", + ".": ">", + "/": "?", + "\\": "|" + }; + + // Special Keys - and their codes + var special_keys = { + 'esc': 27, + 'escape': 27, + 'tab': 9, + 'space': 32, + 'return': 13, + 'enter': 13, + 'backspace': 8, + + 'scrolllock': 145, + 'scroll_lock': 145, + 'scroll': 145, + 'capslock': 20, + 'caps_lock': 20, + 'caps': 20, + 'numlock': 144, + 'num_lock': 144, + 'num': 144, + + 'pause': 19, + 'break': 19, + + 'insert': 45, + 'home': 36, + 'delete': 46, + 'end': 35, + + 'pageup': 33, + 'page_up': 33, + 'pu': 33, + + 'pagedown': 34, + 'page_down': 34, + 'pd': 34, + + 'left': 37, + 'up': 38, + 'right': 39, + 'down': 40, + + 'f1': 112, + 'f2': 113, + 'f3': 114, + 'f4': 115, + 'f5': 116, + 'f6': 117, + 'f7': 118, + 'f8': 119, + 'f9': 120, + 'f10': 121, + 'f11': 122, + 'f12': 123 + }; + + var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0; + + // The event handler for bound element events + function eventHandler(e) { + e = e || $window.event; + + var code, k; + + // Find out which key is pressed + if (e.keyCode) + { + code = e.keyCode; + } + else if (e.which) { + code = e.which; + } + + var character = String.fromCharCode(code).toLowerCase(); + + if (code === 188){character = ",";} // If the user presses , when the type is onkeydown + if (code === 190){character = ".";} // If the user presses , when the type is onkeydown + + var propagate = true; + + //Now we need to determine which shortcut this event is for, we'll do this by iterating over each + //registered shortcut to find the match. We use Find here so that the loop exits as soon + //as we've found the one we're looking for + _.find(_.keys(keyboardManagerService.keyboardEvent), function(key) { + + var shortcutLabel = key; + var shortcutVal = keyboardManagerService.keyboardEvent[key]; + + // Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked + var kp = 0; + + // Some modifiers key + var modifiers = { + shift: { + wanted: false, + pressed: e.shiftKey ? true : false + }, + ctrl: { + wanted: false, + pressed: e.ctrlKey ? true : false + }, + alt: { + wanted: false, + pressed: e.altKey ? true : false + }, + meta: { //Meta is Mac specific + wanted: false, + pressed: e.metaKey ? true : false + } + }; + + var keys = shortcutLabel.split("+"); + var opt = shortcutVal.opt; + var callback = shortcutVal.callback; + + // Foreach keys in label (split on +) + var l = keys.length; + for (var i = 0; i < l; i++) { + + var k = keys[i]; + switch (k) { + case 'ctrl': + case 'control': + kp++; + modifiers.ctrl.wanted = true; + break; + case 'shift': + case 'alt': + case 'meta': + kp++; + modifiers[k].wanted = true; + break; + } + + if (k.length > 1) { // If it is a special key + if (special_keys[k] === code) { + kp++; + } + } + else if (opt['keyCode']) { // If a specific key is set into the config + if (opt['keyCode'] === code) { + kp++; + } + } + else { // The special keys did not match + if (character === k) { + kp++; + } + else { + if (shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase + character = shift_nums[character]; + if (character === k) { + kp++; + } + } + } + } + + } //for end + + if (kp === keys.length && + modifiers.ctrl.pressed === modifiers.ctrl.wanted && + modifiers.shift.pressed === modifiers.shift.wanted && + modifiers.alt.pressed === modifiers.alt.wanted && + modifiers.meta.pressed === modifiers.meta.wanted) { + + //found the right callback! + + // Disable event handler when focus input and textarea + if (opt['inputDisabled']) { + var elt; + if (e.target) { + elt = e.target; + } else if (e.srcElement) { + elt = e.srcElement; + } + + if (elt.nodeType === 3) { elt = elt.parentNode; } + if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA' || elt.hasAttribute('disable-hotkeys')) { + //This exits the Find loop + return true; + } + } + + $timeout(function () { + callback(e); + }, 1); + + if (!opt['propagate']) { // Stop the event + propagate = false; + } + + //This exits the Find loop + return true; + } + + //we haven't found one so continue looking + return false; + + }); + + // Stop the event if required + if (!propagate) { + // e.cancelBubble is supported by IE - this will kill the bubbling process. + e.cancelBubble = true; + e.returnValue = false; + + // e.stopPropagation works in Firefox. + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + return false; + } + } + + // Store all keyboard combination shortcuts + keyboardManagerService.keyboardEvent = {}; + + // Add a new keyboard combination shortcut + keyboardManagerService.bind = function (label, callback, opt) { + + //replace ctrl key with meta key + if(isMac && label !== "ctrl+space"){ + label = label.replace("ctrl","meta"); + } + + var elt; + // Initialize opt object + opt = angular.extend({}, defaultOpt, opt); + label = label.toLowerCase(); + elt = opt.target; + if(typeof opt.target === 'string'){ + elt = document.getElementById(opt.target); + } + + //Ensure we aren't double binding to the same element + type otherwise we'll end up multi-binding + // and raising events for now reason. So here we'll check if the event is already registered for the element + var boundValues = _.values(keyboardManagerService.keyboardEvent); + var found = _.find(boundValues, function (i) { + return i.target === elt && i.event === opt['type']; + }); + + // Store shortcut + keyboardManagerService.keyboardEvent[label] = { + 'callback': callback, + 'target': elt, + 'opt': opt + }; + + if (!found) { + //Attach the function with the event + if (elt.addEventListener) { + elt.addEventListener(opt['type'], eventHandler, false); + } else if (elt.attachEvent) { + elt.attachEvent('on' + opt['type'], eventHandler); + } else { + elt['on' + opt['type']] = eventHandler; + } + } + + }; + // Remove the shortcut - just specify the shortcut and I will remove the binding + keyboardManagerService.unbind = function (label) { + label = label.toLowerCase(); + var binding = keyboardManagerService.keyboardEvent[label]; + delete(keyboardManagerService.keyboardEvent[label]); + + if(!binding){return;} + + var type = binding['event'], + elt = binding['target'], + callback = binding['callback']; + + if(elt.detachEvent){ + elt.detachEvent('on' + type, callback); + }else if(elt.removeEventListener){ + elt.removeEventListener(type, callback, false); + }else{ + elt['on'+type] = false; + } + }; + // + + return keyboardManagerService; +} + +angular.module('umbraco.services').factory('keyboardService', ['$window', '$timeout', keyboardService]); diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/database.html b/src/Umbraco.Web.UI.Client/src/installer/steps/database.html index 6b71f1db9e..fc870858cf 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/database.html +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/database.html @@ -15,11 +15,11 @@
-

Great!, no need to configure anything then, you simply click the continue button below to continue to the next step

+

Great! No need to configure anything, you can simply click the continue button below to continue to the next step

- What is the exact connectionstring we should use? + What is the exact connection string we should use?
@@ -64,7 +64,7 @@
Enter the database user name
diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html b/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html index ad9b1c9cc1..0c78e211dc 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html @@ -1,8 +1,8 @@ 
-

Your permission settings are not ready for umbraco

+

Your permission settings are not ready for Umbraco

- In order to run umbraco, you'll need to update your permission settings. - Detailed information about the correct file & folder permissions for Umbraco can be found + In order to run Umbraco, you'll need to update your permission settings. + Detailed information about the correct file and folder permissions for Umbraco can be found here.

diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/version7upgradereport.html b/src/Umbraco.Web.UI.Client/src/installer/steps/version7upgradereport.html index df1e58d737..f30788e858 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/version7upgradereport.html +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/version7upgradereport.html @@ -2,11 +2,11 @@

Major version upgrade from {{installer.current.model.currentVersion}} to {{installer.current.model.newVersion}}

There were {{installer.current.model.errors.length}} issues detected

- The following compatibility issues were found. If you continue all non-compatible property editors will be converted to a Readonly/Label. + The following compatibility issues were found. If you continue, all non-compatible property editors will be converted to a Readonly/Label. You will be able to change the property editor to a compatible type manually by editing the data type after installation.

- Otherwise if you choose not to proceed you will need to fix the errors listed below. + Otherwise, if you choose not to proceed, you will need to fix the errors listed below. Refer to v{{installer.current.model.newVersion}} upgrade instructions for full details.

@@ -19,4 +19,4 @@

-
\ No newline at end of file +
diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 391fafb3fa..f6490fc79b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -2,6 +2,7 @@ // Core variables and mixins @import "fonts.less"; // Loading fonts @import "variables.less"; // Modify this for custom colors, font-sizes, etc +@import "colors.less"; // Colors from variables but as specific CSS classes @import "mixins.less"; // CSS Reset @@ -74,9 +75,8 @@ @import "sections.less"; @import "helveticons.less"; @import "main.less"; -@import "listview.less"; +@import "listview.less"; @import "gridview.less"; -@import "footer.less"; @import "filter-toggle.less"; @import "forms/umb-validation-label.less"; @@ -206,7 +206,6 @@ @import "utilities/_cursor.less"; //page specific styles -@import "pages/document-type-editor.less"; @import "pages/login.less"; @import "pages/welcome-dashboard.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less index b36c73a61a..7135692ae8 100644 --- a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less +++ b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less @@ -1,5 +1,7 @@ @import "helveticons.less"; +@import "variables.less"; +@import "application/umb-outline.less"; /******* font-face *******/ @@ -13,730 +15,197 @@ /****************************/ body { - overflow: hidden; - height: 100%; - width: 100%; - width: calc(~"100% - 80px"); // 80px is the fixed left menu for toggling the different browser sizes position: absolute; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + height: 100%; + height: calc(~"100% - 40px"); + width: 100%; padding: 0; margin: 0; - font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 20px; - color: #343434; - transition: all 0.2s ease-in-out; - padding-left:80px; + padding-bottom:40px; + background-color: @brownGrayExtraLight; } -h4 { - margin: 0; - font-size: 16px; - background-color: #535353; - color: #b3b3b3; - text-transform: uppercase; - position: initial; - display: block; - padding: 5px 10px; - font-size: 12px; - font-weight: normal; -} - -h5 { - margin: 0 0 6px 0; - font-size: 12px; -} - -ul { - list-style:none; - margin:0; - padding:0; -} - -a, a:hover{ - color:#333; - text-decoration:none; -} - - /****************************/ /* General class */ /****************************/ -.right { - float:right; - display:inline-block; -} - -.left { - float:left; - display:inline-block; -} - -.leftOpen { - padding-left: 330px -} - -.wait { - display: block; - height: 100%; - width: 100%; - background:#fff center center url(../img/loader.gif) no-repeat; -} - -/****************************/ -/* Group button */ -/****************************/ - -.btn-group { - float: right; - margin-right: 10px; -} - -.btn { - display: inline-block; - padding: 4px 12px; - margin-bottom: 0; - font-size: 14px; - line-height: 20px; - color: #000000; - text-align: center; - vertical-align: middle; - cursor: pointer; - background: #f2f2f2; - border: 1px solid #cccccc; - border-radius: 2px; - box-shadow: none; -} - -.btn-group > .btn + .dropdown-toggle { - box-shadow: none; -} - -.btn-group > .btn + .btn { - margin-left: -6px; -} - -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; - box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.btn-group > .btn:last-child, .btn-group > .dropdown-toggle { - border-top-right-radius: 2px; - border-bottom-right-radius: 2px; -} - -.caret { - display: inline-block; - width: 0; - height: 0; - vertical-align: top; - border-top: 4px solid #FFFFFF; - border-right: 4px solid transparent; - border-left: 4px solid transparent; - content: ""; - border-top: 0; - border-bottom: 4px solid #ffffff; - margin-top: 8px; - margin-left: 0; -} - -.dropdown-menu { +.menu-bar { position: absolute; - display: block; - top: auto; - right: 0; - z-index: 1000; - display: block; - float: left; - min-width: 160px; - padding: 5px 0; - margin: -96px 10px 0 0; - margin-bottom: 1px; - list-style: none; - background-color: #ffffff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 6px; - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - background-clip: padding-box; -} - -.dropdown-menu > li > a, -.dropdown-menu > li > button { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 20px; - color: black; - white-space: nowrap; - cursor:pointer; -} - -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus, -.dropdown-menu > li > button:hover, -.dropdown-menu > li > button:focus, -.dropdown-submenu:hover > a, -.dropdown-submenu:focus > a { - color: #000000; - background: #e4e0dd; -} - -/****************************/ -/* Speech bubble */ -/****************************/ - -#speechbubble { - position: absolute; - right: 0; - bottom: 40px; + bottom:0; left: 0; - z-index: 9999; - display: none; - padding: 0; - margin: auto; - margin-left: 300px; - text-align: left; - background: none; - border: none; - border-bottom: none; -} + right: 0; + background-color: @blueExtraDark; + color:@white; -#speechbubble p { - position: relative; - padding: 8px 30px 8px 20px; - margin: auto; - margin-top: 5px; - font-size: 12px; - color: #ffffff; - text-shadow: none; - background-color: #46a546; - border: none; - border-color: transparent; - border-radius: 5px 0 0 5px; -} - - -/****************************/ -/* Main section menu */ -/****************************/ - -.more-options i { - display: inline-block; - width: 5px !important; - height: 5px !important; - margin: 5px 1px 7px 0; - background: #d9d9d9; - border-radius: 20px; -} - -.fix-left-menu { - position: fixed; - top: 0; - left: 0; - width: 80px; - height: 100%; - padding: 0; - margin-left: -80px; font-family: "Lato", Helvetica, Arial, sans-serif; - font-size: 13px; + font-size: 12px; line-height: 16px; - background: #1b264f; - transition: all 0.2s ease-in-out; - z-index: 9999; + + animation: menu-bar-animation 1.2s; + animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1); + } -.avatar { - text-align:center; - padding: 27px 0 29px 0; - border-bottom: 1px solid #2E2246; -} +@keyframes menu-bar-animation { + 0% { + bottom: -50px; + } + 40% { + bottom: -50px; + } + 80% { + bottom: 0px; + } + } -.help { - position: absolute; - bottom: 0; - left: 0; - display: block; - width: 100%; - margin: 0; - font-size: 30px; - text-align: center; - color: #D8D7D9; - opacity: 0.4; - transition: all .3s linear; -} +.menu-bar__right-part { + float: right; + display: flex; + flex: row; -ul.sections { - display: block; - background: #1b264f; - position:absolute; - top: 90px; - width: 80px; - transition: all 0.2s ease-in-out; - list-style:none; - margin:0; - padding:0; - margin-left: -80px; - overflow: auto; - overflow-x: hidden; - height: calc(100% - 91px); - - &::-webkit-scrollbar { - width: 0px; - background: transparent; + > div, > button { + border-left: 1px solid rgba(255, 255, 255, .25); } } -ul.sections li { - display: block; - border-left: 4px #1b264f solid; - transition: all .3s linear; +.menu-bar__title { + display: inline-block; + padding: 11px 15px; + font-weight: bold; + font-size: 13px; +} + +.menu-bar__button { + display: inline-block; + padding: 11px 15px; + height: 40px; + border:none; + background-color: @blueExtraDark; + + text-align: left; + font: inherit; + color: inherit; cursor: pointer; -} -.fix-left-menu ul.sections li a span, -.fix-left-menu ul.sections li a i { - color: #fff; - opacity: .7; - transition: all .3s linear; -} + transition: color 120ms linear, background-color 120ms linear; + + .icon { + margin-right: 10px; + font-size: 18px; + vertical-align: middle; + } + + span { + vertical-align: middle; + } + + > svg { + display: inline-block; + width: 14px; + height: 14px; + fill: #fff; + margin-right: 10px; + vertical-align: middle; + transition: fill 120ms linear; + } -ul.sections li a { - display: block; - width: 100%; - height: 100%; - padding: 20px 4px 15px 0; - margin: 0 0 0 -4px; - text-align: center; - text-decoration: none; - border-bottom: 1px solid #2E2246; &:hover { - span, i { - opacity: 1; - color:#fff; + background-color: lighten(@blueExtraDark, 4%); + } + &.--active { + color: @pinkLight; + > svg { + fill: @pinkLight; } } } -ul.sections li a i { - font-size: 30px; - opacity: 0.8; -} +.preview-menu-option { -ul.sections li a span { - display: block; - font-size: 10px; - line-height: 1.4em; - opacity: 0.8; -} - -ul.sections li.current { - border-left: 4px #f5c1bc solid; -} - -ul.sections li.current a i { - color: #f5c1bc; -} - -ul.sections li.current { - border-left: 4px #f5c1bc solid; -} - -ul.sections li:hover a i, -ul.sections li:hover a span { - opacity: 1; -} - -.fix-left-menu:hover .help { - opacity: 1; -} - -.fix-left-menu.selected, -.sections.selected { - margin-left:0px; -} - -/*************************************************/ -/* Main panel */ -/*************************************************/ - -.main-panel { - position: fixed; - top: 0; - left: 0; - margin-left: -330px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - line-height: 16px; - background: #ffffff; - transition: all 0.2s ease-in-out; - width: 250px; - height: 100%; - padding: 0; - z-index: 999; - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); -} - -.main-panel .header { - padding: 28px 20px 32px 20px; - font-weight: bold; - background: #f8f8f8; - border-bottom: 1px solid #d9d9d9; -} - -.main-panel .header h3 { - color: rgba(179, 179, 179, 0.49); - font-size: 24px; - margin:0; -} - -.main-panel .header h3 i { - position: absolute; - right: 20px; - cursor:pointer; - transition: all 0.2s ease-in-out; -} - -.main-panel .header h3 i:hover { - color:#333; -} - -.main-panel.selected { - margin-left: 80px; - position: absolute; - right: 0; - bottom: 0; - left: 0; - overflow: auto; -} - -.main-panel .content { - padding:20px 0; -} - -/*************************************************/ -/* float-panel */ -/*************************************************/ - -.float-panel { - position: fixed; - top: 0; - z-index: 99; - width: 250px; - height: 100%; - padding: 0; - padding: 20px; - margin-left: -480px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - line-height: 16px; - background: #ffffff; - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - transition: all 0.2s ease-in-out; -} - -.float-panel.selected{ - margin-left: 0px; -} - -/*************************************************/ -/* sample palette color */ -/*************************************************/ - -.samples h4 { - position: initial; - display: block; - padding: 5px 10px; - margin: 0; - border: 0px solid #6B6B6B; - font-weight: normal; - font-size: 12px; -} - -.samples > li { - padding: 6px 20px; - cursor: pointer; -} - -.samples > li:hover, .samples > li.hover { - background: #f8f8f8; -} - -.samples > li ul { - display:table; - width:100%; -} - -.samples > li ul > li { - display: table-cell; - height: 15px; - padding: 0; -} - -/*************************************************/ -/* canvas designer panel */ -/*************************************************/ - -h4.panel-title { - cursor:pointer; - background-color: #f8f8f8; - color: #767676; -} - -.editor-category{ - margin: 5px 10px; - border: 1px solid #D9D9D9; -} - -.canvasdesigner-panel-container { - padding: 10px 10px; -} - -.canvasdesigner-panel-property { - clear: both; - overflow: hidden; - margin: 0 0 10px 0; -} - -.canvasdesigner .box-slider { - padding: 0px 0px 6px 0px; - overflow: hidden; - clear: both; -} - -.field-title { - float: left; - margin-right: 10px; - font-size: 12px; - color: #d9d9d9; -} - -.div-field { - margin-bottom: 10px; - overflow: hidden; - clear: both; -} - -/*************************************************/ -/* font family picker */ -/*************************************************/ - -.fontFamilyPickerPreview { - float: left; - width: 90%; - padding: 8px; - margin-top: 4px; - clear: both; - font-size: 18px; - color: #CDCDCD; - cursor: pointer; - border: 1px solid #CDCDCD; - text-align: center; position: relative; -} - -.fontFamilyPickerPreview span { - font-size: 32px; - line-height: 32px; -} - -.fontPickerDelete { - position: absolute; - margin: 5px 0 0 -15px; - cursor: pointer; - color:#CDCDCD; - right: 0; - top: 0; -} - -.fontFamilyPickerPreview:hover { - border: 1px solid #979797; - color:#979797; -} - -.fontFamilyPickerPreview:hover .fontPickerDelete { - color:#979797; -} - -.canvasdesigner-fontfamilypicker { - margin-bottom:30px; -} - -.canvasdesigner-fontfamilypicker select { - font-size: 12px; - padding: 2px; -} - -.canvasdesigner-fontfamilypicker select.font-list { - width:170px; -} - -.canvasdesigner-fontfamilypicker select.variant-list { - width:60px; -} - -.canvasdesigner-fontfamilypicker { - font-size: 42px; - line-height: 50px; - margin-bottom:40px; -} - -/*************************************************/ -/* slider */ -/*************************************************/ - -.canvasdesigner .ui-widget-content { - background: rgba(0, 0, 0, 0.27) !important; - border: 0 solid #fff !important; - border-radius: 1px !important; -} - -.canvasdesigner .ui-slider-horizontal { - margin: 14px 11px 0 11px; -} - -.ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default, -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, -.ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { - border: 1px solid #535353 !important; - background: #535353 !important; - background-color:none !important; - outline:none; -} - -.canvasdesigner .ui-slider .ui-slider-handle:hover, -.canvasdesigner .ui-slider .ui-slider-handle:focus { - color: #d9d9d9 !important; -} - -.canvasdesigner .ui-slider .ui-slider-handle span { - position: absolute !important; - margin: -15px 0 0 -8px !important; - color: #535353 !important; - font-size: 9px !important; - text-align: -webkit-center !important; - display: block !important; - width: 30px !important; -} - -.canvasdesigner .slider-input { - float: right; - width: 25px; - padding: 0; - margin-top: -9px; - margin-right: 1px; - font-size: 12px; - color: #d9d9d9; - text-align: right; - background-color: transparent; - border: none; - -} - -@-moz-document url-prefix() { - .canvasdesigner .slider-input { - margin-top: -6px; - } -} - -.canvasdesigner .sp-replacer { - padding: 0; - margin: 0; - display: block; - border: none; - height: 26px; - border-radius: 1px; - border: 1px solid #CDCDCD; -} - -.canvasdesigner .sp-replacer:hover { - border: 1px solid #979797; -} - -.canvasdesigner .panel-body { - border-top: none !important; -} - -.canvasdesigner select { - font-size: 12px; -} - -.canvasdesigner .sp-dd { - display: none; -} - -.canvasdesigner .sp-preview { - width: 100%; - height: 100%; - margin-right: 0; - border: none; - display: block; -} - -.canvasdesigner .color-picker-preview { - height: 26px; - border: 1px solid #CDCDCD; - border-radius: 1px; - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); - cursor: pointer; -} - -.canvasdesigner .float-panel .sp-container.sp-flat { - background-color: transparent; - border: none; - padding: 10px; - width: 100%; -} - -.canvasdesigner .float-panel .sp-container.sp-flat .sp-picker-container { - padding: 0; - margin: 0px; - margin-bottom: 10px; - border: none; - width: 100%; -} - -.canvasdesigner .float-panel .sp-container.sp-flat .sp-palette-container { - padding: 0; - height: 32px; - width: 100%; - float: left; - margin: 0; - border: none; -} - -.canvasdesigner .float-panel .sp-container.sp-flat .sp-button-container { - display: none; -} - -.colorPickerDelete { - position: absolute; - margin: -23px 0 0 0; - cursor: pointer; -} - -.borderStyleSelect -{ display: inline-block; - float: right; - width: 75px; - height: 28px; + + > .menu-bar__button { + position: relative; + } + + .dropdown-menu { + display:none; + + position: absolute; + right: 0; + bottom: 100%; + min-width: 200px; + + border-radius: 3px 3px 0 3px; + overflow: hidden; + + background-color: @blueExtraDark; + + > button { + position: relative; + display: list-item; + text-align: left; + width: 100%; + + &.--active { + &::before { + content: ''; + position: absolute; + left:0; + width: 3px; + top: 0; + bottom: 0; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + background-color: @pinkLight; + } + } + } + } + + &.--open { + z-index:1; + box-shadow: 0 5px 10px 0 rgba(0,0,0,.26); + > .menu-bar__button { + z-index: @zindexDropdown + 1; + } + .dropdown-menu { + display:block; + z-index: @zindexDropdown; + box-shadow: 0 5px 10px 0 rgba(0,0,0,.26); + } + } + } + + + /*************************************************/ /* IFrame size */ /*************************************************/ -.desktop { +#demo-iframe-wrapper { + transition: all 240ms cubic-bezier(0.165, 0.84, 0.44, 1); +} + +.fullsize { width: 100%; height: 100%; margin: 0 auto; overflow: hidden; } +.desktop { + width: 1920px; + height: 1080px; +} + .laptop { width: 1366px; height: 768px; @@ -762,13 +231,13 @@ h4.panel-title { height: 360px; } -.border { - margin: 75px auto; - background-color: #ffffff; - border-radius: 10px; +.shadow { + margin: 10px auto; + background-color: @white; + border-radius: 3px; + overflow: hidden; opacity: 1.0; - box-shadow: 0 0 0 29px #E9E9EB, 0 0 0 30px #D8D7D9; - transition: all 0.5s ease-in-out; + box-shadow: 0 5px 20px 0 rgba(0,0,0,.26); } iframe { @@ -786,251 +255,3 @@ iframe { .flip:before { transform: rotate(90deg); } - -/*************************************************/ -/* Image picker */ -/*************************************************/ - -.imagePickerPreview { - height: 20px; - text-align: center; - background-color: #fff; - padding: 6px 0 0 0; - cursor: pointer; - background-size: cover; - border-radius:1px; - border: 1px solid #CDCDCD; - color: #CDCDCD; -} - -.sp-clear-display { - background-image: none !important; -} - -.imagePickerPreview:hover { - border: 1px solid #979797; -} - -.imagePickerPreview:hover i { - color: #979797; -} - -.imagePickerPreview i { - font-size:24px; - color: #CDCDCD; -} - -.canvasdesignerImagePicker { - padding: 0; - margin-top: 10px; - overflow: auto; - text-align: left; - list-style: none; -} - -.canvasdesignerImagePicker ul { - padding: 0; - margin: 0; - margin-left: 20px; - list-style: none; -} - -.canvasdesignerImagePicker li { - display: inline-block; - margin-bottom: 10px; -} - -.canvasdesignerImagePicker ul.media-items li { - margin-right: 20px; -} - -.canvasdesignerImagePicker .media-preview { - position: relative; - display: inline-block; - width: 91px; - height: 78px; - cursor: pointer; - background-position: center center; - background-size: cover; - border: 2px solid #F3F2F2; -} - -.canvasdesignerImagePicker .media-preview .folder { - position: absolute; - top: 30px; - left: 0; - width: 100%; - font-size: 40px; - color: gainsboro; - text-align: center; -} - -.canvasdesignerImagePicker .media-preview .folder-name { - margin-top: -5px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - line-height: 0; - color: grey; -} - -.canvasdesignerImagePicker .media-preview:hover, -.media-preview.selected { - border: 2px solid #84B8F0; -} - -.canvasdesignerImagePicker .modal-dialog { - width: 618px; - font-weight: normal; -} - -.bodyCanvasdesignerImagePicker .breadcrumb { - margin-top: 4px; - margin-bottom: 10px; - font-size: 16px; - text-align: left; -} - -.bodyCanvasdesignerImagePicker .breadcrumb > li { - display: inline; -} - -.breadcrumb{ - font-size: 12px; -} - -.bodyCanvasdesignerImagePicker .breadcrumb > li a { - cursor: pointer; -} - -.bodyCanvasdesignerImagePicker .breadcrumb input { - padding: 0; - margin: -4px -4px; -} - -.bodyCanvasdesignerImagePicker .fileinput-button { - position: absolute; - top: 10px; - right: 10px; - font-size: 19px; -} - -.bodyCanvasdesignerImagePicker input.input-media { - position: absolute; - top: 0; - right: 0; - margin: 0; - font-size: 23px; - cursor: pointer; - opacity: 0; - transform: translate(-300px, 0) scale(4); - direction: ltr; -} - -.bodyCanvasdesignerImagePicker .breadcrumb a.disabled, -.bodyCanvasdesignerImagePicker .breadcrumb a.disabled:hover { - color: grey; - text-decoration: none; - cursor: default; -} - -.canvasdesignerImagePicker h3 { - font-size: 18px; - color: #555555; -} - -/*************************************************/ -/* Border editor */ -/*************************************************/ - -.box-preview { - display:inline-block; -} - -.box-preview li { - height: 30px; - width: 30px; - border-radius: 1px; - border: none; - display:inline-block; - background-color:#535353; - cursor:pointer; - margin-right: 6px; - position:relative; -} - -.box-preview li:last-child{ - margin-right: 0px; -} - -.box-preview li.selected { - border-color:#53a93f !important; -} - -.box-preview li.border-all { - border: 6px solid #b3b3b3; - height: 18px; - width: 18px; - margin-left:0px -} - -.box-preview li.border-left { - border-left: 6px solid #b3b3b3; - width: 24px; -} - -.box-preview li.border-right { - border-right: 6px solid #b3b3b3; - width: 24px; -} - -.box-preview li.border-top { - border-top: 6px solid #b3b3b3; - height: 24px; -} - -.box-preview li.border-bottom { - border-bottom: 6px solid #b3b3b3; - height: 24px; -} - -.bordereditor .color-picker-preview { - display: inline-block; - width: 120px; - float: left; -} - -/*************************************************/ -/* Radius editor */ -/*************************************************/ - -.radius-top-left, .radius-top-right, .radius-bottom-left, .radius-bottom-right { - display: block; - position: absolute; - background-color: #b3b3b3; - width: 7px; - height: 7px; -} - -.box-preview li.selected span { - background-color:#53a93f; -} - -.radius-top-left{ - top:0; - left:0; -} - -.radius-top-right{ - top:0; - right:0; -} - -.radius-bottom-left{ - bottom:0; - left:0; -} - -.radius-bottom-right{ - bottom:0; - right:0 -} diff --git a/src/Umbraco.Web.UI.Client/src/less/colors.less b/src/Umbraco.Web.UI.Client/src/less/colors.less new file mode 100644 index 0000000000..4cb70cdf5e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/colors.less @@ -0,0 +1,81 @@ +.red{color: @red;} +.blue{color: @blue;} +.black{color: @black;} +.turquoise{color: @turquoise;} +.turquoise-d1{color: @turquoise-d1;} + +.text-warning { + color: @orange; +} +.text-error { + color: @red; +} +.text-success { + color: @green; +} + + +//icon colors for tree icons +.color-red, .color-red i{color: @red-d1 !important;} +.color-blue, .color-blue i{color: @turquoise-d1 !important;} +.color-orange, .color-orange i{color: @orange !important;} +.color-green, .color-green i{color: @green-d1 !important;} +.color-yellow, .color-yellow i{color: @yellowIcon !important;} + +/* Colors based on https://zavoloklom.github.io/material-design-color-palette/colors.html */ +.btn-color-black {background-color: @black;} +.color-black i { color: @black;} + +.btn-color-blue-grey {background-color: @blueGrey;} +.color-blue-grey, .color-blue-grey i { color: @blueGrey !important;} + +.btn-color-grey{background-color: @grayIcon;} +.color-grey, .color-grey i { color: @grayIcon !important; } + +.btn-color-brown{background-color: @brownIcon;} +.color-brown, .color-brown i { color: @brownIcon !important; } + +.btn-color-blue{background-color: @blueIcon;} +.color-blue, .color-blue i { color: @blueIcon !important; } + +.btn-color-light-blue{background-color: @lightBlueIcon;} +.color-light-blue, .color-light-blue i {color: @lightBlueIcon !important;} + +.btn-color-cyan{background-color: @cyanIcon;} +.color-cyan, .color-cyan i { color: @cyanIcon !important; } + +.btn-color-green{background-color: @greenIcon;} +.color-green, .color-green i { color: @greenIcon !important; } + +.btn-color-light-green{background-color: @lightGreenIcon;} +.color-light-green, .color-light-green i {color: @lightGreenIcon !important; } + +.btn-color-lime{background-color: @limeIcon;} +.color-lime, .color-lime i { color: @limeIcon !important; } + +.btn-color-yellow{background-color: @yellowIcon;} +.color-yellow, .color-yellow i { color: @yellowIcon !important; } + +.btn-color-amber{background-color: @amberIcon;} +.color-amber, .color-amber i { color: @amberIcon !important; } + +.btn-color-orange{background-color: @orangeIcon;} +.color-orange, .color-orange i { color: @orangeIcon !important; } + +.btn-color-deep-orange{background-color: @deepOrangeIcon;} +.color-deep-orange, .color-deep-orange i { color: @deepOrangeIcon !important; } + +.btn-color-red{background-color: @redIcon;} +.color-red, .color-red i { color: @redIcon !important; } + +.btn-color-pink{background-color: @pinkIcon;} +.color-pink, .color-pink i { color: @pinkIcon !important; } + +.btn-color-purple{background-color: @purpleIcon;} +.color-purple, .color-purple i { color: @purpleIcon !important; } + +.btn-color-deep-purple{background-color: @deepPurpleIcon;} +.color-deep-purple, .color-deep-purple i { color: @deepPurpleIcon !important; } + +.btn-color-indigo{background-color: @indigoIcon;} +.color-indigo, .color-indigo i { color: @indigoIcon !important; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less index a621370d02..456601a7bd 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less @@ -57,7 +57,7 @@ .umb-toggle.umb-toggle--checked & { transform: translateX(20px); - background-color: white; + background-color: @white; } } @@ -75,7 +75,7 @@ .umb-toggle__icon--left { left: 5px; - color: white; + color:@white; transition: opacity 120ms; opacity: 0; // .umb-toggle:hover & { @@ -85,7 +85,7 @@ opacity: 1; } .umb-toggle.umb-toggle--checked:hover & { - color: white; + color:@white; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/card.less b/src/Umbraco.Web.UI.Client/src/less/components/card.less index 8324698685..ed80359833 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/card.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/card.less @@ -5,7 +5,7 @@ .umb-card{ position: relative; padding: 5px 10px 5px 10px; - background: white; + background: @white; width: 100%; .title{padding: 12px; color: @gray-3; border-bottom: 1px solid @gray-8; font-weight: 400; font-size: 16px; text-transform: none; margin: 0 -10px 10px -10px;} @@ -86,7 +86,7 @@ margin: 0 auto; list-style: none; width: 100%; - + display: flex; flex-flow: row wrap; justify-content: flex-start; @@ -119,7 +119,7 @@ padding-top: 100%; border-radius: 3px; transition: background-color 120ms; - + > span { position: absolute; top: 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less index 44cd86a189..1217441f4e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less @@ -17,8 +17,8 @@ } } .umb-editor-sub-header--white { - background-color: white; - border-color: white; + background-color: @white; + border-color: @white; } .umb-editor-sub-header.--state-selection { @@ -33,11 +33,11 @@ transition: box-shadow 240ms; position:sticky; z-index: 30; - + &.umb-sticky-bar--active { box-shadow: 0 6px 3px -3px rgba(0,0,0,.16); } - + .umb-dashboard__content & { top:-20px; // umb-dashboard__content has 20px padding - offset here prevents sticky position from firing when page loads } @@ -45,8 +45,8 @@ .umb-sticky-sentinel { pointer-events: none; - z-index: 5050; - + z-index: 5050; + &.-top { height:1px; transform:translateY(-10px); 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 6859280680..d2a3bdedb1 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 @@ -1,6 +1,6 @@ .umb-editors { .absolute(); - overflow: hidden; + overflow: hidden; .umb-editor { box-shadow: 0px 0 30px 0 rgba(0,0,0,.3); @@ -16,7 +16,7 @@ transform: none; will-change: transform; transition: transform 400ms ease-in-out; - + &.umb-editor--moveRight { transform: translateX(110%); } @@ -28,7 +28,7 @@ will-change: auto; transition: display 0s 320ms; } - + &--level0 { transform: none; } @@ -43,11 +43,11 @@ .umb-editor--level@{i} { transform: translateX(@x); } - + .umb-editor--n@{i} { right:@x; } - + .level-loop(@i - 1); } @@ -62,18 +62,18 @@ .umb-editor { @size: extract(extract(@editorSizes, @iterator), 1); @value: extract(extract(@editorSizes, @iterator), 2); - + &--@{size} { width: @value; will-change: transform; left: auto; - + .umb-editor--container { max-width: @value; } } } - + .create-editor-sizes(@iterator + 1); } @@ -94,3 +94,14 @@ opacity: 1; transition: opacity 320ms 20ms, visibility 0s; } + +.umb-editor--trashed-message { + background:@errorBackground; + color:@errorText; + padding:10px; + margin-bottom:20px; + + i { + margin-right:5px; + } +} \ 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 609cf0af3d..eb8740b385 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less @@ -66,7 +66,7 @@ flex-shrink: 1; flex-basis: auto; position: relative; - padding: 20px; + padding: 30px; background: @white; max-height: calc(100vh - 170px); overflow-y: auto; @@ -117,7 +117,7 @@ .umb-overlay.umb-overlay-center .umb-overlay-drawer { border: none; background: transparent; - padding: 0 20px 20px; + padding: 0 30px 20px; } /* ---------- OVERLAY TARGET ---------- */ diff --git a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less index 9947c793c2..7036d60a63 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less @@ -44,7 +44,7 @@ display: flex; padding: 6px; margin: 10px 0px !important; - background: #F3F3F5; + background: @gray-10; cursor: move; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less index 8945d15ec6..df01477880 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less @@ -3,11 +3,11 @@ min-width: 100%; width: auto; margin-top:1px; - + .umb-tree-item__label { user-select: none; } - + &:hover .umb-tree-item__arrow { visibility: visible; cursor: pointer @@ -36,7 +36,7 @@ overflow: hidden; margin-right: 6px; } - + // Loading Animation // ------------------------ .umb-tree-item__loader { @@ -46,7 +46,7 @@ } .umb-tree-item__label { - padding: 7px 0 5px; + padding: 7px 0 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -71,7 +71,7 @@ left: 0; right: 0; bottom: 0; - border: 2px solid fade(white, 80%); + border: 2px solid fade(@white, 80%); } &:hover { @@ -86,12 +86,12 @@ .umb-tree-item.current > .umb-tree-item__inner { background: @ui-active; color:@ui-active-type; - - // override small icon color. TODO => check usage + + // override small icon color. TODO => check usage &:before { color: @blue; } - + .umb-options { &:hover i { @@ -113,5 +113,5 @@ .umb-tree-item.current-not-active > .umb-tree-item__inner { background: @ui-active-blur; - color:@ui-active-type; + color:@ui-active-type; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less index 21f4a7bda8..c6b9dc7261 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less @@ -103,7 +103,7 @@ a.umb-avatar-btn:hover { text-decoration: none; } a.umb-avatar-btn .umb-avatar { - border: 2px dashed #A2A1A6; + border: 2px dashed @gray-6; } a.umb-avatar-btn .umb-avatar span { font-size: 50px; @@ -114,4 +114,4 @@ a.umb-avatar-btn .umb-avatar span { font-size: 50px; } -/*border-radius: 50%; width: 100px; height: 100px; font-size: 50px; text-align: center; display: flex; align-items: center; justify-content: center; background-color: #F3F3F5; border: 2px dashed #A2A1A6; color: #A2A1A6;*/ +/*border-radius: 50%; width: 100px; height: 100px; font-size: 50px; text-align: center; display: flex; align-items: center; justify-content: center; background-color: @gray-10; border: 2px dashed @gray-6; color: @gray-6;*/ diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less index 3c63d74a47..0afcfdd1f9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-breadcrumbs.less @@ -8,7 +8,7 @@ .umb-breadcrumbs__ancestor { display: flex; - min-height: 25px; + align-items: center; } .umb-breadcrumbs__action { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less index 7f19c4933c..76a4df0056 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less @@ -6,7 +6,7 @@ flex-wrap: wrap; align-items: center; position: relative; - padding: 0 !important; + padding: 0 0 0 26px !important; margin: 0; min-height: 22px; line-height: 22px; @@ -21,7 +21,6 @@ } &__text { - margin: 0 0 0 26px; position: relative; top: 1px; user-select: none; @@ -80,7 +79,7 @@ outline: 2px solid @inputBorderTabFocus; } .tabbing-active &.umb-form-check--checkbox &__input:checked:focus ~ .umb-form-check__state .umb-form-check__check { - border-color: white; + border-color: @white; } // add spacing between when flexed/inline, equal to the width of the input 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 277c2bcbe8..479074fee9 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 @@ -437,7 +437,7 @@ .umb-grid .umb-row.-active-child { background-color: @gray-10; - + .umb-row-title-bar { cursor: inherit; } @@ -445,7 +445,7 @@ .umb-row-title { color: @gray-3; } - + } @@ -582,10 +582,10 @@ .umb-grid .iconBox.selected { -webkit-appearance: none; - background-image: linear-gradient(to bottom,#e6e6e6,#bfbfbf); + background-image: linear-gradient(to bottom,@gray-9,@gray-7); background-repeat: repeat-x; zoom: 1; - border-color: #bfbfbf #bfbfbf #999; + border-color: @gray-7 @gray-7 @gray-6; border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25); box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05); border-radius: 3px; @@ -638,9 +638,9 @@ .umb-grid .mce-toolbar { border-bottom: 1px solid @gray-7; - background-color: white; + background-color: @white; display: none; - + left: 0; right: 0; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less index 34070256ce..e8a62f739d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less @@ -51,7 +51,7 @@ // Color swatch .button { border: none; - color: white; + color: @white; padding: 5px; text-align: center; text-decoration: none; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less index a87e7084fb..f3b53f4def 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less @@ -11,7 +11,7 @@ cursor: pointer; } -.umb-insert-code-box:hover, +.umb-insert-code-box:hover, .umb-insert-code-box.-selected { background-color: @ui-option-hover; color: @ui-action-type-hover; @@ -32,7 +32,7 @@ .umb-insert-code-box__check { width: 18px; height: 18px; - background: @gray-10;x + background: @gray-10; border-radius: 50%; display: flex; align-items: center; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less index cdc6cfcb63..9ebd6d6e5d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-layout-selector.less @@ -23,10 +23,10 @@ .umb-layout-selector__dropdown { position: absolute; padding: 5px; - background: #333; + background: @grayDark; z-index: 999; display: flex; - background: #fff; + background: @white; flex-wrap: wrap; flex-direction: column; transform: translate(-50%,0); diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-list-view-settings.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-list-view-settings.less index f6dfed63c1..ba46c68a57 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-list-view-settings.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-list-view-settings.less @@ -52,7 +52,7 @@ tbody tr { background: @gray-10; - border-bottom: 1px solid #fff; + border-bottom: 1px solid @white; } th { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less index 76223589e4..8beff55b7c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-logviewer.less @@ -83,7 +83,7 @@ top: 0; line-height: 32px; right: 140px; - color: #fdb45c; + color: @yellow-d1; cursor: pointer; } @@ -92,7 +92,7 @@ top: 0; line-height: 32px; right: 120px; - color: #bbbabf; + color: @gray-7; cursor: pointer; } @@ -133,7 +133,7 @@ } .exception { - border-left: 4px solid #D42054; + border-left: 4px solid @red; padding: 0 10px 10px 10px; box-shadow: rgba(0,0,0,0.07) 2px 2px 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less index 50244c2079..4feadc272c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less @@ -42,6 +42,10 @@ .umb-media-grid__item.-selectable { cursor: pointer; + + .tabbing-active &:focus { + outline: 2px solid @inputBorderTabFocus; + } } .umb-media-grid__item.-file { @@ -54,7 +58,7 @@ color: @ui-selected-type; } } -.umb-media-grid__item.-selected, +.umb-media-grid__item.-selected, .umb-media-grid__item.-selectable:hover { &::before { content: ""; @@ -118,6 +122,7 @@ .umb-media-grid__item-overlay { display: flex; + width: 100%; opacity: 0; position: absolute; right: 0; @@ -130,13 +135,17 @@ overflow: hidden; color: @black; white-space: nowrap; - border-top:1px solid fade(black, 4%); + border-top:1px solid fade(@black, 4%); background: fade(@white, 92%); transition: opacity 150ms; - + &:hover { text-decoration: underline; } + + .tabbing-active &:focus { + opacity: 1; + } } .umb-media-grid__info { @@ -181,7 +190,7 @@ align-items: center; color: @black; transition: opacity 150ms; - + &:hover { color: @ui-action-discreet-type-hover; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-list-view.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-list-view.less index 7a2939bd30..8cd08f5045 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-list-view.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-list-view.less @@ -13,9 +13,15 @@ margin-right: 5px; } +.umb-mini-list-view__breadcrumb { + .flex; + margin-bottom: 10px; + min-height: 25px; +} + .umb-mini-list-view__back { - font-size: 12px; - margin-right: 5px; + font-size: 13px; + margin-right: 5px; color: @gray-4; display: flex; align-items: center; 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 bf0dd9d109..699496f5d3 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 @@ -82,7 +82,7 @@ padding: 15px 5px; color:@ui-option-type; border-radius: 3px 3px 0 0; - + &:hover { color:@ui-option-type-hover; } @@ -104,7 +104,7 @@ padding-left: 30px; } } - + } .umb-nested-content__icons { @@ -226,19 +226,19 @@ display: none !important; } -.umb-nested-content__doctypepicker table input, +.umb-nested-content__doctypepicker table input, .umb-nested-content__doctypepicker table select { width: 100%; padding-right: 0; } -.umb-nested-content__doctypepicker table td.icon-navigation, +.umb-nested-content__doctypepicker table td.icon-navigation, .umb-nested-content__doctypepicker i.umb-nested-content__help-icon { vertical-align: middle; color: @gray-7; } -.umb-nested-content__doctypepicker table td.icon-navigation:hover, +.umb-nested-content__doctypepicker table td.icon-navigation:hover, .umb-nested-content__doctypepicker i.umb-nested-content__help-icon:hover { color: @gray-2; } @@ -248,11 +248,11 @@ } .umb-nested-content__placeholder { - padding: 4px 6px; - border: 1px dashed #d8d7d9; + padding: 4px 6px; + border: 1px dashed @gray-8; background: 0 0; cursor: pointer; - color: #1b264f; + color: @blueExtraDark; -webkit-animation: fadeIn .5s; animation: fadeIn .5s; text-align: center; @@ -265,8 +265,8 @@ } .umb-nested-content__placeholder:hover { - color: #2152a3; - border-color: #2152a3; + color: @blueMid; + border-color: @blueMid; text-decoration: none; } @@ -288,7 +288,7 @@ // the attribute selector ensures the change only applies to the linkpicker overlay .form-horizontal .umb-nested-content--narrow [ng-controller*="Umbraco.Overlays.LinkPickerController"] .controls-row { margin-left:0!important; - + .umb-textarea, .umb-textstring { width:100%; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less index 16457787a3..b808e6574a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less @@ -67,6 +67,7 @@ height: 100%; width: 100%; border-radius: 3px; + border: 0 none; text-decoration: none !important; transition: border-color 100ms ease; background-color: @white; @@ -193,9 +194,8 @@ color: @black; box-sizing: border-box; justify-content: center; - border-top: 1px solid @gray-8; - border-bottom: 1px solid @gray-8; - border-right: 1px solid @gray-8; + border: 1px solid @gray-8; + border-left: 0; padding: 10px 0; background: @white; } @@ -411,6 +411,7 @@ } .umb-gallery__thumbnail { + background: transparent; flex: 0 1 100px; border: 1px solid @ui-action-discreet-border; border-radius: 3px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less index 3ce284870e..3f0b981ac6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less @@ -39,7 +39,7 @@ top: -15px; border-radius: 3px 3px 0 0; - + border-top-left-radius: 3px; border-top-right-radius: 3px; @@ -49,8 +49,8 @@ .box-shadow(0 5px 20px rgba(0,0,0,.3)); - background-color: white; - + background-color: @white; + } .umb-property .umb-property-actions { @@ -71,12 +71,12 @@ } .umb-property-actions__menu { - + position: absolute; z-index: 1000; display: block; - + float: left; min-width: 160px; list-style: none; @@ -85,11 +85,11 @@ border-top-left-radius: 0; margin-top:1px; - + } .umb-contextmenu-item > button { - + z-index:2;// need to stay on top of menu-toggle-open shadow. } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less index 1461d0f223..6ae92ffa4e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less @@ -1,7 +1,7 @@ .umb-range-slider.noUi-target { - background: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%); + background: linear-gradient(to bottom, @grayLighter 0%, @grayLighter 100%); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); border-radius: 20px; height: 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less index 7caec3c78e..a612af65ef 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-details.less @@ -1,7 +1,7 @@ .umb-user-details-avatar { margin-bottom: 20px; padding-bottom: 20px; - border-bottom: 1px solid #d8d7d9; + border-bottom: 1px solid @gray-8; } div.umb-user-details-actions > div { @@ -81,7 +81,7 @@ a.umb-user-details-details__back-link { margin-bottom: 30px; margin-right: 0; } - + .umb-user-details-details__sidebar { flex: 1 1 auto; width: 100%; diff --git a/src/Umbraco.Web.UI.Client/src/less/dashboards/getstarted.less b/src/Umbraco.Web.UI.Client/src/less/dashboards/getstarted.less index 807d0e863f..d64ffef098 100644 --- a/src/Umbraco.Web.UI.Client/src/less/dashboards/getstarted.less +++ b/src/Umbraco.Web.UI.Client/src/less/dashboards/getstarted.less @@ -22,8 +22,8 @@ text-align: center; display: flex; align-items: center; - border: 1px solid #d8d7d9; - background-color: #fff; + border: 1px solid @gray-8; + background-color: @white; margin: 0 0 0.5em; @media (min-width: 500px) { diff --git a/src/Umbraco.Web.UI.Client/src/less/dashboards/nucache.less b/src/Umbraco.Web.UI.Client/src/less/dashboards/nucache.less index 4ebe1d47b0..91922300fb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/dashboards/nucache.less +++ b/src/Umbraco.Web.UI.Client/src/less/dashboards/nucache.less @@ -4,7 +4,7 @@ } .top-border { - border-top: 2px solid #f3f3f5; + border-top: 2px solid @gray-10; } .no-left-padding { diff --git a/src/Umbraco.Web.UI.Client/src/less/dashboards/umbraco-forms.less b/src/Umbraco.Web.UI.Client/src/less/dashboards/umbraco-forms.less index b51bfeffa9..21ec047d41 100644 --- a/src/Umbraco.Web.UI.Client/src/less/dashboards/umbraco-forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/dashboards/umbraco-forms.less @@ -106,7 +106,7 @@ .step-text { font-size: 16px; line-height: 1.5; - color: #4c4c4c; + color: @gray; margin-bottom: 20px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/footer.less b/src/Umbraco.Web.UI.Client/src/less/footer.less deleted file mode 100644 index 93fdb6ab5e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/less/footer.less +++ /dev/null @@ -1,2 +0,0 @@ -// Footer -// ------------------------- diff --git a/src/Umbraco.Web.UI.Client/src/less/gridview.less b/src/Umbraco.Web.UI.Client/src/less/gridview.less index bb684cd69b..238feead90 100644 --- a/src/Umbraco.Web.UI.Client/src/less/gridview.less +++ b/src/Umbraco.Web.UI.Client/src/less/gridview.less @@ -365,12 +365,12 @@ .usky-grid .iconBox.selected { -webkit-appearance: none; - background-image: -webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf)); - background-image: -webkit-linear-gradient(top,#e6e6e6,#bfbfbf); - background-image: linear-gradient(to bottom,#e6e6e6,#bfbfbf); + background-image: -webkit-gradient(linear,0 0,0 100%,from(@gray-9),to(@gray-7)); + background-image: -webkit-linear-gradient(top,@gray-9,@gray-7); + background-image: linear-gradient(to bottom,@gray-9,@gray-7); background-repeat: repeat-x; zoom: 1; - border-color: #bfbfbf #bfbfbf #999; + border-color: @gray-7 @gray-7 @gray-6; border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25); box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05); border-radius: 3px; @@ -379,7 +379,7 @@ .usky-grid .iconBox i { font-size:16px !important; - color: #5F5F5F; + color: @gray-4; display:block; } diff --git a/src/Umbraco.Web.UI.Client/src/less/installer.less b/src/Umbraco.Web.UI.Client/src/less/installer.less index 865f015ffa..e964ed3c6f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/installer.less +++ b/src/Umbraco.Web.UI.Client/src/less/installer.less @@ -1,6 +1,7 @@ // Core variables and mixins @import "fonts.less"; // Loading fonts @import "variables.less"; // Modify this for custom colors, font-sizes, etc +@import "colors.less"; @import "mixins.less"; @import "buttons.less"; @import "forms.less"; @@ -133,8 +134,8 @@ legend { } input.ng-dirty.ng-invalid { - border-color: #b94a48; - color: #b94a48; + border-color: @pink; + color: @pink; } .disabled { diff --git a/src/Umbraco.Web.UI.Client/src/less/legacydialog.less b/src/Umbraco.Web.UI.Client/src/less/legacydialog.less index ca920d22c2..f1835a682c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/legacydialog.less +++ b/src/Umbraco.Web.UI.Client/src/less/legacydialog.less @@ -16,8 +16,8 @@ margin-top: 10px; height: 100%; overflow: auto; - border-top: 1px solid #ccc; - border-top: 1px solid #ccc; + border-top: 1px solid @gray-8; + border-top: 1px solid @gray-8; padding: 5px; } @@ -30,11 +30,11 @@ .umb-dialog .diff table th { padding: 5px; width: 25%; - border-bottom: 1px solid #ccc; + border-bottom: 1px solid @gray-8; } .umb-dialog .diff table td { - border-bottom: 1px solid #ccc; + border-bottom: 1px solid @gray-8; padding: 3px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index 86a1acbeae..0d646d11c6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -183,6 +183,17 @@ h5.-black { .umb-control-group .umb-el-wrap { padding: 0; } +.umb-control-group .control-header { + + .control-label { + float: left; + } + + .control-description { + display: block; + clear: both; + } +} .form-horizontal .umb-control-group .control-header { float: left; width: 160px; @@ -190,15 +201,12 @@ h5.-black { text-align: left; .control-label { - float: left; width: auto; padding-top: 0; text-align: left; } .control-description { - display: block; - clear: both; max-width:480px;// avoiding description becoming too wide when its placed on top of property. margin-bottom: 10px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/mixins.less b/src/Umbraco.Web.UI.Client/src/less/mixins.less index e49755338b..21b9c5c550 100644 --- a/src/Umbraco.Web.UI.Client/src/less/mixins.less +++ b/src/Umbraco.Web.UI.Client/src/less/mixins.less @@ -332,7 +332,7 @@ } // Gradient Bar Colors for buttons and alerts -.gradientBar(@primaryColor, @secondaryColor, @textColor: #fff) { +.gradientBar(@primaryColor, @secondaryColor, @textColor: @white) { color: @textColor; #gradient > .vertical(@primaryColor, @secondaryColor); border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%); @@ -341,7 +341,7 @@ // Gradients #gradient { - .horizontal(@startColor: #555, @endColor: #333) { + .horizontal(@startColor: @gray, @endColor: @grayDark) { background-color: @endColor; background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+ background-image: -webkit-gradient(linear, 0 0, 100% 0, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ @@ -350,7 +350,7 @@ background-image: linear-gradient(to right, @startColor, @endColor); // Standard, IE10 background-repeat: repeat-x; } - .vertical(@startColor: #555, @endColor: #333) { + .vertical(@startColor: @gray, @endColor: @grayDark) { background-color: mix(@startColor, @endColor, 60%); background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ @@ -359,7 +359,7 @@ background-image: linear-gradient(to bottom, @startColor, @endColor); // Standard, IE10 background-repeat: repeat-x; } - .directional(@startColor: #555, @endColor: #333, @deg: 45deg) { + .directional(@startColor: @gray, @endColor: @grayDark, @deg: 45deg) { background-color: @endColor; background-repeat: repeat-x; background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+ @@ -367,7 +367,7 @@ background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10 background-image: linear-gradient(@deg, @startColor, @endColor); // Standard, IE10 } - .horizontal-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + .horizontal-three-colors(@startColor: @lightBlueIcon, @midColor: @purple-l1, @colorStop: 50%, @endColor: @pink) { background-color: mix(@midColor, @endColor, 80%); background-image: -webkit-gradient(left, linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); background-image: -webkit-linear-gradient(left, @startColor, @midColor @colorStop, @endColor); @@ -377,7 +377,7 @@ background-repeat: no-repeat; } - .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { + .vertical-three-colors(@startColor: @lightBlueIcon, @midColor: @deepPurpleIcon, @colorStop: 50%, @endColor: @pink) { background-color: mix(@midColor, @endColor, 80%); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor); @@ -386,7 +386,7 @@ background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor); background-repeat: no-repeat; } - .radial(@innerColor: #555, @outerColor: #333) { + .radial(@innerColor: @gray, @outerColor: @grayDark) { background-color: @outerColor; background-image: -webkit-gradient(radial, center center, 0, center center, 460, from(@innerColor), to(@outerColor)); background-image: -webkit-radial-gradient(circle, @innerColor, @outerColor); @@ -394,7 +394,7 @@ background-image: -o-radial-gradient(circle, @innerColor, @outerColor); background-repeat: no-repeat; } - .striped(@color: #555, @angle: 45deg) { + .striped(@color: @gray, @angle: 45deg) { background-color: @color; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,.15)), color-stop(.75, rgba(255,255,255,.15)), color-stop(.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); @@ -404,7 +404,7 @@ } } -.checkeredBackground(@backgroundColor: #eee, @fillColor: #000, @fillOpacity: 0.25) { +.checkeredBackground(@backgroundColor: @gray-9, @fillColor: @black, @fillOpacity: 0.25) { background-image: url('data:image/svg+xml;charset=utf-8,\ \ \ @@ -435,14 +435,14 @@ // Button backgrounds // ------------------ -.buttonBackground(@startColor, @hoverColor: @startColor, @textColor: #fff, @textColorHover: @textColor) { - +.buttonBackground(@startColor, @hoverColor: @startColor, @textColor: @white, @textColorHover: @textColor) { + color: @textColor; border-color: @startColor @startColor darken(@startColor, 15%); border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%); - + background-color: @startColor; - + .caret { border-top-color: @textColor; border-bottom-color: @textColor; @@ -453,7 +453,7 @@ color: @textColorHover; background-color: @hoverColor; } - + &.disabled, &[disabled] { color: @white; background-color: @sand-1; diff --git a/src/Umbraco.Web.UI.Client/src/less/modals.less b/src/Umbraco.Web.UI.Client/src/less/modals.less index fa23e08983..925f845c4c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/modals.less +++ b/src/Umbraco.Web.UI.Client/src/less/modals.less @@ -85,7 +85,7 @@ bottom: 0px; position: absolute; padding: 0px; - background: #fff; + background: @white; } .umb-dialog .umb-btn-toolbar .umb-control-group{ diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/document-type-editor.less b/src/Umbraco.Web.UI.Client/src/less/pages/document-type-editor.less deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/nonodes.less b/src/Umbraco.Web.UI.Client/src/less/pages/nonodes.less index 868a358d21..7c493ab18e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/pages/nonodes.less +++ b/src/Umbraco.Web.UI.Client/src/less/pages/nonodes.less @@ -1,5 +1,6 @@ @import "../fonts.less"; // Loading fonts @import "../variables.less"; // Loading variables +@import "../colors.less"; // Loading colors that were in variables abbr, address, diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 823daedf22..5a71635c4d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -1,13 +1,13 @@ // // Container styles // -------------------------------------------------- -.umb-property-editor { +.umb-property-editor { width: 100%; } .umb-property-editor-tiny { width: 60px; - + &.umb-editor-push { width:30%; min-width:0; @@ -30,7 +30,7 @@ } .umb-codeeditor{ - width: 99%; + width: 99%; } // displays property inline with preceeding @@ -139,7 +139,7 @@ margin-bottom: 0; vertical-align: middle; padding: 6px 10px; - background: #f7f7f7; + background: @gray-11; flex: 0 0 auto; } @@ -169,8 +169,8 @@ border: 1px solid @white; padding: 6px 10px; font-family: monospace; - border: 1px solid #dfdfe1; - background: #f7f7f7; + border: 1px solid @gray-8; + background: @gray-11; margin: 0 15px 0 3px; border-radius: 3px; } @@ -232,6 +232,7 @@ } .umb-mediapicker-multi > div { width:100%; + .umb-property-editor--limit-width(); } @@ -253,7 +254,7 @@ color: @ui-action-discreet-type-hover; border-color: @ui-action-discreet-type-hover; } - + &:focus { outline: none; .tabbing-active &:after { @@ -358,11 +359,11 @@ max-width: 100%; } .umb-mediapicker { - + .umb-sortable-thumbnails li { border:none; } - + } .umb-mediapicker .umb-sortable-thumbnails li { @@ -623,7 +624,7 @@ .imagecropper .umb-cropper__container { position: relative; margin-bottom: 10px; - max-width: 100%; + max-width: 100%; border: 1px solid @gray-10; @media (min-width: 769px) { @@ -822,7 +823,7 @@ background: @blueExtraDark; position: relative; user-select: all; - + .icon-trash { position: relative; cursor: pointer; @@ -843,7 +844,7 @@ border: none; background: @white; } - + .twitter-typeahead { margin: 10px; margin-top: 16px; @@ -880,6 +881,21 @@ .umb-datepicker p {margin-top:10px;} .umb-datepicker p a{color: @gray-3;} +// +// Link picker +// -------------------------------------------------- +.umb-linkpicker { + .umb-linkpicker__url { + width: 50%; + padding-right: 5px; + } + + .umb-linkpicker__anchor { + width: 50%; + padding-left: 5px; + } +} + // // Code mirror - even though this isn't a proprety editor right now, it could be so I'm putting the styles here // -------------------------------------------------- diff --git a/src/Umbraco.Web.UI.Client/src/less/rte-content.less b/src/Umbraco.Web.UI.Client/src/less/rte-content.less index 5fd7bbf1c3..3fe4e52f92 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte-content.less @@ -1,4 +1,5 @@ @import "variables.less"; +@import "colors.less"; .mce-content-body .umb-macro-holder { border: 3px dotted @pinkLight; diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index 445ed7eb4a..d6d38f540a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -3,7 +3,7 @@ .umb-rte { position: relative; - + .umb-property-editor--limit-width(); } @@ -116,7 +116,7 @@ } } -.umb-rte .mce-btn.mce-active, .umb-rte .mce-btn.mce-active:active, +.umb-rte .mce-btn.mce-active, .umb-rte .mce-btn.mce-active:active, .umb-rte .mce-btn.mce-active:hover, .umb-rte .mce-btn.mce-active:focus { background: @gray-9; border-color: transparent; @@ -158,7 +158,7 @@ } .umb-grid .umb-rte { - border: 1px solid #d8d7d9; + border: 1px solid @gray-8; max-width: none; } diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index e8f6d4ee58..6071c4a5ef 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -76,11 +76,11 @@ @gray-10: #F3F3F5; @gray-11: #F6F6F7; -@sand-1: hsl(22, 18%, 84%);// added 2019 -@sand-2: hsl(22, 34%, 88%);// added 2019 -@sand-5: hsl(22, 31%, 93%);// added 2019 -@sand-6: hsl(22, 29%, 95%);// added 2019 -@sand-7: hsl(22, 26%, 97%);// added 2019 +@sand-1: #DED4CF;// added 2019 +@sand-2: #EBDED6;// added 2019 +@sand-5: #F3ECE8;// added 2019 +@sand-6: #F6F1EF;// added 2019 +@sand-7: #F9F7F5;// added 2019 // Additional Icon Colours @@ -124,7 +124,7 @@ //@u-greyLight: #f2ebe6;// added 2019 @u-white: #f9f7f4;// added 2019 -@u-black: black;// added 2019 +@u-black: @black;// added 2019 // UI colors @@ -132,7 +132,7 @@ @ui-option-type: @blueExtraDark; @ui-option-type-hover: @blueMid; -@ui-option: white; +@ui-option: @white; @ui-option-hover: @sand-7; @ui-option-disabled-type: @gray-6; @@ -161,14 +161,14 @@ @ui-light-active-type-hover: @blueMid; -@ui-action: white; +@ui-action: @white; @ui-action-hover: @sand-7; @ui-action-type: @blueExtraDark; @ui-action-type-hover: @blueMid; @ui-action-border: @blueExtraDark; @ui-action-border-hover: @blueMid; -@ui-action-discreet: white; +@ui-action-discreet: @white; @ui-action-discreet-hover: @sand-7; @ui-action-discreet-type: @blueExtraDark; @ui-action-discreet-type-hover: @blueMid; @@ -194,96 +194,11 @@ - -.red{color: @red;} -.blue{color: @blue;} -.black{color: @black;} -.turquoise{color: @turquoise;} -.turquoise-d1{color: @turquoise-d1;} - -.text-warning { - color: @orange; -} -.text-error { - color: @red; -} -.text-success { - color: @green; -} - - -//icon colors for tree icons -.color-red, .color-red i{color: @red-d1 !important;} -.color-blue, .color-blue i{color: @turquoise-d1 !important;} -.color-orange, .color-orange i{color: @orange !important;} -.color-green, .color-green i{color: @green-d1 !important;} -.color-yellow, .color-yellow i{color: @yellowIcon !important;} - -/* Colors based on https://zavoloklom.github.io/material-design-color-palette/colors.html */ -.btn-color-black {background-color: @black;} -.color-black i { color: @black;} - -.btn-color-blue-grey {background-color: @blueGrey;} -.color-blue-grey, .color-blue-grey i { color: @blueGrey !important;} - -.btn-color-grey{background-color: @grayIcon;} -.color-grey, .color-grey i { color: @grayIcon !important; } - -.btn-color-brown{background-color: @brownIcon;} -.color-brown, .color-brown i { color: @brownIcon !important; } - -.btn-color-blue{background-color: @blueIcon;} -.color-blue, .color-blue i { color: @blueIcon !important; } - -.btn-color-light-blue{background-color: @lightBlueIcon;} -.color-light-blue, .color-light-blue i {color: @lightBlueIcon !important;} - -.btn-color-cyan{background-color: @cyanIcon;} -.color-cyan, .color-cyan i { color: @cyanIcon !important; } - -.btn-color-green{background-color: @greenIcon;} -.color-green, .color-green i { color: @greenIcon !important; } - -.btn-color-light-green{background-color: @lightGreenIcon;} -.color-light-green, .color-light-green i {color: @lightGreenIcon !important; } - -.btn-color-lime{background-color: @limeIcon;} -.color-lime, .color-lime i { color: @limeIcon !important; } - -.btn-color-yellow{background-color: @yellowIcon;} -.color-yellow, .color-yellow i { color: @yellowIcon !important; } - -.btn-color-amber{background-color: @amberIcon;} -.color-amber, .color-amber i { color: @amberIcon !important; } - -.btn-color-orange{background-color: @orangeIcon;} -.color-orange, .color-orange i { color: @orangeIcon !important; } - -.btn-color-deep-orange{background-color: @deepOrangeIcon;} -.color-deep-orange, .color-deep-orange i { color: @deepOrangeIcon !important; } - -.btn-color-red{background-color: @redIcon;} -.color-red, .color-red i { color: @redIcon !important; } - -.btn-color-pink{background-color: @pinkIcon;} -.color-pink, .color-pink i { color: @pinkIcon !important; } - -.btn-color-purple{background-color: @purpleIcon;} -.color-purple, .color-purple i { color: @purpleIcon !important; } - -.btn-color-deep-purple{background-color: @deepPurpleIcon;} -.color-deep-purple, .color-deep-purple i { color: @deepPurpleIcon !important; } - -.btn-color-indigo{background-color: @indigoIcon;} -.color-indigo, .color-indigo i { color: @indigoIcon !important; } - - - // Scaffolding // ------------------------- @appHeaderHeight: 55px; @bodyBackground: @gray-10; -@textColor: #000; +@textColor: @black; @editorHeaderHeight: 70px; @editorFooterHeight: 50px; diff --git a/src/Umbraco.Web.UI.Client/src/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/navigation.controller.js index b585d22e9f..194c45afe6 100644 --- a/src/Umbraco.Web.UI.Client/src/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/navigation.controller.js @@ -510,6 +510,14 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar if (!event) { return; } + closeTree(); + }; + + $scope.onOutsideClick = function() { + closeTree(); + }; + + function closeTree() { if (!appState.getGlobalState("touchDevice")) { treeActive = false; $timeout(function () { @@ -518,7 +526,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar } }, 300); } - }; + } $scope.toggleLanguageSelector = function () { $scope.page.languageSelectorIsOpen = !$scope.page.languageSelectorIsOpen; diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index c4cb821818..dc40338d01 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -7,6 +7,31 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi .controller("previewController", function ($scope, $window, $location) { + $scope.currentCulture = {iso:'', title:'...', icon:'icon-loading'} + var cultures = []; + + $scope.tabbingActive = false; + // There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution. + // For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2 + function handleFirstTab(evt) { + if (evt.keyCode === 9) { + $scope.tabbingActive = true; + $scope.$digest(); + window.removeEventListener('keydown', handleFirstTab); + window.addEventListener('mousedown', disableTabbingActive); + } + } + + function disableTabbingActive(evt) { + $scope.tabbingActive = false; + $scope.$digest(); + window.removeEventListener('mousedown', disableTabbingActive); + window.addEventListener("keydown", handleFirstTab); + } + + window.addEventListener("keydown", handleFirstTab); + + //gets a real query string value function getParameterByName(name, url) { if (!url) url = $window.location.href; @@ -75,15 +100,55 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.valueAreLoaded = false; $scope.devices = [ - { name: "desktop", css: "desktop", icon: "icon-display", title: "Desktop" }, - { name: "laptop - 1366px", css: "laptop border", icon: "icon-laptop", title: "Laptop" }, - { name: "iPad portrait - 768px", css: "iPad-portrait border", icon: "icon-ipad", title: "Tablet portrait" }, - { name: "iPad landscape - 1024px", css: "iPad-landscape border", icon: "icon-ipad flip", title: "Tablet landscape" }, - { name: "smartphone portrait - 480px", css: "smartphone-portrait border", icon: "icon-iphone", title: "Smartphone portrait" }, - { name: "smartphone landscape - 320px", css: "smartphone-landscape border", icon: "icon-iphone flip", title: "Smartphone landscape" } + { name: "fullsize", css: "fullsize", icon: "icon-application-window-alt", title: "Browser" }, + { name: "desktop", css: "desktop shadow", icon: "icon-display", title: "Desktop" }, + { name: "laptop - 1366px", css: "laptop shadow", icon: "icon-laptop", title: "Laptop" }, + { name: "iPad portrait - 768px", css: "iPad-portrait shadow", icon: "icon-ipad", title: "Tablet portrait" }, + { name: "iPad landscape - 1024px", css: "iPad-landscape shadow", icon: "icon-ipad flip", title: "Tablet landscape" }, + { name: "smartphone portrait - 480px", css: "smartphone-portrait shadow", icon: "icon-iphone", title: "Smartphone portrait" }, + { name: "smartphone landscape - 320px", css: "smartphone-landscape shadow", icon: "icon-iphone flip", title: "Smartphone landscape" } ]; $scope.previewDevice = $scope.devices[0]; + $scope.sizeOpen = false; + $scope.cultureOpen = false; + + $scope.toggleSizeOpen = function() { + $scope.sizeOpen = toggleMenu($scope.sizeOpen); + } + $scope.toggleCultureOpen = function() { + $scope.cultureOpen = toggleMenu($scope.cultureOpen); + } + + function toggleMenu(isCurrentlyOpen) { + if (isCurrentlyOpen === false) { + closeOthers(); + return true; + } else { + return false; + } + } + function closeOthers() { + $scope.sizeOpen = false; + $scope.cultureOpen = false; + } + + $scope.windowClickHandler = function() { + closeOthers(); + } + function windowBlurHandler() { + closeOthers(); + $scope.$digest(); + } + + var win = angular.element($window); + + win.on("blur", windowBlurHandler); + + $scope.$on("$destroy", function () { + win.off("blur", handleBlwindowBlurHandlerur ); + }); + function setPageUrl(){ $scope.pageId = $location.search().id || getParameterByName("id"); @@ -123,6 +188,8 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.onFrameLoaded = function (iframe) { $scope.frameLoaded = true; configureSignalR(iframe); + + $scope.currentCultureIso = $location.search().culture || null; }; /*****************************************************************************/ @@ -136,17 +203,32 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi /*****************************************************************************/ /* Change culture */ /*****************************************************************************/ - $scope.changeCulture = function (culture) { - if($location.search().culture !== culture){ + $scope.changeCulture = function (iso) { + if($location.search().culture !== iso) { $scope.frameLoaded = false; - $location.search("culture", culture); + $scope.currentCultureIso = iso; + $location.search("culture", iso); setPageUrl(); } }; - - $scope.isCurrentCulture = function(culture) { - return $location.search().culture === culture; + $scope.registerCulture = function(iso, title, isDefault) { + var cultureObject = {iso: iso, title: title, isDefault: isDefault}; + cultures.push(cultureObject); } + + $scope.$watch("currentCultureIso", function(oldIso, newIso) { + // if no culture is selected, we will pick the default one: + if ($scope.currentCultureIso === null) { + $scope.currentCulture = cultures.find(function(culture) { + return culture.isDefault === true; + }) + return; + } + $scope.currentCulture = cultures.find(function(culture) { + return culture.iso === $scope.currentCultureIso; + }) + }); + }) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js index 67604aca44..3b405333bf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/copy/copy.controller.js @@ -1,98 +1,99 @@ - (function() { - "use strict"; +(function () { + "use strict"; - function CopyController($scope, localizationService, eventsService, entityHelper) { + function CopyController($scope, localizationService, eventsService, entityHelper) { - var vm = this; + var vm = this; - vm.labels = {}; - vm.hideSearch = hideSearch; - vm.selectResult = selectResult; - vm.onSearchResults = onSearchResults; - vm.submit = submit; - vm.close = close; + vm.labels = {}; + vm.hideSearch = hideSearch; + vm.selectResult = selectResult; + vm.onSearchResults = onSearchResults; + vm.onToggle = toggleHandler; + vm.submit = submit; + vm.close = close; - var dialogOptions = $scope.model; - var node = dialogOptions.currentNode; + var dialogOptions = $scope.model; + var node = dialogOptions.currentNode; - $scope.model.relateToOriginal = true; - $scope.dialogTreeApi = {}; + $scope.model.relateToOriginal = true; + $scope.dialogTreeApi = {}; - vm.searchInfo = { - searchFromId: null, - searchFromName: null, - showSearch: false, - results: [], - selectedSearchResults: [] - }; + vm.searchInfo = { + searchFromId: null, + searchFromName: null, + showSearch: false, + results: [], + selectedSearchResults: [] + }; - // get entity type based on the section - $scope.entityType = entityHelper.getEntityTypeFromSection(dialogOptions.section); + // get entity type based on the section + $scope.entityType = entityHelper.getEntityTypeFromSection(dialogOptions.section); - function onInit() { + function onInit() { - var labelKeys = [ - "general_copy", - "defaultdialogs_relateToOriginalLabel" - ]; + var labelKeys = [ + "general_copy", + "defaultdialogs_relateToOriginalLabel" + ]; - localizationService.localizeMany(labelKeys).then(function (data) { + localizationService.localizeMany(labelKeys).then(function (data) { - vm.labels.title = data[0]; - vm.labels.relateToOriginal = data[1]; + vm.labels.title = data[0]; + vm.labels.relateToOriginal = data[1]; - setTitle(vm.labels.title); - }); - } + setTitle(vm.labels.title); + }); + } - function setTitle(value) { - if (!$scope.model.title) { - $scope.model.title = value; - } - } + function setTitle(value) { + if (!$scope.model.title) { + $scope.model.title = value; + } + } - function nodeSelectHandler(args) { - if (args && args.event) { - args.event.preventDefault(); - args.event.stopPropagation(); - } + function nodeSelectHandler(args) { + if (args && args.event) { + args.event.preventDefault(); + args.event.stopPropagation(); + } - //eventsService.emit("editors.content.copyController.select", args); + //eventsService.emit("editors.content.copyController.select", args); - if ($scope.model.target) { - //un-select if there's a current one selected - $scope.model.target.selected = false; - } + if ($scope.model.target) { + //un-select if there's a current one selected + $scope.model.target.selected = false; + } - $scope.model.target = args.node; - $scope.model.target.selected = true; - } + $scope.model.target = args.node; + $scope.model.target.selected = true; + } - function nodeExpandedHandler(args) { - // open mini list view for list views - if (args.node.metaData.isContainer) { - openMiniListView(args.node); - } - } + function nodeExpandedHandler(args) { + // open mini list view for list views + if (args.node.metaData.isContainer) { + openMiniListView(args.node); + } + } - function hideSearch() { - vm.searchInfo.showSearch = false; - vm.searchInfo.searchFromId = null; - vm.searchInfo.searchFromName = null; - vm.searchInfo.results = []; - } + function hideSearch() { + vm.searchInfo.showSearch = false; + vm.searchInfo.searchFromId = null; + vm.searchInfo.searchFromName = null; + vm.searchInfo.results = []; + } - // method to select a search result - function selectResult(evt, result) { - result.selected = result.selected === true ? false : true; - nodeSelectHandler({ event: evt, node: result }); - } + // method to select a search result + function selectResult(evt, result) { + result.selected = result.selected === true ? false : true; + nodeSelectHandler({ event: evt, node: result }); + } - //callback when there are search results - function onSearchResults(results) { - vm.searchInfo.results = results; - vm.searchInfo.showSearch = true; - } + //callback when there are search results + function onSearchResults(results) { + vm.searchInfo.results = results; + vm.searchInfo.showSearch = true; + } $scope.onTreeInit = function () { $scope.dialogTreeApi.callbacks.treeNodeSelect(nodeSelectHandler); @@ -100,19 +101,19 @@ } - // Mini list view - $scope.selectListViewNode = function (node) { - node.selected = node.selected === true ? false : true; - nodeSelectHandler({ node: node }); - }; + // Mini list view + $scope.selectListViewNode = function (node) { + node.selected = node.selected === true ? false : true; + nodeSelectHandler({ node: node }); + }; - $scope.closeMiniListView = function () { - $scope.miniListView = undefined; - }; + $scope.closeMiniListView = function () { + $scope.miniListView = undefined; + }; - function openMiniListView(node) { - $scope.miniListView = node; - } + function openMiniListView(node) { + $scope.miniListView = node; + } function submit() { if ($scope.model && $scope.model.submit) { @@ -126,9 +127,16 @@ } } - onInit(); - } + function toggleHandler(type) { + // If the relateToOriginal toggle is clicked + if (type === "relate") { + $scope.model.relateToOriginal = !$scope.model.relateToOriginal; + } + } - angular.module("umbraco").controller("Umbraco.Editors.CopyController", CopyController); + onInit(); + } + + 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 index 07c44e90ee..86c0186374 100644 --- 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 @@ -2,7 +2,7 @@ -
+
-
-
- -
-
+ + + + + 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 index 6ba2ec0270..a7d2dbbee2 100644 --- 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 @@ -1,4 +1,4 @@ -
+
@@ -16,7 +16,7 @@
- + - + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-content.html index 9cd245b0d5..eb12fc7940 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-content.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-variant-content.html @@ -27,6 +27,12 @@ + + +
+ This item is in the Recycle Bin +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html index 57a4fdae05..82b21e4c3b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html @@ -16,5 +16,7 @@ - {{vm.text}} + + + {{vm.text}} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html new file mode 100644 index 0000000000..62b2052771 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/member/umb-member-node-info.html @@ -0,0 +1,50 @@ +
+
+ + + +
+
{{ group.label }}
+
+
+ + + +
+
+ +
+ +
+ + + + + + {{node.createDateFormatted}} by {{ node.owner.name }} + + + + {{node.updateDateFormatted}} + + + + + + + + +
{{ node.id }}
+ {{ node.key }} +
+ +
+
+
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html index 46660fc685..c2f9ceebc4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html @@ -6,12 +6,12 @@
-
+
{{inheritsFrom}} -
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html b/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html index 6ef4378106..ee9034fac6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html @@ -8,10 +8,10 @@
- + - - - diff --git a/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html b/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html index ecb1e34e0c..42876cc27a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/assigndomain.html @@ -45,7 +45,7 @@ - + diff --git a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/delete.html b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/delete.html index 324fb2ae9f..f7f036178f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/delete.html @@ -5,7 +5,7 @@ Are you sure you want to delete {{currentNode.name}}?

- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardintro.html deleted file mode 100644 index c29b52d96e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardintro.html +++ /dev/null @@ -1,10 +0,0 @@ -

Start here

- -

Get started with Members right now

-

Use the tool below to search for an existing member.

- -

More about members

- -
diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/profiler.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/profiler.html index 6bff0bba9b..95c6af9f79 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/profiler.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/profiler.html @@ -27,7 +27,7 @@
- + diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js index 474b07d12c..66983bbc05 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js @@ -10,7 +10,11 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic var evts = []; var vm = this; - + + vm.header = {}; + vm.header.editorfor = "visuallyHiddenTexts_newDataType"; + vm.header.setPageTitle = true; + //setup scope vars vm.page = {}; vm.page.loading = false; diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/edit.html index 0bb2b01e31..2d481de852 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/edit.html @@ -8,7 +8,9 @@ hide-icon="true" hide-description="true" hide-alias="true" - navigation="vm.page.navigation"> + navigation="vm.page.navigation" + editorfor="vm.header.editorfor" + setpagetitle="vm.header.setPageTitle"> diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/delete.html index c84eeeedae..e7fdf4f96e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/delete.html @@ -11,7 +11,7 @@
+ on-cancel="cancel" confirm-button-style="danger">
@@ -31,7 +31,7 @@ - +
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js index 9d73aa8838..b04cccbc0d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js @@ -64,7 +64,8 @@ if (infiniteMode) { documentTypeId = $scope.model.id; create = $scope.model.create; - noTemplate = $scope.model.notemplate; + if (create && !documentTypeId) documentTypeId = -1; + noTemplate = $scope.model.notemplate || $scope.model.noTemplate; isElement = $scope.model.isElement; allowVaryByCulture = $scope.model.allowVaryByCulture; vm.submitButtonKey = "buttons_saveAndClose"; diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/importdocumenttype.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/importdocumenttype.html index 5fd453bcd7..8f84a0cceb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/importdocumenttype.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/importdocumenttype.html @@ -9,7 +9,7 @@
- diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/delete.html b/src/Umbraco.Web.UI.Client/src/views/macros/delete.html index e6c24507b6..834d7f97ee 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/macros/delete.html @@ -5,6 +5,6 @@ Are you sure you want to delete {{vm.name}}?

- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/edit.html b/src/Umbraco.Web.UI.Client/src/views/macros/edit.html index ddb5f98781..9640419ee2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/macros/edit.html @@ -6,7 +6,8 @@ alias="vm.macro.alias" hide-description="true" hide-icon="true" - navigation="vm.page.navigation"> + navigation="vm.page.navigation"editorfor="vm.header.editorfor" + setpagetitle="vm.header.setPageTitle"> diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js index 272e5dd03a..3261739d36 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/macros/macros.edit.controller.js @@ -11,7 +11,10 @@ function MacrosEditController($scope, $q, $routeParams, macroResource, editorSta var vm = this; vm.promises = {}; - + vm.header = {}; + vm.header.editorfor = "general_macro"; + vm.header.setPageTitle = true; + vm.page = {}; vm.page.loading = false; vm.page.saveButtonState = "init"; diff --git a/src/Umbraco.Web.UI.Client/src/views/macros/views/parameters.html b/src/Umbraco.Web.UI.Client/src/views/macros/views/parameters.html index 6ea79fe16d..ccdb41384f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/macros/views/parameters.html +++ b/src/Umbraco.Web.UI.Client/src/views/macros/views/parameters.html @@ -10,13 +10,13 @@
- +
{{parameter.label}} ({{parameter.key}}){{parameter.editor}}
- Edit - Remove + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/delete.html index 971d8f30ee..c059710ebb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/delete.html @@ -27,7 +27,7 @@ - +
diff --git a/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.controller.js new file mode 100644 index 0000000000..635c536816 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.controller.js @@ -0,0 +1,11 @@ +(function () { + "use strict"; + + function MemberAppContentController($scope) { + + var vm = this; + + } + + angular.module("umbraco").controller("Umbraco.Editors.Member.Apps.ContentController", MemberAppContentController); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html new file mode 100644 index 0000000000..29df5ba638 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/member/apps/content/content.html @@ -0,0 +1,17 @@ +
+ +
+ +
+
{{ group.label }}
+
+ +
+ + + +
+ +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/member/apps/info/info.html b/src/Umbraco.Web.UI.Client/src/views/member/apps/info/info.html new file mode 100644 index 0000000000..bd9cb98b64 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/member/apps/info/info.html @@ -0,0 +1,4 @@ + + diff --git a/src/Umbraco.Web.UI.Client/src/views/member/edit.html b/src/Umbraco.Web.UI.Client/src/views/member/edit.html index 58bebe1e34..3fec222350 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/edit.html @@ -14,29 +14,22 @@ hide-icon="true" hide-description="true" hide-alias="true" + navigation="content.apps" + on-select-navigation-item="appChanged(item)" show-back-button="showBack()" on-back="onBack()" editorfor="header.editorfor" setpagetitle="header.setPageTitle"> - - -
- -
-
{{ group.label }}
+ + +
+
+
- -
- - - -
-
- -
+ diff --git a/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js index 263f78f314..c0967df232 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js @@ -45,9 +45,7 @@ function MemberEditController($scope, $routeParams, $location, appState, memberR $scope.content = data; - setHeaderNameState($scope.content); - - editorState.set($scope.content); + init(); $scope.page.loading = false; @@ -59,9 +57,7 @@ function MemberEditController($scope, $routeParams, $location, appState, memberR .then(function (data) { $scope.content = data; - setHeaderNameState($scope.content); - - editorState.set($scope.content); + init(); $scope.page.loading = false; @@ -90,9 +86,7 @@ function MemberEditController($scope, $routeParams, $location, appState, memberR $scope.content = data; - setHeaderNameState($scope.content); - - editorState.set($scope.content); + init(); if (!infiniteMode) { var path = buildTreePath(data); @@ -121,8 +115,48 @@ function MemberEditController($scope, $routeParams, $location, appState, memberR } - function setHeaderNameState(content) { - $scope.page.nameLocked = true; + function init() { + + var content = $scope.content; + + // we need to check wether an app is present in the current data, if not we will present the default app. + var isAppPresent = false; + + // on first init, we dont have any apps. but if we are re-initializing, we do, but ... + if ($scope.app) { + + // lets check if it still exists as part of our apps array. (if not we have made a change to our docType, even just a re-save of the docType it will turn into new Apps.) + _.forEach(content.apps, function (app) { + if (app === $scope.app) { + isAppPresent = true; + } + }); + + // if we did reload our DocType, but still have the same app we will try to find it by the alias. + if (isAppPresent === false) { + _.forEach(content.apps, function (app) { + if (app.alias === $scope.app.alias) { + isAppPresent = true; + app.active = true; + $scope.appChanged(app); + } + }); + } + + } + + // if we still dont have a app, lets show the first one: + if (isAppPresent === false) { + content.apps[0].active = true; + $scope.appChanged(content.apps[0]); + } + + if (content.membershipScenario === 0) { + $scope.page.nameLocked = true; + } + + editorState.set($scope.content); + } /** Just shows a simple notification that there are client side validation issues to be fixed */ @@ -184,6 +218,15 @@ function MemberEditController($scope, $routeParams, $location, appState, memberR }; + $scope.appChanged = function (app) { + $scope.app = app; + + // setup infinite mode + if (infiniteMode) { + $scope.page.submitButtonLabelKey = "buttons_saveAndClose"; + } + } + $scope.showBack = function () { return !!listName; } diff --git a/src/Umbraco.Web.UI.Client/src/views/membergroups/delete.html b/src/Umbraco.Web.UI.Client/src/views/membergroups/delete.html index fff0348b87..17a71fa9de 100644 --- a/src/Umbraco.Web.UI.Client/src/views/membergroups/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/membergroups/delete.html @@ -5,7 +5,7 @@ Are you sure you want to delete {{currentNode.name}}?

- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/membertypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/membertypes/delete.html index 84eeaa0182..d87b6737f4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/membertypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/membertypes/delete.html @@ -16,7 +16,7 @@ - + diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html index 9568536d5f..14aeaf1b58 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html @@ -7,19 +7,31 @@
- -
{{category.name}}
-
+ {{category.name}} +
@@ -30,7 +42,7 @@ @@ -64,7 +78,7 @@ @@ -140,9 +156,9 @@
@@ -274,7 +290,7 @@
@@ -304,7 +320,7 @@
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.html b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.html index 0adbc2aaa2..b5d187eb57 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.html @@ -5,7 +5,7 @@ Are you sure you want to delete {{currentNode.name}}?

- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html index fa1baa1eff..4964007fb0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html @@ -12,7 +12,7 @@ Are you sure you want to delete {{currentNode.name}}?

- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html index 1056b45f9d..af2dba1d7b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html @@ -21,7 +21,7 @@
- Remove +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.html index 0aa4b5e561..742f5bafa4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.prevalues.html @@ -53,7 +53,7 @@
- Cancel +
@@ -65,8 +65,8 @@

{{item.alias}} ({{item.width}}px × {{item.height}}px)

- Edit - Remove + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/includeproperties.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/includeproperties.prevalues.html index ba08aa2293..c6675ccec8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/includeproperties.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/includeproperties.prevalues.html @@ -12,7 +12,7 @@
- +
@@ -31,7 +31,7 @@
- (system field) + (system field)
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index 80e2fa7ce7..c9d4caf312 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -99,6 +99,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl function sync() { $scope.model.value = $scope.ids.join(); + removeAllEntriesAction.isDisabled = $scope.ids.length === 0; }; function setDirty() { @@ -247,6 +248,31 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl return true; } + function removeAllEntries() { + $scope.mediaItems.length = 0;// AngularJS way to empty the array. + $scope.ids.length = 0;// AngularJS way to empty the array. + sync(); + setDirty(); + } + + var removeAllEntriesAction = { + labelKey: 'clipboard_labelForRemoveAllEntries', + labelTokens: [], + icon: 'trash', + method: removeAllEntries, + isDisabled: true + }; + + if (multiPicker === true) { + var propertyActions = [ + removeAllEntriesAction + ]; + + if ($scope.umbProperty) { + $scope.umbProperty.setPropertyActions(propertyActions); + } + } + $scope.sortableOptions = { containment: 'parent', cursor: 'move', diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index 635a80dbe9..d625917afb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -55,8 +55,8 @@ }); function setCurrentNode(node) { - vm.currentNode = node; updateModel(); + vm.currentNode = node; } var copyAllEntries = function() { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.controller.js index 4a9a07428d..6e807ffaa4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.controller.js @@ -53,10 +53,6 @@ }); } - $scope.canAdd = function () { - return !$scope.model.docTypes || !$scope.model.value || $scope.model.value.length < $scope.model.docTypes.length; - } - $scope.remove = function (index) { $scope.model.value.splice(index, 1); } @@ -112,6 +108,7 @@ }); }); } + $scope.canAdd = function () { return !$scope.model.value || _.some($scope.model.elemTypes, function (elType) { return !_.find($scope.model.value, function (c) { @@ -120,7 +117,6 @@ }); } - $scope.openElemTypeModal = function ($event, config) { //we have to add the alias to the objects (they are stored as ncAlias) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html index b147e5ccca..d0b5823f74 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html @@ -3,6 +3,6 @@
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html index 72b6fb7279..ee2929e3ea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html @@ -2,16 +2,12 @@
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js index 884cc62d43..4a7fff99f8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js @@ -15,7 +15,22 @@ function textAreaController($scope, validationMessageService) { if ($scope.model.config && $scope.model.config.maxChars) { $scope.model.maxlength = true; } - + + $scope.$on("formSubmitting", function() { + if ($scope.isLengthValid()) { + $scope.textareaFieldForm.textarea.$setValidity("maxChars", true); + } else { + $scope.textareaFieldForm.textarea.$setValidity("maxChars", false); + } + }); + + $scope.isLengthValid = function() { + if (!$scope.model.maxlength) { + return true; + } + return $scope.model.config.maxChars >= $scope.model.count; + } + $scope.model.change = function () { if ($scope.model.value) { $scope.model.count = $scope.model.value.length; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html index 04bd8590d2..d255c4a5d6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html @@ -10,7 +10,7 @@
%0% characters left.
-
+
Maximum %0% characters, %1% too many.
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js index e86d8caef4..b47c3584b3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js @@ -11,7 +11,19 @@ function textboxController($scope, validationMessageService) { // if no max is specified in the config $scope.model.config.maxChars = 500; } - + + $scope.$on("formSubmitting", function() { + if ($scope.isLengthValid()) { + $scope.textboxFieldForm.textbox.$setValidity("maxChars", true); + } else { + $scope.textboxFieldForm.textbox.$setValidity("maxChars", false); + } + }); + + $scope.isLengthValid = function() { + return $scope.model.config.maxChars >= $scope.model.count; + } + $scope.model.change = function () { if ($scope.model.value) { $scope.model.count = $scope.model.value.length; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html index e1f5dac733..5d86259e93 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.html @@ -19,7 +19,7 @@

{{model.label}} %0% characters left.

%0% characters left.

-
+

{{model.label}} Maximum %0% characters, %1% too many.

diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html index b937d0869d..246e5bdb7a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/delete.html @@ -5,7 +5,7 @@ Are you sure you want to delete {{currentNode.name}}?

- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js index 44fbf6ffe9..74b3e31b87 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.controller.js @@ -10,6 +10,10 @@ function RelationTypeEditController($scope, $routeParams, relationTypeResource, var vm = this; + vm.header = {}; + vm.header.editorfor = "relationType_tabRelationType"; + vm.header.setPageTitle = true; + vm.page = {}; vm.page.loading = false; vm.page.saveButtonState = "init"; diff --git a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html index 2c86161bda..35e7aa5176 100644 --- a/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/relationtypes/edit.html @@ -8,7 +8,9 @@ alias="vm.relationType.alias" hide-description="true" hide-icon="true" - navigation="vm.page.navigation"> + navigation="vm.page.navigation" + editorfor="vm.header.editorfor" + setpagetitle="vm.header.setPageTitle"> diff --git a/src/Umbraco.Web.UI.Client/src/views/scripts/delete.html b/src/Umbraco.Web.UI.Client/src/views/scripts/delete.html index aae686d5ce..c8f6a2a9b5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/scripts/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/scripts/delete.html @@ -5,7 +5,7 @@ Are you sure you want to delete {{currentNode.name}}?

- + diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/delete.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/delete.html index d32a7197e1..abd082d413 100644 --- a/src/Umbraco.Web.UI.Client/src/views/stylesheets/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/delete.html @@ -5,7 +5,7 @@ Are you sure you want to delete {{currentNode.name}}?

- + diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/delete.html b/src/Umbraco.Web.UI.Client/src/views/templates/delete.html index 7c3af5937e..d45353db26 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/delete.html @@ -12,7 +12,7 @@ Are you sure you want to delete {{currentNode.name}}?

- + diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html index ee77e4c14e..b35a29d2de 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html @@ -207,7 +207,7 @@ @@ -249,7 +249,7 @@
0 && filterCollection && filterCollection.length > 0) { + for (var i = 0; i < selectedCollection.length; i++) { + for (var j = 0; j < filterCollection.length; j++) { + if (filterCollection[j][keyField] === selectedCollection[i]) { + filterCollection[j].selected = true; + } + } + } + } + } + function getSortLabel(sortKey, sortDirection) { var found = _.find(vm.userSortData, function (i) { @@ -467,6 +511,7 @@ vm.usersOptions.userStates.splice(index, 1); } + updateLocation("userStates", vm.usersOptions.userStates.join(",")); getUsers(); } @@ -483,20 +528,28 @@ vm.usersOptions.userGroups.splice(index, 1); } + updateLocation("userGroups", vm.usersOptions.userGroups.join(",")); getUsers(); } function setOrderByFilter(value, direction) { vm.usersOptions.orderBy = value; vm.usersOptions.orderDirection = direction; + updateLocation("orderBy", value); + updateLocation("orderDirection", direction); getUsers(); } function changePageNumber(pageNumber) { vm.usersOptions.pageNumber = pageNumber; + updateLocation("pageNumber", pageNumber); getUsers(); } + function updateLocation(key, value) { + $location.search(key, value); + } + function createUser(addUserForm) { if (formHelper.submitForm({ formCtrl: addUserForm, scope: $scope })) { @@ -575,17 +628,53 @@ } function goToUser(user) { - $location.path(pathToUser(user)).search("create", null).search("invite", null); + $location.path(pathToUser(user)) + .search("orderBy", vm.usersOptions.orderBy) + .search("orderDirection", vm.usersOptions.orderDirection) + .search("pageNumber", vm.usersOptions.pageNumber) + .search("userStates", getUsersOptionsFilterCollectionAsDelimitedStringOrNull(vm.usersOptions.userStates)) + .search("userGroups", getUsersOptionsFilterCollectionAsDelimitedStringOrNull(vm.usersOptions.userGroups)) + .search("create", null) + .search("invite", null); + } + + function getUsersOptionsFilterCollectionAsDelimitedStringOrNull(collection) { + if (collection && collection.length > 0) { + return collection.join(","); + } + + return null; } function getEditPath(user) { - return pathToUser(user) + "?mculture=" + $location.search().mculture; + return pathToUser(user) + usersOptionsAsQueryString(); } - + function pathToUser(user) { return "/users/users/user/" + user.id; } + function usersOptionsAsQueryString() { + var qs = "?orderBy=" + vm.usersOptions.orderBy + + "&orderDirection=" + vm.usersOptions.orderDirection + + "&pageNumber=" + vm.usersOptions.pageNumber; + + qs += addUsersOptionsFilterCollectionToQueryString("userStates", vm.usersOptions.userStates); + qs += addUsersOptionsFilterCollectionToQueryString("userGroups", vm.usersOptions.userGroups); + + qs += "&mculture=" + $location.search().mculture; + + return qs; + } + + function addUsersOptionsFilterCollectionToQueryString(name, collection) { + if (collection && collection.length > 0) { + return "&" + name + "=" + collection.join(","); + } + + return ""; + } + // helpers function getUsers() { @@ -604,6 +693,7 @@ formatDates(vm.users); setUserDisplayState(vm.users); vm.userStatesFilter = usersHelper.getUserStatesFilter(data.userStates); + initUserStateSelections(); vm.loading = false; diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html index 1ed148a23f..afaaf865c8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html @@ -505,6 +505,7 @@

diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 803029b081..d1de4de358 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -351,6 +351,10 @@ 9000 / http://localhost:9000/ + http://localhost:8600/ + 8500 + / + http://localhost:8500 False False diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml index d7d3737bf3..e46ae58096 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml @@ -1,4 +1,4 @@ -@using Umbraco.Core +@using Umbraco.Core @using ClientDependency.Core @using ClientDependency.Core.Mvc @using Umbraco.Web.Composing @@ -8,8 +8,7 @@ @{ var disableDevicePreview = Model.DisableDevicePreview.ToString().ToLowerInvariant(); - Html - .RequiresCss("assets/css/canvasdesigner.css", "Umbraco"); + Html.RequiresCss("assets/css/canvasdesigner.css", "Umbraco"); } @@ -23,7 +22,7 @@ new BasicPath("Umbraco", Current.IOHelper.ResolveUrl(Current.Configs.Global().UmbracoPath))) - +
@if (string.IsNullOrWhiteSpace(Model.PreviewExtendedHeaderView) == false) @@ -35,31 +34,40 @@

-
-
- -
-
    -
  • - -
  • +
+ + +
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 0deac8b50f..a74c6c6243 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -620,6 +620,7 @@ Fortryd Celle margen Vælg + Ryd Luk Luk vindue Kommentar diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 3f9564f8b2..7a3575f98f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -301,6 +301,7 @@ Select the date and time to publish and/or unpublish the content item. Create new Paste from clipboard + This item is in the Recycle Bin Create a new Content Template from '%0%' @@ -625,6 +626,7 @@ Cancel Cell margin Choose + Clear Close Close Window Comment @@ -2178,6 +2180,7 @@ To manage your website, simply open the Umbraco back office and start adding con Partial View Partial View Macro Member + Data type References diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index e4b88400e4..db84d945b0 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -305,6 +305,7 @@ Select the date and time to publish and/or unpublish the content item. Create new Paste from clipboard + This item is in the Recycle Bin Create a new Content Template from '%0%' @@ -631,6 +632,7 @@ Cancel Cell margin Choose + Clear Close Close Window Comment @@ -2194,6 +2196,7 @@ To manage your website, simply open the Umbraco back office and start adding con Partial View Partial View Macro Member + Data type References diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml index 8ddf2fae26..31846e9e07 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml @@ -361,6 +361,7 @@ Avbryt Cellmarginal Välj + Rensa Stäng Stäng fönstret Kommentar diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Embed.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Embed.cshtml index 393157bcf8..4a915a444b 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Embed.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Embed.cshtml @@ -1,7 +1,10 @@ @model dynamic @using Umbraco.Web.Templates - +@{ + string embedValue = Convert.ToString(Model.value); + embedValue = embedValue.DetectIsJson() ? Model.value.preview : Model.value; +}
- @Html.Raw(Model.value) + @Html.Raw(embedValue)
diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index 00b556fd34..44244db4e0 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -38,9 +38,141 @@ - Preview modeClick to end]]> + + Preview mode + + … + + + Click to end + + + + ]]> - + false + true + false + + + + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index a7f6e1e0f1..698c771db9 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -38,7 +38,140 @@ - Preview modeClick to end]]> + + Preview mode + + … + + + Click to end + + + + ]]> + false + true + false + + + + @@ -109,4 +252,17 @@ umbracoApplicationUrl=""> - + + + \ No newline at end of file diff --git a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentCacheRefresher.cs index 0422d3c674..5c997efeaf 100644 --- a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; @@ -140,6 +141,7 @@ namespace Umbraco.Web.Cache public class JsonPayload { + [JsonConstructor] public JsonPayload(int id, Guid? key, TreeChangeTypes changeTypes) { Id = id; diff --git a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs index dfb85aad6a..9d0c8c8450 100644 --- a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs @@ -26,7 +26,6 @@ namespace Umbraco.Web.Cache public static readonly Guid UniqueId = Guid.Parse("3E0F95D8-0BE5-44B8-8394-2B8750B62654"); private readonly IPublishedSnapshotService _publishedSnapshotService; - private readonly IDomainService _domainService; public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs index 8c251cacd2..add7e2f16a 100644 --- a/src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs +++ b/src/Umbraco.Web/ContentApps/ContentEditorContentAppFactory.cs @@ -14,6 +14,7 @@ namespace Umbraco.Web.ContentApps private ContentApp _contentApp; private ContentApp _mediaApp; + private ContentApp _memberApp; public ContentApp GetContentAppFor(object o, IEnumerable userGroups) { @@ -45,6 +46,16 @@ namespace Umbraco.Web.ContentApps case IMedia _: return null; + case IMember _: + return _memberApp ?? (_memberApp = new ContentApp + { + Alias = "umbContent", + Name = "Content", + Icon = Constants.Icons.Content, + View = "views/member/apps/content/content.html", + Weight = Weight + }); + default: throw new NotSupportedException($"Object type {o.GetType()} is not supported here."); } diff --git a/src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs index 49be194349..fac03c43d0 100644 --- a/src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs +++ b/src/Umbraco.Web/ContentApps/ContentInfoContentAppFactory.cs @@ -13,6 +13,7 @@ namespace Umbraco.Web.ContentApps private ContentApp _contentApp; private ContentApp _mediaApp; + private ContentApp _memberApp; public ContentApp GetContentAppFor(object o, IEnumerable userGroups) { @@ -37,6 +38,15 @@ namespace Umbraco.Web.ContentApps View = "views/media/apps/info/info.html", Weight = Weight }); + case IMember _: + return _memberApp ?? (_memberApp = new ContentApp + { + Alias = "umbInfo", + Name = "Info", + Icon = "icon-info", + View = "views/member/apps/info/info.html", + Weight = Weight + }); default: throw new NotSupportedException($"Object type {o.GetType()} is not supported here."); diff --git a/src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs b/src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs index e715dc569e..3e0dea0f5e 100644 --- a/src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs +++ b/src/Umbraco.Web/ContentApps/ListViewContentAppFactory.cs @@ -45,6 +45,8 @@ namespace Umbraco.Web.ContentApps entityType = "media"; dtdId = Core.Constants.DataTypes.DefaultMediaListView; break; + case IMember member: + return null; default: throw new NotSupportedException($"Object type {o.GetType()} is not supported here."); } diff --git a/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs b/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs index aebd13b6c4..24548a8089 100644 --- a/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; @@ -18,8 +19,8 @@ namespace Umbraco.Web.Editors [PrefixlessBodyModelValidator] public abstract class BackOfficeNotificationsController : UmbracoAuthorizedJsonController { - protected BackOfficeNotificationsController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + protected BackOfficeNotificationsController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { } } diff --git a/src/Umbraco.Web/Editors/Binders/MemberBinder.cs b/src/Umbraco.Web/Editors/Binders/MemberBinder.cs index 63bf0f0473..c63e2c13a6 100644 --- a/src/Umbraco.Web/Editors/Binders/MemberBinder.cs +++ b/src/Umbraco.Web/Editors/Binders/MemberBinder.cs @@ -5,6 +5,7 @@ using System.Web.Http.ModelBinding; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using System.Linq; using Umbraco.Web.Composing; @@ -17,15 +18,17 @@ namespace Umbraco.Web.Editors.Binders internal class MemberBinder : IModelBinder { private readonly ContentModelBinderHelper _modelBinderHelper; + private readonly IShortStringHelper _shortStringHelper; private readonly ServiceContext _services; - public MemberBinder() : this(Current.Services) + public MemberBinder() : this(Current.Services, Current.ShortStringHelper) { } - public MemberBinder(ServiceContext services) + public MemberBinder(ServiceContext services, IShortStringHelper shortStringHelper) { - _services = services; + _services = services ?? throw new ArgumentNullException(nameof(services)); + _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); _modelBinderHelper = new ContentModelBinderHelper(); } @@ -107,7 +110,7 @@ namespace Umbraco.Web.Editors.Binders /// private void FilterMembershipProviderProperties(IContentTypeBase contentType) { - var defaultProps = ConventionsHelper.GetStandardPropertyTypeStubs(Current.ShortStringHelper); + var defaultProps = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper); //remove all membership properties, these values are set with the membership provider. var exclude = defaultProps.Select(x => x.Value.Alias).ToArray(); FilterContentTypeProperties(contentType, exclude); diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index ecf0c94483..96a99830d6 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -34,8 +34,6 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)] public class CodeFileController : BackOfficeNotificationsController { - private readonly IShortStringHelper _shortStringHelper; - public CodeFileController( IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, @@ -46,9 +44,9 @@ namespace Umbraco.Web.Editors IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + { - _shortStringHelper = shortStringHelper; } /// @@ -242,7 +240,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } - return snippets.Select(snippet => new SnippetDisplay() {Name = snippet.SplitPascalCasing(_shortStringHelper).ToFirstUpperInvariant(), FileName = snippet}); + return snippets.Select(snippet => new SnippetDisplay() {Name = snippet.SplitPascalCasing(ShortStringHelper).ToFirstUpperInvariant(), FileName = snippet}); } /// diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index d6fe5060a9..952a924adb 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -11,31 +11,32 @@ using System.Web.Http.ModelBinding; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Models.Mapping; -using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi; -using Umbraco.Web.WebApi.Filters; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Events; using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Entities; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Validation; -using Umbraco.Web.Composing; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Actions; +using Umbraco.Web.Composing; using Umbraco.Web.ContentApps; using Umbraco.Web.Editors.Binders; using Umbraco.Web.Editors.Filters; -using Umbraco.Core.Models.Entities; -using Umbraco.Core.Persistence; -using Umbraco.Core.Security; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Mapping; +using Umbraco.Web.Mvc; using Umbraco.Web.Routing; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; -using Umbraco.Core.Dictionary; using Umbraco.Core.Strings; namespace Umbraco.Web.Editors @@ -1692,6 +1693,7 @@ namespace Umbraco.Web.Editors var permission = Services.UserService.GetPermissions(Security.CurrentUser, node.Path); + if (permission.AssignedPermissions.Contains(ActionAssignDomain.ActionLetter.ToString(), StringComparer.Ordinal) == false) { var response = Request.CreateResponse(HttpStatusCode.BadRequest); diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index 10dc95cc68..4f9e41eeb0 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -19,7 +19,6 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; - namespace Umbraco.Web.Editors { /// @@ -29,7 +28,6 @@ namespace Umbraco.Web.Editors public abstract class ContentControllerBase : BackOfficeNotificationsController { protected ICultureDictionary CultureDictionary { get; } - public IShortStringHelper ShortStringHelper { get; } protected ContentControllerBase( ICultureDictionary cultureDictionary, @@ -42,10 +40,9 @@ namespace Umbraco.Web.Editors IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + :base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { CultureDictionary = cultureDictionary; - ShortStringHelper = shortStringHelper; } protected HttpResponseMessage HandleContentNotFound(object id, bool throwException = true) diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 4563a51afb..e64485e5ab 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -14,11 +14,9 @@ using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; -using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; @@ -52,7 +50,6 @@ namespace Umbraco.Web.Editors private readonly IGlobalSettings _globalSettings; private readonly PropertyEditorCollection _propertyEditors; private readonly IScopeProvider _scopeProvider; - private readonly IShortStringHelper _shortStringHelper; public ContentTypeController(IEntityXmlSerializer serializer, ICultureDictionary cultureDictionary, @@ -63,13 +60,12 @@ namespace Umbraco.Web.Editors IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IScopeProvider scopeProvider, IShortStringHelper shortStringHelper) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { _serializer = serializer; _globalSettings = globalSettings; _propertyEditors = propertyEditors; _scopeProvider = scopeProvider; - _shortStringHelper = shortStringHelper; } public int GetCount() @@ -229,9 +225,9 @@ namespace Umbraco.Web.Editors public CreatedContentTypeCollectionResult PostCreateCollection(int parentId, string collectionName, bool collectionCreateTemplate, string collectionItemName, bool collectionItemCreateTemplate, string collectionIcon, string collectionItemIcon) { // create item doctype - var itemDocType = new ContentType(_shortStringHelper, parentId); + var itemDocType = new ContentType(ShortStringHelper, parentId); itemDocType.Name = collectionItemName; - itemDocType.Alias = collectionItemName.ToSafeAlias(_shortStringHelper, true); + itemDocType.Alias = collectionItemName.ToSafeAlias(ShortStringHelper, true); itemDocType.Icon = collectionItemIcon; // create item doctype template @@ -245,9 +241,9 @@ namespace Umbraco.Web.Editors Services.ContentTypeService.Save(itemDocType); // create collection doctype - var collectionDocType = new ContentType(_shortStringHelper, parentId); + var collectionDocType = new ContentType(ShortStringHelper, parentId); collectionDocType.Name = collectionName; - collectionDocType.Alias = collectionName.ToSafeAlias(_shortStringHelper, true); + collectionDocType.Alias = collectionName.ToSafeAlias(ShortStringHelper, true); collectionDocType.Icon = collectionIcon; collectionDocType.IsContainer = true; collectionDocType.AllowedContentTypes = new List() @@ -380,10 +376,10 @@ namespace Umbraco.Web.Editors if (parentId != Constants.System.Root) { var parent = Services.ContentTypeService.Get(parentId); - ct = parent != null ? new ContentType(_shortStringHelper, parent, string.Empty) : new ContentType(_shortStringHelper, parentId); + ct = parent != null ? new ContentType(ShortStringHelper, parent, string.Empty) : new ContentType(ShortStringHelper, parentId); } else - ct = new ContentType(_shortStringHelper, parentId); + ct = new ContentType(ShortStringHelper, parentId); ct.Icon = Constants.Icons.Content; @@ -528,7 +524,7 @@ namespace Umbraco.Web.Editors } var dataInstaller = new PackageDataInstallation(Logger, Services.FileService, Services.MacroService, Services.LocalizationService, - Services.DataTypeService, Services.EntityService, Services.ContentTypeService, Services.ContentService, _propertyEditors, _scopeProvider, _shortStringHelper, _globalSettings, Services.TextService); + Services.DataTypeService, Services.EntityService, Services.ContentTypeService, Services.ContentService, _propertyEditors, _scopeProvider, ShortStringHelper, _globalSettings, Services.TextService); var xd = new XmlDocument {XmlResolver = null}; xd.Load(filePath); diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index 138ef7e43d..1e4500ddac 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -28,8 +29,8 @@ namespace Umbraco.Web.Editors public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController where TContentType : class, IContentTypeComposition { - protected ContentTypeControllerBase(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + protected ContentTypeControllerBase(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { CultureDictionary = cultureDictionary; } diff --git a/src/Umbraco.Web/Editors/CurrentUserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs index f6b0c48e4d..b235aabf6a 100644 --- a/src/Umbraco.Web/Editors/CurrentUserController.cs +++ b/src/Umbraco.Web/Editors/CurrentUserController.cs @@ -31,7 +31,6 @@ namespace Umbraco.Web.Editors public class CurrentUserController : UmbracoAuthorizedJsonController { private readonly IMediaFileSystem _mediaFileSystem; - private readonly IShortStringHelper _shortStringHelper; public CurrentUserController( IGlobalSettings globalSettings, @@ -44,10 +43,9 @@ namespace Umbraco.Web.Editors UmbracoHelper umbracoHelper, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { _mediaFileSystem = mediaFileSystem; - _shortStringHelper = shortStringHelper; } /// @@ -181,7 +179,7 @@ namespace Umbraco.Web.Editors public async Task PostSetAvatar() { //borrow the logic from the user controller - return await UsersController.PostSetAvatarInternal(Request, Services.UserService, AppCaches.RuntimeCache, _mediaFileSystem, _shortStringHelper, Security.GetUserId().ResultOr(0)); + return await UsersController.PostSetAvatarInternal(Request, Services.UserService, AppCaches.RuntimeCache, _mediaFileSystem, ShortStringHelper, Security.GetUserId().ResultOr(0)); } /// diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index cf6fc32113..80d4172473 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -16,6 +16,7 @@ using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Core.Dashboards; using Umbraco.Core.Strings; using Umbraco.Web.Services; diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 5329a312e5..670c574d87 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -8,6 +8,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -38,8 +39,8 @@ namespace Umbraco.Web.Editors { private readonly PropertyEditorCollection _propertyEditors; - public DataTypeController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + public DataTypeController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { _propertyEditors = propertyEditors; } diff --git a/src/Umbraco.Web/Editors/DictionaryController.cs b/src/Umbraco.Web/Editors/DictionaryController.cs index d132fdc201..ce808bab1e 100644 --- a/src/Umbraco.Web/Editors/DictionaryController.cs +++ b/src/Umbraco.Web/Editors/DictionaryController.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; @@ -33,8 +34,8 @@ namespace Umbraco.Web.Editors [EnableOverrideAuthorization] public class DictionaryController : BackOfficeNotificationsController { - public DictionaryController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + public DictionaryController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { } diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 8371978903..c0a5212535 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -11,7 +11,6 @@ using System.Net.Http; using System.Net.Http.Formatting; using System.Reflection; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; using Umbraco.Core.Cache; @@ -51,7 +50,6 @@ namespace Umbraco.Web.Editors { private readonly ITreeService _treeService; private readonly UmbracoTreeSearcher _treeSearcher; - private readonly IShortStringHelper _shortStringHelper; private readonly SearchableTreeCollection _searchableTreeCollection; public EntityController( @@ -67,12 +65,12 @@ namespace Umbraco.Web.Editors SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) + { _treeService = treeService; _searchableTreeCollection = searchableTreeCollection; _treeSearcher = treeSearcher; - _shortStringHelper = shortStringHelper; } /// @@ -102,7 +100,7 @@ namespace Umbraco.Web.Editors /// public dynamic GetSafeAlias(string value, bool camelCase = true) { - var returnValue = string.IsNullOrWhiteSpace(value) ? string.Empty : value.ToSafeAlias(_shortStringHelper, camelCase); + var returnValue = string.IsNullOrWhiteSpace(value) ? string.Empty : value.ToSafeAlias(ShortStringHelper, camelCase); dynamic returnObj = new System.Dynamic.ExpandoObject(); returnObj.alias = returnValue; returnObj.original = value; diff --git a/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs b/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs index bad0531724..1b2ddf2ace 100644 --- a/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs +++ b/src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs @@ -6,10 +6,10 @@ using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; using Umbraco.Core; -using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Editors.Filters @@ -21,17 +21,20 @@ namespace Umbraco.Web.Editors.Filters { private readonly IMemberTypeService _memberTypeService; private readonly IMemberService _memberService; + private readonly IShortStringHelper _shortStringHelper; public MemberSaveModelValidator( ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IMemberTypeService memberTypeService, - IMemberService memberService) + IMemberService memberService, + IShortStringHelper shortStringHelper) : base(logger, umbracoContextAccessor, textService) { _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); + _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); } /// @@ -89,7 +92,7 @@ namespace Umbraco.Web.Editors.Filters public override bool ValidateProperties(MemberSave model, IContentProperties modelWithProperties, HttpActionContext actionContext) { var propertiesToValidate = model.Properties.ToList(); - var defaultProps = ConventionsHelper.GetStandardPropertyTypeStubs(Current.ShortStringHelper); + var defaultProps = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper); var exclude = defaultProps.Select(x => x.Value.Alias).ToArray(); foreach (var remove in exclude) { diff --git a/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs b/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs index 629a5a4eec..51fa5652ea 100644 --- a/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs +++ b/src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs @@ -3,6 +3,7 @@ using System.Web.Http.Controllers; using System.Web.Http.Filters; using Umbraco.Core.Logging; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; @@ -18,24 +19,26 @@ namespace Umbraco.Web.Editors.Filters private readonly ILocalizedTextService _textService; private readonly IMemberTypeService _memberTypeService; private readonly IMemberService _memberService; + private readonly IShortStringHelper _shortStringHelper; public MemberSaveValidationAttribute() - : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.TextService, Current.Services.MemberTypeService, Current.Services.MemberService) + : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.TextService, Current.Services.MemberTypeService, Current.Services.MemberService, Current.ShortStringHelper) { } - public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IMemberTypeService memberTypeService, IMemberService memberService) + public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IMemberTypeService memberTypeService, IMemberService memberService, IShortStringHelper shortStringHelper) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _textService = textService ?? throw new ArgumentNullException(nameof(textService)); _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); - _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));; + _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); + _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); } public override void OnActionExecuting(HttpActionContext actionContext) { var model = (MemberSave)actionContext.ActionArguments["contentItem"]; - var contentItemValidator = new MemberSaveModelValidator(_logger, _umbracoContextAccessor,_textService, _memberTypeService, _memberService); + var contentItemValidator = new MemberSaveModelValidator(_logger, _umbracoContextAccessor,_textService, _memberTypeService, _memberService, _shortStringHelper); //now do each validation step if (contentItemValidator.ValidateExistingContent(model, actionContext)) if (contentItemValidator.ValidateProperties(model, model, actionContext)) diff --git a/src/Umbraco.Web/Editors/HelpController.cs b/src/Umbraco.Web/Editors/HelpController.cs index ccbbcaeee8..39dbbc435c 100644 --- a/src/Umbraco.Web/Editors/HelpController.cs +++ b/src/Umbraco.Web/Editors/HelpController.cs @@ -13,14 +13,23 @@ namespace Umbraco.Web.Editors { var url = string.Format(baseUrl + "/Umbraco/Documentation/Lessons/GetContextHelpDocs?sectionAlias={0}&treeAlias={1}", section, tree); - if (_httpClient == null) - _httpClient = new HttpClient(); + try + { - //fetch dashboard json and parse to JObject - var json = await _httpClient.GetStringAsync(url); - var result = JsonConvert.DeserializeObject>(json); - if (result != null) - return result; + if (_httpClient == null) + _httpClient = new HttpClient(); + + //fetch dashboard json and parse to JObject + var json = await _httpClient.GetStringAsync(url); + var result = JsonConvert.DeserializeObject>(json); + if (result != null) + return result; + + } + catch (HttpRequestException rex) + { + Logger.Info(GetType(), $"Check your network connection, exception: {rex.Message}"); + } return new List(); } diff --git a/src/Umbraco.Web/Editors/LogController.cs b/src/Umbraco.Web/Editors/LogController.cs index 1924d9c228..f02a79b574 100644 --- a/src/Umbraco.Web/Editors/LogController.cs +++ b/src/Umbraco.Web/Editors/LogController.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; @@ -32,8 +33,9 @@ namespace Umbraco.Web.Editors IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, - IMediaFileSystem mediaFileSystem) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + IMediaFileSystem mediaFileSystem, + IShortStringHelper shortStringHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { _mediaFileSystem = mediaFileSystem; } diff --git a/src/Umbraco.Web/Editors/MacroRenderingController.cs b/src/Umbraco.Web/Editors/MacroRenderingController.cs index 2940f81f28..9e9b7efc1b 100644 --- a/src/Umbraco.Web/Editors/MacroRenderingController.cs +++ b/src/Umbraco.Web/Editors/MacroRenderingController.cs @@ -164,7 +164,7 @@ namespace Umbraco.Web.Editors var macro = new Macro(_shortStringHelper) { - Alias = macroName.ToSafeAlias(_shortStringHelper), + Alias = macroName.ToSafeAlias(ShortStringHelper), Name = macroName, MacroSource = model.VirtualPath.EnsureStartsWith("~"), MacroType = MacroTypes.PartialView diff --git a/src/Umbraco.Web/Editors/MacrosController.cs b/src/Umbraco.Web/Editors/MacrosController.cs index 26138b7f26..12f0933500 100644 --- a/src/Umbraco.Web/Editors/MacrosController.cs +++ b/src/Umbraco.Web/Editors/MacrosController.cs @@ -9,7 +9,6 @@ using System.Web.Http; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; @@ -31,13 +30,11 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(Constants.Trees.Macros)] public class MacrosController : BackOfficeNotificationsController { - private readonly IShortStringHelper _shortStringHelper; private readonly IMacroService _macroService; public MacrosController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { - _shortStringHelper = shortStringHelper; _macroService = Services.MacroService; } @@ -58,7 +55,7 @@ namespace Umbraco.Web.Editors return this.ReturnErrorResponse("Name can not be empty"); } - var alias = name.ToSafeAlias(_shortStringHelper); + var alias = name.ToSafeAlias(ShortStringHelper); if (_macroService.GetByAlias(alias) != null) { @@ -72,7 +69,7 @@ namespace Umbraco.Web.Editors try { - var macro = new Macro(_shortStringHelper) + var macro = new Macro(ShortStringHelper) { Alias = alias, Name = name, @@ -326,7 +323,6 @@ namespace Umbraco.Web.Editors /// Finds partial view files in app plugin folders. /// /// - /// The . /// private IEnumerable FindPartialViewFilesInPluginFolders() { diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 4151a0e9a0..fee7128f69 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Text; using System.Threading.Tasks; using System.Web.Http; +using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; using Umbraco.Core; using Umbraco.Core.IO; @@ -22,21 +24,30 @@ using System.Web.Http.Controllers; using Umbraco.Core.Cache; using Umbraco.Web.Composing; using Umbraco.Core.Configuration; -using Umbraco.Web.WebApi.Filters; -using Umbraco.Core.Persistence.Querying; -using Notification = Umbraco.Web.Models.ContentEditing.Notification; -using Umbraco.Core.Persistence; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Dictionary; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; using Umbraco.Core.Models.ContentEditing; using Umbraco.Core.Models.Editors; +using Umbraco.Core.Models.Entities; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Validation; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.ContentApps; using Umbraco.Web.Editors.Binders; using Umbraco.Web.Editors.Filters; -using Umbraco.Core.Models.Entities; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; -using Umbraco.Core.Dictionary; +using Notification = Umbraco.Web.Models.ContentEditing.Notification; using Umbraco.Core.Strings; namespace Umbraco.Web.Editors diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index e590f2860f..b0f21e26e0 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -40,7 +40,7 @@ namespace Umbraco.Web.Editors private readonly IShortStringHelper _shortStringHelper; public MediaTypeController(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { _shortStringHelper = shortStringHelper; } diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index 2c5c079c36..4c395a56f9 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -1,36 +1,36 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; +using System.Threading.Tasks; using System.Web.Http; using System.Web.Http.ModelBinding; using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; -using Umbraco.Web.WebApi; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi.Filters; -using System.Collections.Generic; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; +using Umbraco.Core.Strings; using Umbraco.Web.ContentApps; using Umbraco.Web.Editors.Binders; using Umbraco.Web.Editors.Filters; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; -using Umbraco.Core.Dictionary; -using Umbraco.Web.Security; -using Umbraco.Core.Security; -using System.Threading.Tasks; using Umbraco.Core.Strings; namespace Umbraco.Web.Editors diff --git a/src/Umbraco.Web/Editors/MemberTypeController.cs b/src/Umbraco.Web/Editors/MemberTypeController.cs index 7fd2342356..8f425323d7 100644 --- a/src/Umbraco.Web/Editors/MemberTypeController.cs +++ b/src/Umbraco.Web/Editors/MemberTypeController.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; @@ -20,7 +21,6 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors { - /// /// An API controller used for dealing with member types /// @@ -28,8 +28,8 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})] public class MemberTypeController : ContentTypeControllerBase { - public MemberTypeController(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) - : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + public MemberTypeController(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) + : base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { } @@ -98,7 +98,7 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public MemberTypeDisplay GetEmpty() { - var ct = new MemberType(Current.ShortStringHelper, -1); + var ct = new MemberType(ShortStringHelper, -1); ct.Icon = Constants.Icons.Member; var dto = Mapper.Map(ct); diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 64141a32f2..8b62b23688 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -16,6 +16,7 @@ using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.JavaScript; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; @@ -38,8 +39,8 @@ namespace Umbraco.Web.Editors private readonly IUmbracoVersion _umbracoVersion; public PackageInstallController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, - IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IUmbracoVersion umbracoVersion) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper, IUmbracoVersion umbracoVersion) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { _umbracoVersion = umbracoVersion; diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs index eb70342ec5..55b3ced23a 100644 --- a/src/Umbraco.Web/Editors/RelationTypeController.cs +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -10,7 +10,6 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; @@ -29,8 +28,6 @@ namespace Umbraco.Web.Editors [EnableOverrideAuthorization] public class RelationTypeController : BackOfficeNotificationsController { - private readonly IShortStringHelper _shortStringHelper; - public RelationTypeController( IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, @@ -41,9 +38,8 @@ namespace Umbraco.Web.Editors IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { - _shortStringHelper = shortStringHelper; } /// @@ -113,7 +109,7 @@ namespace Umbraco.Web.Editors /// A containing the persisted relation type's ID. public HttpResponseMessage PostCreate(RelationTypeSave relationType) { - var relationTypePersisted = new RelationType(relationType.Name, relationType.Name.ToSafeAlias(_shortStringHelper, true), relationType.IsBidirectional, relationType.ChildObjectType, relationType.ParentObjectType); + var relationTypePersisted = new RelationType(relationType.Name, relationType.Name.ToSafeAlias(ShortStringHelper, true), relationType.IsBidirectional, relationType.ChildObjectType, relationType.ParentObjectType); try { diff --git a/src/Umbraco.Web/Editors/SectionController.cs b/src/Umbraco.Web/Editors/SectionController.cs index ac98f576d4..102f962ecb 100644 --- a/src/Umbraco.Web/Editors/SectionController.cs +++ b/src/Umbraco.Web/Editors/SectionController.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Trees; using Section = Umbraco.Web.Models.ContentEditing.Section; using Umbraco.Web.Models.Trees; @@ -26,8 +27,8 @@ namespace Umbraco.Web.Editors private readonly ITreeService _treeService; public SectionController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, - IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { _dashboardService = dashboardService; _sectionService = sectionService; diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index 949bbfb8ae..524ecea1eb 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -1,17 +1,18 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Web.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; @@ -23,8 +24,8 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(Constants.Trees.Templates)] public class TemplateController : BackOfficeNotificationsController { - public TemplateController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + public TemplateController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { } @@ -82,7 +83,7 @@ namespace Umbraco.Web.Editors public TemplateDisplay GetScaffold(int id) { //empty default - var dt = new Template(Current.ShortStringHelper, "", ""); + var dt = new Template(ShortStringHelper, string.Empty, string.Empty); dt.Path = "-1"; if (id > 0) diff --git a/src/Umbraco.Web/Editors/TinyMceController.cs b/src/Umbraco.Web/Editors/TinyMceController.cs index d701ce8ddb..65e3f7ea18 100644 --- a/src/Umbraco.Web/Editors/TinyMceController.cs +++ b/src/Umbraco.Web/Editors/TinyMceController.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using System.Web.Http; using Umbraco.Core; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Services; using Umbraco.Core.Strings; @@ -26,11 +25,10 @@ namespace Umbraco.Web.Editors Constants.Applications.Members)] public class TinyMceController : UmbracoAuthorizedApiController { - private IMediaService _mediaService; - private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private readonly IMediaService _mediaService; + private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private readonly IShortStringHelper _shortStringHelper; - public TinyMceController(IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IShortStringHelper shortStringHelper) { _mediaService = mediaService; @@ -41,7 +39,6 @@ namespace Umbraco.Web.Editors [HttpPost] public async Task UploadImage() { - if (Request.Content.IsMimeMultipartContent() == false) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); diff --git a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs index 385ed89b8f..4f8876738b 100644 --- a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs +++ b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs @@ -1,9 +1,11 @@ -using Umbraco.Core; +using System; +using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; @@ -24,9 +26,12 @@ namespace Umbraco.Web.Editors { } - protected UmbracoAuthorizedJsonController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) + protected UmbracoAuthorizedJsonController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { + ShortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); } + + protected IShortStringHelper ShortStringHelper { get; } } } diff --git a/src/Umbraco.Web/Editors/UserGroupsController.cs b/src/Umbraco.Web/Editors/UserGroupsController.cs index b7e18389a9..368a2ee535 100644 --- a/src/Umbraco.Web/Editors/UserGroupsController.cs +++ b/src/Umbraco.Web/Editors/UserGroupsController.cs @@ -8,6 +8,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Editors.Filters; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; @@ -106,7 +107,7 @@ namespace Umbraco.Web.Editors /// public UserGroupDisplay GetEmptyUserGroup() { - return Mapper.Map(new UserGroup(Current.ShortStringHelper)); + return Mapper.Map(new UserGroup(ShortStringHelper)); } /// diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index 4d1166cc15..09590c6d2b 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -43,7 +43,6 @@ namespace Umbraco.Web.Editors public class UsersController : UmbracoAuthorizedJsonController { private readonly IMediaFileSystem _mediaFileSystem; - private readonly IShortStringHelper _shortStringHelper; public UsersController( IGlobalSettings globalSettings, @@ -56,10 +55,9 @@ namespace Umbraco.Web.Editors UmbracoHelper umbracoHelper, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { _mediaFileSystem = mediaFileSystem; - _shortStringHelper = shortStringHelper; } /// @@ -80,7 +78,7 @@ namespace Umbraco.Web.Editors [AdminUsersAuthorize] public async Task PostSetAvatar(int id) { - return await PostSetAvatarInternal(Request, Services.UserService, AppCaches.RuntimeCache, _mediaFileSystem, _shortStringHelper, id); + return await PostSetAvatarInternal(Request, Services.UserService, AppCaches.RuntimeCache, _mediaFileSystem, ShortStringHelper, id); } internal static async Task PostSetAvatarInternal(HttpRequestMessage request, IUserService userService, IAppCache cache, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, int id) diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 302c1d8271..4f4d61b4a8 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -64,7 +64,8 @@ namespace Umbraco.Web var htmlBadge = String.Format(Current.Configs.Settings().Content.PreviewBadge, Current.IOHelper.ResolveUrl(Current.Configs.Global().UmbracoPath), - Current.UmbracoContext.HttpContext.Server.UrlEncode(Current.UmbracoContext.HttpContext.Request.Path)); + Current.UmbracoContext.HttpContext.Server.UrlEncode(Current.UmbracoContext.HttpContext.Request.Path), + Current.UmbracoContext.PublishedRequest.PublishedContent.Id); return new MvcHtmlString(htmlBadge); } return new MvcHtmlString(""); diff --git a/src/Umbraco.Web/Install/FilePermissionHelper.cs b/src/Umbraco.Web/Install/FilePermissionHelper.cs index 53b0f953b9..877c2488f6 100644 --- a/src/Umbraco.Web/Install/FilePermissionHelper.cs +++ b/src/Umbraco.Web/Install/FilePermissionHelper.cs @@ -46,8 +46,8 @@ namespace Umbraco.Web.Install /// /// This will test the directories for write access /// - /// - /// + /// + /// /// /// If this is false, the easiest way to test for write access is to write a temp file, however some folder will cause /// an App Domain restart if a file is written to the folder, so in that case we need to use the ACL APIs which aren't as diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs index 88b957ad96..97d4d2db79 100644 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs @@ -77,8 +77,8 @@ namespace Umbraco.Web.Macros /// The content. /// /// This is for usage only. - internal PublishedContentHashtableConverter(IContent content, IVariationContextAccessor variationContextAccessor, IUserService userService) - : this(new PagePublishedContent(content, variationContextAccessor, userService)) + internal PublishedContentHashtableConverter(IContent content, IVariationContextAccessor variationContextAccessor, IUserService userService, IShortStringHelper shortStringHelper) + : this(new PagePublishedContent(content, variationContextAccessor, userService, shortStringHelper)) { } #endregion @@ -185,6 +185,7 @@ namespace Umbraco.Web.Macros private readonly IPublishedProperty[] _properties; private IReadOnlyDictionary _cultureInfos; private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IShortStringHelper _shortStringHelper; private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); @@ -193,10 +194,12 @@ namespace Umbraco.Web.Macros Id = id; } - public PagePublishedContent(IContent inner, IVariationContextAccessor variationContextAccessor, IUserService userService) + public PagePublishedContent(IContent inner, IVariationContextAccessor variationContextAccessor, IUserService userService, IShortStringHelper shortStringHelper) { - _inner = inner ?? throw new NullReferenceException("content"); + _inner = inner ?? throw new ArgumentNullException(nameof(inner)); _variationContextAccessor = variationContextAccessor; + _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); + Id = _inner.Id; Key = _inner.Key; @@ -243,7 +246,7 @@ namespace Umbraco.Web.Macros var urlSegmentProviders = Current.UrlSegmentProviders; // TODO inject return _cultureInfos = _inner.PublishCultureInfos.Values - .ToDictionary(x => x.Culture, x => new PublishedCultureInfo(x.Culture, x.Name, _inner.GetUrlSegment(Current.ShortStringHelper, urlSegmentProviders, x.Culture), x.Date)); + .ToDictionary(x => x.Culture, x => new PublishedCultureInfo(x.Culture, x.Name, _inner.GetUrlSegment(_shortStringHelper, urlSegmentProviders, x.Culture), x.Date)); } } diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs index f4200c4963..2f53f6f73b 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs @@ -1,4 +1,8 @@ -using System.Runtime.Serialization; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Umbraco.Core.Models; +using Umbraco.Core.Models.ContentEditing; +using Umbraco.Core.Models.Membership; namespace Umbraco.Web.Models.ContentEditing { @@ -8,11 +12,30 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "content", Namespace = "")] public class MemberDisplay : ListViewAwareContentItemDisplayBase { + public MemberDisplay() + { + // MemberProviderFieldMapping = new Dictionary(); + ContentApps = new List(); + } + [DataMember(Name = "username")] public string Username { get; set; } [DataMember(Name = "email")] public string Email { get; set; } + //[DataMember(Name = "membershipScenario")] + //public MembershipScenario MembershipScenario { get; set; } + + // /// + // /// This is used to indicate how to map the membership provider properties to the save model, this mapping + // /// will change if a developer has opted to have custom member property aliases specified in their membership provider config, + // /// or if we are editing a member that is not an Umbraco member (custom provider) + // /// + // [DataMember(Name = "fieldConfig")] + // public IDictionary MemberProviderFieldMapping { get; set; } + + [DataMember(Name = "apps")] + public IEnumerable ContentApps { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs index 9323417b0a..1f6df93367 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeMapDefinition.cs @@ -31,7 +31,7 @@ namespace Umbraco.Web.Models.Mapping public ContentTypeMapDefinition(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IFileService fileService, IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, - ILogger logger) + ILogger logger, IShortStringHelper shortStringHelper) { _propertyEditors = propertyEditors; _dataTypeService = dataTypeService; @@ -40,7 +40,7 @@ namespace Umbraco.Web.Models.Mapping _mediaTypeService = mediaTypeService; _memberTypeService = memberTypeService; _logger = logger; - _shortStringHelper = Current.ShortStringHelper; + _shortStringHelper = shortStringHelper; } @@ -510,7 +510,7 @@ namespace Umbraco.Web.Models.Mapping { MapTypeToDisplayBase(source, target); - var groupsMapper = new PropertyTypeGroupMapper(_propertyEditors, _dataTypeService, _logger); + var groupsMapper = new PropertyTypeGroupMapper(_propertyEditors, _dataTypeService, _shortStringHelper, _logger); target.Groups = groupsMapper.Map(source); } diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/MediaMapDefinition.cs index bf89e11370..367e7cb606 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapDefinition.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Dictionary; using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Trees; @@ -19,15 +20,17 @@ namespace Umbraco.Web.Models.Mapping private readonly ILogger _logger; private readonly IMediaService _mediaService; private readonly IMediaTypeService _mediaTypeService; + private readonly PropertyEditorCollection _propertyEditorCollection; private readonly TabsAndPropertiesMapper _tabsAndPropertiesMapper; public MediaMapDefinition(ICultureDictionary cultureDictionary, ILogger logger, CommonMapper commonMapper, IMediaService mediaService, IMediaTypeService mediaTypeService, - ILocalizedTextService localizedTextService) + ILocalizedTextService localizedTextService, PropertyEditorCollection propertyEditorCollection) { _logger = logger; _commonMapper = commonMapper; _mediaService = mediaService; _mediaTypeService = mediaTypeService; + _propertyEditorCollection = propertyEditorCollection; _tabsAndPropertiesMapper = new TabsAndPropertiesMapper(cultureDictionary, localizedTextService); } @@ -58,7 +61,7 @@ namespace Umbraco.Web.Models.Mapping target.Id = source.Id; target.IsChildOfListView = DermineIsChildOfListView(source); target.Key = source.Key; - target.MediaLink = string.Join(",", source.GetUrls(Current.Configs.Settings().Content, _logger)); + target.MediaLink = string.Join(",", source.GetUrls(Current.Configs.Settings().Content, _logger, _propertyEditorCollection)); target.Name = source.Name; target.Owner = _commonMapper.GetOwner(source, context); target.ParentId = source.ParentId; diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs index 37f86ae61e..ee241f3245 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs @@ -26,12 +26,13 @@ namespace Umbraco.Web.Models.Mapping mapper.Define((source, context) => new MemberBasic(), Map); mapper.Define((source, context) => new MemberGroupDisplay(), Map); mapper.Define((source, context) => new ContentPropertyCollectionDto(), Map); - } + } // Umbraco.Code.MapAll -Properties -Errors -Edited -Updater -Alias -IsChildOfListView // Umbraco.Code.MapAll -Trashed -IsContainer -VariesByCulture private void Map(IMember source, MemberDisplay target, MapperContext context) { + target.ContentApps = _commonMapper.GetContentApps(source); target.ContentTypeId = source.ContentType.Id; target.ContentTypeAlias = source.ContentType.Alias; target.ContentTypeName = source.ContentType.Name; diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs index d2b83abe9d..000e37f31f 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupMapper.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; @@ -16,12 +17,14 @@ namespace Umbraco.Web.Models.Mapping { private readonly PropertyEditorCollection _propertyEditors; private readonly IDataTypeService _dataTypeService; + private readonly IShortStringHelper _shortStringHelper; private readonly ILogger _logger; - public PropertyTypeGroupMapper(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, ILogger logger) + public PropertyTypeGroupMapper(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IShortStringHelper shortStringHelper, ILogger logger) { _propertyEditors = propertyEditors; _dataTypeService = dataTypeService; + _shortStringHelper = shortStringHelper; _logger = logger; } @@ -151,7 +154,7 @@ namespace Umbraco.Web.Models.Mapping // handle locked properties var lockedPropertyAliases = new List(); // add built-in member property aliases to list of aliases to be locked - foreach (var propertyAlias in ConventionsHelper.GetStandardPropertyTypeStubs(Current.ShortStringHelper).Keys) + foreach (var propertyAlias in ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper).Keys) { lockedPropertyAliases.Add(propertyAlias); } diff --git a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs index 486ec924ee..8844b848bc 100644 --- a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Sections; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Actions; using Umbraco.Web.Services; @@ -29,9 +30,10 @@ namespace Umbraco.Web.Models.Mapping private readonly AppCaches _appCaches; private readonly IGlobalSettings _globalSettings; private readonly IMediaFileSystem _mediaFileSystem; + private readonly IShortStringHelper _shortStringHelper; public UserMapDefinition(ILocalizedTextService textService, IUserService userService, IEntityService entityService, ISectionService sectionService, - AppCaches appCaches, ActionCollection actions, IGlobalSettings globalSettings, IMediaFileSystem mediaFileSystem) + AppCaches appCaches, ActionCollection actions, IGlobalSettings globalSettings, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper) { _sectionService = sectionService; _entityService = entityService; @@ -41,11 +43,12 @@ namespace Umbraco.Web.Models.Mapping _appCaches = appCaches; _globalSettings = globalSettings; _mediaFileSystem = mediaFileSystem; + _shortStringHelper = shortStringHelper; } public void DefineMaps(UmbracoMapper mapper) { - mapper.Define((source, context) => new UserGroup(Current.ShortStringHelper) { CreateDate = DateTime.UtcNow }, Map); + mapper.Define((source, context) => new UserGroup(_shortStringHelper) { CreateDate = DateTime.UtcNow }, Map); mapper.Define(Map); mapper.Define((source, context) => new ContentEditing.UserProfile(), Map); mapper.Define((source, context) => new UserGroupBasic(), Map); diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index e8b9ca7fb1..fdf38e78b3 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -1,19 +1,18 @@ using System; using System.Linq; using System.Web; -using System.Web.Compilation; using System.Web.Mvc; using System.Web.Routing; using System.Web.SessionState; using Umbraco.Core; -using Umbraco.Core.Logging; using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Core.Strings; +using Umbraco.Web.Features; using Umbraco.Web.Models; using Umbraco.Web.Routing; -using System.Collections.Generic; using Umbraco.Core.Strings; using Current = Umbraco.Web.Composing.Current; -using Umbraco.Web.Features; namespace Umbraco.Web.Mvc { diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index c25a42195e..b7a3a5bb02 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -212,7 +212,8 @@ namespace Umbraco.Web.Mvc markupToInject = string.Format(Current.Configs.Settings().Content.PreviewBadge, Current.IOHelper.ResolveUrl(Current.Configs.Global().UmbracoPath), - Server.UrlEncode(Current.UmbracoContext.HttpContext.Request.Url?.PathAndQuery)); + Server.UrlEncode(Current.UmbracoContext.HttpContext.Request.Url?.PathAndQuery), + Current.UmbracoContext.PublishedRequest.PublishedContent.Id); } else { diff --git a/src/Umbraco.Web/Mvc/ValidateMvcAngularAntiForgeryTokenAttribute.cs b/src/Umbraco.Web/Mvc/ValidateMvcAngularAntiForgeryTokenAttribute.cs index 756ca7f05c..0803941a70 100644 --- a/src/Umbraco.Web/Mvc/ValidateMvcAngularAntiForgeryTokenAttribute.cs +++ b/src/Umbraco.Web/Mvc/ValidateMvcAngularAntiForgeryTokenAttribute.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.Mvc var userIdentity = filterContext.HttpContext.User.Identity as ClaimsIdentity; if (userIdentity != null) { - //if there is not CookiePath claim, then exist + //if there is not CookiePath claim, then exit if (userIdentity.HasClaim(x => x.Type == ClaimTypes.CookiePath) == false) { base.OnActionExecuting(filterContext); diff --git a/src/Umbraco.Web/Profiling/WebProfilingController.cs b/src/Umbraco.Web/Profiling/WebProfilingController.cs index a8935da033..218ecd0669 100644 --- a/src/Umbraco.Web/Profiling/WebProfilingController.cs +++ b/src/Umbraco.Web/Profiling/WebProfilingController.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Editors; using Umbraco.Web.WebApi.Filters; @@ -17,8 +18,8 @@ namespace Umbraco.Web.Profiling { private readonly IRuntimeState _runtimeState; - public WebProfilingController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + public WebProfilingController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper) { _runtimeState = runtimeState; } diff --git a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs index b8ab0958a8..f5f0abe183 100644 --- a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -29,8 +30,9 @@ namespace Umbraco.Web.PropertyEditors IDataTypeService dataTypeService, ILocalizationService localizationService, ILogger logger, - IIOHelper ioHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, Current.ShortStringHelper) + IIOHelper ioHelper, + IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) { _dataTypeService = dataTypeService; _localizationService = localizationService; @@ -42,11 +44,11 @@ namespace Umbraco.Web.PropertyEditors return new ContentPickerConfigurationEditor(_ioHelper); } - protected override IDataValueEditor CreateValueEditor() => new ContentPickerPropertyValueEditor(_dataTypeService, _localizationService, Attribute); + protected override IDataValueEditor CreateValueEditor() => new ContentPickerPropertyValueEditor(_dataTypeService, _localizationService, ShortStringHelper, Attribute); internal class ContentPickerPropertyValueEditor : DataValueEditor, IDataValueReference { - public ContentPickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, DataEditorAttribute attribute) : base(dataTypeService, localizationService,Current.Services.TextService, Current.ShortStringHelper, attribute) + public ContentPickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) : base(dataTypeService, localizationService,Current.Services.TextService, shortStringHelper, attribute) { } diff --git a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs index ccc88d24e2..00bfa3cfcf 100644 --- a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs @@ -3,6 +3,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -23,8 +24,8 @@ namespace Umbraco.Web.PropertyEditors /// Initializes a new instance of the class. /// /// - public DateTimePropertyEditor(ILogger logger, IIOHelper ioHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, Current.ShortStringHelper) + public DateTimePropertyEditor(ILogger logger, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs b/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs index cf1dd310cc..f2886da5b2 100644 --- a/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs @@ -13,8 +13,8 @@ namespace Umbraco.Web.PropertyEditors /// internal class DateValueEditor : DataValueEditor { - public DateValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, Current.Services.TextService,Current.ShortStringHelper, attribute) + public DateValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) { Validators.Add(new DateTimeValidator()); } diff --git a/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs index 2d636b3cad..d2a508e1d0 100644 --- a/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs @@ -3,6 +3,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -20,8 +21,8 @@ namespace Umbraco.Web.PropertyEditors /// /// Initializes a new instance of the class. /// - public DecimalPropertyEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, Current.ShortStringHelper) + public DecimalPropertyEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) { } /// diff --git a/src/Umbraco.Web/PropertyEditors/EmailAddressPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/EmailAddressPropertyEditor.cs index cf4522db68..b5e715b0f4 100644 --- a/src/Umbraco.Web/PropertyEditors/EmailAddressPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/EmailAddressPropertyEditor.cs @@ -4,6 +4,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -20,8 +21,8 @@ namespace Umbraco.Web.PropertyEditors /// /// The constructor will setup the property editor based on the attribute if one is found /// - public EmailAddressPropertyEditor(ILogger logger, IIOHelper ioHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public EmailAddressPropertyEditor(ILogger logger, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs index d767414fd6..f3a737c3f5 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Media; namespace Umbraco.Web.PropertyEditors @@ -19,7 +20,7 @@ namespace Umbraco.Web.PropertyEditors "fileupload", Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-download-alt")] - public class FileUploadPropertyEditor : DataEditor + public class FileUploadPropertyEditor : DataEditor, IDataEditorWithMediaPath { private readonly IMediaFileSystem _mediaFileSystem; private readonly IContentSection _contentSection; @@ -27,8 +28,8 @@ namespace Umbraco.Web.PropertyEditors private readonly IDataTypeService _dataTypeService; private readonly ILocalizationService _localizationService; - public FileUploadPropertyEditor(ILogger logger, IMediaFileSystem mediaFileSystem, IContentSection contentSection, IDataTypeService dataTypeService, ILocalizationService localizationService) - : base(logger, dataTypeService, localizationService, Current.Services.TextService,Current.ShortStringHelper) + public FileUploadPropertyEditor(ILogger logger, IMediaFileSystem mediaFileSystem, IContentSection contentSection, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, Current.Services.TextService, shortStringHelper) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); _contentSection = contentSection; @@ -43,11 +44,13 @@ namespace Umbraco.Web.PropertyEditors /// The corresponding property value editor. protected override IDataValueEditor CreateValueEditor() { - var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileSystem, _dataTypeService, _localizationService); + var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileSystem, _dataTypeService, _localizationService, ShortStringHelper); editor.Validators.Add(new UploadFileTypeValidator()); return editor; } + public string GetMediaPath(object value) => value?.ToString(); + /// /// Gets a value indicating whether a property is an upload field. /// diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs index 54e8eaac5d..f850bb9c1b 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs @@ -6,6 +6,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -16,8 +17,8 @@ namespace Umbraco.Web.PropertyEditors { private readonly IMediaFileSystem _mediaFileSystem; - public FileUploadPropertyValueEditor(DataEditorAttribute attribute, IMediaFileSystem mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService) - : base(dataTypeService, localizationService, Current.Services.TextService,Current.ShortStringHelper, attribute) + public FileUploadPropertyValueEditor(DataEditorAttribute attribute, IMediaFileSystem mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper) + : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); } diff --git a/src/Umbraco.Web/PropertyEditors/GridConfiguration.cs b/src/Umbraco.Web/PropertyEditors/GridConfiguration.cs index 7c9a549aef..74ea517fa2 100644 --- a/src/Umbraco.Web/PropertyEditors/GridConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/GridConfiguration.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web.PropertyEditors public JObject Items { get; set; } // TODO: Make these strongly typed, for now this works though - [ConfigurationField("rte", "Rich text editor", "views/propertyeditors/rte/rte.prevalues.html", Description = "Rich text editor configuration")] + [ConfigurationField("rte", "Rich text editor", "views/propertyeditors/rte/rte.prevalues.html", Description = "Rich text editor configuration", HideLabel = true)] public JObject Rte { get; set; } [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 2ecc174694..4b9ad490aa 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -10,11 +10,11 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Templates; namespace Umbraco.Web.PropertyEditors { - /// /// Represents a grid property and parameter editor. /// @@ -28,11 +28,11 @@ namespace Umbraco.Web.PropertyEditors Group = Constants.PropertyEditors.Groups.RichContent)] public class GridPropertyEditor : DataEditor { - private IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IDataTypeService _dataTypeService; private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; - private ILogger _logger; + private readonly ILogger _logger; private readonly IMediaService _mediaService; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private readonly HtmlImageSourceParser _imageSourceParser; @@ -49,8 +49,9 @@ namespace Umbraco.Web.PropertyEditors HtmlImageSourceParser imageSourceParser, RichTextEditorPastedImages pastedImages, HtmlLocalLinkParser localLinkParser, - IIOHelper ioHelper) - : base(logger, dataTypeService, localizationService, Current.Services.TextService, Current.ShortStringHelper) + IIOHelper ioHelper, + IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, Current.Services.TextService, shortStringHelper) { _umbracoContextAccessor = umbracoContextAccessor; _dataTypeService = dataTypeService; @@ -70,7 +71,7 @@ namespace Umbraco.Web.PropertyEditors /// Overridden to ensure that the value is validated /// /// - protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger, _dataTypeService, _localizationService, _imageSourceParser, _pastedImages, _localLinkParser); + protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger, _dataTypeService, _localizationService, _imageSourceParser, _pastedImages, _localLinkParser, ShortStringHelper); protected override IConfigurationEditor CreateConfigurationEditor() => new GridConfigurationEditor(_ioHelper); @@ -92,14 +93,15 @@ namespace Umbraco.Web.PropertyEditors ILocalizationService localizationService, HtmlImageSourceParser imageSourceParser, RichTextEditorPastedImages pastedImages, - HtmlLocalLinkParser localLinkParser) - : base(dataTypeService, localizationService, Current.Services.TextService, Current.ShortStringHelper, attribute) + HtmlLocalLinkParser localLinkParser, + IShortStringHelper shortStringHelper) + : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) { _umbracoContextAccessor = umbracoContextAccessor; _imageSourceParser = imageSourceParser; _pastedImages = pastedImages; - _richTextPropertyValueEditor = new RichTextPropertyEditor.RichTextPropertyValueEditor(attribute, mediaService, contentTypeBaseServiceProvider, umbracoContextAccessor,logger, dataTypeService, localizationService, imageSourceParser, localLinkParser, pastedImages); - _mediaPickerPropertyValueEditor = new MediaPickerPropertyEditor.MediaPickerPropertyValueEditor(dataTypeService, localizationService, attribute); + _richTextPropertyValueEditor = new RichTextPropertyEditor.RichTextPropertyValueEditor(attribute, mediaService, contentTypeBaseServiceProvider, umbracoContextAccessor,logger, dataTypeService, localizationService, shortStringHelper, imageSourceParser, localLinkParser, pastedImages); + _mediaPickerPropertyValueEditor = new MediaPickerPropertyEditor.MediaPickerPropertyValueEditor(dataTypeService, localizationService, shortStringHelper, attribute); } /// diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs index cfe32a1095..7901f2d862 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.PropertyEditors HideLabel = false, Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-crop")] - public class ImageCropperPropertyEditor : DataEditor + public class ImageCropperPropertyEditor : DataEditor, IDataEditorWithMediaPath { private readonly IMediaFileSystem _mediaFileSystem; private readonly IContentSection _contentSettings; @@ -54,11 +54,13 @@ namespace Umbraco.Web.PropertyEditors _autoFillProperties = new UploadAutoFillProperties(_mediaFileSystem, logger, _contentSettings); } + public string GetMediaPath(object value) => GetFileSrcFromPropertyValue(value, out _, false); + /// /// Creates the corresponding property value editor. /// /// The corresponding property value editor. - protected override IDataValueEditor CreateValueEditor() => new ImageCropperPropertyValueEditor(Attribute, Logger, _mediaFileSystem, _dataTypeService, _localizationService); + protected override IDataValueEditor CreateValueEditor() => new ImageCropperPropertyValueEditor(Attribute, Logger, _mediaFileSystem, _dataTypeService, _localizationService, ShortStringHelper); /// /// Creates the corresponding preValue editor. @@ -138,8 +140,9 @@ namespace Umbraco.Web.PropertyEditors /// /// /// The deserialized value + /// Should the path returned be the application relative path /// - private string GetFileSrcFromPropertyValue(object propVal, out JObject deserializedValue) + private string GetFileSrcFromPropertyValue(object propVal, out JObject deserializedValue, bool relative = true) { deserializedValue = null; if (propVal == null || !(propVal is string str)) return null; @@ -147,7 +150,7 @@ namespace Umbraco.Web.PropertyEditors deserializedValue = GetJObject(str, true); if (deserializedValue?["src"] == null) return null; var src = deserializedValue["src"].Value(); - return _mediaFileSystem.GetRelativePath(src); + return relative ? _mediaFileSystem.GetRelativePath(src) : src; } /// diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs index d703851b3a..edc13277b0 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using File = System.IO.File; namespace Umbraco.Web.PropertyEditors @@ -22,8 +23,8 @@ namespace Umbraco.Web.PropertyEditors private readonly ILogger _logger; private readonly IMediaFileSystem _mediaFileSystem; - public ImageCropperPropertyValueEditor(DataEditorAttribute attribute, ILogger logger, IMediaFileSystem mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService) - : base(dataTypeService, localizationService, Current.Services.TextService, Current.ShortStringHelper, attribute) + public ImageCropperPropertyValueEditor(DataEditorAttribute attribute, ILogger logger, IMediaFileSystem mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper) + : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); diff --git a/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs index 17539e8c5b..a484d326fe 100644 --- a/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs @@ -3,6 +3,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -24,8 +25,8 @@ namespace Umbraco.Web.PropertyEditors /// Initializes a new instance of the class. /// /// - public ListViewPropertyEditor(ILogger logger, IIOHelper iioHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public ListViewPropertyEditor(ILogger logger, IIOHelper iioHelper, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { _iioHelper = iioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs index cea594e095..5a88d4b6e7 100644 --- a/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs @@ -3,6 +3,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -23,8 +24,8 @@ namespace Umbraco.Web.PropertyEditors /// /// Initializes a new instance of the class. /// - public MarkdownPropertyEditor(ILogger logger, IIOHelper ioHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public MarkdownPropertyEditor(ILogger logger, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs index 55cfbd172e..a7eaf822b8 100644 --- a/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs @@ -47,11 +47,12 @@ namespace Umbraco.Web.PropertyEditors /// protected override IConfigurationEditor CreateConfigurationEditor() => new MediaPickerConfigurationEditor(_ioHelper); - protected override IDataValueEditor CreateValueEditor() => new MediaPickerPropertyValueEditor(_dataTypeService, _localizationService, Attribute); + protected override IDataValueEditor CreateValueEditor() => new MediaPickerPropertyValueEditor(_dataTypeService, _localizationService, ShortStringHelper, Attribute); internal class MediaPickerPropertyValueEditor : DataValueEditor, IDataValueReference { - public MediaPickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, DataEditorAttribute attribute) : base(dataTypeService,localizationService, Current.Services.TextService,Current.ShortStringHelper,attribute) + public MediaPickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService,localizationService, Current.Services.TextService, shortStringHelper,attribute) { } diff --git a/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs index 0f08686cec..4c546b1815 100644 --- a/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs @@ -2,6 +2,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -14,8 +15,8 @@ namespace Umbraco.Web.PropertyEditors Icon = Constants.Icons.MemberGroup)] public class MemberGroupPickerPropertyEditor : DataEditor { - public MemberGroupPickerPropertyEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public MemberGroupPickerPropertyEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { } } } diff --git a/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs index aba5e32183..68e95b7a2b 100644 --- a/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs @@ -2,6 +2,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -14,8 +15,8 @@ namespace Umbraco.Web.PropertyEditors Icon = Constants.Icons.Member)] public class MemberPickerPropertyEditor : DataEditor { - public MemberPickerPropertyEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public MemberPickerPropertyEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { } protected override IConfigurationEditor CreateConfigurationEditor() => new MemberPickerConfiguration(); diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs index 8e0011c6a0..f3efcb0a18 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -22,8 +23,8 @@ namespace Umbraco.Web.PropertyEditors private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; - public MultiNodeTreePickerPropertyEditor(ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, IIOHelper ioHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, Current.ShortStringHelper) + public MultiNodeTreePickerPropertyEditor(ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) { _dataTypeService = dataTypeService; _localizationService = localizationService; @@ -32,11 +33,12 @@ namespace Umbraco.Web.PropertyEditors protected override IConfigurationEditor CreateConfigurationEditor() => new MultiNodePickerConfigurationEditor(_ioHelper); - protected override IDataValueEditor CreateValueEditor() => new MultiNodeTreePickerPropertyValueEditor(_dataTypeService, _localizationService, Attribute); + protected override IDataValueEditor CreateValueEditor() => new MultiNodeTreePickerPropertyValueEditor(_dataTypeService, _localizationService, ShortStringHelper, Attribute); public class MultiNodeTreePickerPropertyValueEditor : DataValueEditor, IDataValueReference { - public MultiNodeTreePickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, DataEditorAttribute attribute): base(dataTypeService, localizationService, Current.Services.TextService, Current.ShortStringHelper, attribute) + public MultiNodeTreePickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) { } diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs index 1070584688..d4b0692ebf 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs @@ -5,10 +5,8 @@ using Umbraco.Core.IO; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Logging; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.PublishedCache; -using System.Collections.Generic; -using Umbraco.Core.Models.Editors; -using Newtonsoft.Json; namespace Umbraco.Web.PropertyEditors { @@ -28,8 +26,8 @@ namespace Umbraco.Web.PropertyEditors private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; - public MultiUrlPickerPropertyEditor(ILogger logger, IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, IDataTypeService dataTypeService, ILocalizationService localizationService, IIOHelper ioHelper) - : base(logger, dataTypeService, localizationService, Current.Services.TextService,Current.ShortStringHelper, EditorType.PropertyValue) + public MultiUrlPickerPropertyEditor(ILogger logger, IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, IDataTypeService dataTypeService, ILocalizationService localizationService, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, EditorType.PropertyValue) { _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); @@ -40,6 +38,6 @@ namespace Umbraco.Web.PropertyEditors protected override IConfigurationEditor CreateConfigurationEditor() => new MultiUrlPickerConfigurationEditor(_ioHelper); - protected override IDataValueEditor CreateValueEditor() => new MultiUrlPickerValueEditor(_entityService, _publishedSnapshotAccessor, Logger, _dataTypeService, _localizationService, Attribute); + protected override IDataValueEditor CreateValueEditor() => new MultiUrlPickerValueEditor(_entityService, _publishedSnapshotAccessor, Logger, _dataTypeService, _localizationService, ShortStringHelper, Attribute); } } diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs index 6363048b34..5564d44e4a 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerValueEditor.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Models.Editors; using Umbraco.Core.Models.Entities; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.PublishedCache; @@ -22,8 +23,8 @@ namespace Umbraco.Web.PropertyEditors private readonly ILogger _logger; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - public MultiUrlPickerValueEditor(IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, Current.Services.TextService,Current.ShortStringHelper, attribute) + public MultiUrlPickerValueEditor(IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) { _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); diff --git a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs index 9ccc8e3b39..ee5cbaebfb 100644 --- a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; using Umbraco.Core.Services; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -36,8 +37,8 @@ namespace Umbraco.Web.PropertyEditors /// /// Initializes a new instance of the class. /// - public MultipleTextStringPropertyEditor(ILogger logger, IIOHelper ioHelper, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService) - : base(logger, dataTypeService, localizationService, Current.Services.TextService,Current.ShortStringHelper) + public MultipleTextStringPropertyEditor(ILogger logger, IIOHelper ioHelper, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper) + : base(logger, dataTypeService, localizationService, Current.Services.TextService, shortStringHelper) { _ioHelper = ioHelper; _dataTypeService = dataTypeService; @@ -46,7 +47,7 @@ namespace Umbraco.Web.PropertyEditors } /// - protected override IDataValueEditor CreateValueEditor() => new MultipleTextStringPropertyValueEditor(_dataTypeService, _localizationService,Attribute, _localizedTextService); + protected override IDataValueEditor CreateValueEditor() => new MultipleTextStringPropertyValueEditor(_dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, Attribute); /// protected override IConfigurationEditor CreateConfigurationEditor() => new MultipleTextStringConfigurationEditor(_ioHelper); @@ -58,8 +59,8 @@ namespace Umbraco.Web.PropertyEditors { private readonly ILocalizedTextService _localizedTextService; - public MultipleTextStringPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, DataEditorAttribute attribute, ILocalizedTextService localizedTextService) - : base(dataTypeService, localizationService, Current.Services.TextService,Current.ShortStringHelper, attribute) + public MultipleTextStringPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) { _localizedTextService = localizedTextService; } diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs index 540ad12dcd..05f3752664 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs @@ -66,7 +66,7 @@ namespace Umbraco.Web.PropertyEditors #region Value Editor - protected override IDataValueEditor CreateValueEditor() => new NestedContentPropertyValueEditor(_dataTypeService, _localizationService, Attribute, PropertyEditors, _contentTypeService); + protected override IDataValueEditor CreateValueEditor() => new NestedContentPropertyValueEditor(_dataTypeService, _localizationService, _contentTypeService, ShortStringHelper, Attribute, PropertyEditors); internal class NestedContentPropertyValueEditor : DataValueEditor, IDataValueReference { @@ -78,8 +78,8 @@ namespace Umbraco.Web.PropertyEditors Current.Services.ContentTypeService.GetAll().ToDictionary(c => c.Alias) ); - public NestedContentPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, IContentTypeService contentTypeService) - : base(dataTypeService, localizationService, Current.Services.TextService, Current.ShortStringHelper, attribute) + public NestedContentPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IContentTypeService contentTypeService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute, PropertyEditorCollection propertyEditors) + : base(dataTypeService, localizationService, Current.Services.TextService, shortStringHelper, attribute) { _propertyEditors = propertyEditors; _dataTypeService = dataTypeService; diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs b/src/Umbraco.Web/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs index adf9eff55b..abd994541e 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs @@ -1,6 +1,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors { @@ -17,8 +18,8 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors /// /// Initializes a new instance of the class. /// - public ContentTypeParameterEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, Current.ShortStringHelper) + public ContentTypeParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", false); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs index cb104dacb2..571a1b4a1b 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs @@ -2,6 +2,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors { @@ -18,8 +19,8 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors /// /// Initializes a new instance of the class. /// - public MultipleContentPickerParameterEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public MultipleContentPickerParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiPicker", "1"); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs index 0ebb7a47e7..f13d0474d9 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs @@ -1,6 +1,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors { @@ -11,8 +12,8 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class MultipleContentTypeParameterEditor : DataEditor { - public MultipleContentTypeParameterEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, Current.ShortStringHelper) + public MultipleContentTypeParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", true); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs index 7874dae979..a60573104a 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs @@ -2,6 +2,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors { @@ -19,8 +20,8 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors /// /// Initializes a new instance of the class. /// - public MultipleMediaPickerParameterEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public MultipleMediaPickerParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { DefaultConfiguration.Add("multiPicker", "1"); } diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs index 93f515de6a..cbfcef9d85 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs @@ -1,6 +1,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors { @@ -11,8 +12,8 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class MultiplePropertyGroupParameterEditor : DataEditor { - public MultiplePropertyGroupParameterEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public MultiplePropertyGroupParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", true); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs index a8e716be61..f4cf788dd1 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs @@ -1,6 +1,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors { @@ -11,8 +12,8 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class MultiplePropertyTypeParameterEditor : DataEditor { - public MultiplePropertyTypeParameterEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public MultiplePropertyTypeParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", "1"); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs b/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs index 5efff95be1..b0a926586d 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs @@ -1,6 +1,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors { @@ -11,8 +12,8 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class PropertyGroupParameterEditor : DataEditor { - public PropertyGroupParameterEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public PropertyGroupParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", "0"); diff --git a/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs b/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs index fc8a5474f7..4e49741da3 100644 --- a/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs @@ -1,6 +1,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors.ParameterEditors { @@ -11,8 +12,8 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors "entitypicker")] public class PropertyTypeParameterEditor : DataEditor { - public PropertyTypeParameterEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, Current.ShortStringHelper) + public PropertyTypeParameterEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) { // configure DefaultConfiguration.Add("multiple", "0"); diff --git a/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs index 61af4b5248..c010d21cd2 100644 --- a/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs @@ -4,6 +4,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -25,8 +26,8 @@ namespace Umbraco.Web.PropertyEditors /// /// The constructor will setup the property editor based on the attribute if one is found /// - public RadioButtonsPropertyEditor(ILogger logger, ILocalizedTextService textService, IIOHelper ioHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, Current.ShortStringHelper) + public RadioButtonsPropertyEditor(ILogger logger, ILocalizedTextService textService, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService,Current.Services.TextService, shortStringHelper) { _textService = textService; _ioHelper = ioHelper; diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 29d97d88c8..7895ad6372 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -28,16 +28,17 @@ namespace Umbraco.Web.PropertyEditors Icon = "icon-browser-window")] public class RichTextPropertyEditor : DataEditor { - private IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly HtmlImageSourceParser _imageSourceParser; private readonly HtmlLocalLinkParser _localLinkParser; private readonly RichTextEditorPastedImages _pastedImages; private readonly IDataTypeService _dataTypeService; private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; - private ILogger _logger; + private readonly ILogger _logger; private readonly IMediaService _mediaService; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private readonly IShortStringHelper _shortStringHelper; /// /// The constructor will setup the property editor based on the attribute if one is found @@ -55,7 +56,7 @@ namespace Umbraco.Web.PropertyEditors IShortStringHelper shortStringHelper, IIOHelper ioHelper, ILocalizedTextService localizedTextService) - : base(logger, dataTypeService, localizationService, localizedTextService,shortStringHelper) + : base(logger, dataTypeService, localizationService, localizedTextService, shortStringHelper) { _umbracoContextAccessor = umbracoContextAccessor; _imageSourceParser = imageSourceParser; @@ -67,13 +68,14 @@ namespace Umbraco.Web.PropertyEditors _logger = logger; _mediaService = mediaService; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _shortStringHelper = shortStringHelper; } /// /// Create a custom value editor /// /// - protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger, _dataTypeService, _localizationService, _imageSourceParser, _localLinkParser, _pastedImages); + protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger, _dataTypeService, _localizationService, _shortStringHelper, _imageSourceParser, _localLinkParser, _pastedImages); protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(_ioHelper); @@ -89,8 +91,8 @@ namespace Umbraco.Web.PropertyEditors private readonly HtmlLocalLinkParser _localLinkParser; private readonly RichTextEditorPastedImages _pastedImages; - public RichTextPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, HtmlImageSourceParser imageSourceParser, HtmlLocalLinkParser localLinkParser, RichTextEditorPastedImages pastedImages) - : base(dataTypeService, localizationService,Current.Services.TextService, Current.ShortStringHelper, attribute) + public RichTextPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, HtmlImageSourceParser imageSourceParser, HtmlLocalLinkParser localLinkParser, RichTextEditorPastedImages pastedImages) + : base(dataTypeService, localizationService,Current.Services.TextService, shortStringHelper, attribute) { _umbracoContextAccessor = umbracoContextAccessor; _imageSourceParser = imageSourceParser; diff --git a/src/Umbraco.Web/PropertyEditors/SliderPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/SliderPropertyEditor.cs index 3e689b49b6..c2be432247 100644 --- a/src/Umbraco.Web/PropertyEditors/SliderPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/SliderPropertyEditor.cs @@ -3,6 +3,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -21,8 +22,8 @@ namespace Umbraco.Web.PropertyEditors /// /// Initializes a new instance of the class. /// - public SliderPropertyEditor(ILogger logger, IIOHelper ioHelper) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public SliderPropertyEditor(ILogger logger, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs index 74e6eb07af..78c0b4f854 100644 --- a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -28,22 +29,22 @@ namespace Umbraco.Web.PropertyEditors private readonly IIOHelper _ioHelper; private readonly ILocalizedTextService _localizedTextService; - public TagsPropertyEditor(ManifestValueValidatorCollection validators, ILogger logger, IIOHelper ioHelper, ILocalizedTextService localizedTextService) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public TagsPropertyEditor(ManifestValueValidatorCollection validators, ILogger logger, IIOHelper ioHelper, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { _validators = validators; _ioHelper = ioHelper; _localizedTextService = localizedTextService; } - protected override IDataValueEditor CreateValueEditor() => new TagPropertyValueEditor(Current.Services.DataTypeService, Current.Services.LocalizationService, Attribute); + protected override IDataValueEditor CreateValueEditor() => new TagPropertyValueEditor(Current.Services.DataTypeService, Current.Services.LocalizationService, ShortStringHelper, Attribute); protected override IConfigurationEditor CreateConfigurationEditor() => new TagConfigurationEditor(_validators, _ioHelper, _localizedTextService); internal class TagPropertyValueEditor : DataValueEditor { - public TagPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, DataEditorAttribute attribute) - : base(dataTypeService, localizationService,Current.Services.TextService, Current.ShortStringHelper, attribute) + public TagPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute) + : base(dataTypeService, localizationService,Current.Services.TextService, shortStringHelper, attribute) { } /// diff --git a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs index e66dec8080..cb95affa39 100644 --- a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs @@ -2,6 +2,7 @@ using Umbraco.Web.Composing; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Web.PropertyEditors { @@ -14,8 +15,8 @@ namespace Umbraco.Web.PropertyEditors Icon = Constants.Icons.User)] public class UserPickerPropertyEditor : DataEditor { - public UserPickerPropertyEditor(ILogger logger) - : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService,Current.ShortStringHelper) + public UserPickerPropertyEditor(ILogger logger, IShortStringHelper shortStringHelper) + : base(logger, Current.Services.DataTypeService, Current.Services.LocalizationService, Current.Services.TextService, shortStringHelper) { } protected override IConfigurationEditor CreateConfigurationEditor() => new UserPickerConfiguration(); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 03f931c18a..19be13b762 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -238,11 +238,24 @@ namespace Umbraco.Web.PublishedCache.NuCache var lockInfo = new WriteLockInfo(); try { - Lock(lockInfo); + try + { + // Trying to lock could throw exceptions so always make sure to clean up. + Lock(lockInfo); + } + finally + { + try + { + _localDb?.Dispose(); + } + catch { /* TBD: May already be throwing so don't throw again */} + finally + { + _localDb = null; + } + } - if (_localDb == null) return; - _localDb.Dispose(); - _localDb = null; } finally { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs index 1edb20633f..4515e235ad 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs @@ -207,7 +207,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource VersionDate = dto.EditVersionDate, WriterId = dto.EditWriterId, Properties = nested.PropertyData, - CultureInfos = nested.CultureData + CultureInfos = nested.CultureData, + UrlSegment = nested.UrlSegment }; } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 1afb62b8b6..e07e94e3aa 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -33,7 +32,6 @@ using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.PublishedCache.NuCache { - internal class PublishedSnapshotService : PublishedSnapshotServiceBase { private readonly ServiceContext _serviceContext; @@ -51,6 +49,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly ITypeFinder _typeFinder; private readonly IHostingEnvironment _hostingEnvironment; + private readonly IShortStringHelper _shortStringHelper; // volatile because we read it with no lock private volatile bool _isReady; @@ -62,7 +61,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private BPlusTree _localContentDb; private BPlusTree _localMediaDb; - private bool _localDbExists; + private bool _localContentDbExists; + private bool _localMediaDbExists; // define constant - determines whether to use cache when previewing // to store eg routes, property converted values, anything - caching @@ -84,7 +84,8 @@ namespace Umbraco.Web.PublishedCache.NuCache IPublishedModelFactory publishedModelFactory, UrlSegmentProviderCollection urlSegmentProviders, ITypeFinder typeFinder, - IHostingEnvironment hostingEnvironment) + IHostingEnvironment hostingEnvironment, + IShortStringHelper shortStringHelper) : base(publishedSnapshotAccessor, variationContextAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) @@ -103,6 +104,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _urlSegmentProviders = urlSegmentProviders; _typeFinder = typeFinder; _hostingEnvironment = hostingEnvironment; + _shortStringHelper = shortStringHelper; // we need an Xml serializer here so that the member cache can support XPath, // for members this is done by navigating the serialized-to-xml member @@ -136,9 +138,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to // figure out whether it can read the databases or it should populate them from sql - _logger.Info("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localDbExists); - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, Current.PublishedModelFactory, _localContentDb); - _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localDbExists); + _logger.Info("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); + _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, Current.PublishedModelFactory, _localContentDb); + _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, Current.PublishedModelFactory, _localMediaDb); } else @@ -179,14 +181,15 @@ namespace Umbraco.Web.PublishedCache.NuCache var path = GetLocalFilesPath(); var localContentDbPath = Path.Combine(path, "NuCache.Content.db"); var localMediaDbPath = Path.Combine(path, "NuCache.Media.db"); - var localContentDbExists = File.Exists(localContentDbPath); - var localMediaDbExists = File.Exists(localMediaDbPath); - _localDbExists = localContentDbExists && localMediaDbExists; - // if both local databases exist then GetTree will open them, else new databases will be created - _localContentDb = BTree.GetTree(localContentDbPath, _localDbExists); - _localMediaDb = BTree.GetTree(localMediaDbPath, _localDbExists); - _logger.Info("Registered with MainDom, localContentDbExists? {LocalContentDbExists}, localMediaDbExists? {LocalMediaDbExists}", localContentDbExists, localMediaDbExists); + _localContentDbExists = File.Exists(localContentDbPath); + _localMediaDbExists = File.Exists(localMediaDbPath); + + // if both local databases exist then GetTree will open them, else new databases will be created + _localContentDb = BTree.GetTree(localContentDbPath, _localContentDbExists); + _localMediaDb = BTree.GetTree(localMediaDbPath, _localMediaDbExists); + + _logger.Info("Registered with MainDom, localContentDbExists? {LocalContentDbExists}, localMediaDbExists? {LocalMediaDbExists}", _localContentDbExists, _localMediaDbExists); } /// @@ -219,11 +222,15 @@ namespace Umbraco.Web.PublishedCache.NuCache try { - if (_localDbExists) + if (_localContentDbExists) { okContent = LockAndLoadContent(scope => LoadContentFromLocalDbLocked(true)); if (!okContent) _logger.Warn("Loading content from local db raised warnings, will reload from database."); + } + + if (_localMediaDbExists) + { okMedia = LockAndLoadMedia(scope => LoadMediaFromLocalDbLocked(true)); if (!okMedia) _logger.Warn("Loading media from local db raised warnings, will reload from database."); @@ -1401,7 +1408,7 @@ namespace Umbraco.Web.PublishedCache.NuCache cultureData[cultureInfo.Culture] = new CultureVariation { Name = cultureInfo.Name, - UrlSegment = content.GetUrlSegment(Current.ShortStringHelper, _urlSegmentProviders, cultureInfo.Culture), + UrlSegment = content.GetUrlSegment(_shortStringHelper, _urlSegmentProviders, cultureInfo.Culture), Date = content.GetUpdateDate(cultureInfo.Culture) ?? DateTime.MinValue, IsDraft = cultureIsDraft }; @@ -1413,7 +1420,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { PropertyData = propertyData, CultureData = cultureData, - UrlSegment = content.GetUrlSegment(Current.ShortStringHelper, _urlSegmentProviders) + UrlSegment = content.GetUrlSegment(_shortStringHelper, _urlSegmentProviders) }; var dto = new ContentNuDto diff --git a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs index 02dc4ebf29..89abde0576 100644 --- a/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultMediaUrlProvider.cs @@ -1,7 +1,7 @@ using System; -using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.Routing { @@ -10,13 +10,26 @@ namespace Umbraco.Web.Routing /// public class DefaultMediaUrlProvider : IMediaUrlProvider { + private readonly PropertyEditorCollection _propertyEditors; + + public DefaultMediaUrlProvider(PropertyEditorCollection propertyEditors) + { + _propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); + } + + [Obsolete("Use the constructor with all parameters instead")] + public DefaultMediaUrlProvider() : this(Current.PropertyEditors) + { + } + /// public virtual UrlInfo GetMediaUrl(UmbracoContext umbracoContext, IPublishedContent content, - string propertyAlias, - UrlMode mode, string culture, Uri current) + string propertyAlias, UrlMode mode, string culture, Uri current) { var prop = content.GetProperty(propertyAlias); - var value = prop?.GetValue(culture); + + // get the raw source value since this is what is used by IDataEditorWithMediaPath for processing + var value = prop?.GetSourceValue(culture); if (value == null) { return null; @@ -25,15 +38,10 @@ namespace Umbraco.Web.Routing var propType = prop.PropertyType; string path = null; - switch (propType.EditorAlias) + if (_propertyEditors.TryGet(propType.EditorAlias, out var editor) + && editor is IDataEditorWithMediaPath dataEditor) { - case Constants.PropertyEditors.Aliases.UploadField: - path = value.ToString(); - break; - case Constants.PropertyEditors.Aliases.ImageCropper: - //get the url from the json format - path = value is ImageCropperValue stronglyTyped ? stronglyTyped.Src : value.ToString(); - break; + path = dataEditor.GetMediaPath(value); } var url = AssembleUrl(path, current, mode); diff --git a/src/Umbraco.Web/Runtime/WebInitialComponent.cs b/src/Umbraco.Web/Runtime/WebInitialComponent.cs index 84315211b0..0cf509400c 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComponent.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComponent.cs @@ -15,6 +15,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; +using Umbraco.Core.Strings; using Umbraco.Core.IO; using Umbraco.Core.Strings; using Umbraco.Web.Install; @@ -177,7 +178,7 @@ namespace Umbraco.Web.Runtime umbracoPath + "/RenderMvc/{action}/{id}", new { controller = "RenderMvc", action = "Index", id = UrlParameter.Optional } ); - defaultRoute.RouteHandler = new RenderRouteHandler(umbracoContextAccessor, ControllerBuilder.Current.GetControllerFactory(),shortStringHelper); + defaultRoute.RouteHandler = new RenderRouteHandler(umbracoContextAccessor, ControllerBuilder.Current.GetControllerFactory(), shortStringHelper); // register install routes RouteTable.Routes.RegisterArea(); diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs index fc66b6502e..9de6c94638 100644 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -1,4 +1,5 @@ -using System.Web; +using System; +using System.Web; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; @@ -37,8 +38,9 @@ namespace Umbraco.Web.Runtime IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo, IDbProviderFactoryCreator dbProviderFactoryCreator, - IBulkSqlInsertProvider bulkSqlInsertProvider): - base(configs, umbracoVersion, ioHelper, logger, profiler ,new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, bulkSqlInsertProvider) + IBulkSqlInsertProvider bulkSqlInsertProvider, + IMainDom mainDom): + base(configs, umbracoVersion, ioHelper, logger, profiler ,new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, bulkSqlInsertProvider, mainDom) { _umbracoApplication = umbracoApplication; diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index 47e97593f0..c0475b1f79 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -199,7 +199,7 @@ namespace Umbraco.Web.Scheduling { lock (_locker) { - var task = _runningTask ?? Task.FromResult(0); + var task = _runningTask ?? Task.CompletedTask; return new ThreadingTaskImmutable(task); } } @@ -211,8 +211,9 @@ namespace Umbraco.Web.Scheduling /// An awaitable object. /// /// Used to wait until the runner has terminated. - /// This is for unit tests and should not be used otherwise. In most cases when the runner - /// has terminated, the application domain is going down and it is not the right time to do things. + /// + /// The only time the runner will be terminated is by the Hosting Environment when the application is being shutdown. + /// /// internal ThreadingTaskImmutable TerminatedAwaitable { @@ -338,29 +339,37 @@ namespace Umbraco.Web.Scheduling if (_isRunning == false) return; // done already } + var hasTasks = TaskCount > 0; + + if (!force && hasTasks) + _logger.Info("{LogPrefix} Waiting for tasks to complete", _logPrefix); + // complete the queue // will stop waiting on the queue or on a latch _tasks.Complete(); if (force) { - // we must bring everything down, now - Thread.Sleep(100); // give time to Complete() + // we must bring everything down, now lock (_locker) { // was Complete() enough? - if (_isRunning == false) return; + // if _tasks.Complete() ended up triggering code to stop the runner and reset + // the _isRunning flag, then there's no need to initiate a cancel on the cancelation token. + if (_isRunning == false) + return; } + // try to cancel running async tasks (cannot do much about sync tasks) // break latched tasks // stop processing the queue - _shutdownTokenSource.Cancel(false); // false is the default - _shutdownTokenSource.Dispose(); + _shutdownTokenSource?.Cancel(false); // false is the default + _shutdownTokenSource?.Dispose(); _shutdownTokenSource = null; } // tasks in the queue will be executed... - if (wait == false) return; + if (!wait) return; _runningTask?.Wait(CancellationToken.None); // wait for whatever is running to end... } @@ -428,7 +437,7 @@ namespace Umbraco.Web.Scheduling lock (_locker) { // deal with race condition - if (_shutdownToken.IsCancellationRequested == false && _tasks.Count > 0) continue; + if (_shutdownToken.IsCancellationRequested == false && TaskCount > 0) continue; // if we really have nothing to do, stop _logger.Debug("{LogPrefix} Stopping", _logPrefix); @@ -453,7 +462,7 @@ namespace Umbraco.Web.Scheduling // if KeepAlive is false then don't block, exit if there is // no task in the buffer - yes, there is a race condition, which // we'll take care of - if (_options.KeepAlive == false && _tasks.Count == 0) + if (_options.KeepAlive == false && TaskCount == 0) return null; try @@ -503,15 +512,19 @@ namespace Umbraco.Web.Scheduling // returns the task that completed // - latched.Latch completes when the latch releases // - _tasks.Completion completes when the runner completes - // - tokenTaskSource.Task completes when this task, or the whole runner, is cancelled + // - tokenTaskSource.Task completes when this task, or the whole runner is cancelled var task = await Task.WhenAny(latched.Latch, _tasks.Completion, tokenTaskSource.Task); // ok to run now if (task == latched.Latch) return bgTask; + // we are shutting down if the _tasks.Complete(); was called or the shutdown token was cancelled + var isShuttingDown = _shutdownToken.IsCancellationRequested || task == _tasks.Completion; + // if shutting down, return the task only if it runs on shutdown - if (_shutdownToken.IsCancellationRequested == false && latched.RunsOnShutdown) return bgTask; + if (isShuttingDown && latched.RunsOnShutdown) + return bgTask; // else, either it does not run on shutdown or it's been cancelled, dispose latched.Dispose(); @@ -578,17 +591,18 @@ namespace Umbraco.Web.Scheduling // triggers when the hosting environment requests that the runner terminates internal event TypedEventHandler, EventArgs> Terminating; - // triggers when the runner has terminated (no task can be added, no task is running) + // triggers when the hosting environment has terminated (no task can be added, no task is running) internal event TypedEventHandler, EventArgs> Terminated; private void OnEvent(TypedEventHandler, EventArgs> handler, string name) { - if (handler == null) return; OnEvent(handler, name, EventArgs.Empty); } private void OnEvent(TypedEventHandler, TArgs> handler, string name, TArgs e) { + _logger.Debug("{LogPrefix} OnEvent {EventName}", _logPrefix, name); + if (handler == null) return; try @@ -664,17 +678,16 @@ namespace Umbraco.Web.Scheduling #endregion + #region IRegisteredObject.Stop + /// - /// Requests a registered object to un-register. + /// Used by IRegisteredObject.Stop and shutdown on threadpool threads to not block shutdown times. /// - /// true to indicate the registered object should un-register from the hosting - /// environment before returning; otherwise, false. - /// - /// "When the application manager needs to stop a registered object, it will call the Stop method." - /// The application manager will call the Stop method to ask a registered object to un-register. During - /// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method. - /// - public void Stop(bool immediate) + /// + /// + /// An awaitable Task that is used to handle the shutdown. + /// + internal Task StopInternal(bool immediate) { // the first time the hosting environment requests that the runner terminates, // raise the Terminating event - that could be used to prevent any process that @@ -693,33 +706,90 @@ namespace Umbraco.Web.Scheduling if (onTerminating) OnEvent(Terminating, "Terminating"); - if (immediate == false) + // Run the Stop commands on another thread since IRegisteredObject.Stop calls are called sequentially + // with a single aspnet thread during shutdown and we don't want to delay other calls to IRegisteredObject.Stop. + if (!immediate) { - // The Stop method is first called with the immediate parameter set to false. The object can either complete - // processing, call the UnregisterObject method, and then return or it can return immediately and complete - // processing asynchronously before calling the UnregisterObject method. + return Task.Run(StopInitial, CancellationToken.None); + } + else + { + lock (_locker) + { + if (_terminated) return Task.CompletedTask; + return Task.Run(StopImmediate, CancellationToken.None); + } + } + } - _logger.Info("{LogPrefix} Waiting for tasks to complete", _logPrefix); + /// + /// Requests a registered object to un-register. + /// + /// true to indicate the registered object should un-register from the hosting + /// environment before returning; otherwise, false. + /// + /// "When the application manager needs to stop a registered object, it will call the Stop method." + /// The application manager will call the Stop method to ask a registered object to un-register. During + /// processing of the Stop method, the registered object must call the HostingEnvironment.UnregisterObject method. + /// + public void Stop(bool immediate) => StopInternal(immediate); + + /// + /// Called when immediate == false for IRegisteredObject.Stop(bool immediate) + /// + /// + /// Called on a threadpool thread + /// + private void StopInitial() + { + // immediate == false when the app is trying to wind down, immediate == true will be called either: + // after a call with immediate == false or if the app is not trying to wind down and needs to immediately stop. + // So Stop may be called twice or sometimes only once. + + try + { Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait - + } + finally + { // raise the completed event only after the running threading task has completed lock (_locker) { if (_runningTask != null) - _runningTask.ContinueWith(_ => Terminate(false)); + _runningTask.ContinueWith(_ => StopImmediate()); else - Terminate(false); + StopImmediate(); } } - else - { - // If the registered object does not complete processing before the application manager's time-out - // period expires, the Stop method is called again with the immediate parameter set to true. When the - // immediate parameter is true, the registered object must call the UnregisterObject method before returning; - // otherwise, its registration will be removed by the application manager. - _logger.Info("{LogPrefix} Canceling tasks", _logPrefix); + // If the shutdown token was not canceled in the Shutdown call above, it means there was still tasks + // being processed, in which case we'll give it a couple seconds + if (!_shutdownToken.IsCancellationRequested) + { + // If we are called with immediate == false, wind down above and then shutdown within 2 seconds, + // we want to shut down the app as quick as possible, if we wait until immediate == true, this can + // take a very long time since immediate will only be true when a new request is received on the new + // appdomain (or another iis timeout occurs ... which can take some time). + Thread.Sleep(2000); //we are already on a threadpool thread + StopImmediate(); + } + } + + /// + /// Called when immediate == true for IRegisteredObject.Stop(bool immediate) + /// + /// + /// Called on a threadpool thread + /// + private void StopImmediate() + { + _logger.Info("{LogPrefix} Canceling tasks", _logPrefix); + try + { Shutdown(true, true); // cancel all tasks, wait for the current one to end + } + finally + { Terminate(true); } } @@ -732,7 +802,13 @@ namespace Umbraco.Web.Scheduling // raise the Terminated event // complete the awaitable completion source, if any - HostingEnvironment.UnregisterObject(this); + if (immediate) + { + //only unregister when it's the final call, else we won't be notified of the final call + HostingEnvironment.UnregisterObject(this); + } + + if (_terminated) return; // already taken care of TaskCompletionSource terminatedSource; lock (_locker) @@ -747,7 +823,9 @@ namespace Umbraco.Web.Scheduling OnEvent(Terminated, "Terminated"); - terminatedSource.SetResult(0); + terminatedSource.TrySetResult(0); } + + #endregion } } diff --git a/src/Umbraco.Web/Scheduling/KeepAlive.cs b/src/Umbraco.Web/Scheduling/KeepAlive.cs index c07430df04..9a22c59566 100644 --- a/src/Umbraco.Web/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Web/Scheduling/KeepAlive.cs @@ -3,6 +3,8 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Sync; @@ -11,14 +13,16 @@ namespace Umbraco.Web.Scheduling internal class KeepAlive : RecurringTaskBase { private readonly IRuntimeState _runtime; + private readonly IKeepAliveSection _keepAliveSection; private readonly IProfilingLogger _logger; private static HttpClient _httpClient; public KeepAlive(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IProfilingLogger logger) + IRuntimeState runtime, IKeepAliveSection keepAliveSection, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; + _keepAliveSection = keepAliveSection; _logger = logger; if (_httpClient == null) _httpClient = new HttpClient(); @@ -46,25 +50,27 @@ namespace Umbraco.Web.Scheduling using (_logger.DebugDuration("Keep alive executing", "Keep alive complete")) { - string umbracoAppUrl = null; - + var keepAlivePingUrl = _keepAliveSection.KeepAlivePingUrl; try { - umbracoAppUrl = _runtime.ApplicationUrl.ToString(); - if (umbracoAppUrl.IsNullOrWhiteSpace()) + if (keepAlivePingUrl.Contains("{umbracoApplicationUrl}")) { - _logger.Warn("No url for service (yet), skip."); - return true; // repeat + var umbracoAppUrl = _runtime.ApplicationUrl.ToString(); + if (umbracoAppUrl.IsNullOrWhiteSpace()) + { + _logger.Warn("No umbracoApplicationUrl for service (yet), skip."); + return true; // repeat + } + + keepAlivePingUrl = keepAlivePingUrl.Replace("{umbracoApplicationUrl}", umbracoAppUrl.TrimEnd('/')); } - var url = umbracoAppUrl.TrimEnd('/') + "/api/keepalive/ping"; - - var request = new HttpRequestMessage(HttpMethod.Get, url); + var request = new HttpRequestMessage(HttpMethod.Get, keepAlivePingUrl); var result = await _httpClient.SendAsync(request, token); } catch (Exception ex) { - _logger.Error(ex, "Keep alive failed (at '{UmbracoAppUrl}').", umbracoAppUrl); + _logger.Error(ex, "Keep alive failed (at '{keepAlivePingUrl}').", keepAlivePingUrl); } } diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs index 4ad822b871..f719a82e4e 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs @@ -98,7 +98,11 @@ namespace Umbraco.Web.Scheduling var tasks = new List(); - tasks.Add(RegisterKeepAlive()); + if (settings.KeepAlive.DisableKeepAliveTask == false) + { + tasks.Add(RegisterKeepAlive(settings.KeepAlive)); + } + tasks.Add(RegisterScheduledPublishing()); tasks.Add(RegisterLogScrubber(settings)); tasks.Add(RegisterTempFileCleanup()); @@ -111,11 +115,11 @@ namespace Umbraco.Web.Scheduling }); } - private IBackgroundTask RegisterKeepAlive() + private IBackgroundTask RegisterKeepAlive(IKeepAliveSection keepAliveSection) { // ping/keepalive // on all servers - var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, _logger); + var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, keepAliveSection, _logger); _keepAliveRunner.TryAdd(task); return task; } diff --git a/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs b/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs index 0691c28d74..dd02119a66 100644 --- a/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs +++ b/src/Umbraco.Web/Security/BackOfficeCookieAuthenticationProvider.cs @@ -40,6 +40,9 @@ namespace Umbraco.Web.Security : Guid.NewGuid(); backOfficeIdentity.SessionId = session.ToString(); + + //since it is a cookie-based authentication add that claim + backOfficeIdentity.AddClaim(new Claim(ClaimTypes.CookiePath, "/", ClaimValueTypes.String, UmbracoBackOfficeIdentity.Issuer, UmbracoBackOfficeIdentity.Issuer, backOfficeIdentity)); } base.ResponseSignIn(context); diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index fad2f8d4ae..ddfa654340 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Cache; using Umbraco.Web.Composing; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Web.Editors; using Umbraco.Web.Security.Providers; using System.ComponentModel.DataAnnotations; @@ -31,6 +32,7 @@ namespace Umbraco.Web.Security private readonly IPublicAccessService _publicAccessService; private readonly AppCaches _appCaches; private readonly ILogger _logger; + private readonly IShortStringHelper _shortStringHelper; #region Constructors @@ -44,7 +46,8 @@ namespace Umbraco.Web.Security IMemberTypeService memberTypeService, IPublicAccessService publicAccessService, AppCaches appCaches, - ILogger logger + ILogger logger, + IShortStringHelper shortStringHelper ) { HttpContext = httpContext; @@ -54,6 +57,7 @@ namespace Umbraco.Web.Security _publicAccessService = publicAccessService; _appCaches = appCaches; _logger = logger; + _shortStringHelper = shortStringHelper; _membershipProvider = membershipProvider ?? throw new ArgumentNullException(nameof(membershipProvider)); _roleProvider = roleProvider ?? throw new ArgumentNullException(nameof(roleProvider)); @@ -396,7 +400,7 @@ namespace Umbraco.Web.Security var memberType = _memberTypeService.Get(member.ContentTypeId); - var builtIns = ConventionsHelper.GetStandardPropertyTypeStubs(Current.ShortStringHelper).Select(x => x.Key).ToArray(); + var builtIns = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper).Select(x => x.Key).ToArray(); model.MemberProperties = GetMemberPropertiesViewModel(memberType, builtIns, member).ToList(); @@ -416,7 +420,7 @@ namespace Umbraco.Web.Security if (memberType == null) throw new InvalidOperationException("Could not find a member type with alias " + memberTypeAlias); - var builtIns = ConventionsHelper.GetStandardPropertyTypeStubs(Current.ShortStringHelper).Select(x => x.Key).ToArray(); + var builtIns = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper).Select(x => x.Key).ToArray(); var model = RegisterModel.CreateModel(); model.MemberTypeAlias = memberTypeAlias; model.MemberProperties = GetMemberPropertiesViewModel(memberType, builtIns).ToList(); diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index ffcb466727..b13719f6e9 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -1,20 +1,16 @@ using System; -using System.Collections; using System.Globalization; using System.IO; using System.Linq; using System.Web.Mvc; using System.Web.Routing; -using Umbraco.Core; -using Umbraco.Web.Models; -using Umbraco.Web.Mvc; -using Umbraco.Web.Routing; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Web.Macros; -using Current = Umbraco.Web.Composing.Current; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; +using Umbraco.Core.Strings; namespace Umbraco.Web.Templates { @@ -40,7 +36,7 @@ namespace Umbraco.Web.Templates _fileService = fileService ?? throw new ArgumentNullException(nameof(fileService)); _languageService = textService ?? throw new ArgumentNullException(nameof(textService)); _webRoutingSection = webRoutingSection ?? throw new ArgumentNullException(nameof(webRoutingSection)); - _shortStringHelper = shortStringHelper; + _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); } public void Render(int pageId, int? altTemplateId, StringWriter writer) diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index faa6f56d2b..9b43f54130 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -454,8 +454,8 @@ namespace Umbraco.Web.Trees internal IEnumerable GetAllowedUserMenuItemsForNode(IUmbracoEntity dd) { - var permission = Services.UserService.GetPermissions(Security.CurrentUser, dd.Path); - return Current.Actions.FromEntityPermission(permission).Select(x => new MenuItem(x)); + var permissionsForPath = Services.UserService.GetPermissionsForPath(Security.CurrentUser, dd.Path).GetAllPermissions(); + return Current.Actions.GetByLetters(permissionsForPath).Select(x => new MenuItem(x)); } /// diff --git a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs index bd80f63897..2046baf2d3 100644 --- a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs @@ -2,7 +2,10 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; +using Umbraco.Web.Search; using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Trees @@ -10,8 +13,15 @@ namespace Umbraco.Web.Trees [CoreTree] [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] [Tree(Constants.Applications.Settings, Constants.Trees.MemberTypes, SortOrder = 2, TreeGroup = Constants.Trees.Groups.Settings)] - public class MemberTypeTreeController : MemberTypeAndGroupTreeControllerBase + public class MemberTypeTreeController : MemberTypeAndGroupTreeControllerBase, ISearchableTree { + private readonly UmbracoTreeSearcher _treeSearcher; + + public MemberTypeTreeController(UmbracoTreeSearcher treeSearcher) + { + _treeSearcher = treeSearcher; + } + protected override TreeNode CreateRootNode(FormDataCollection queryStrings) { var root = base.CreateRootNode(queryStrings); @@ -25,5 +35,9 @@ namespace Umbraco.Web.Trees .OrderBy(x => x.Name) .Select(dt => CreateTreeNode(dt, Constants.ObjectTypes.MemberType, id, queryStrings, Constants.Icons.MemberType, false)); } + + public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + => _treeSearcher.EntitySearch(UmbracoObjectTypes.MemberType, query, pageSize, pageIndex, out totalFound, searchFrom); + } } diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 255c2a80c1..3943e3d17f 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -22,8 +22,9 @@ namespace Umbraco.Web var dbProviderFactoryCreator = new UmbracoDbProviderFactoryCreator(connectionStringConfig.ProviderName); var bulkSqlInsertProvider = connectionStringConfig.ProviderName == Constants.DbProviderNames.SqlCe ? (IBulkSqlInsertProvider) new SqlCeBulkSqlInsertProvider() : new SqlServerBulkSqlInsertProvider(); + var mainDom = new MainDom(logger, hostingEnvironment); - return new WebRuntime(this, configs, umbracoVersion, ioHelper, logger, profiler, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, bulkSqlInsertProvider); + return new WebRuntime(this, configs, umbracoVersion, ioHelper, logger, profiler, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, bulkSqlInsertProvider, mainDom); } /// diff --git a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs index e5aade0169..a69d714091 100644 --- a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs +++ b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web protected IUmbracoContextAccessor UmbracoContextAccessor => Current.UmbracoContextAccessor; protected IGlobalSettings GlobalSettings => Current.Configs.Global(); protected IUmbracoSettingsSection UmbracoSettings => Current.Configs.Settings(); - protected IUserPasswordConfiguration UserPasswordConfig => Current.Configs.UserPasswordConfig(); + protected IUserPasswordConfiguration UserPasswordConfig => Current.Configs.UserPasswordConfiguration(); protected IRuntimeState RuntimeState => Current.RuntimeState; protected ServiceContext Services => Current.Services; protected UmbracoMapper Mapper => Current.Mapper; diff --git a/src/Umbraco.Web/WebApi/Filters/ValidateAngularAntiForgeryTokenAttribute.cs b/src/Umbraco.Web/WebApi/Filters/ValidateAngularAntiForgeryTokenAttribute.cs index 0abdfb5d2f..f147a2a4cb 100644 --- a/src/Umbraco.Web/WebApi/Filters/ValidateAngularAntiForgeryTokenAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/ValidateAngularAntiForgeryTokenAttribute.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.WebApi.Filters var userIdentity = ((ApiController) actionContext.ControllerContext.Controller).User.Identity as ClaimsIdentity; if (userIdentity != null) { - //if there is not CookiePath claim, then exist + //if there is not CookiePath claim, then exit if (userIdentity.HasClaim(x => x.Type == ClaimTypes.CookiePath) == false) { base.OnActionExecuting(actionContext);